Maint: deduplicate ProtonVPN servers by entry IP
This commit is contained in:
@@ -168,7 +168,7 @@ func (a *AllServers) GetProtonvpn() (servers []ProtonvpnServer) {
|
|||||||
for i, serverToCopy := range a.Protonvpn.Servers {
|
for i, serverToCopy := range a.Protonvpn.Servers {
|
||||||
servers[i] = serverToCopy
|
servers[i] = serverToCopy
|
||||||
servers[i].EntryIP = copyIP(serverToCopy.EntryIP)
|
servers[i].EntryIP = copyIP(serverToCopy.EntryIP)
|
||||||
servers[i].ExitIP = copyIP(serverToCopy.ExitIP)
|
servers[i].ExitIPs = copyIPs(serverToCopy.ExitIPs)
|
||||||
}
|
}
|
||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func Test_AllServers_GetCopy(t *testing.T) {
|
|||||||
Protonvpn: ProtonvpnServers{
|
Protonvpn: ProtonvpnServers{
|
||||||
Servers: []ProtonvpnServer{{
|
Servers: []ProtonvpnServer{{
|
||||||
EntryIP: net.IP{1, 2, 3, 4},
|
EntryIP: net.IP{1, 2, 3, 4},
|
||||||
ExitIP: net.IP{1, 2, 3, 4},
|
ExitIPs: []net.IP{{1, 2, 3, 4}},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
Purevpn: PurevpnServers{
|
Purevpn: PurevpnServers{
|
||||||
|
|||||||
@@ -107,13 +107,13 @@ type PrivatevpnServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProtonvpnServer struct {
|
type ProtonvpnServer struct {
|
||||||
Country string `json:"country"`
|
Country string `json:"country"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
City string `json:"city"`
|
City string `json:"city"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
EntryIP net.IP `json:"entry_ip"`
|
EntryIP net.IP `json:"entry_ip"`
|
||||||
ExitIP net.IP `json:"exit_ip"` // TODO verify it matches with public IP once connected
|
ExitIPs []net.IP `json:"exit_ip"` // TODO verify it matches with public IP once connected
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurevpnServer struct {
|
type PurevpnServer struct {
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func Test_versions(t *testing.T) {
|
|||||||
"Protonvpn": {
|
"Protonvpn": {
|
||||||
model: models.ProtonvpnServer{},
|
model: models.ProtonvpnServer{},
|
||||||
version: allServers.Protonvpn.Version,
|
version: allServers.Protonvpn.Version,
|
||||||
digest: "b964085b",
|
digest: "4cb74c3a",
|
||||||
},
|
},
|
||||||
"Purevpn": {
|
"Purevpn": {
|
||||||
model: models.PurevpnServer{},
|
model: models.PurevpnServer{},
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
37
internal/updater/providers/protonvpn/iptoserver.go
Normal file
37
internal/updater/providers/protonvpn/iptoserver.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package protonvpn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ipToServer map[string]models.ProtonvpnServer
|
||||||
|
|
||||||
|
func (its ipToServer) add(country, region, city, name, hostname string,
|
||||||
|
entryIP, exitIP net.IP) {
|
||||||
|
key := entryIP.String()
|
||||||
|
|
||||||
|
server, ok := its[key]
|
||||||
|
if !ok {
|
||||||
|
server.Country = country
|
||||||
|
server.Region = region
|
||||||
|
server.City = city
|
||||||
|
server.Name = name
|
||||||
|
server.Hostname = hostname
|
||||||
|
server.EntryIP = entryIP
|
||||||
|
server.ExitIPs = []net.IP{exitIP}
|
||||||
|
} else {
|
||||||
|
server.ExitIPs = append(server.ExitIPs, exitIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
its[key] = server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (its ipToServer) toServersSlice() (servers []models.ProtonvpnServer) {
|
||||||
|
servers = make([]models.ProtonvpnServer, 0, len(its))
|
||||||
|
for _, server := range its {
|
||||||
|
servers = append(servers, server)
|
||||||
|
}
|
||||||
|
return servers
|
||||||
|
}
|
||||||
@@ -33,63 +33,46 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
|
|||||||
ErrNotEnoughServers, count, minServers)
|
ErrNotEnoughServers, count, minServers)
|
||||||
}
|
}
|
||||||
|
|
||||||
servers = make([]models.ProtonvpnServer, 0, count)
|
ipToServer := make(ipToServer, count)
|
||||||
for _, logicalServer := range data.LogicalServers {
|
for _, logicalServer := range data.LogicalServers {
|
||||||
|
region := getStringValue(logicalServer.Region)
|
||||||
|
city := getStringValue(logicalServer.City)
|
||||||
|
name := logicalServer.Name
|
||||||
for _, physicalServer := range logicalServer.Servers {
|
for _, physicalServer := range logicalServer.Servers {
|
||||||
server, warning, err := makeServer(
|
if physicalServer.Status == 0 { // disabled so skip server
|
||||||
physicalServer, logicalServer, countryCodes)
|
warnings = append(warnings,
|
||||||
|
"ignoring server "+physicalServer.Domain+" with status 0")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname := physicalServer.Domain
|
||||||
|
entryIP := physicalServer.EntryIP
|
||||||
|
exitIP := physicalServer.ExitIP
|
||||||
|
|
||||||
|
// Note: for multi-hop use the server name or hostname
|
||||||
|
// instead of the country
|
||||||
|
countryCode := logicalServer.ExitCountry
|
||||||
|
country, warning := codeToCountry(countryCode, countryCodes)
|
||||||
if warning != "" {
|
if warning != "" {
|
||||||
warnings = append(warnings, warning)
|
warnings = append(warnings, warning)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
ipToServer.add(country, region, city, name, hostname, entryIP, exitIP)
|
||||||
warnings = append(warnings, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
servers = append(servers, server)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(servers) < minServers {
|
if len(ipToServer) < minServers {
|
||||||
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
|
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
|
||||||
ErrNotEnoughServers, len(servers), minServers)
|
ErrNotEnoughServers, len(ipToServer), minServers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
servers = ipToServer.toServersSlice()
|
||||||
|
|
||||||
sortServers(servers)
|
sortServers(servers)
|
||||||
|
|
||||||
return servers, warnings, nil
|
return servers, warnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var errServerStatusZero = errors.New("ignoring server with status 0")
|
|
||||||
|
|
||||||
func makeServer(physical physicalServer, logical logicalServer,
|
|
||||||
countryCodes map[string]string) (server models.ProtonvpnServer,
|
|
||||||
warning string, err error) {
|
|
||||||
if physical.Status == 0 {
|
|
||||||
return server, "", fmt.Errorf("%w: %s",
|
|
||||||
errServerStatusZero, physical.Domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
countryCode := logical.ExitCountry
|
|
||||||
country, warning := codeToCountry(countryCode, countryCodes)
|
|
||||||
|
|
||||||
server = models.ProtonvpnServer{
|
|
||||||
// Note: for multi-hop use the server name or hostname
|
|
||||||
// instead of the country
|
|
||||||
Country: country,
|
|
||||||
Region: getStringValue(logical.Region),
|
|
||||||
City: getStringValue(logical.City),
|
|
||||||
Name: logical.Name,
|
|
||||||
Hostname: physical.Domain,
|
|
||||||
EntryIP: physical.EntryIP,
|
|
||||||
ExitIP: physical.ExitIP,
|
|
||||||
}
|
|
||||||
|
|
||||||
return server, warning, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getStringValue(ptr *string) string {
|
func getStringValue(ptr *string) string {
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
Reference in New Issue
Block a user