From c3a680944785f8338bae55670a01e301bc830a4b Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Wed, 22 Apr 2026 00:42:23 +0000 Subject: [PATCH] fix(pia): try x.y.128.1 and x.y.0.1 from the gateway IP to find the API IP address --- .../privateinternetaccess/portforward.go | 60 +++++++++++++++---- .../privateinternetaccess/provider.go | 2 + 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/internal/provider/privateinternetaccess/portforward.go b/internal/provider/privateinternetaccess/portforward.go index be2a4b5b..7fa2ca4c 100644 --- a/internal/provider/privateinternetaccess/portforward.go +++ b/internal/provider/privateinternetaccess/portforward.go @@ -39,7 +39,6 @@ func (p *Provider) PortForward(ctx context.Context, } serverName := objects.ServerName - apiIP := buildAPIIPAddress(objects.Gateway) logger := objects.Logger if !objects.CanPortForward { @@ -68,9 +67,14 @@ func (p *Provider) PortForward(ctx context.Context, } } + p.apiIP, err = findAPIIP(ctx, privateIPClient, objects.Gateway) + if err != nil { + return nil, fmt.Errorf("finding API IP address: %w", err) + } + if !dataFound || expired { client := objects.Client - data, err = refreshPIAPortForwardData(ctx, client, privateIPClient, apiIP, + data, err = refreshPIAPortForwardData(ctx, client, privateIPClient, p.apiIP, p.portForwardPath, objects.Username, objects.Password) if err != nil { return nil, fmt.Errorf("refreshing port forward data: %w", err) @@ -80,7 +84,7 @@ func (p *Provider) PortForward(ctx context.Context, logger.Info("Port forwarded data expires in " + format.FriendlyDuration(durationToExpiration)) // First time binding - if err := bindPort(ctx, privateIPClient, apiIP, data); err != nil { + if err := bindPort(ctx, privateIPClient, p.apiIP, data); err != nil { return nil, fmt.Errorf("binding port: %w", err) } @@ -99,8 +103,6 @@ func (p *Provider) KeepPortForward(ctx context.Context, panic("gateway is not set") } - apiIP := buildAPIIPAddress(objects.Gateway) - privateIPClient, err := newHTTPClient(objects.ServerName) if err != nil { return fmt.Errorf("creating custom HTTP client: %w", err) @@ -128,7 +130,7 @@ func (p *Provider) KeepPortForward(ctx context.Context, } return ctx.Err() case <-keepAliveTimer.C: - err = bindPort(ctx, privateIPClient, apiIP, data) + err = bindPort(ctx, privateIPClient, p.apiIP, data) if err != nil { return fmt.Errorf("binding port: %w", err) } @@ -140,15 +142,53 @@ func (p *Provider) KeepPortForward(ctx context.Context, } } -func buildAPIIPAddress(gateway netip.Addr) (api netip.Addr) { +var errAPIIPNotFound = errors.New("API IP address not found") + +func findAPIIP(ctx context.Context, client *http.Client, gateway netip.Addr) ( + apiIP netip.Addr, err error, +) { if gateway.Is6() { panic("IPv6 gateway not supported") } gatewayBytes := gateway.As4() - gatewayBytes[2] = 128 - gatewayBytes[3] = 1 - return netip.AddrFrom4(gatewayBytes) + gatewayBytes[3] = 1 // x.y.z.1 + + gatewayBytes[2] = 128 // x.y.128.1 + oldAPIIP := netip.AddrFrom4(gatewayBytes) + gatewayBytes[2] = 0 // x.y.0.1 - new API IP reported by some users + newAPIIP := netip.AddrFrom4(gatewayBytes) + possibleIPs := []netip.Addr{oldAPIIP, newAPIIP} + + errs := make([]error, 0, len(possibleIPs)) + for _, ip := range possibleIPs { + const timeout = 5 * time.Second + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + url := url.URL{ + Scheme: "https", + Host: net.JoinHostPort(ip.String(), "19999"), + Path: "/ping", + } + + request, err := http.NewRequestWithContext(ctx, http.MethodGet, url.String(), nil) + if err != nil { + errs = append(errs, fmt.Errorf("trying IP %s: %w", ip, err)) + continue + } + + response, err := client.Do(request) + if err != nil { + errs = append(errs, fmt.Errorf("trying IP %s: %w", ip, err)) + continue + } + + _ = response.Body.Close() + return ip, nil + } + + return netip.Addr{}, fmt.Errorf("%w: %w", errAPIIPNotFound, errors.Join(errs...)) } func refreshPIAPortForwardData(ctx context.Context, client, privateIPClient *http.Client, diff --git a/internal/provider/privateinternetaccess/provider.go b/internal/provider/privateinternetaccess/provider.go index eeb5b549..ca64f31a 100644 --- a/internal/provider/privateinternetaccess/provider.go +++ b/internal/provider/privateinternetaccess/provider.go @@ -3,6 +3,7 @@ package privateinternetaccess import ( "math/rand" "net/http" + "net/netip" "time" "github.com/qdm12/gluetun/internal/constants/providers" @@ -17,6 +18,7 @@ type Provider struct { common.Fetcher // Port forwarding portForwardPath string + apiIP netip.Addr } func New(storage common.Storage, randSource rand.Source,