104 lines
2.8 KiB
Go
104 lines
2.8 KiB
Go
package protonvpn
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/netip"
|
|
"time"
|
|
|
|
"github.com/qdm12/gluetun/internal/natpmp"
|
|
"github.com/qdm12/gluetun/internal/provider/utils"
|
|
)
|
|
|
|
var (
|
|
ErrGatewayIPNotValid = errors.New("gateway IP address is not valid")
|
|
)
|
|
|
|
// PortForward obtains a VPN server side port forwarded from ProtonVPN gateway.
|
|
func (p *Provider) PortForward(ctx context.Context, _ *http.Client,
|
|
logger utils.Logger, gateway netip.Addr, _ string) (
|
|
port uint16, err error) {
|
|
if !gateway.IsValid() {
|
|
return 0, fmt.Errorf("%w", ErrGatewayIPNotValid)
|
|
}
|
|
|
|
client := natpmp.New()
|
|
_, externalIPv4Address, err := client.ExternalAddress(ctx,
|
|
gateway)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("getting external IPv4 address: %w", err)
|
|
}
|
|
|
|
logger.Info("gateway external IPv4 address is " + externalIPv4Address.String())
|
|
networkProtocols := []string{"udp", "tcp"}
|
|
const internalPort, externalPort = 0, 0
|
|
const lifetime = 60 * time.Second
|
|
for _, networkProtocol := range networkProtocols {
|
|
_, assignedInternalPort, assignedExternalPort, assignedLiftetime, err :=
|
|
client.AddPortMapping(ctx, gateway, networkProtocol,
|
|
internalPort, externalPort, lifetime)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("adding port mapping: %w", err)
|
|
}
|
|
|
|
if assignedLiftetime != lifetime {
|
|
logger.Warn(fmt.Sprintf("assigned lifetime %s differs"+
|
|
" from requested lifetime %s",
|
|
assignedLiftetime, lifetime))
|
|
}
|
|
|
|
if assignedInternalPort != assignedExternalPort {
|
|
logger.Warn(fmt.Sprintf("internal port assigned %d differs"+
|
|
" from external port assigned %d",
|
|
assignedInternalPort, assignedExternalPort))
|
|
}
|
|
|
|
port = assignedExternalPort
|
|
}
|
|
|
|
return port, nil
|
|
}
|
|
|
|
func (p *Provider) KeepPortForward(ctx context.Context, port uint16,
|
|
gateway netip.Addr, _ string, logger utils.Logger) (err error) {
|
|
client := natpmp.New()
|
|
const refreshTimeout = 45 * time.Second
|
|
timer := time.NewTimer(refreshTimeout)
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
case <-timer.C:
|
|
}
|
|
|
|
networkProtocols := []string{"udp", "tcp"}
|
|
const internalPort = 0
|
|
const lifetime = 60 * time.Second
|
|
|
|
for _, networkProtocol := range networkProtocols {
|
|
_, assignedInternalPort, assignedExternalPort, assignedLiftetime, err :=
|
|
client.AddPortMapping(ctx, gateway, networkProtocol,
|
|
internalPort, port, lifetime)
|
|
if err != nil {
|
|
return fmt.Errorf("adding port mapping: %w", err)
|
|
}
|
|
|
|
if assignedLiftetime != lifetime {
|
|
logger.Warn(fmt.Sprintf("assigned lifetime %s differs"+
|
|
" from requested lifetime %s",
|
|
assignedLiftetime, lifetime))
|
|
}
|
|
|
|
if assignedInternalPort != assignedExternalPort {
|
|
logger.Warn(fmt.Sprintf("internal port assigned %d differs"+
|
|
" from external port assigned %d",
|
|
assignedInternalPort, assignedExternalPort))
|
|
}
|
|
}
|
|
|
|
timer.Reset(refreshTimeout)
|
|
}
|
|
}
|