Feat: rework Surfshark servers data (#575)
- Feat: `MULTIHOP_ONLY` variable - Feat: `COUNTRY` variable - Feat: `CITY` variable - Feat: `REGION` variable, with retro-compatibility - Feat: merge servers from API, zip and hardcoded hostnames - Fix: remove outdated and duplicate servers - Maint: faster update with fully parallel DNS resolutions
This commit is contained in:
@@ -40,6 +40,9 @@ type ServerSelection struct { //nolint:maligned
|
||||
// VPNUnlimited
|
||||
StreamOnly bool `json:"stream_only"`
|
||||
|
||||
// Surfshark
|
||||
MultiHopOnly bool `json:"multihop_only"`
|
||||
|
||||
OpenVPN OpenVPNSelection `json:"openvpn"`
|
||||
Wireguard WireguardSelection `json:"wireguard"`
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func (settings *Provider) readSurfshark(r reader) (err error) {
|
||||
@@ -14,9 +15,14 @@ func (settings *Provider) readSurfshark(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
|
||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.SurfsharkCountryChoices())
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable REGION: %w", err)
|
||||
return fmt.Errorf("environment variable COUNTRY: %w", err)
|
||||
}
|
||||
|
||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.SurfsharkCityChoices())
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable CITY: %w", err)
|
||||
}
|
||||
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices())
|
||||
@@ -24,5 +30,45 @@ func (settings *Provider) readSurfshark(r reader) (err error) {
|
||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||
}
|
||||
|
||||
regionChoices := constants.SurfsharkRegionChoices()
|
||||
regionChoices = append(regionChoices, constants.SurfsharkRetroLocChoices()...)
|
||||
regions, err := r.env.CSVInside("REGION", regionChoices)
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable REGION: %w", err)
|
||||
}
|
||||
|
||||
// Retro compatibility
|
||||
// TODO remove in v4
|
||||
for i, region := range regions {
|
||||
locationData, isRetro :=
|
||||
surfsharkConvertRetroLoc(region)
|
||||
if !isRetro {
|
||||
continue
|
||||
}
|
||||
|
||||
regions[i] = locationData.Region
|
||||
settings.ServerSelection.Countries = append(settings.ServerSelection.Countries, locationData.Country)
|
||||
if locationData.City != "" { // city is empty for some servers
|
||||
settings.ServerSelection.Cities = append(settings.ServerSelection.Cities, locationData.City)
|
||||
}
|
||||
settings.ServerSelection.Hostnames = append(settings.ServerSelection.Hostnames, locationData.Hostname)
|
||||
}
|
||||
|
||||
settings.ServerSelection.MultiHopOnly, err = r.env.YesNo("MULTIHOP_ONLY")
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err)
|
||||
}
|
||||
|
||||
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
|
||||
}
|
||||
|
||||
// TODO remove in v4.
|
||||
func surfsharkConvertRetroLoc(retroLoc string) (
|
||||
locationData models.SurfsharkLocationData, isRetro bool) {
|
||||
for _, data := range constants.SurfsharkLocationData() {
|
||||
if retroLoc == data.RetroLoc {
|
||||
return data, true
|
||||
}
|
||||
}
|
||||
return locationData, false
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -108,7 +108,7 @@ func Test_versions(t *testing.T) {
|
||||
"Surfshark": {
|
||||
model: models.SurfsharkServer{},
|
||||
version: allServers.Surfshark.Version,
|
||||
digest: "58de06d8",
|
||||
digest: "3ccaa772",
|
||||
},
|
||||
"Torguard": {
|
||||
model: models.TorguardServer{},
|
||||
|
||||
@@ -19,6 +19,209 @@ func SurfsharkRegionChoices() (choices []string) {
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func SurfsharkCountryChoices() (choices []string) {
|
||||
servers := SurfsharkServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Country
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func SurfsharkCityChoices() (choices []string) {
|
||||
servers := SurfsharkServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
// TODO remove in v4.
|
||||
func SurfsharkRetroLocChoices() (choices []string) {
|
||||
locationData := SurfsharkLocationData()
|
||||
choices = make([]string, len(locationData))
|
||||
for i := range locationData {
|
||||
choices[i] = locationData[i].RetroLoc
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func SurfsharkHostToLocation() (hostToLocation map[string]models.SurfsharkLocationData) {
|
||||
locationData := SurfsharkLocationData()
|
||||
hostToLocation = make(map[string]models.SurfsharkLocationData, len(locationData))
|
||||
for _, data := range locationData {
|
||||
hostToLocation[data.Hostname] = data
|
||||
}
|
||||
return hostToLocation
|
||||
}
|
||||
|
||||
// TODO remove retroRegion and servers from API in v4.
|
||||
func SurfsharkLocationData() (data []models.SurfsharkLocationData) {
|
||||
//nolint:lll
|
||||
return []models.SurfsharkLocationData{
|
||||
{Region: "Asia Pacific", Country: "Australia", City: "Adelaide", RetroLoc: "Australia Adelaide", Hostname: "au-adl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Australia", City: "Brisbane", RetroLoc: "Australia Brisbane", Hostname: "au-bne.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Australia", City: "Melbourne", RetroLoc: "Australia Melbourne", Hostname: "au-mel.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Australia", City: "Perth", RetroLoc: "Australia Perth", Hostname: "au-per.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Australia", City: "Sydney", RetroLoc: "Australia Sydney", Hostname: "au-syd.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Azerbaijan", City: "Baku", RetroLoc: "Azerbaijan", Hostname: "az-bak.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Hong Kong", City: "Hong Kong", RetroLoc: "Hong Kong", Hostname: "hk-hkg.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "India", City: "Chennai", RetroLoc: "India Chennai", Hostname: "in-chn.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "India", City: "Indore", RetroLoc: "India Indore", Hostname: "in-idr.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "India", City: "Mumbai", RetroLoc: "India Mumbai", Hostname: "in-mum.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Indonesia", City: "Jakarta", RetroLoc: "Indonesia", Hostname: "id-jak.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st001", Hostname: "jp-tok-st001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st002", Hostname: "jp-tok-st002.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st003", Hostname: "jp-tok-st003.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st004", Hostname: "jp-tok-st004.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st005", Hostname: "jp-tok-st005.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st006", Hostname: "jp-tok-st006.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st007", Hostname: "jp-tok-st007.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st008", Hostname: "jp-tok-st008.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st009", Hostname: "jp-tok-st009.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st010", Hostname: "jp-tok-st010.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st011", Hostname: "jp-tok-st011.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st012", Hostname: "jp-tok-st012.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st013", Hostname: "jp-tok-st013.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo", Hostname: "jp-tok.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Kazakhstan", City: "Oral", RetroLoc: "Kazakhstan", Hostname: "kz-ura.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Malaysia", City: "Kuala Lumpur", RetroLoc: "Malaysia", Hostname: "my-kul.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "New Zealand", City: "Auckland", RetroLoc: "New Zealand", Hostname: "nz-akl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Philippines", City: "Manila", RetroLoc: "Philippines", Hostname: "ph-mnl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Singapore Hong Kong", City: "Hong Kong", RetroLoc: "Singapore Hong Kong", Hostname: "sg-hk.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Asia Pacific", Country: "Singapore in", City: "", RetroLoc: "Singapore in", Hostname: "sg-in.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore mp001", Hostname: "sg-sng-mp001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st001", Hostname: "sg-sng-st001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st002", Hostname: "sg-sng-st002.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st003", Hostname: "sg-sng-st003.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st004", Hostname: "sg-sng-st004.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore", Hostname: "sg-sng.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "South Korea", City: "Seoul", RetroLoc: "Korea", Hostname: "kr-seo.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Taiwan", City: "Taichung City", RetroLoc: "Taiwan", Hostname: "tw-tai.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Thailand", City: "Bangkok", RetroLoc: "Thailand", Hostname: "th-bkk.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Asia Pacific", Country: "Vietnam", City: "Ho Chi Minh City", RetroLoc: "Vietnam", Hostname: "vn-hcm.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Albania", City: "Tirana", RetroLoc: "Albania", Hostname: "al-tia.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Austria", City: "Vienna", RetroLoc: "Austria", Hostname: "at-vie.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Belgium", City: "Brussels", RetroLoc: "Belgium", Hostname: "be-bru.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Bosnia and Herzegovina", City: "Sarajevo", RetroLoc: "Bosnia and Herzegovina", Hostname: "ba-sjj.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Bulgaria", City: "Sofia", RetroLoc: "Bulgaria", Hostname: "bg-sof.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Croatia", City: "Zagreb", RetroLoc: "Croatia", Hostname: "hr-zag.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Cyprus", City: "Nicosia", RetroLoc: "Cyprus", Hostname: "cy-nic.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Czech Republic", City: "Prague", RetroLoc: "Czech Republic", Hostname: "cz-prg.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Denmark", City: "Copenhagen", RetroLoc: "Denmark", Hostname: "dk-cph.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Estonia", City: "Tallinn", RetroLoc: "Estonia", Hostname: "ee-tll.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Finland", City: "Helsinki", RetroLoc: "Finland", Hostname: "fi-hel.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "France Sweden", City: "", RetroLoc: "France Sweden", Hostname: "fr-se.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Europe", Country: "France", City: "Bordeaux", RetroLoc: "France Bordeaux", Hostname: "fr-bod.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "France", City: "Marseille", RetroLoc: "France Marseilles", Hostname: "fr-mrs.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "France", City: "Paris", RetroLoc: "France Paris", Hostname: "fr-par.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany Singapour", City: "", RetroLoc: "Germany Singapour", Hostname: "de-sg.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Europe", Country: "Germany UK", City: "", RetroLoc: "Germany UK", Hostname: "de-uk.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Europe", Country: "Germany", City: "Berlin", RetroLoc: "Germany Berlin", Hostname: "de-ber.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st001", Hostname: "de-fra-st001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st002", Hostname: "de-fra-st002.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st003", Hostname: "de-fra-st003.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st004", Hostname: "de-fra-st004.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st005", Hostname: "de-fra-st005.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main", Hostname: "de-fra.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt mp001", Hostname: "de-fra-mp001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Greece", City: "Athens", RetroLoc: "Greece", Hostname: "gr-ath.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Hungary", City: "Budapest", RetroLoc: "Hungary", Hostname: "hu-bud.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Iceland", City: "Reykjavik", RetroLoc: "Iceland", Hostname: "is-rkv.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "India UK", City: "", RetroLoc: "India UK", Hostname: "in-uk.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Europe", Country: "Ireland", City: "Dublin", RetroLoc: "Ireland", Hostname: "ie-dub.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Italy", City: "Milan", RetroLoc: "Italy Milan", Hostname: "it-mil.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Italy", City: "Rome", RetroLoc: "Italy Rome", Hostname: "it-rom.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Latvia", City: "Riga", RetroLoc: "Latvia", Hostname: "lv-rig.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Luxembourg", City: "Luxembourg", RetroLoc: "Luxembourg", Hostname: "lu-ste.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Moldova", City: "Chisinau", RetroLoc: "Moldova", Hostname: "md-chi.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Netherlands", City: "Amsterdam", RetroLoc: "Netherlands Amsterdam mp001", Hostname: "nl-ams-mp001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Netherlands", City: "Amsterdam", RetroLoc: "Netherlands Amsterdam st001", Hostname: "nl-ams-st001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Netherlands", City: "Amsterdam", RetroLoc: "Netherlands Amsterdam", Hostname: "nl-ams.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "North Macedonia", City: "Skopje", RetroLoc: "North Macedonia", Hostname: "mk-skp.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Norway", City: "Oslo", RetroLoc: "Norway", Hostname: "no-osl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Poland", City: "Gdansk", RetroLoc: "Poland Gdansk", Hostname: "pl-gdn.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Poland", City: "Warsaw", RetroLoc: "Poland Warsaw", Hostname: "pl-waw.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Portugal", City: "Lisbon", RetroLoc: "Portugal Lisbon", Hostname: "pt-lis.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Portugal", City: "Porto", RetroLoc: "Portugal Porto", Hostname: "pt-opo.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Romania", City: "Bucharest", RetroLoc: "Romania", Hostname: "ro-buc.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Russia", City: "Moscow", RetroLoc: "Russia Moscow", Hostname: "ru-mos.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Serbia", City: "Belgrade", RetroLoc: "Serbia", Hostname: "rs-beg.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Singapore Netherlands", City: "", RetroLoc: "Singapore Netherlands", Hostname: "sg-nl.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Europe", Country: "Slovakia", City: "Bratislava", RetroLoc: "Slovekia", Hostname: "sk-bts.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Slovenia", City: "Ljubljana", RetroLoc: "Slovenia", Hostname: "si-lju.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Spain", City: "Barcelona", RetroLoc: "Spain Barcelona", Hostname: "es-bcn.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Spain", City: "Madrid", RetroLoc: "Spain Madrid", Hostname: "es-mad.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Spain", City: "Valencia", RetroLoc: "Spain Valencia", Hostname: "es-vlc.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Sweden", City: "Stockholm", RetroLoc: "Sweden", Hostname: "se-sto.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Switzerland", City: "Zurich", RetroLoc: "Switzerland", Hostname: "ch-zur.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "Turkey", City: "Istanbul", RetroLoc: "Turkey Istanbul", Hostname: "tr-ist.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "UK France", City: "", RetroLoc: "UK France", Hostname: "uk-fr.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Europe", Country: "UK Germany", City: "", RetroLoc: "UK Germany", Hostname: "uk-de.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Europe", Country: "Ukraine", City: "Kyiv", RetroLoc: "Ukraine", Hostname: "ua-iev.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "Glasgow", RetroLoc: "UK Glasgow", Hostname: "uk-gla.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London mp001", Hostname: "uk-lon-mp001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st001", Hostname: "uk-lon-st001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st002", Hostname: "uk-lon-st002.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st003", Hostname: "uk-lon-st003.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st004", Hostname: "uk-lon-st004.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st005", Hostname: "uk-lon-st005.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London", Hostname: "uk-lon.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "United Kingdom", City: "Manchester", RetroLoc: "UK Manchester", Hostname: "uk-man.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "US Netherlands", City: "", RetroLoc: "US Netherlands", Hostname: "us-nl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Europe", Country: "US Portugal", City: "", RetroLoc: "US Portugal", Hostname: "us-pt.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "Middle East and Africa", Country: "Israel", City: "Tel Aviv", RetroLoc: "Israel", Hostname: "il-tlv.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Middle East and Africa", Country: "Nigeria", City: "Lagos", RetroLoc: "Nigeria", Hostname: "ng-lag.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Middle East and Africa", Country: "South Africa", City: "Johannesburg", RetroLoc: "South Africa", Hostname: "za-jnb.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "Middle East and Africa", Country: "United Arab Emirates", City: "Dubai", RetroLoc: "United Arab Emirates", Hostname: "ae-dub.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Argentina", City: "Buenos Aires", RetroLoc: "Argentina Buenos Aires", Hostname: "ar-bua.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Australia US", City: "", RetroLoc: "Australia US", Hostname: "au-us.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "The Americas", Country: "Brazil", City: "Sao Paulo", RetroLoc: "Brazil", Hostname: "br-sao.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Canada US", City: "", RetroLoc: "Canada US", Hostname: "ca-us.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "The Americas", Country: "Canada", City: "Montreal", RetroLoc: "Canada Montreal", Hostname: "ca-mon.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Canada", City: "Toronto", RetroLoc: "Canada Toronto mp001", Hostname: "ca-tor-mp001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Canada", City: "Toronto", RetroLoc: "Canada Toronto", Hostname: "ca-tor.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Canada", City: "Vancouver", RetroLoc: "Canada Vancouver", Hostname: "ca-van.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Chile", City: "Santiago", RetroLoc: "Chile", Hostname: "cl-san.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Colombia", City: "Bogota", RetroLoc: "Colombia", Hostname: "co-bog.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Costa Rica", City: "San Jose", RetroLoc: "Costa Rica", Hostname: "cr-sjn.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Mexico", City: "Mexico City", RetroLoc: "Mexico City Mexico", Hostname: "mx-mex.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "Netherlands US", City: "", RetroLoc: "Netherlands US", Hostname: "nl-us.prod.surfshark.com", MultiHop: true},
|
||||
{Region: "The Americas", Country: "United States", City: "Atlanta", RetroLoc: "US Atlanta", Hostname: "us-atl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Bend", RetroLoc: "US Bend", Hostname: "us-bdn.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Boston", RetroLoc: "US Boston", Hostname: "us-bos.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Buffalo", RetroLoc: "US Buffalo", Hostname: "us-buf.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Charlotte", RetroLoc: "US Charlotte", Hostname: "us-clt.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Chicago", RetroLoc: "US Chicago", Hostname: "us-chi.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Dallas", RetroLoc: "US Dallas", Hostname: "us-dal.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Denver", RetroLoc: "US Denver", Hostname: "us-den.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Detroit", RetroLoc: "US Gahanna", Hostname: "us-dtw.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Houston", RetroLoc: "US Houston", Hostname: "us-hou.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Kansas City", RetroLoc: "US Kansas City", Hostname: "us-kan.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Las Vegas", RetroLoc: "US Las Vegas", Hostname: "us-las.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Latham", RetroLoc: "US Latham", Hostname: "us-ltm.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Los Angeles", RetroLoc: "US Los Angeles", Hostname: "us-lax.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Manassas", RetroLoc: "US Maryland", Hostname: "us-mnz.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Miami", RetroLoc: "US Miami", Hostname: "us-mia.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City mp001", Hostname: "us-nyc-mp001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st001", Hostname: "us-nyc-st001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st002", Hostname: "us-nyc-st002.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st003", Hostname: "us-nyc-st003.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st004", Hostname: "us-nyc-st004.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st005", Hostname: "us-nyc-st005.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City", Hostname: "us-nyc.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Orlando", RetroLoc: "US Orlando", Hostname: "us-orl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Phoenix", RetroLoc: "US Phoenix", Hostname: "us-phx.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Salt Lake City", RetroLoc: "US Salt Lake City", Hostname: "us-slc.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "San Francisco", RetroLoc: "US San Francisco mp001", Hostname: "us-sfo-mp001.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "San Francisco", RetroLoc: "US San Francisco", Hostname: "us-sfo.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Seattle", RetroLoc: "US Seatle", Hostname: "us-sea.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "St. Louis", RetroLoc: "US Saint Louis", Hostname: "us-stl.prod.surfshark.com", MultiHop: false},
|
||||
{Region: "The Americas", Country: "United States", City: "Tampa", RetroLoc: "US Tampa", Hostname: "us-tpa.prod.surfshark.com", MultiHop: false},
|
||||
}
|
||||
}
|
||||
|
||||
func SurfsharkHostnameChoices() (choices []string) {
|
||||
servers := SurfsharkServers()
|
||||
choices = make([]string, len(servers))
|
||||
|
||||
12
internal/models/location.go
Normal file
12
internal/models/location.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
// SurfsharkLocationData is required to keep location data on Surfshark
|
||||
// servers that are not obtained through their API.
|
||||
type SurfsharkLocationData struct {
|
||||
Region string
|
||||
Country string
|
||||
City string
|
||||
RetroLoc string // TODO remove in v4
|
||||
Hostname string
|
||||
MultiHop bool
|
||||
}
|
||||
@@ -119,7 +119,11 @@ type PurevpnServer struct {
|
||||
|
||||
type SurfsharkServer struct {
|
||||
Region string `json:"region"`
|
||||
Country string `json:"country"` // Country is also used for multi-hop
|
||||
City string `json:"city"`
|
||||
RetroLoc string `json:"retroloc"` // TODO remove in v4
|
||||
Hostname string `json:"hostname"`
|
||||
MultiHop bool `json:"multihop"`
|
||||
TCP bool `json:"tcp"`
|
||||
UDP bool `json:"udp"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
|
||||
@@ -12,9 +12,12 @@ func (s *Surfshark) filterServers(selection configuration.ServerSelection) (
|
||||
switch {
|
||||
case
|
||||
utils.FilterByPossibilities(server.Region, selection.Regions),
|
||||
utils.FilterByPossibilities(server.Country, selection.Countries),
|
||||
utils.FilterByPossibilities(server.City, selection.Cities),
|
||||
utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
|
||||
selection.OpenVPN.TCP && !server.TCP,
|
||||
!selection.OpenVPN.TCP && !server.UDP:
|
||||
!selection.OpenVPN.TCP && !server.UDP,
|
||||
selection.MultiHopOnly && !server.MultiHop:
|
||||
default:
|
||||
servers = append(servers, server)
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ func (u *updater) updatePurevpn(ctx context.Context) (err error) {
|
||||
func (u *updater) updateSurfshark(ctx context.Context) (err error) {
|
||||
minServers := getMinServers(len(u.servers.Surfshark.Servers))
|
||||
servers, warnings, err := surfshark.GetServers(
|
||||
ctx, u.unzipper, u.presolver, minServers)
|
||||
ctx, u.unzipper, u.client, u.presolver, minServers)
|
||||
if u.options.CLI {
|
||||
for _, warning := range warnings {
|
||||
u.logger.Warn("Surfshark: " + warning)
|
||||
|
||||
@@ -6,21 +6,43 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
// Note: no multi-hop and some servers are missing from their API.
|
||||
func addServersFromAPI(ctx context.Context, client *http.Client,
|
||||
hts hostToServer) (err error) {
|
||||
data, err := fetchAPI(ctx, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostToLocation := constants.SurfsharkHostToLocation()
|
||||
|
||||
const tcp, udp = true, true
|
||||
for _, serverData := range data {
|
||||
locationData := hostToLocation[serverData.Host] // TODO remove in v4
|
||||
retroLoc := locationData.RetroLoc // empty string if the host has no retro-compatible region
|
||||
hts.add(serverData.Host, serverData.Region, serverData.Country,
|
||||
serverData.Location, retroLoc, tcp, udp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
|
||||
ErrUnmarshalResponseBody = errors.New("failed unmarshaling response body")
|
||||
)
|
||||
|
||||
//nolint:unused
|
||||
type serverData struct {
|
||||
Host string `json:"connectionName"`
|
||||
Region string `json:"region"`
|
||||
Country string `json:"country"`
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
//nolint:unused,deadcode
|
||||
func fetchAPI(ctx context.Context, client *http.Client) (
|
||||
servers []serverData, err error) {
|
||||
const url = "https://my.surfshark.com/vpn/api/v4/server/clusters"
|
||||
@@ -37,7 +59,8 @@ func fetchAPI(ctx context.Context, client *http.Client) (
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("%w: %s", ErrHTTPStatusCodeNotOK, response.Status)
|
||||
return nil, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK,
|
||||
response.StatusCode, response.Status)
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
|
||||
166
internal/updater/providers/surfshark/api_test.go
Normal file
166
internal/updater/providers/surfshark/api_test.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package surfshark
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_addServersFromAPI(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
hts hostToServer
|
||||
responseStatus int
|
||||
responseBody io.ReadCloser
|
||||
expected hostToServer
|
||||
err error
|
||||
}{
|
||||
"fetch API error": {
|
||||
responseStatus: http.StatusNoContent,
|
||||
err: errors.New("HTTP status code not OK: 204 No Content"),
|
||||
},
|
||||
"success": {
|
||||
hts: hostToServer{
|
||||
"existinghost": {Hostname: "existinghost"},
|
||||
},
|
||||
responseStatus: http.StatusOK,
|
||||
responseBody: ioutil.NopCloser(strings.NewReader(`[
|
||||
{"connectionName":"host1","region":"region1","country":"country1","location":"location1"},
|
||||
{"connectionName":"host2","region":"region2","country":"country1","location":"location2"}
|
||||
]`)),
|
||||
expected: map[string]models.SurfsharkServer{
|
||||
"existinghost": {Hostname: "existinghost"},
|
||||
"host1": {
|
||||
Region: "region1",
|
||||
Country: "country1",
|
||||
City: "location1",
|
||||
Hostname: "host1",
|
||||
TCP: true,
|
||||
UDP: true,
|
||||
},
|
||||
"host2": {
|
||||
Region: "region2",
|
||||
Country: "country1",
|
||||
City: "location2",
|
||||
Hostname: "host2",
|
||||
TCP: true,
|
||||
UDP: true,
|
||||
}},
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
client := &http.Client{
|
||||
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
assert.Equal(t, http.MethodGet, r.Method)
|
||||
assert.Equal(t, r.URL.String(), "https://my.surfshark.com/vpn/api/v4/server/clusters")
|
||||
return &http.Response{
|
||||
StatusCode: testCase.responseStatus,
|
||||
Status: http.StatusText(testCase.responseStatus),
|
||||
Body: testCase.responseBody,
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
|
||||
err := addServersFromAPI(ctx, client, testCase.hts)
|
||||
|
||||
assert.Equal(t, testCase.expected, testCase.hts)
|
||||
if testCase.err != nil {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_fetchAPI(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
responseStatus int
|
||||
responseBody io.ReadCloser
|
||||
data []serverData
|
||||
err error
|
||||
}{
|
||||
"http response status not ok": {
|
||||
responseStatus: http.StatusNoContent,
|
||||
err: errors.New("HTTP status code not OK: 204 No Content"),
|
||||
},
|
||||
"nil body": {
|
||||
responseStatus: http.StatusOK,
|
||||
err: errors.New("failed unmarshaling response body: EOF"),
|
||||
},
|
||||
"no server": {
|
||||
responseStatus: http.StatusOK,
|
||||
responseBody: ioutil.NopCloser(strings.NewReader(`[]`)),
|
||||
data: []serverData{},
|
||||
},
|
||||
"success": {
|
||||
responseStatus: http.StatusOK,
|
||||
responseBody: ioutil.NopCloser(strings.NewReader(`[
|
||||
{"connectionName":"host1","region":"region1","country":"country1","location":"location1"},
|
||||
{"connectionName":"host2","region":"region2","country":"country1","location":"location2"}
|
||||
]`)),
|
||||
data: []serverData{
|
||||
{
|
||||
Region: "region1",
|
||||
Country: "country1",
|
||||
Location: "location1",
|
||||
Host: "host1",
|
||||
},
|
||||
{
|
||||
Region: "region2",
|
||||
Country: "country1",
|
||||
Location: "location2",
|
||||
Host: "host2",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
client := &http.Client{
|
||||
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
||||
assert.Equal(t, http.MethodGet, r.Method)
|
||||
assert.Equal(t, r.URL.String(), "https://my.surfshark.com/vpn/api/v4/server/clusters")
|
||||
return &http.Response{
|
||||
StatusCode: testCase.responseStatus,
|
||||
Status: http.StatusText(testCase.responseStatus),
|
||||
Body: testCase.responseBody,
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
|
||||
data, err := fetchAPI(ctx, client)
|
||||
|
||||
assert.Equal(t, testCase.data, data)
|
||||
if testCase.err != nil {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,20 @@ package surfshark
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type hostToServer map[string]models.SurfsharkServer
|
||||
|
||||
func (hts hostToServer) add(host, region string, tcp, udp bool) {
|
||||
func (hts hostToServer) add(host, region, country, city, retroLoc string, tcp, udp bool) {
|
||||
server, ok := hts[host]
|
||||
if !ok {
|
||||
server.Hostname = host
|
||||
server.Region = region
|
||||
server.Country = country
|
||||
server.City = city
|
||||
server.RetroLoc = retroLoc
|
||||
}
|
||||
if tcp {
|
||||
server.TCP = tcp
|
||||
@@ -32,16 +34,6 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
|
||||
return hosts
|
||||
}
|
||||
|
||||
func (hts hostToServer) toSubdomainsSlice() (subdomains []string) {
|
||||
subdomains = make([]string, 0, len(hts))
|
||||
const suffix = ".prod.surfshark.com"
|
||||
for host := range hts {
|
||||
subdomain := strings.TrimSuffix(host, suffix)
|
||||
subdomains = append(subdomains, subdomain)
|
||||
}
|
||||
return subdomains
|
||||
}
|
||||
|
||||
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
|
||||
for host, IPs := range hostToIPs {
|
||||
server := hts[host]
|
||||
|
||||
22
internal/updater/providers/surfshark/location.go
Normal file
22
internal/updater/providers/surfshark/location.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package surfshark
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
var (
|
||||
errHostnameNotFound = errors.New("hostname not found in hostname to location mapping")
|
||||
)
|
||||
|
||||
func getHostInformation(host string, hostnameToLocation map[string]models.SurfsharkLocationData) (
|
||||
data models.SurfsharkLocationData, err error) {
|
||||
locationData, ok := hostnameToLocation[host]
|
||||
if !ok {
|
||||
return locationData, fmt.Errorf("%w: %s", errHostnameNotFound, host)
|
||||
}
|
||||
|
||||
return locationData, nil
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
package surfshark
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
errSuffixNotFound = errors.New("suffix not found")
|
||||
errSubdomainNotFound = errors.New("subdomain not found in subdomain to region mapping")
|
||||
)
|
||||
|
||||
func parseHost(host string, subdomainToRegion map[string]string) (
|
||||
region string, err error) {
|
||||
const suffix = ".prod.surfshark.com"
|
||||
if !strings.HasSuffix(host, suffix) {
|
||||
return "", fmt.Errorf("%w: %s in %s",
|
||||
errSuffixNotFound, suffix, host)
|
||||
}
|
||||
|
||||
subdomain := strings.TrimSuffix(host, suffix)
|
||||
region, ok := subdomainToRegion[subdomain]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%w: %s", errSubdomainNotFound, subdomain)
|
||||
}
|
||||
|
||||
return region, nil
|
||||
}
|
||||
|
||||
func subdomainToRegion() (mapping map[string]string) {
|
||||
return map[string]string{
|
||||
"ae-dub": "United Arab Emirates",
|
||||
"al-tia": "Albania",
|
||||
"at-vie": "Austria",
|
||||
"au-adl": "Australia Adelaide",
|
||||
"au-bne": "Australia Brisbane",
|
||||
"au-mel": "Australia Melbourne",
|
||||
"au-per": "Australia Perth",
|
||||
"au-syd": "Australia Sydney",
|
||||
"au-us": "Australia US",
|
||||
"az-bak": "Azerbaijan",
|
||||
"ba-sjj": "Bosnia and Herzegovina",
|
||||
"be-bru": "Belgium",
|
||||
"bg-sof": "Bulgaria",
|
||||
"br-sao": "Brazil",
|
||||
"ca-mon": "Canada Montreal",
|
||||
"ca-tor": "Canada Toronto",
|
||||
"ca-us": "Canada US",
|
||||
"ca-van": "Canada Vancouver",
|
||||
"ch-zur": "Switzerland",
|
||||
"cl-san": "Chile",
|
||||
"co-bog": "Colombia",
|
||||
"cr-sjn": "Costa Rica",
|
||||
"cy-nic": "Cyprus",
|
||||
"cz-prg": "Czech Republic",
|
||||
"de-ber": "Germany Berlin",
|
||||
"de-fra": "Germany Frankfurt am Main",
|
||||
"de-fra-st001": "Germany Frankfurt am Main st001",
|
||||
"de-fra-st002": "Germany Frankfurt am Main st002",
|
||||
"de-fra-st003": "Germany Frankfurt am Main st003",
|
||||
"de-fra-st004": "Germany Frankfurt am Main st004",
|
||||
"de-fra-st005": "Germany Frankfurt am Main st005",
|
||||
"de-muc": "Germany Munich",
|
||||
"de-nue": "Germany Nuremberg",
|
||||
"de-sg": "Germany Singapour",
|
||||
"de-uk": "Germany UK",
|
||||
"dk-cph": "Denmark",
|
||||
"ee-tll": "Estonia",
|
||||
"es-bcn": "Spain Barcelona",
|
||||
"es-mad": "Spain Madrid",
|
||||
"es-vlc": "Spain Valencia",
|
||||
"fi-hel": "Finland",
|
||||
"fr-bod": "France Bordeaux",
|
||||
"fr-mrs": "France Marseilles",
|
||||
"fr-par": "France Paris",
|
||||
"fr-se": "France Sweden",
|
||||
"gr-ath": "Greece",
|
||||
"hk-hkg": "Hong Kong",
|
||||
"hr-zag": "Croatia",
|
||||
"hu-bud": "Hungary",
|
||||
"id-jak": "Indonesia",
|
||||
"ie-dub": "Ireland",
|
||||
"il-tlv": "Israel",
|
||||
"in-chn": "India Chennai",
|
||||
"in-idr": "India Indore",
|
||||
"in-mum": "India Mumbai",
|
||||
"in-uk": "India UK",
|
||||
"is-rkv": "Iceland",
|
||||
"it-mil": "Italy Milan",
|
||||
"it-rom": "Italy Rome",
|
||||
"jp-tok": "Japan Tokyo",
|
||||
"jp-tok-st001": "Japan Tokyo st001",
|
||||
"jp-tok-st002": "Japan Tokyo st002",
|
||||
"jp-tok-st003": "Japan Tokyo st003",
|
||||
"jp-tok-st004": "Japan Tokyo st004",
|
||||
"jp-tok-st005": "Japan Tokyo st005",
|
||||
"jp-tok-st006": "Japan Tokyo st006",
|
||||
"jp-tok-st007": "Japan Tokyo st007",
|
||||
"jp-tok-st008": "Japan Tokyo st008",
|
||||
"jp-tok-st009": "Japan Tokyo st009",
|
||||
"jp-tok-st010": "Japan Tokyo st010",
|
||||
"jp-tok-st011": "Japan Tokyo st011",
|
||||
"jp-tok-st012": "Japan Tokyo st012",
|
||||
"jp-tok-st013": "Japan Tokyo st013",
|
||||
"kr-seo": "Korea",
|
||||
"kz-ura": "Kazakhstan",
|
||||
"lu-ste": "Luxembourg",
|
||||
"lv-rig": "Latvia",
|
||||
"ly-tip": "Libya",
|
||||
"md-chi": "Moldova",
|
||||
"mk-skp": "North Macedonia",
|
||||
"my-kul": "Malaysia",
|
||||
"ng-lag": "Nigeria",
|
||||
"nl-ams": "Netherlands Amsterdam",
|
||||
"nl-ams-st001": "Netherlands Amsterdam st001",
|
||||
"nl-us": "Netherlands US",
|
||||
"no-osl": "Norway",
|
||||
"nz-akl": "New Zealand",
|
||||
"ph-mnl": "Philippines",
|
||||
"pl-gdn": "Poland Gdansk",
|
||||
"pl-waw": "Poland Warsaw",
|
||||
"pt-lis": "Portugal Lisbon",
|
||||
"pt-lou": "Portugal Loule",
|
||||
"pt-opo": "Portugal Porto",
|
||||
"py-asu": "Paraguay",
|
||||
"ro-buc": "Romania",
|
||||
"rs-beg": "Serbia",
|
||||
"ru-mos": "Russia Moscow",
|
||||
"ru-spt": "Russia St. Petersburg",
|
||||
"se-sto": "Sweden",
|
||||
"sg-hk": "Singapore Hong Kong",
|
||||
"sg-nl": "Singapore Netherlands",
|
||||
"sg-sng": "Singapore",
|
||||
"sg-in": "Singapore in",
|
||||
"sg-sng-st001": "Singapore st001",
|
||||
"sg-sng-st002": "Singapore st002",
|
||||
"sg-sng-st003": "Singapore st003",
|
||||
"sg-sng-st004": "Singapore st004",
|
||||
"sg-sng-mp001": "Singapore mp001",
|
||||
"si-lju": "Slovenia",
|
||||
"sk-bts": "Slovekia",
|
||||
"th-bkk": "Thailand",
|
||||
"tr-bur": "Turkey",
|
||||
"tw-tai": "Taiwan",
|
||||
"ua-iev": "Ukraine",
|
||||
"uk-de": "UK Germany",
|
||||
"uk-fr": "UK France",
|
||||
"uk-gla": "UK Glasgow",
|
||||
"uk-lon": "UK London",
|
||||
"uk-lon-mp001": "UK London mp001",
|
||||
"uk-lon-st001": "UK London st001",
|
||||
"uk-lon-st002": "UK London st002",
|
||||
"uk-lon-st003": "UK London st003",
|
||||
"uk-lon-st004": "UK London st004",
|
||||
"uk-lon-st005": "UK London st005",
|
||||
"uk-man": "UK Manchester",
|
||||
"us-atl": "US Atlanta",
|
||||
"us-bdn": "US Bend",
|
||||
"us-bos": "US Boston",
|
||||
"us-buf": "US Buffalo",
|
||||
"us-chi": "US Chicago",
|
||||
"us-clt": "US Charlotte",
|
||||
"us-dal": "US Dallas",
|
||||
"us-den": "US Denver",
|
||||
"us-dtw": "US Gahanna",
|
||||
"us-hou": "US Houston",
|
||||
"us-kan": "US Kansas City",
|
||||
"us-las": "US Las Vegas",
|
||||
"us-lax": "US Los Angeles",
|
||||
"us-ltm": "US Latham",
|
||||
"us-mia": "US Miami",
|
||||
"us-mnz": "US Maryland",
|
||||
"us-nl": "US Netherlands",
|
||||
"us-nyc": "US New York City",
|
||||
"us-nyc-mp001": "US New York City mp001",
|
||||
"us-nyc-st001": "US New York City st001",
|
||||
"us-nyc-st002": "US New York City st002",
|
||||
"us-nyc-st003": "US New York City st003",
|
||||
"us-nyc-st004": "US New York City st004",
|
||||
"us-nyc-st005": "US New York City st005",
|
||||
"us-orl": "US Orlando",
|
||||
"us-phx": "US Phoenix",
|
||||
"us-pt": "US Portugal",
|
||||
"us-sea": "US Seatle",
|
||||
"us-sfo": "US San Francisco",
|
||||
"us-slc": "US Salt Lake City",
|
||||
"us-stl": "US Saint Louis",
|
||||
"us-tpa": "US Tampa",
|
||||
"vn-hcm": "Vietnam",
|
||||
"za-jnb": "South Africa",
|
||||
"ar-bua": "Argentina Buenos Aires",
|
||||
"tr-ist": "Turkey Istanbul",
|
||||
"mx-mex": "Mexico City Mexico",
|
||||
"ca-tor-mp001": "Canada Toronto mp001",
|
||||
"de-fra-mp001": "Germany Frankfurt mp001",
|
||||
"nl-ams-mp001": "Netherlands Amsterdam mp001",
|
||||
"us-sfo-mp001": "US San Francisco mp001",
|
||||
}
|
||||
}
|
||||
20
internal/updater/providers/surfshark/remaining.go
Normal file
20
internal/updater/providers/surfshark/remaining.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package surfshark
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
// getRemainingServers finds extra servers not found in the API or in the ZIP file.
|
||||
func getRemainingServers(hts hostToServer) {
|
||||
hostnameToLocationLeft := constants.SurfsharkHostToLocation()
|
||||
for _, hostnameDone := range hts.toHostsSlice() {
|
||||
delete(hostnameToLocationLeft, hostnameDone)
|
||||
}
|
||||
|
||||
for hostname, locationData := range hostnameToLocationLeft {
|
||||
// we assume the server supports TCP and UDP
|
||||
const tcp, udp = true, true
|
||||
hts.add(hostname, locationData.Region, locationData.Country,
|
||||
locationData.City, locationData.RetroLoc, tcp, udp)
|
||||
}
|
||||
}
|
||||
57
internal/updater/providers/surfshark/resolve_test.go
Normal file
57
internal/updater/providers/surfshark/resolve_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package surfshark
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver/mock_resolver"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_resolveHosts(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
ctx := context.Background()
|
||||
presolver := mock_resolver.NewMockParallel(ctrl)
|
||||
hosts := []string{"host1", "host2"}
|
||||
const minServers = 10
|
||||
|
||||
expectedHostToIPs := map[string][]net.IP{
|
||||
"host1": {{1, 2, 3, 4}},
|
||||
"host2": {{2, 3, 4, 5}},
|
||||
}
|
||||
expectedWarnings := []string{"warning1", "warning2"}
|
||||
expectedErr := errors.New("dummy")
|
||||
|
||||
const (
|
||||
maxFailRatio = 0.1
|
||||
maxDuration = 20 * time.Second
|
||||
betweenDuration = time.Second
|
||||
maxNoNew = 2
|
||||
maxFails = 2
|
||||
)
|
||||
expectedSettings := resolver.ParallelSettings{
|
||||
MaxFailRatio: maxFailRatio,
|
||||
MinFound: minServers,
|
||||
Repeat: resolver.RepeatSettings{
|
||||
MaxDuration: maxDuration,
|
||||
BetweenDuration: betweenDuration,
|
||||
MaxNoNew: maxNoNew,
|
||||
MaxFails: maxFails,
|
||||
SortIPs: true,
|
||||
},
|
||||
}
|
||||
presolver.EXPECT().Resolve(ctx, hosts, expectedSettings).
|
||||
Return(expectedHostToIPs, expectedWarnings, expectedErr)
|
||||
|
||||
hostToIPs, warnings, err := resolveHosts(ctx, presolver, hosts, minServers)
|
||||
assert.Equal(t, expectedHostToIPs, hostToIPs)
|
||||
assert.Equal(t, expectedWarnings, warnings)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
}
|
||||
9
internal/updater/providers/surfshark/roundtrip_test.go
Normal file
9
internal/updater/providers/surfshark/roundtrip_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package surfshark
|
||||
|
||||
import "net/http"
|
||||
|
||||
type roundTripFunc func(r *http.Request) (*http.Response, error)
|
||||
|
||||
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
return f(r)
|
||||
}
|
||||
@@ -6,71 +6,36 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"net/http"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/updater/openvpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||
)
|
||||
|
||||
var ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
var (
|
||||
ErrGetZip = errors.New("cannot get OpenVPN ZIP file")
|
||||
ErrGetAPI = errors.New("cannot fetch server information from API")
|
||||
ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
)
|
||||
|
||||
func GetServers(ctx context.Context, unzipper unzip.Unzipper,
|
||||
presolver resolver.Parallel, minServers int) (
|
||||
client *http.Client, presolver resolver.Parallel, minServers int) (
|
||||
servers []models.SurfsharkServer, warnings []string, err error) {
|
||||
const url = "https://my.surfshark.com/vpn/api/v1/server/configurations"
|
||||
contents, err := unzipper.FetchAndExtract(ctx, url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
} else if len(contents) < minServers {
|
||||
return nil, nil, fmt.Errorf("%w: %d and expected at least %d",
|
||||
ErrNotEnoughServers, len(contents), minServers)
|
||||
}
|
||||
|
||||
subdomainToRegion := subdomainToRegion()
|
||||
hts := make(hostToServer)
|
||||
|
||||
for fileName, content := range contents {
|
||||
if !strings.HasSuffix(fileName, ".ovpn") {
|
||||
continue // not an OpenVPN file
|
||||
}
|
||||
|
||||
tcp, udp, err := openvpn.ExtractProto(content)
|
||||
if err != nil {
|
||||
// treat error as warning and go to next file
|
||||
warning := err.Error() + " in " + fileName
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
|
||||
host, warning, err := openvpn.ExtractHost(content)
|
||||
if warning != "" {
|
||||
warnings = append(warnings, warning)
|
||||
}
|
||||
if err != nil {
|
||||
// treat error as warning and go to next file
|
||||
warning := err.Error() + " in " + fileName
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
|
||||
region, err := parseHost(host, subdomainToRegion)
|
||||
if err != nil {
|
||||
// treat error as warning and go to next file
|
||||
warning := err.Error()
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
|
||||
hts.add(host, region, tcp, udp)
|
||||
err = addServersFromAPI(ctx, client, hts)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("%w: %s", ErrGetAPI, err)
|
||||
}
|
||||
|
||||
if len(hts) < minServers {
|
||||
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
|
||||
ErrNotEnoughServers, len(hts), minServers)
|
||||
warnings, err = addOpenVPNServersFromZip(ctx, unzipper, hts)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("%w: %s", ErrGetZip, err)
|
||||
}
|
||||
|
||||
getRemainingServers(hts)
|
||||
|
||||
hosts := hts.toHostsSlice()
|
||||
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers)
|
||||
warnings = append(warnings, newWarnings...)
|
||||
@@ -82,20 +47,6 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
|
||||
|
||||
servers = hts.toServersSlice()
|
||||
|
||||
// process subdomain entries in mapping that were not in the Zip file
|
||||
subdomainsDone := hts.toSubdomainsSlice()
|
||||
for _, subdomainDone := range subdomainsDone {
|
||||
delete(subdomainToRegion, subdomainDone)
|
||||
}
|
||||
remainingServers, newWarnings, err := getRemainingServers(
|
||||
ctx, subdomainToRegion, presolver)
|
||||
warnings = append(warnings, newWarnings...)
|
||||
if err != nil {
|
||||
return nil, warnings, err
|
||||
}
|
||||
|
||||
servers = append(servers, remainingServers...)
|
||||
|
||||
if len(servers) < minServers {
|
||||
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
|
||||
ErrNotEnoughServers, len(servers), minServers)
|
||||
@@ -105,39 +56,3 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
|
||||
|
||||
return servers, warnings, nil
|
||||
}
|
||||
|
||||
func getRemainingServers(ctx context.Context,
|
||||
subdomainToRegionLeft map[string]string, presolver resolver.Parallel) (
|
||||
servers []models.SurfsharkServer, warnings []string, err error) {
|
||||
hosts := make([]string, 0, len(subdomainToRegionLeft))
|
||||
const suffix = ".prod.surfshark.com"
|
||||
for subdomain := range subdomainToRegionLeft {
|
||||
hosts = append(hosts, subdomain+suffix)
|
||||
}
|
||||
|
||||
const minServers = 0
|
||||
hostToIPs, warnings, err := resolveHosts(ctx, presolver, hosts, minServers)
|
||||
if err != nil {
|
||||
return nil, warnings, err
|
||||
}
|
||||
|
||||
servers = make([]models.SurfsharkServer, 0, len(hostToIPs))
|
||||
for host, IPs := range hostToIPs {
|
||||
region, err := parseHost(host, subdomainToRegionLeft)
|
||||
if err != nil {
|
||||
return nil, warnings, err
|
||||
}
|
||||
|
||||
// we assume the server supports TCP and UDP
|
||||
server := models.SurfsharkServer{
|
||||
Region: region,
|
||||
Hostname: host,
|
||||
TCP: true,
|
||||
UDP: true,
|
||||
IPs: IPs,
|
||||
}
|
||||
servers = append(servers, server)
|
||||
}
|
||||
|
||||
return servers, warnings, nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@ import (
|
||||
func sortServers(servers []models.SurfsharkServer) {
|
||||
sort.Slice(servers, func(i, j int) bool {
|
||||
if servers[i].Region == servers[j].Region {
|
||||
return servers[i].Hostname < servers[j].Hostname
|
||||
if servers[i].Country == servers[j].Country {
|
||||
if servers[i].City == servers[j].City {
|
||||
return servers[i].Hostname < servers[j].Hostname
|
||||
}
|
||||
return servers[i].City < servers[j].City
|
||||
}
|
||||
return servers[i].Country < servers[j].Country
|
||||
}
|
||||
return servers[i].Region < servers[j].Region
|
||||
})
|
||||
|
||||
42
internal/updater/providers/surfshark/sort_test.go
Normal file
42
internal/updater/providers/surfshark/sort_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package surfshark
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_sortServers(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := map[string]struct {
|
||||
initialServers []models.SurfsharkServer
|
||||
sortedServers []models.SurfsharkServer
|
||||
}{
|
||||
"no server": {},
|
||||
"sorted servers": {
|
||||
initialServers: []models.SurfsharkServer{
|
||||
{Region: "Z", Country: "B", City: "A", Hostname: "A"},
|
||||
{Country: "B", City: "A", Hostname: "A"},
|
||||
{Country: "A", City: "A", Hostname: "B"},
|
||||
{Country: "A", City: "A", Hostname: "A"},
|
||||
{Country: "A", City: "B", Hostname: "A"},
|
||||
},
|
||||
sortedServers: []models.SurfsharkServer{
|
||||
{Country: "A", City: "A", Hostname: "A"},
|
||||
{Country: "A", City: "A", Hostname: "B"},
|
||||
{Country: "A", City: "B", Hostname: "A"},
|
||||
{Country: "B", City: "A", Hostname: "A"},
|
||||
{Region: "Z", Country: "B", City: "A", Hostname: "A"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
sortServers(testCase.initialServers)
|
||||
assert.Equal(t, testCase.sortedServers, testCase.initialServers)
|
||||
})
|
||||
}
|
||||
}
|
||||
71
internal/updater/providers/surfshark/zip.go
Normal file
71
internal/updater/providers/surfshark/zip.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package surfshark
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/updater/openvpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||
)
|
||||
|
||||
func addOpenVPNServersFromZip(ctx context.Context,
|
||||
unzipper unzip.Unzipper, hts hostToServer) (
|
||||
warnings []string, err error) {
|
||||
const url = "https://my.surfshark.com/vpn/api/v1/server/configurations"
|
||||
contents, err := unzipper.FetchAndExtract(ctx, url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostnamesDone := hts.toHostsSlice()
|
||||
hostnamesDoneSet := make(map[string]struct{}, len(hostnamesDone))
|
||||
for _, hostname := range hostnamesDone {
|
||||
hostnamesDoneSet[hostname] = struct{}{}
|
||||
}
|
||||
|
||||
hostToLocation := constants.SurfsharkHostToLocation()
|
||||
|
||||
for fileName, content := range contents {
|
||||
if !strings.HasSuffix(fileName, ".ovpn") {
|
||||
continue // not an OpenVPN file
|
||||
}
|
||||
|
||||
host, warning, err := openvpn.ExtractHost(content)
|
||||
if warning != "" {
|
||||
warnings = append(warnings, warning)
|
||||
}
|
||||
if err != nil {
|
||||
// treat error as warning and go to next file
|
||||
warning := err.Error() + " in " + fileName
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok := hostnamesDoneSet[host]
|
||||
if ok {
|
||||
continue // already done in API
|
||||
}
|
||||
|
||||
tcp, udp, err := openvpn.ExtractProto(content)
|
||||
if err != nil {
|
||||
// treat error as warning and go to next file
|
||||
warning := err.Error() + " in " + fileName
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := getHostInformation(host, hostToLocation)
|
||||
if err != nil {
|
||||
// treat error as warning and go to next file
|
||||
warning := err.Error()
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
|
||||
hts.add(host, data.Region, data.Country, data.City,
|
||||
data.RetroLoc, tcp, udp)
|
||||
}
|
||||
|
||||
return warnings, nil
|
||||
}
|
||||
Reference in New Issue
Block a user