Files
gluetun/internal/provider/expressvpn/updater/servers.go
Quentin McGaw c5b9ee89c6 expressvpn wip
2026-08-06 03:22:06 +00:00

85 lines
2.4 KiB
Go

package updater
import (
"context"
"fmt"
"sort"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common"
)
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers []models.Server, err error,
) {
servers, warnings, err := fetchServersFromWebsite(ctx, u.httpClient)
for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil {
return nil, fmt.Errorf("fetching servers: %w", err)
}
// Generate candidate hostnames for each location
serverToCandidateHostnames := make(map[*models.Server][]string, len(servers))
var allCandidateHostnames []string
for _, server := range servers {
candidateHostnames, err := generateCandidateHostnames(server)
if err != nil {
u.warner.Warn(fmt.Sprintf("generating candidate hostnames for %s - %s: %s",
server.Country, server.City, err))
continue
}
serverToCandidateHostnames[&server] = candidateHostnames
allCandidateHostnames = append(allCandidateHostnames, candidateHostnames...)
}
// Resolve all candidate hostnames in parallel
resolveSettings := parallelResolverSettings(allCandidateHostnames)
hostToIPs, _, err := u.parallelResolver.Resolve(ctx, resolveSettings)
// Ignore resolution warnings since we are mostly bruteforcing DNS records
// so most candidate hostnames will fail resolving.
if err != nil {
return nil, fmt.Errorf("resolving hostnames: %w", err)
}
foundServers := make([]models.Server, 0, len(servers))
for server, candidateHostnames := range serverToCandidateHostnames {
success := false
for _, candidate := range candidateHostnames {
ips := hostToIPs[candidate]
if len(ips) > 0 {
success = true
workingServer := *server
workingServer.Hostname = candidate
workingServer.IPs = ips
foundServers = append(foundServers, workingServer)
}
}
if success {
continue
}
// Log a warning on the bad server location that did not resolve any IPs
serverName := server.Country
if server.City != "" {
serverName += " - " + server.City
}
if server.Number > 0 {
serverName += fmt.Sprintf(" (%d)", server.Number)
}
u.warner.Warn(fmt.Sprintf("no IPs resolved for %s (tried %v)", serverName, candidateHostnames))
}
servers = foundServers
if len(servers) < minServers {
return nil, fmt.Errorf("%w: %d and expected at least %d",
common.ErrNotEnoughServers, len(servers), minServers)
}
sort.Sort(models.SortableServers(servers))
return servers, nil
}