Feature: updater changes to have more VPN IP addresses (#364)
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
@@ -65,7 +66,8 @@ func tryCyberghostHostname(ctx context.Context, lookupIP lookupIPFunc,
|
|||||||
defer func() {
|
defer func() {
|
||||||
<-guard
|
<-guard
|
||||||
}()
|
}()
|
||||||
IPs, err := resolveRepeat(ctx, lookupIP, host, 2)
|
const repetition = 10
|
||||||
|
IPs, err := resolveRepeat(ctx, lookupIP, host, repetition, time.Second)
|
||||||
if err != nil || len(IPs) == 0 {
|
if err != nil || len(IPs) == 0 {
|
||||||
results <- models.CyberghostServer{}
|
results <- models.CyberghostServer{}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -34,10 +34,9 @@ func findPrivadoServersFromZip(ctx context.Context, client network.Client, looku
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hosts := make([]string, 0, len(contents))
|
||||||
for fileName, content := range contents {
|
for fileName, content := range contents {
|
||||||
if err := ctx.Err(); err != nil {
|
|
||||||
return nil, warnings, err
|
|
||||||
}
|
|
||||||
hostname, warning, err := extractHostFromOVPN(content)
|
hostname, warning, err := extractHostFromOVPN(content)
|
||||||
if len(warning) > 0 {
|
if len(warning) > 0 {
|
||||||
warnings = append(warnings, warning)
|
warnings = append(warnings, warning)
|
||||||
@@ -45,16 +44,23 @@ func findPrivadoServersFromZip(ctx context.Context, client network.Client, looku
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, warnings, fmt.Errorf("%w in %q", err, fileName)
|
return nil, warnings, fmt.Errorf("%w in %q", err, fileName)
|
||||||
}
|
}
|
||||||
const repetition = 1
|
hosts = append(hosts, hostname)
|
||||||
IPs, err := resolveRepeat(ctx, lookupIP, hostname, repetition)
|
}
|
||||||
switch {
|
|
||||||
case err != nil:
|
const repetition = 1
|
||||||
return nil, warnings, err
|
const timeBetween = 1
|
||||||
case len(IPs) == 0:
|
const failOnErr = false
|
||||||
|
hostToIPs, newWarnings, _ := parallelResolve(ctx, lookupIP, hosts, repetition, timeBetween, failOnErr)
|
||||||
|
warnings = append(warnings, newWarnings...)
|
||||||
|
|
||||||
|
for hostname, IPs := range hostToIPs {
|
||||||
|
switch len(IPs) {
|
||||||
|
case 0:
|
||||||
warning := fmt.Sprintf("no IP address found for host %q", hostname)
|
warning := fmt.Sprintf("no IP address found for host %q", hostname)
|
||||||
warnings = append(warnings, warning)
|
warnings = append(warnings, warning)
|
||||||
continue
|
continue
|
||||||
case len(IPs) > 1:
|
case 1:
|
||||||
|
default:
|
||||||
warning := fmt.Sprintf("more than one IP address found for host %q", hostname)
|
warning := fmt.Sprintf("more than one IP address found for host %q", hostname)
|
||||||
warnings = append(warnings, warning)
|
warnings = append(warnings, warning)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,11 +38,9 @@ func findPurevpnServers(ctx context.Context, client network.Client, lookupIP loo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
uniqueServers := map[string]models.PurevpnServer{}
|
|
||||||
|
hosts := make([]string, 0, len(contents))
|
||||||
for fileName, content := range contents {
|
for fileName, content := range contents {
|
||||||
if err := ctx.Err(); err != nil {
|
|
||||||
return nil, warnings, err
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(fileName, "-tcp.ovpn") {
|
if strings.HasSuffix(fileName, "-tcp.ovpn") {
|
||||||
continue // only parse UDP files
|
continue // only parse UDP files
|
||||||
}
|
}
|
||||||
@@ -53,12 +51,20 @@ func findPurevpnServers(ctx context.Context, client network.Client, lookupIP loo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, warnings, fmt.Errorf("%w in %q", err, fileName)
|
return nil, warnings, fmt.Errorf("%w in %q", err, fileName)
|
||||||
}
|
}
|
||||||
const repetition = 5
|
hosts = append(hosts, host)
|
||||||
IPs, err := resolveRepeat(ctx, lookupIP, host, repetition)
|
}
|
||||||
switch {
|
|
||||||
case err != nil:
|
const repetition = 20
|
||||||
return nil, warnings, err
|
const timeBetween = time.Second
|
||||||
case len(IPs) == 0:
|
const failOnErr = true
|
||||||
|
hostToIPs, _, err := parallelResolve(ctx, lookupIP, hosts, repetition, timeBetween, failOnErr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, warnings, err
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueServers := make(map[string]models.PurevpnServer, len(hostToIPs))
|
||||||
|
for host, IPs := range hostToIPs {
|
||||||
|
if len(IPs) == 0 {
|
||||||
warning := fmt.Sprintf("no IP address found for host %q", host)
|
warning := fmt.Sprintf("no IP address found for host %q", host)
|
||||||
warnings = append(warnings, warning)
|
warnings = append(warnings, warning)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
"sort"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newResolver(resolverAddress string) *net.Resolver {
|
func newResolver(resolverAddress string) *net.Resolver {
|
||||||
|
d := net.Dialer{}
|
||||||
|
resolverAddress = net.JoinHostPort(resolverAddress, "53")
|
||||||
return &net.Resolver{
|
return &net.Resolver{
|
||||||
PreferGo: true,
|
PreferGo: true,
|
||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
d := net.Dialer{}
|
return d.DialContext(ctx, "udp", resolverAddress)
|
||||||
return d.DialContext(ctx, "udp", net.JoinHostPort(resolverAddress, "53"))
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,35 +33,76 @@ func newLookupIP(r *net.Resolver) lookupIPFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string, n int) (ips []net.IP, err error) {
|
func parallelResolve(ctx context.Context, lookupIP lookupIPFunc, hosts []string,
|
||||||
foundIPs := make(chan []net.IP)
|
repetition int, timeBetween time.Duration, failOnErr bool) (
|
||||||
errors := make(chan error)
|
hostToIPs map[string][]net.IP, warnings []string, err error) {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
go func() {
|
type result struct {
|
||||||
newIPs, err := lookupIP(ctx, host)
|
host string
|
||||||
if err != nil {
|
ips []net.IP
|
||||||
errors <- err
|
|
||||||
} else {
|
|
||||||
foundIPs <- newIPs
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uniqueIPs := make(map[string]struct{})
|
results := make(chan result)
|
||||||
for i := 0; i < n; i++ {
|
defer close(results)
|
||||||
select {
|
errors := make(chan error)
|
||||||
case newIPs := <-foundIPs:
|
defer close(errors)
|
||||||
for _, ip := range newIPs {
|
|
||||||
key := ip.String()
|
for _, host := range hosts {
|
||||||
uniqueIPs[key] = struct{}{}
|
go func(host string) {
|
||||||
|
ips, err := resolveRepeat(ctx, lookupIP, host, repetition, timeBetween)
|
||||||
|
if err != nil {
|
||||||
|
errors <- err
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
results <- result{
|
||||||
|
host: host,
|
||||||
|
ips: ips,
|
||||||
|
}
|
||||||
|
}(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostToIPs = make(map[string][]net.IP, len(hosts))
|
||||||
|
|
||||||
|
for range hosts {
|
||||||
|
select {
|
||||||
case newErr := <-errors:
|
case newErr := <-errors:
|
||||||
if err == nil {
|
if !failOnErr {
|
||||||
|
warnings = append(warnings, newErr.Error())
|
||||||
|
} else if err == nil {
|
||||||
err = newErr
|
err = newErr
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
case r := <-results:
|
||||||
|
hostToIPs[r.host] = r.ips
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostToIPs, warnings, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string,
|
||||||
|
repetition int, timeBetween time.Duration) (ips []net.IP, err error) {
|
||||||
|
uniqueIPs := make(map[string]struct{})
|
||||||
|
|
||||||
|
for i := 0; i < repetition; i++ {
|
||||||
|
newIPs, err := lookupIP(ctx, host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, ip := range newIPs {
|
||||||
|
key := ip.String()
|
||||||
|
uniqueIPs[key] = struct{}{}
|
||||||
|
}
|
||||||
|
timer := time.NewTimer(timeBetween)
|
||||||
|
select {
|
||||||
|
case <-timer.C:
|
||||||
|
case <-ctx.Done():
|
||||||
|
if !timer.Stop() {
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
return nil, ctx.Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,5 +119,5 @@ func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string, n in
|
|||||||
return bytes.Compare(ips[i], ips[j]) < 1
|
return bytes.Compare(ips[i], ips[j]) < 1
|
||||||
})
|
})
|
||||||
|
|
||||||
return ips, err
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ func Test_resolveRepeat(t *testing.T) {
|
|||||||
},
|
},
|
||||||
lookupIPErr: fmt.Errorf("feeling sick"),
|
lookupIPErr: fmt.Errorf("feeling sick"),
|
||||||
n: 1,
|
n: 1,
|
||||||
ips: []net.IP{},
|
|
||||||
err: fmt.Errorf("feeling sick"),
|
err: fmt.Errorf("feeling sick"),
|
||||||
},
|
},
|
||||||
"successful": {
|
"successful": {
|
||||||
@@ -66,7 +65,7 @@ func Test_resolveRepeat(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ips, err := resolveRepeat(
|
ips, err := resolveRepeat(
|
||||||
context.Background(), lookupIP, host, testCase.n)
|
context.Background(), lookupIP, host, testCase.n, 0)
|
||||||
if testCase.err != nil {
|
if testCase.err != nil {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
"github.com/qdm12/golibs/network"
|
"github.com/qdm12/golibs/network"
|
||||||
@@ -48,13 +49,24 @@ func findSurfsharkServersFromAPI(ctx context.Context, client network.Client, loo
|
|||||||
if err := json.Unmarshal(b, &jsonServers); err != nil {
|
if err := json.Unmarshal(b, &jsonServers); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hosts := make([]string, len(jsonServers))
|
||||||
|
for i := range jsonServers {
|
||||||
|
hosts[i] = jsonServers[i].Host
|
||||||
|
}
|
||||||
|
|
||||||
|
const repetition = 20
|
||||||
|
const timeBetween = time.Second
|
||||||
|
const failOnErr = true
|
||||||
|
hostToIPs, _, err := parallelResolve(ctx, lookupIP, hosts, repetition, timeBetween, failOnErr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for _, jsonServer := range jsonServers {
|
for _, jsonServer := range jsonServers {
|
||||||
host := jsonServer.Host
|
host := jsonServer.Host
|
||||||
const repetition = 5
|
IPs := hostToIPs[host]
|
||||||
IPs, err := resolveRepeat(ctx, lookupIP, host, repetition)
|
if len(IPs) == 0 {
|
||||||
if err != nil {
|
|
||||||
return nil, warnings, err
|
|
||||||
} else if len(IPs) == 0 {
|
|
||||||
warning := fmt.Sprintf("no IP address found for host %q", host)
|
warning := fmt.Sprintf("no IP address found for host %q", host)
|
||||||
warnings = append(warnings, warning)
|
warnings = append(warnings, warning)
|
||||||
continue
|
continue
|
||||||
@@ -76,10 +88,8 @@ func findSurfsharkServersFromZip(ctx context.Context, client network.Client, loo
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
mapping := surfsharkSubdomainToRegion()
|
mapping := surfsharkSubdomainToRegion()
|
||||||
|
hosts := make([]string, 0, len(contents))
|
||||||
for fileName, content := range contents {
|
for fileName, content := range contents {
|
||||||
if err := ctx.Err(); err != nil {
|
|
||||||
return nil, warnings, err
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(fileName, "_tcp.ovpn") {
|
if strings.HasSuffix(fileName, "_tcp.ovpn") {
|
||||||
continue // only parse UDP files
|
continue // only parse UDP files
|
||||||
}
|
}
|
||||||
@@ -92,11 +102,19 @@ func findSurfsharkServersFromZip(ctx context.Context, client network.Client, loo
|
|||||||
warnings = append(warnings, err.Error()+" in "+fileName)
|
warnings = append(warnings, err.Error()+" in "+fileName)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
const repetition = 5
|
hosts = append(hosts, host)
|
||||||
IPs, err := resolveRepeat(ctx, lookupIP, host, repetition)
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, warnings, err
|
const repetition = 20
|
||||||
} else if len(IPs) == 0 {
|
const timeBetween = time.Second
|
||||||
|
const failOnErr = true
|
||||||
|
hostToIPs, _, err := parallelResolve(ctx, lookupIP, hosts, repetition, timeBetween, failOnErr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, warnings, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for host, IPs := range hostToIPs {
|
||||||
|
if len(IPs) == 0 {
|
||||||
warning := fmt.Sprintf("no IP address found for host %q", host)
|
warning := fmt.Sprintf("no IP address found for host %q", host)
|
||||||
warnings = append(warnings, warning)
|
warnings = append(warnings, warning)
|
||||||
continue
|
continue
|
||||||
@@ -118,11 +136,8 @@ func findSurfsharkServersFromZip(ctx context.Context, client network.Client, loo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process entries in mapping that were not in zip file
|
// process entries in mapping that were not in zip file
|
||||||
remainingServers, newWarnings, err := getRemainingServers(ctx, mapping, lookupIP)
|
remainingServers, newWarnings := getRemainingServers(ctx, mapping, lookupIP)
|
||||||
warnings = append(warnings, newWarnings...)
|
warnings = append(warnings, newWarnings...)
|
||||||
if err != nil {
|
|
||||||
return nil, warnings, err
|
|
||||||
}
|
|
||||||
servers = append(servers, remainingServers...)
|
servers = append(servers, remainingServers...)
|
||||||
|
|
||||||
sort.Slice(servers, func(i, j int) bool {
|
sort.Slice(servers, func(i, j int) bool {
|
||||||
@@ -132,31 +147,28 @@ func findSurfsharkServersFromZip(ctx context.Context, client network.Client, loo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getRemainingServers(ctx context.Context, mapping map[string]string, lookupIP lookupIPFunc) (
|
func getRemainingServers(ctx context.Context, mapping map[string]string, lookupIP lookupIPFunc) (
|
||||||
servers []models.SurfsharkServer, warnings []string, err error) {
|
servers []models.SurfsharkServer, warnings []string) {
|
||||||
for subdomain, region := range mapping {
|
hosts := make([]string, len(mapping))
|
||||||
if err := ctx.Err(); err != nil {
|
i := 0
|
||||||
return servers, warnings, err
|
for subdomain := range mapping {
|
||||||
}
|
hosts[i] = subdomain + ".prod.surfshark.com"
|
||||||
host := subdomain + ".prod.surfshark.com"
|
}
|
||||||
const repetition = 3
|
|
||||||
IPs, err := resolveRepeat(ctx, lookupIP, host, repetition)
|
const repetition = 20
|
||||||
if err != nil {
|
const timeBetween = time.Second
|
||||||
warning := fmt.Sprintf("subdomain %q for region %q from mapping: %s", subdomain, region, err)
|
const failOnErr = false
|
||||||
warnings = append(warnings, warning)
|
hostToIPs, warnings, _ := parallelResolve(ctx, lookupIP, hosts, repetition, timeBetween, failOnErr)
|
||||||
continue
|
|
||||||
} else if len(IPs) == 0 {
|
for host, IPs := range hostToIPs {
|
||||||
warning := fmt.Sprintf("subdomain %q for region %q from mapping did not resolve to any IP address",
|
subdomain := strings.TrimSuffix(host, ".prod.surfshark.com")
|
||||||
subdomain, region)
|
|
||||||
warnings = append(warnings, warning)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
server := models.SurfsharkServer{
|
server := models.SurfsharkServer{
|
||||||
Region: region,
|
Region: mapping[subdomain],
|
||||||
IPs: uniqueSortedIPs(IPs),
|
IPs: uniqueSortedIPs(IPs),
|
||||||
}
|
}
|
||||||
servers = append(servers, server)
|
servers = append(servers, server)
|
||||||
}
|
}
|
||||||
return servers, warnings, nil
|
|
||||||
|
return servers, warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringifySurfsharkServers(servers []models.SurfsharkServer) (s string) {
|
func stringifySurfsharkServers(servers []models.SurfsharkServer) (s string) {
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ func findVyprvpnServers(ctx context.Context, client network.Client, lookupIP loo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostToRegion := make(map[string]string, len(contents))
|
||||||
for fileName, content := range contents {
|
for fileName, content := range contents {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, warnings, err
|
return nil, warnings, err
|
||||||
@@ -46,15 +48,29 @@ func findVyprvpnServers(ctx context.Context, client network.Client, lookupIP loo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, warnings, fmt.Errorf("%w in %s", err, fileName)
|
return nil, warnings, fmt.Errorf("%w in %s", err, fileName)
|
||||||
}
|
}
|
||||||
const repetitions = 1
|
|
||||||
IPs, err := resolveRepeat(ctx, lookupIP, host, repetitions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, warnings, err
|
|
||||||
}
|
|
||||||
region := strings.TrimSuffix(fileName, ".ovpn")
|
region := strings.TrimSuffix(fileName, ".ovpn")
|
||||||
region = strings.ReplaceAll(region, " - ", " ")
|
region = strings.ReplaceAll(region, " - ", " ")
|
||||||
|
hostToRegion[host] = region
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts := make([]string, len(hostToRegion))
|
||||||
|
i := 0
|
||||||
|
for host := range hostToRegion {
|
||||||
|
hosts[i] = host
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
const repetition = 1
|
||||||
|
const timeBetween = 1
|
||||||
|
const failOnErr = true
|
||||||
|
hostToIPs, _, err := parallelResolve(ctx, lookupIP, hosts, repetition, timeBetween, failOnErr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, warnings, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for host, IPs := range hostToIPs {
|
||||||
server := models.VyprvpnServer{
|
server := models.VyprvpnServer{
|
||||||
Region: region,
|
Region: hostToRegion[host],
|
||||||
IPs: uniqueSortedIPs(IPs),
|
IPs: uniqueSortedIPs(IPs),
|
||||||
}
|
}
|
||||||
servers = append(servers, server)
|
servers = append(servers, server)
|
||||||
|
|||||||
Reference in New Issue
Block a user