IVPN server data update code and ISP filter (#578)

- Use IVPN's HTTP API instead of their .zip file
- Unit tests for API and GetServers
- Paves the way for Wireguard
- Update server information for IVPN
- Add `ISP` filter for IVPN
This commit is contained in:
Quentin McGaw
2021-08-22 20:11:56 -07:00
committed by GitHub
parent b69dcb62e3
commit c348343b22
17 changed files with 711 additions and 614 deletions

View File

@@ -6,78 +6,64 @@ 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 (
ErrFetchAPI = errors.New("failed fetching API")
ErrNotEnoughServers = errors.New("not enough servers found")
)
func GetServers(ctx context.Context, unzipper unzip.Unzipper,
func GetServers(ctx context.Context, client *http.Client,
presolver resolver.Parallel, minServers int) (
servers []models.IvpnServer, warnings []string, err error) {
const url = "https://www.ivpn.net/releases/config/ivpn-openvpn-config.zip"
contents, err := unzipper.FetchAndExtract(ctx, url)
data, err := fetchAPI(ctx, client)
if err != nil {
return nil, nil, err
} else if len(contents) < minServers {
return nil, nil, fmt.Errorf("%w: %s", ErrFetchAPI, err)
}
hosts := make([]string, 0, len(data.Servers))
for _, serverData := range data.Servers {
host := serverData.Hostnames.OpenVPN
if host == "" {
continue // Wireguard
}
hosts = append(hosts, host)
}
if len(hosts) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers)
ErrNotEnoughServers, len(hosts), minServers)
}
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
}
country, city := parseFilename(fileName)
hts.add(host, country, city, tcp, udp)
}
if len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hts), minServers)
}
hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers)
warnings = append(warnings, newWarnings...)
hostToIPs, warnings, err := resolveHosts(ctx, presolver, hosts, minServers)
if err != nil {
return nil, warnings, err
}
hts.adaptWithIPs(hostToIPs)
servers = make([]models.IvpnServer, 0, len(hosts))
for _, serverData := range data.Servers {
host := serverData.Hostnames.OpenVPN
if serverData.Hostnames.OpenVPN == "" {
continue // Wireguard
}
servers = hts.toServersSlice()
if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers)
server := models.IvpnServer{
Country: serverData.Country,
City: serverData.City,
ISP: serverData.ISP,
Hostname: serverData.Hostnames.OpenVPN,
// TCP is not supported
UDP: true,
IPs: hostToIPs[host],
}
servers = append(servers, server)
}
sortServers(servers)