mirror of
https://github.com/qdm12/gluetun.git
synced 2026-06-18 09:24:07 +02:00
chore(updater): move updater packages to pkg/updaters/<name>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package privatevpn
|
||||
|
||||
import "strings"
|
||||
|
||||
func codeToCountry(countryCode string, countryCodes map[string]string) (
|
||||
country string, warning string,
|
||||
) {
|
||||
countryCode = strings.ToLower(countryCode)
|
||||
country, ok := countryCodes[countryCode]
|
||||
if !ok {
|
||||
warning = "unknown country code: " + countryCode
|
||||
country = countryCode
|
||||
}
|
||||
return country, warning
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package privatevpn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var trailingNumber = regexp.MustCompile(` [0-9]+$`)
|
||||
|
||||
var (
|
||||
errBadPrefix = errors.New("bad prefix in file name")
|
||||
errBadSuffix = errors.New("bad suffix in file name")
|
||||
errNotEnoughParts = errors.New("not enough parts in file name")
|
||||
)
|
||||
|
||||
func parseFilename(fileName string) (
|
||||
countryCode, city string, err error,
|
||||
) {
|
||||
fileName = strings.ReplaceAll(fileName, " ", "") // remove spaces
|
||||
|
||||
const prefix = "PrivateVPN-"
|
||||
if !strings.HasPrefix(fileName, prefix) {
|
||||
return "", "", fmt.Errorf("%w: %s", errBadPrefix, fileName)
|
||||
}
|
||||
s := strings.TrimPrefix(fileName, prefix)
|
||||
|
||||
const tcpSuffix = "-TUN-443.ovpn"
|
||||
const udpSuffix = "-TUN-1194.ovpn"
|
||||
switch {
|
||||
case strings.HasSuffix(fileName, tcpSuffix):
|
||||
s = strings.TrimSuffix(s, tcpSuffix)
|
||||
case strings.HasSuffix(fileName, udpSuffix):
|
||||
s = strings.TrimSuffix(s, udpSuffix)
|
||||
default:
|
||||
return "", "", fmt.Errorf("%w: %s", errBadSuffix, fileName)
|
||||
}
|
||||
|
||||
s = trailingNumber.ReplaceAllString(s, "")
|
||||
|
||||
parts := strings.Split(s, "-")
|
||||
const minParts = 2
|
||||
if len(parts) < minParts {
|
||||
return "", "", fmt.Errorf("%w: %s", errNotEnoughParts, fileName)
|
||||
}
|
||||
countryCode, city = parts[0], parts[1]
|
||||
|
||||
countryCode = strings.ToLower(countryCode)
|
||||
if countryCode == "co" && strings.HasPrefix(city, "Bogot") {
|
||||
city = "Bogota"
|
||||
}
|
||||
|
||||
return countryCode, city, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package privatevpn
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type hostToServer map[string]models.Server
|
||||
|
||||
// TODO check if server supports TCP and UDP.
|
||||
func (hts hostToServer) add(host, country, city string) {
|
||||
server, ok := hts[host]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
server.VPN = vpn.OpenVPN
|
||||
server.Hostname = host
|
||||
server.Country = country
|
||||
server.City = city
|
||||
server.UDP = true
|
||||
server.TCP = true
|
||||
hts[host] = server
|
||||
}
|
||||
|
||||
func (hts hostToServer) toHostsSlice() (hosts []string) {
|
||||
hosts = make([]string, 0, len(hts))
|
||||
for host := range hts {
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
|
||||
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
|
||||
for host, IPs := range hostToIPs {
|
||||
server := hts[host]
|
||||
server.IPs = IPs
|
||||
hts[host] = server
|
||||
}
|
||||
for host, server := range hts {
|
||||
if len(server.IPs) == 0 {
|
||||
delete(hts, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (hts hostToServer) toServersSlice() (servers []models.Server) {
|
||||
servers = make([]models.Server, 0, len(hts))
|
||||
for _, server := range hts {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package privatevpn
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
)
|
||||
|
||||
func parallelResolverSettings(hosts []string) (settings resolver.ParallelSettings) {
|
||||
const (
|
||||
maxFailRatio = 0.1
|
||||
maxDuration = 6 * time.Second
|
||||
betweenDuration = time.Second
|
||||
maxNoNew = 2
|
||||
maxFails = 2
|
||||
)
|
||||
return resolver.ParallelSettings{
|
||||
Hosts: hosts,
|
||||
MaxFailRatio: maxFailRatio,
|
||||
Repeat: resolver.RepeatSettings{
|
||||
MaxDuration: maxDuration,
|
||||
BetweenDuration: betweenDuration,
|
||||
MaxNoNew: maxNoNew,
|
||||
MaxFails: maxFails,
|
||||
SortIPs: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package privatevpn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/common"
|
||||
"github.com/qdm12/gluetun/internal/updater/openvpn"
|
||||
)
|
||||
|
||||
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
|
||||
servers []models.Server, err error,
|
||||
) {
|
||||
const url = "https://privatevpn.com/client/PrivateVPN-TUN.zip"
|
||||
contents, err := u.unzipper.FetchAndExtract(ctx, url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(contents) < minServers {
|
||||
return nil, fmt.Errorf("%w: %d and expected at least %d",
|
||||
common.ErrNotEnoughServers, len(contents), minServers)
|
||||
}
|
||||
|
||||
countryCodes := constants.CountryCodes()
|
||||
|
||||
hts := make(hostToServer)
|
||||
noHostnameServers := make([]models.Server, 0, 1) // there is only one for now
|
||||
|
||||
for fileName, content := range contents {
|
||||
if !strings.HasSuffix(fileName, ".ovpn") {
|
||||
continue // not an OpenVPN file
|
||||
}
|
||||
|
||||
countryCode, city, err := parseFilename(fileName)
|
||||
if err != nil {
|
||||
// treat error as warning and go to next file
|
||||
u.warner.Warn(err.Error() + " in " + fileName)
|
||||
continue
|
||||
}
|
||||
|
||||
country, warning := codeToCountry(countryCode, countryCodes)
|
||||
if warning != "" {
|
||||
u.warner.Warn(warning)
|
||||
}
|
||||
|
||||
host, warning, err := openvpn.ExtractHost(content)
|
||||
if warning != "" {
|
||||
u.warner.Warn(warning)
|
||||
}
|
||||
if err == nil { // found host
|
||||
hts.add(host, country, city)
|
||||
continue
|
||||
}
|
||||
|
||||
ips, extractIPErr := openvpn.ExtractIPs(content)
|
||||
if warning != "" {
|
||||
u.warner.Warn(warning)
|
||||
}
|
||||
if extractIPErr != nil {
|
||||
// treat extract host error as warning and go to next file
|
||||
u.warner.Warn(extractIPErr.Error() + " in " + fileName)
|
||||
continue
|
||||
}
|
||||
server := models.Server{
|
||||
VPN: vpn.OpenVPN,
|
||||
Country: country,
|
||||
City: city,
|
||||
IPs: ips,
|
||||
UDP: true,
|
||||
TCP: true,
|
||||
}
|
||||
noHostnameServers = append(noHostnameServers, server)
|
||||
}
|
||||
|
||||
if len(noHostnameServers)+len(hts) < minServers {
|
||||
return nil, fmt.Errorf("%w: %d and expected at least %d",
|
||||
common.ErrNotEnoughServers, len(servers)+len(hts), minServers)
|
||||
}
|
||||
|
||||
hosts := hts.toHostsSlice()
|
||||
|
||||
resolveSettings := parallelResolverSettings(hosts)
|
||||
hostToIPs, warnings, err := u.parallelResolver.Resolve(ctx, resolveSettings)
|
||||
for _, warning := range warnings {
|
||||
u.warner.Warn(warning)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(noHostnameServers)+len(hostToIPs) < minServers {
|
||||
return nil, fmt.Errorf("%w: %d and expected at least %d",
|
||||
common.ErrNotEnoughServers, len(servers), minServers)
|
||||
}
|
||||
|
||||
hts.adaptWithIPs(hostToIPs)
|
||||
|
||||
servers = hts.toServersSlice()
|
||||
servers = append(servers, noHostnameServers...)
|
||||
|
||||
sort.Sort(models.SortableServers(servers))
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package privatevpn
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/provider/common"
|
||||
)
|
||||
|
||||
type Updater struct {
|
||||
unzipper common.Unzipper
|
||||
parallelResolver common.ParallelResolver
|
||||
warner common.Warner
|
||||
}
|
||||
|
||||
func New(unzipper common.Unzipper, warner common.Warner,
|
||||
parallelResolver common.ParallelResolver,
|
||||
) *Updater {
|
||||
return &Updater{
|
||||
unzipper: unzipper,
|
||||
parallelResolver: parallelResolver,
|
||||
warner: warner,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Updater) Version() uint16 {
|
||||
return 4 //nolint:mnd
|
||||
}
|
||||
Reference in New Issue
Block a user