chore(all): replace net.IP with netip.Addr

This commit is contained in:
Quentin McGaw
2023-05-20 19:58:18 +00:00
parent 00ee6ff9a7
commit 0a29337c3b
91 changed files with 525 additions and 590 deletions

View File

@@ -3,8 +3,8 @@ package cli
import (
"context"
"fmt"
"net"
"net/http"
"net/netip"
"strings"
"time"
@@ -28,11 +28,11 @@ type Unzipper interface {
type ParallelResolver interface {
Resolve(ctx context.Context, settings resolver.ParallelSettings) (
hostToIPs map[string][]net.IP, warnings []string, err error)
hostToIPs map[string][]netip.Addr, warnings []string, err error)
}
type IPFetcher interface {
FetchMultiInfo(ctx context.Context, ips []net.IP) (data []ipinfo.Response, err error)
FetchMultiInfo(ctx context.Context, ips []netip.Addr) (data []ipinfo.Response, err error)
}
type IPv6Checker interface {

View File

@@ -2,7 +2,7 @@ package settings
import (
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gotree"
@@ -13,9 +13,9 @@ type DNS struct {
// ServerAddress is the DNS server to use inside
// the Go program and for the system.
// It defaults to '127.0.0.1' to be used with the
// DoT server. It cannot be nil in the internal
// DoT server. It cannot be the zero value in the internal
// state.
ServerAddress net.IP
ServerAddress netip.Addr
// KeepNameserver is true if the Docker DNS server
// found in /etc/resolv.conf should be kept.
// Note settings this to true will go around the
@@ -39,7 +39,7 @@ func (d DNS) validate() (err error) {
func (d *DNS) Copy() (copied DNS) {
return DNS{
ServerAddress: helpers.CopyIP(d.ServerAddress),
ServerAddress: d.ServerAddress,
KeepNameserver: helpers.CopyBoolPtr(d.KeepNameserver),
DoT: d.DoT.copy(),
}
@@ -63,7 +63,7 @@ func (d *DNS) overrideWith(other DNS) {
}
func (d *DNS) setDefaults() {
localhost := net.IPv4(127, 0, 0, 1) //nolint:gomnd
localhost := netip.AddrFrom4([4]byte{127, 0, 0, 1})
d.ServerAddress = helpers.DefaultIP(d.ServerAddress, localhost)
d.KeepNameserver = helpers.DefaultBool(d.KeepNameserver, false)
d.DoT.setDefaults()

View File

@@ -1,8 +1,6 @@
package helpers
import (
"fmt"
"net"
"net/netip"
"time"
@@ -81,25 +79,6 @@ func CopyLogLevelPtr(original *log.Level) (copied *log.Level) {
return copied
}
func CopyIP(original net.IP) (copied net.IP) {
if original == nil {
return nil
}
copied = make(net.IP, len(original))
copy(copied, original)
return copied
}
func CopyNetipAddress(original netip.Addr) (copied netip.Addr) {
// AsSlice creates a new byte slice so no need to copy the bytes.
bytes := original.AsSlice()
copied, ok := netip.AddrFromSlice(bytes)
if !ok {
panic(fmt.Sprintf("cannot deep copy address with bytes %#v", bytes))
}
return copied
}
func CopyStringSlice(original []string) (copied []string) {
if original == nil {
return nil
@@ -136,9 +115,6 @@ func CopyNetipAddressesSlice(original []netip.Addr) (copied []netip.Addr) {
}
copied = make([]netip.Addr, len(original))
for i := range original {
copied[i] = CopyNetipAddress(original[i])
}
copy(copied, original)
return copied
}

View File

@@ -1,7 +1,7 @@
package helpers
import (
"net"
"net/netip"
"time"
"github.com/qdm12/log"
@@ -101,9 +101,9 @@ func DefaultLogLevel(existing *log.Level,
return result
}
func DefaultIP(existing net.IP, defaultValue net.IP) (
result net.IP) {
if existing != nil {
func DefaultIP(existing netip.Addr, defaultValue netip.Addr) (
result netip.Addr) {
if existing.IsValid() {
return existing
}
return defaultValue

View File

@@ -1,7 +1,7 @@
package helpers
import (
"net"
"fmt"
"net/http"
"net/netip"
"time"
@@ -96,14 +96,17 @@ func MergeWithUint32(existing, other *uint32) (result *uint32) {
return result
}
func MergeWithIP(existing, other net.IP) (result net.IP) {
if existing != nil {
func MergeWithIP(existing, other netip.Addr) (result netip.Addr) {
if existing.IsValid() {
return existing
} else if !other.IsValid() {
return existing
} else if other == nil {
return nil
}
result = make(net.IP, len(other))
copy(result, other)
result, ok := netip.AddrFromSlice(other.AsSlice())
if !ok {
panic(fmt.Sprintf("failed copying other address: %s", other))
}
return result
}

View File

@@ -1,7 +1,7 @@
package helpers
import (
"net"
"fmt"
"net/http"
"net/netip"
"time"
@@ -84,12 +84,14 @@ func OverrideWithUint32(existing, other *uint32) (result *uint32) {
return result
}
func OverrideWithIP(existing, other net.IP) (result net.IP) {
if other == nil {
func OverrideWithIP(existing, other netip.Addr) (result netip.Addr) {
if !other.IsValid() {
return existing
}
result = make(net.IP, len(other))
copy(result, other)
result, ok := netip.AddrFromSlice(other.AsSlice())
if !ok {
panic(fmt.Sprintf("failed copying other address: %s", other))
}
return result
}

View File

@@ -3,7 +3,7 @@ package settings
import (
"errors"
"fmt"
"net"
"net/netip"
"strings"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
@@ -21,10 +21,10 @@ type ServerSelection struct { //nolint:maligned
VPN string
// TargetIP is the server endpoint IP address to use.
// It will override any IP address from the picked
// built-in server. It cannot be nil in the internal
// state, and can be set to an empty net.IP{} to indicate
// built-in server. It cannot be the empty value in the internal
// state, and can be set to the unspecified address to indicate
// there is not target IP address to use.
TargetIP net.IP
TargetIP netip.Addr
// Counties is the list of countries to filter VPN servers with.
Countries []string
// Regions is the list of regions to filter VPN servers with.
@@ -202,7 +202,7 @@ func validateServerFilters(settings ServerSelection, filterChoices models.Filter
func (ss *ServerSelection) copy() (copied ServerSelection) {
return ServerSelection{
VPN: ss.VPN,
TargetIP: helpers.CopyIP(ss.TargetIP),
TargetIP: ss.TargetIP,
Countries: helpers.CopyStringSlice(ss.Countries),
Regions: helpers.CopyStringSlice(ss.Regions),
Cities: helpers.CopyStringSlice(ss.Cities),
@@ -261,7 +261,7 @@ func (ss *ServerSelection) overrideWith(other ServerSelection) {
func (ss *ServerSelection) setDefaults(vpnProvider string) {
ss.VPN = helpers.DefaultString(ss.VPN, vpn.OpenVPN)
ss.TargetIP = helpers.DefaultIP(ss.TargetIP, net.IP{})
ss.TargetIP = helpers.DefaultIP(ss.TargetIP, netip.IPv4Unspecified())
ss.OwnedOnly = helpers.DefaultBool(ss.OwnedOnly, false)
ss.FreeOnly = helpers.DefaultBool(ss.FreeOnly, false)
ss.PremiumOnly = helpers.DefaultBool(ss.PremiumOnly, false)
@@ -278,7 +278,7 @@ func (ss ServerSelection) String() string {
func (ss ServerSelection) toLinesNode() (node *gotree.Node) {
node = gotree.New("Server selection settings:")
node.Appendf("VPN type: %s", ss.VPN)
if len(ss.TargetIP) > 0 {
if !ss.TargetIP.IsUnspecified() {
node.Appendf("Target IP address: %s", ss.TargetIP)
}

View File

@@ -3,7 +3,6 @@ package settings
import (
"errors"
"fmt"
"net"
"net/netip"
"github.com/qdm12/dns/pkg/provider"
@@ -155,14 +154,24 @@ func (u Unbound) ToUnboundFormat() (settings unbound.Settings, err error) {
}, nil
}
func (u Unbound) GetFirstPlaintextIPv4() (ipv4 net.IP, err error) {
var (
ErrConvertingNetip = errors.New("converting net.IP to netip.Addr failed")
)
func (u Unbound) GetFirstPlaintextIPv4() (ipv4 netip.Addr, err error) {
s := u.Providers[0]
provider, err := provider.Parse(s)
if err != nil {
return nil, err
return ipv4, err
}
return provider.DNS().IPv4[0], nil
ip := provider.DNS().IPv4[0]
ipv4, ok := netip.AddrFromSlice(ip)
if !ok {
return ipv4, fmt.Errorf("%w: for ip %s (%#v)",
ErrConvertingNetip, ip, ip)
}
return ipv4.Unmap(), nil
}
func (u Unbound) String() string {

View File

@@ -2,7 +2,7 @@ package settings
import (
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
"github.com/qdm12/gluetun/internal/constants/providers"
@@ -15,9 +15,9 @@ type WireguardSelection struct {
// It is only used with VPN providers generating Wireguard
// configurations specific to each server and user.
// To indicate it should not be used, it should be set
// to the empty net.IP{} slice. It can never be nil
// to netaddr.IPv4Unspecified(). It can never be the zero value
// in the internal state.
EndpointIP net.IP
EndpointIP netip.Addr
// EndpointPort is a the server port to use for the VPN server.
// It is optional for VPN providers IVPN, Mullvad, Surfshark
// and Windscribe, and compulsory for the others.
@@ -40,7 +40,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
providers.Surfshark, providers.Windscribe:
// endpoint IP addresses are baked in
case providers.Custom:
if len(w.EndpointIP) == 0 {
if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() {
return fmt.Errorf("%w", ErrWireguardEndpointIPNotSet)
}
default: // Providers not supporting Wireguard
@@ -109,7 +109,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
func (w *WireguardSelection) copy() (copied WireguardSelection) {
return WireguardSelection{
EndpointIP: helpers.CopyIP(w.EndpointIP),
EndpointIP: w.EndpointIP,
EndpointPort: helpers.CopyUint16Ptr(w.EndpointPort),
PublicKey: w.PublicKey,
}
@@ -128,7 +128,7 @@ func (w *WireguardSelection) overrideWith(other WireguardSelection) {
}
func (w *WireguardSelection) setDefaults() {
w.EndpointIP = helpers.DefaultIP(w.EndpointIP, net.IP{})
w.EndpointIP = helpers.DefaultIP(w.EndpointIP, netip.IPv4Unspecified())
w.EndpointPort = helpers.DefaultUint16(w.EndpointPort, 0)
}
@@ -139,7 +139,7 @@ func (w WireguardSelection) String() string {
func (w WireguardSelection) toLinesNode() (node *gotree.Node) {
node = gotree.New("Wireguard selection settings:")
if len(w.EndpointIP) > 0 {
if !w.EndpointIP.IsUnspecified() {
node.Appendf("Endpoint IP address: %s", w.EndpointIP)
}

View File

@@ -2,7 +2,7 @@ package env
import (
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings"
)
@@ -26,19 +26,19 @@ func (s *Source) readDNS() (dns settings.DNS, err error) {
return dns, nil
}
func (s *Source) readDNSServerAddress() (address net.IP, err error) {
func (s *Source) readDNSServerAddress() (address netip.Addr, err error) {
key, value := s.getEnvWithRetro("DNS_ADDRESS", "DNS_PLAINTEXT_ADDRESS")
if value == "" {
return nil, nil
return address, nil
}
address = net.ParseIP(value)
if address == nil {
return nil, fmt.Errorf("environment variable %s: %w: %s", key, ErrIPAddressParse, value)
address, err = netip.ParseAddr(value)
if err != nil {
return address, fmt.Errorf("environment variable %s: %w", key, err)
}
// TODO remove in v4
if !address.Equal(net.IPv4(127, 0, 0, 1)) { //nolint:gomnd
if address.Unmap().Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
s.warner.Warn(key + " is set to " + value +
" so the DNS over TLS (DoT) server will not be used." +
" The default value changed to 127.0.0.1 so it uses the internal DoT serves." +

View File

@@ -3,7 +3,7 @@ package env
import (
"errors"
"fmt"
"net"
"net/netip"
"strconv"
"strings"
@@ -113,16 +113,15 @@ var (
ErrInvalidIP = errors.New("invalid IP address")
)
func (s *Source) readOpenVPNTargetIP() (ip net.IP, err error) {
func (s *Source) readOpenVPNTargetIP() (ip netip.Addr, err error) {
envKey, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "OPENVPN_TARGET_IP")
if value == "" {
return nil, nil
return ip, nil
}
ip = net.ParseIP(value)
if ip == nil {
return nil, fmt.Errorf("environment variable %s: %w: %s",
envKey, ErrInvalidIP, value)
ip, err = netip.ParseAddr(value)
if err != nil {
return ip, fmt.Errorf("environment variable %s: %w", envKey, err)
}
return ip, nil

View File

@@ -1,9 +1,8 @@
package env
import (
"errors"
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/govalid/port"
@@ -26,18 +25,15 @@ func (s *Source) readWireguardSelection() (
return selection, nil
}
var ErrIPAddressParse = errors.New("cannot parse IP address")
func (s *Source) readWireguardEndpointIP() (endpointIP net.IP, err error) {
func (s *Source) readWireguardEndpointIP() (endpointIP netip.Addr, err error) {
key, value := s.getEnvWithRetro("VPN_ENDPOINT_IP", "WIREGUARD_ENDPOINT_IP")
if value == "" {
return nil, nil
return endpointIP, nil
}
endpointIP = net.ParseIP(value)
if endpointIP == nil {
return nil, fmt.Errorf("environment variable %s: %w: %s",
key, ErrIPAddressParse, value)
endpointIP, err = netip.ParseAddr(value)
if err != nil {
return endpointIP, fmt.Errorf("environment variable %s: %w", key, err)
}
return endpointIP, nil

View File

@@ -1,7 +1,7 @@
package dns
import (
"net"
"net/netip"
"github.com/qdm12/dns/pkg/nameserver"
)
@@ -12,14 +12,14 @@ func (l *Loop) useUnencryptedDNS(fallback bool) {
// Try with user provided plaintext ip address
// if it's not 127.0.0.1 (default for DoT)
targetIP := settings.ServerAddress
if targetIP != nil && !targetIP.Equal(net.IPv4(127, 0, 0, 1)) { //nolint:gomnd
if targetIP.Compare(netip.AddrFrom4([4]byte{127, 0, 0, 1})) != 0 {
if fallback {
l.logger.Info("falling back on plaintext DNS at address " + targetIP.String())
} else {
l.logger.Info("using plaintext DNS at address " + targetIP.String())
}
nameserver.UseDNSInternally(targetIP)
err := nameserver.UseDNSSystemWide(l.resolvConf, targetIP, *settings.KeepNameserver)
nameserver.UseDNSInternally(targetIP.AsSlice())
err := nameserver.UseDNSSystemWide(l.resolvConf, targetIP.AsSlice(), *settings.KeepNameserver)
if err != nil {
l.logger.Error(err.Error())
}
@@ -38,8 +38,8 @@ func (l *Loop) useUnencryptedDNS(fallback bool) {
} else {
l.logger.Info("using plaintext DNS at address " + targetIP.String())
}
nameserver.UseDNSInternally(targetIP)
err = nameserver.UseDNSSystemWide(l.resolvConf, targetIP, *settings.KeepNameserver)
nameserver.UseDNSInternally(targetIP.AsSlice())
err = nameserver.UseDNSSystemWide(l.resolvConf, targetIP.AsSlice(), *settings.KeepNameserver)
if err != nil {
l.logger.Error(err.Error())
}

View File

@@ -43,8 +43,8 @@ func (l *Loop) setupUnbound(ctx context.Context) (
}
// use Unbound
nameserver.UseDNSInternally(settings.ServerAddress)
err = nameserver.UseDNSSystemWide(l.resolvConf, settings.ServerAddress,
nameserver.UseDNSInternally(settings.ServerAddress.AsSlice())
err = nameserver.UseDNSSystemWide(l.resolvConf, settings.ServerAddress.AsSlice(),
*settings.KeepNameserver)
if err != nil {
l.logger.Error(err.Error())

View File

@@ -130,7 +130,7 @@ func (c *Config) enable(ctx context.Context) (err error) {
}
func (c *Config) allowVPNIP(ctx context.Context) (err error) {
if c.vpnConnection.IP == nil {
if !c.vpnConnection.IP.IsValid() {
return nil
}

View File

@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"net"
"net/netip"
"os"
"os/exec"
@@ -146,8 +145,7 @@ func (c *Config) acceptOutputTrafficToVPN(ctx context.Context,
instruction := fmt.Sprintf("%s OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol,
connection.Protocol, connection.Port)
isIPv4 := connection.IP.To4() != nil
if isIPv4 {
if connection.IP.Is4() {
return c.runIptablesInstruction(ctx, instruction)
} else if c.ip6Tables == "" {
return fmt.Errorf("accept output to VPN server: %w", ErrNeedIP6Tables)
@@ -157,8 +155,8 @@ func (c *Config) acceptOutputTrafficToVPN(ctx context.Context,
// Thanks to @npawelek.
func (c *Config) acceptOutputFromIPToSubnet(ctx context.Context,
intf string, sourceIP net.IP, destinationSubnet netip.Prefix, remove bool) error {
doIPv4 := sourceIP.To4() != nil && destinationSubnet.Addr().Is4()
intf string, sourceIP netip.Addr, destinationSubnet netip.Prefix, remove bool) error {
doIPv4 := sourceIP.Is4() && destinationSubnet.Addr().Is4()
interfaceFlag := "-o " + intf
if intf == "*" { // all interfaces

View File

@@ -25,7 +25,7 @@ func (c *Config) SetVPNConnection(ctx context.Context,
}
remove := true
if c.vpnConnection.IP != nil {
if c.vpnConnection.IP.IsValid() {
for _, defaultRoute := range c.defaultRoutes {
if err := c.acceptOutputTrafficToVPN(ctx, defaultRoute.NetInterface, c.vpnConnection, remove); err != nil {
c.logger.Error("cannot remove outdated VPN connection rule: " + err.Error())

View File

@@ -1,14 +1,14 @@
package models
import (
"net"
"net/netip"
)
type Connection struct {
// Type is the connection type and can be "openvpn" or "wireguard"
Type string `json:"type"`
// IP is the VPN server IP address.
IP net.IP `json:"ip"`
IP netip.Addr `json:"ip"`
// Port is the VPN server port.
Port uint16 `json:"port"`
// Protocol can be "tcp" or "udp".
@@ -24,15 +24,15 @@ type Connection struct {
}
func (c *Connection) Equal(other Connection) bool {
return c.IP.Equal(other.IP) && c.Port == other.Port &&
return c.IP.Compare(other.IP) == 0 && c.Port == other.Port &&
c.Protocol == other.Protocol && c.Hostname == other.Hostname &&
c.ServerName == other.ServerName && c.PubKey == other.PubKey
}
// UpdateEmptyWith updates each field of the connection where the
// value is not set using the value given as arguments.
func (c *Connection) UpdateEmptyWith(ip net.IP, port uint16, protocol string) {
if c.IP == nil {
func (c *Connection) UpdateEmptyWith(ip netip.Addr, port uint16, protocol string) {
if !c.IP.IsValid() {
c.IP = ip
}
if c.Port == 0 {

View File

@@ -1,9 +1,11 @@
package models
import "net"
import (
"net/netip"
)
type PublicIP struct {
IP net.IP `json:"public_ip,omitempty"`
IP netip.Addr `json:"public_ip,omitempty"`
Region string `json:"region,omitempty"`
Country string `json:"country,omitempty"`
City string `json:"city,omitempty"`
@@ -16,7 +18,7 @@ type PublicIP struct {
func (p *PublicIP) Copy() (publicIPCopy PublicIP) {
publicIPCopy = PublicIP{
IP: make(net.IP, len(p.IP)),
IP: p.IP,
Region: p.Region,
Country: p.Country,
City: p.City,
@@ -26,6 +28,5 @@ func (p *PublicIP) Copy() (publicIPCopy PublicIP) {
PostalCode: p.PostalCode,
Timezone: p.Timezone,
}
copy(publicIPCopy.IP, p.IP)
return publicIPCopy
}

View File

@@ -3,7 +3,7 @@ package models
import (
"errors"
"fmt"
"net"
"net/netip"
"reflect"
"strings"
@@ -32,7 +32,7 @@ type Server struct {
Premium bool `json:"premium,omitempty"`
PortForward bool `json:"port_forward,omitempty"`
Keep bool `json:"keep,omitempty"`
IPs []net.IP `json:"ips,omitempty"`
IPs []netip.Addr `json:"ips,omitempty"`
}
var (
@@ -72,13 +72,13 @@ func (s *Server) Equal(other Server) (equal bool) {
return reflect.DeepEqual(serverCopy, other)
}
func ipsAreEqual(a, b []net.IP) (equal bool) {
func ipsAreEqual(a, b []netip.Addr) (equal bool) {
if len(a) != len(b) {
return false
}
for i := range a {
if !a[i].Equal(b[i]) {
if a[i].Compare(b[i]) != 0 {
return false
}
}

View File

@@ -1,7 +1,7 @@
package models
import (
"net"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
@@ -17,28 +17,28 @@ func Test_Server_Equal(t *testing.T) {
}{
"same IPs": {
a: &Server{
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
b: Server{
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
equal: true,
},
"same IP strings": {
a: &Server{
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
b: Server{
IPs: []net.IP{{1, 2, 3, 4}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
equal: true,
},
"different IPs": {
a: &Server{
IPs: []net.IP{{1, 2, 3, 4}, {2, 3, 4, 5}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4}), netip.AddrFrom4([4]byte{2, 3, 4, 5})},
},
b: Server{
IPs: []net.IP{{1, 2, 3, 4}, {1, 2, 3, 4}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4}), netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
},
"all fields equal": {
@@ -61,7 +61,7 @@ func Test_Server_Equal(t *testing.T) {
Free: true,
Stream: true,
PortForward: true,
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
Keep: true,
},
b: Server{
@@ -83,7 +83,7 @@ func Test_Server_Equal(t *testing.T) {
Free: true,
Stream: true,
PortForward: true,
IPs: []net.IP{net.IPv4(1, 2, 3, 4)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
Keep: true,
},
equal: true,

View File

@@ -3,7 +3,7 @@ package extract
import (
"errors"
"fmt"
"net"
"net/netip"
"strconv"
"strings"
@@ -25,12 +25,12 @@ func extractDataFromLines(lines []string) (
connection.UpdateEmptyWith(ip, port, protocol)
if connection.Protocol != "" && connection.IP != nil {
if connection.Protocol != "" && connection.IP.IsValid() {
break
}
}
if connection.IP == nil {
if !connection.IP.IsValid() {
return connection, errRemoteLineNotFound
}
@@ -49,24 +49,24 @@ func extractDataFromLines(lines []string) (
}
func extractDataFromLine(line string) (
ip net.IP, port uint16, protocol string, err error) {
ip netip.Addr, port uint16, protocol string, err error) {
switch {
case strings.HasPrefix(line, "proto "):
protocol, err = extractProto(line)
if err != nil {
return nil, 0, "", fmt.Errorf("extracting protocol from proto line: %w", err)
return ip, 0, "", fmt.Errorf("extracting protocol from proto line: %w", err)
}
return nil, 0, protocol, nil
return ip, 0, protocol, nil
case strings.HasPrefix(line, "remote "):
ip, port, protocol, err = extractRemote(line)
if err != nil {
return nil, 0, "", fmt.Errorf("extracting from remote line: %w", err)
return ip, 0, "", fmt.Errorf("extracting from remote line: %w", err)
}
return ip, port, protocol, nil
}
return nil, 0, "", nil
return ip, 0, "", nil
}
var (
@@ -95,19 +95,19 @@ var (
errPortNotValid = errors.New("port is not valid")
)
func extractRemote(line string) (ip net.IP, port uint16,
func extractRemote(line string) (ip netip.Addr, port uint16,
protocol string, err error) {
fields := strings.Fields(line)
n := len(fields)
if n < 2 || n > 4 {
return nil, 0, "", fmt.Errorf("%w: %s", errRemoteLineFieldsCount, line)
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errRemoteLineFieldsCount, line)
}
host := fields[1]
ip = net.ParseIP(host)
if ip == nil {
return nil, 0, "", fmt.Errorf("%w: %s", errHostNotIP, host)
ip, err = netip.ParseAddr(host)
if err != nil {
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errHostNotIP, host)
// TODO resolve hostname once there is an option to allow it through
// the firewall before the VPN is up.
}
@@ -115,9 +115,9 @@ func extractRemote(line string) (ip net.IP, port uint16,
if n > 2 { //nolint:gomnd
portInt, err := strconv.Atoi(fields[2])
if err != nil {
return nil, 0, "", fmt.Errorf("%w: %s", errPortNotValid, line)
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errPortNotValid, line)
} else if portInt < 1 || portInt > 65535 {
return nil, 0, "", fmt.Errorf("%w: %d must be between 1 and 65535", errPortNotValid, portInt)
return netip.Addr{}, 0, "", fmt.Errorf("%w: %d must be between 1 and 65535", errPortNotValid, portInt)
}
port = uint16(portInt)
}
@@ -127,7 +127,7 @@ func extractRemote(line string) (ip net.IP, port uint16,
case "tcp", "udp":
protocol = fields[3]
default:
return nil, 0, "", fmt.Errorf("%w: %s", errProtocolNotSupported, fields[3])
return netip.Addr{}, 0, "", fmt.Errorf("%w: %s", errProtocolNotSupported, fields[3])
}
}

View File

@@ -2,7 +2,7 @@ package extract
import (
"errors"
"net"
"net/netip"
"testing"
"github.com/qdm12/gluetun/internal/constants"
@@ -22,7 +22,7 @@ func Test_extractDataFromLines(t *testing.T) {
"success": {
lines: []string{"bla bla", "proto tcp", "remote 1.2.3.4 1194 tcp", "dev tun6"},
connection: models.Connection{
IP: net.IPv4(1, 2, 3, 4),
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
Port: 1194,
Protocol: constants.TCP,
},
@@ -34,7 +34,7 @@ func Test_extractDataFromLines(t *testing.T) {
"only use first values found": {
lines: []string{"proto udp", "proto tcp", "remote 1.2.3.4 443 tcp", "remote 5.2.3.4 1194 udp"},
connection: models.Connection{
IP: net.IPv4(1, 2, 3, 4),
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
Port: 443,
Protocol: constants.UDP,
},
@@ -49,7 +49,7 @@ func Test_extractDataFromLines(t *testing.T) {
"default TCP port": {
lines: []string{"remote 1.2.3.4", "proto tcp"},
connection: models.Connection{
IP: net.IPv4(1, 2, 3, 4),
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
Port: 443,
Protocol: constants.TCP,
},
@@ -57,7 +57,7 @@ func Test_extractDataFromLines(t *testing.T) {
"default UDP port": {
lines: []string{"remote 1.2.3.4", "proto udp"},
connection: models.Connection{
IP: net.IPv4(1, 2, 3, 4),
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
Port: 1194,
Protocol: constants.UDP,
},
@@ -88,7 +88,7 @@ func Test_extractDataFromLine(t *testing.T) {
testCases := map[string]struct {
line string
ip net.IP
ip netip.Addr
port uint16
protocol string
isErr error
@@ -110,7 +110,7 @@ func Test_extractDataFromLine(t *testing.T) {
},
"extract remote success": {
line: "remote 1.2.3.4 1194 udp",
ip: net.IPv4(1, 2, 3, 4),
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
port: 1194,
protocol: constants.UDP,
},
@@ -186,7 +186,7 @@ func Test_extractRemote(t *testing.T) {
testCases := map[string]struct {
line string
ip net.IP
ip netip.Addr
port uint16
protocol string
err error
@@ -205,7 +205,7 @@ func Test_extractRemote(t *testing.T) {
},
"only IP host": {
line: "remote 1.2.3.4",
ip: net.IPv4(1, 2, 3, 4),
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
},
"port not an integer": {
line: "remote 1.2.3.4 bad",
@@ -225,7 +225,7 @@ func Test_extractRemote(t *testing.T) {
},
"IP host and port": {
line: "remote 1.2.3.4 8000",
ip: net.IPv4(1, 2, 3, 4),
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
port: 8000,
},
"invalid protocol": {
@@ -234,7 +234,7 @@ func Test_extractRemote(t *testing.T) {
},
"IP host and port and protocol": {
line: "remote 1.2.3.4 8000 udp",
ip: net.IPv4(1, 2, 3, 4),
ip: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
port: 8000,
protocol: constants.UDP,
},

View File

@@ -1,14 +1,14 @@
package state
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/provider"
)
type StartData struct {
PortForwarder provider.PortForwarder
Gateway net.IP // needed for PIA
Gateway netip.Addr // needed for PIA
ServerName string // needed for PIA
Interface string // tun0 for example
}

View File

@@ -4,8 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"net/netip"
"github.com/qdm12/gluetun/internal/provider/common"
)
@@ -20,14 +20,14 @@ type apiServer struct {
CountryCode string `json:"country_code"`
Location string `json:"location"`
Continent string `json:"continent"`
IPv4In1 net.IP `json:"ip_v4_in1"`
IPv4In2 net.IP `json:"ip_v4_in2"`
IPv4In3 net.IP `json:"ip_v4_in3"`
IPv4In4 net.IP `json:"ip_v4_in4"`
IPv6In1 net.IP `json:"ip_v6_in1"`
IPv6In2 net.IP `json:"ip_v6_in2"`
IPv6In3 net.IP `json:"ip_v6_in3"`
IPv6In4 net.IP `json:"ip_v6_in4"`
IPv4In1 netip.Addr `json:"ip_v4_in1"`
IPv4In2 netip.Addr `json:"ip_v4_in2"`
IPv4In3 netip.Addr `json:"ip_v4_in3"`
IPv4In4 netip.Addr `json:"ip_v4_in4"`
IPv6In1 netip.Addr `json:"ip_v6_in1"`
IPv6In2 netip.Addr `json:"ip_v6_in2"`
IPv6In3 netip.Addr `json:"ip_v6_in3"`
IPv6In4 netip.Addr `json:"ip_v6_in4"`
Health string `json:"health"`
}

View File

@@ -3,7 +3,7 @@ package updater
import (
"context"
"fmt"
"net"
"net/netip"
"sort"
"strings"
@@ -57,12 +57,12 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
baseWireguardServer.WgPubKey = "PyLCXAQT8KkM4T+dUsOQfn+Ub3pGxfGlxkIApuig+hk="
ipv4WireguadServer := baseWireguardServer
ipv4WireguadServer.IPs = []net.IP{apiServer.IPv4In1}
ipv4WireguadServer.IPs = []netip.Addr{apiServer.IPv4In1}
ipv4WireguadServer.Hostname = apiServer.CountryCode + ".vpn.airdns.org"
servers = append(servers, ipv4WireguadServer)
ipv6WireguadServer := baseWireguardServer
ipv6WireguadServer.IPs = []net.IP{apiServer.IPv6In1}
ipv6WireguadServer.IPs = []netip.Addr{apiServer.IPv6In1}
ipv6WireguadServer.Hostname = apiServer.CountryCode + ".ipv6.vpn.airdns.org"
servers = append(servers, ipv6WireguadServer)
@@ -74,22 +74,22 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
// Ignore IPs 1 and 2 since tls-crypt is superior to tls-auth really.
ipv4In3OpenVPNServer := baseOpenVPNServer
ipv4In3OpenVPNServer.IPs = []net.IP{apiServer.IPv4In3}
ipv4In3OpenVPNServer.IPs = []netip.Addr{apiServer.IPv4In3}
ipv4In3OpenVPNServer.Hostname = apiServer.CountryCode + "3.vpn.airdns.org"
servers = append(servers, ipv4In3OpenVPNServer)
ipv6In3OpenVPNServer := baseOpenVPNServer
ipv6In3OpenVPNServer.IPs = []net.IP{apiServer.IPv6In3}
ipv6In3OpenVPNServer.IPs = []netip.Addr{apiServer.IPv6In3}
ipv6In3OpenVPNServer.Hostname = apiServer.CountryCode + "3.ipv6.vpn.airdns.org"
servers = append(servers, ipv6In3OpenVPNServer)
ipv4In4OpenVPNServer := baseOpenVPNServer
ipv4In4OpenVPNServer.IPs = []net.IP{apiServer.IPv4In4}
ipv4In4OpenVPNServer.IPs = []netip.Addr{apiServer.IPv4In4}
ipv4In4OpenVPNServer.Hostname = apiServer.CountryCode + "4.vpn.airdns.org"
servers = append(servers, ipv4In4OpenVPNServer)
ipv6In4OpenVPNServer := baseOpenVPNServer
ipv6In4OpenVPNServer.IPs = []net.IP{apiServer.IPv6In4}
ipv6In4OpenVPNServer.IPs = []netip.Addr{apiServer.IPv6In4}
ipv6In4OpenVPNServer.Hostname = apiServer.CountryCode + "4.ipv6.vpn.airdns.org"
servers = append(servers, ipv6In4OpenVPNServer)
}

View File

@@ -6,7 +6,7 @@ package common
import (
context "context"
net "net"
netip "net/netip"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
@@ -39,10 +39,10 @@ func (m *MockParallelResolver) EXPECT() *MockParallelResolverMockRecorder {
}
// Resolve mocks base method.
func (m *MockParallelResolver) Resolve(arg0 context.Context, arg1 resolver.ParallelSettings) (map[string][]net.IP, []string, error) {
func (m *MockParallelResolver) Resolve(arg0 context.Context, arg1 resolver.ParallelSettings) (map[string][]netip.Addr, []string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Resolve", arg0, arg1)
ret0, _ := ret[0].(map[string][]net.IP)
ret0, _ := ret[0].(map[string][]netip.Addr)
ret1, _ := ret[1].([]string)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2

View File

@@ -2,4 +2,5 @@ package common
// Exceptionally, these mocks are exported since they are used by all
// provider subpackages tests, and it reduces test code duplication a lot.
// Note mocks.go might need to be removed before re-generating it.
//go:generate mockgen -destination=mocks.go -package $GOPACKAGE . ParallelResolver,Storage,Unzipper,Warner

View File

@@ -3,7 +3,7 @@ package common
import (
"context"
"errors"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/publicip/ipinfo"
@@ -21,7 +21,7 @@ type Fetcher interface {
type ParallelResolver interface {
Resolve(ctx context.Context, settings resolver.ParallelSettings) (
hostToIPs map[string][]net.IP, warnings []string, err error)
hostToIPs map[string][]netip.Addr, warnings []string, err error)
}
type Unzipper interface {
@@ -34,5 +34,5 @@ type Warner interface {
}
type IPFetcher interface {
FetchMultiInfo(ctx context.Context, ips []net.IP) (data []ipinfo.Response, err error)
FetchMultiInfo(ctx context.Context, ips []netip.Addr) (data []ipinfo.Response, err error)
}

View File

@@ -1,7 +1,7 @@
package custom
import (
"net"
"net/netip"
"testing"
"github.com/qdm12/gluetun/internal/configuration/settings"
@@ -46,7 +46,7 @@ func Test_modifyConfig(t *testing.T) {
Verbosity: intPtr(0),
}.WithDefaults(providers.Custom),
connection: models.Connection{
IP: net.IPv4(1, 2, 3, 4),
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
Port: 1194,
Protocol: constants.UDP,
},

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/constants/vpn"
@@ -47,7 +47,7 @@ func (hts hostToServer) hostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -3,7 +3,7 @@ package expressvpn
import (
"errors"
"math/rand"
"net"
"net/netip"
"testing"
"github.com/golang/mock/gomock"
@@ -41,7 +41,7 @@ func Test_Provider_GetConnection(t *testing.T) {
},
"default OpenVPN TCP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -52,7 +52,7 @@ func Test_Provider_GetConnection(t *testing.T) {
},
"default OpenVPN UDP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -61,14 +61,14 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 1195,
Protocol: constants.UDP,
},
},
"default Wireguard port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
VPN: vpn.Wireguard,

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -33,7 +33,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"sort"
"github.com/qdm12/gluetun/internal/constants/vpn"
@@ -38,7 +38,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"testing"
"github.com/qdm12/gluetun/internal/constants/vpn"
@@ -134,17 +134,17 @@ func Test_hostToServer_adaptWithIPs(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
initialHTS hostToServer
hostToIPs map[string][]net.IP
hostToIPs map[string][]netip.Addr
expectedHTS hostToServer
}{
"create server": {
initialHTS: hostToServer{},
hostToIPs: map[string][]net.IP{
"A": {{1, 2, 3, 4}},
hostToIPs: map[string][]netip.Addr{
"A": {netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
expectedHTS: hostToServer{
"A": models.Server{
IPs: []net.IP{{1, 2, 3, 4}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
},
},
@@ -154,13 +154,13 @@ func Test_hostToServer_adaptWithIPs(t *testing.T) {
Country: "country",
},
},
hostToIPs: map[string][]net.IP{
"A": {{1, 2, 3, 4}},
hostToIPs: map[string][]netip.Addr{
"A": {netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
expectedHTS: hostToServer{
"A": models.Server{
Country: "country",
IPs: []net.IP{{1, 2, 3, 4}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
},
},
},
@@ -170,7 +170,7 @@ func Test_hostToServer_adaptWithIPs(t *testing.T) {
Country: "country",
},
},
hostToIPs: map[string][]net.IP{},
hostToIPs: map[string][]netip.Addr{},
expectedHTS: hostToServer{},
},
}

View File

@@ -3,7 +3,7 @@ package updater
import (
"context"
"errors"
"net"
"net/netip"
"testing"
"time"
@@ -32,7 +32,7 @@ func Test_Updater_GetServers(t *testing.T) {
// Resolution
expectResolve bool
resolverSettings resolver.ParallelSettings
hostToIPs map[string][]net.IP
hostToIPs map[string][]netip.Addr
resolveWarnings []string
resolveErr error
@@ -161,9 +161,9 @@ func Test_Updater_GetServers(t *testing.T) {
SortIPs: true,
},
},
hostToIPs: map[string][]net.IP{
"hosta": {{1, 1, 1, 1}, {2, 2, 2, 2}},
"hostb": {{3, 3, 3, 3}, {4, 4, 4, 4}},
hostToIPs: map[string][]netip.Addr{
"hosta": {netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})},
"hostb": {netip.AddrFrom4([4]byte{3, 3, 3, 3}), netip.AddrFrom4([4]byte{4, 4, 4, 4})},
},
resolveWarnings: []string{"resolve warning"},
servers: []models.Server{
@@ -173,7 +173,7 @@ func Test_Updater_GetServers(t *testing.T) {
City: "City A",
Hostname: "hosta",
UDP: true,
IPs: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})},
},
{
VPN: vpn.OpenVPN,
@@ -181,7 +181,7 @@ func Test_Updater_GetServers(t *testing.T) {
City: "City B",
Hostname: "hostb",
UDP: true,
IPs: []net.IP{{3, 3, 3, 3}, {4, 4, 4, 4}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{3, 3, 3, 3}), netip.AddrFrom4([4]byte{4, 4, 4, 4})},
},
},
},

View File

@@ -3,8 +3,8 @@ package ivpn
import (
"errors"
"math/rand"
"net"
"net/http"
"net/netip"
"testing"
"github.com/golang/mock/gomock"
@@ -41,7 +41,7 @@ func Test_Provider_GetConnection(t *testing.T) {
},
"default OpenVPN TCP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -50,14 +50,14 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 443,
Protocol: constants.TCP,
},
},
"default OpenVPN UDP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -66,21 +66,21 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 1194,
Protocol: constants.UDP,
},
},
"default Wireguard port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, WgPubKey: "x"},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}, WgPubKey: "x"},
},
selection: settings.ServerSelection{
VPN: vpn.Wireguard,
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.Wireguard,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 58237,
Protocol: constants.UDP,
PubKey: "x",

View File

@@ -4,8 +4,8 @@ import (
"context"
"errors"
"io"
"net"
"net/http"
"net/netip"
"strings"
"testing"
"time"
@@ -36,7 +36,7 @@ func Test_Updater_GetServers(t *testing.T) {
// Resolution
expectResolve bool
resolveSettings resolver.ParallelSettings
hostToIPs map[string][]net.IP
hostToIPs map[string][]netip.Addr
resolveWarnings []string
resolveErr error
@@ -109,24 +109,24 @@ func Test_Updater_GetServers(t *testing.T) {
SortIPs: true,
},
},
hostToIPs: map[string][]net.IP{
"hosta": {{1, 1, 1, 1}, {2, 2, 2, 2}},
"hostb": {{3, 3, 3, 3}, {4, 4, 4, 4}},
"hostc": {{5, 5, 5, 5}, {6, 6, 6, 6}},
hostToIPs: map[string][]netip.Addr{
"hosta": {netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})},
"hostb": {netip.AddrFrom4([4]byte{3, 3, 3, 3}), netip.AddrFrom4([4]byte{4, 4, 4, 4})},
"hostc": {netip.AddrFrom4([4]byte{5, 5, 5, 5}), netip.AddrFrom4([4]byte{6, 6, 6, 6})},
},
resolveWarnings: []string{"resolve warning"},
servers: []models.Server{
{VPN: vpn.OpenVPN, Country: "Country1",
City: "City A", Hostname: "hosta", TCP: true, UDP: true,
IPs: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})}},
{VPN: vpn.OpenVPN, Country: "Country2",
City: "City B", Hostname: "hostb", TCP: true, UDP: true,
IPs: []net.IP{{3, 3, 3, 3}, {4, 4, 4, 4}}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{3, 3, 3, 3}), netip.AddrFrom4([4]byte{4, 4, 4, 4})}},
{VPN: vpn.Wireguard,
Country: "Country3", City: "City C",
Hostname: "hostc",
WgPubKey: "xyz",
IPs: []net.IP{{5, 5, 5, 5}, {6, 6, 6, 6}}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{5, 5, 5, 5}), netip.AddrFrom4([4]byte{6, 6, 6, 6})}},
},
},
}

View File

@@ -3,8 +3,8 @@ package mullvad
import (
"errors"
"math/rand"
"net"
"net/http"
"net/netip"
"testing"
"github.com/golang/mock/gomock"
@@ -41,7 +41,7 @@ func Test_Provider_GetConnection(t *testing.T) {
},
"default OpenVPN TCP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -50,14 +50,14 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 443,
Protocol: constants.TCP,
},
},
"default OpenVPN UDP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -66,21 +66,21 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 1194,
Protocol: constants.UDP,
},
},
"default Wireguard port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, WgPubKey: "x"},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}, WgPubKey: "x"},
},
selection: settings.ServerSelection{
VPN: vpn.Wireguard,
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.Wireguard,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 51820,
Protocol: constants.UDP,
PubKey: "x",

View File

@@ -3,7 +3,7 @@ package updater
import (
"errors"
"fmt"
"net"
"net/netip"
"strings"
"github.com/qdm12/gluetun/internal/constants/vpn"
@@ -14,8 +14,8 @@ type hostToServer map[string]models.Server
var (
ErrNoIP = errors.New("no IP address for VPN server")
ErrParseIPv4 = errors.New("cannot parse IPv4 address")
ErrParseIPv6 = errors.New("cannot parse IPv6 address")
ErrIPIsNotV4 = errors.New("IP address is not IPv4")
ErrIPIsNotV6 = errors.New("IP address is not IPv6")
ErrVPNTypeNotSupported = errors.New("VPN type not supported")
)
@@ -48,17 +48,21 @@ func (hts hostToServer) add(data serverData) (err error) {
}
if data.IPv4 != "" {
ipv4 := net.ParseIP(data.IPv4)
if ipv4 == nil || ipv4.To4() == nil {
return fmt.Errorf("%w: %s", ErrParseIPv4, data.IPv4)
ipv4, err := netip.ParseAddr(data.IPv4)
if err != nil {
return fmt.Errorf("parsing IPv4 address: %w", err)
} else if !ipv4.Is4() {
return fmt.Errorf("%w: %s", ErrIPIsNotV4, data.IPv4)
}
server.IPs = append(server.IPs, ipv4)
}
if data.IPv6 != "" {
ipv6 := net.ParseIP(data.IPv6)
if ipv6 == nil || ipv6.To4() != nil {
return fmt.Errorf("%w: %s", ErrParseIPv6, data.IPv6)
ipv6, err := netip.ParseAddr(data.IPv6)
if err != nil {
return fmt.Errorf("parsing IPv6 address: %w", err)
} else if !ipv6.Is6() {
return fmt.Errorf("%w: %s", ErrIPIsNotV6, data.IPv6)
}
server.IPs = append(server.IPs, ipv6)
}

View File

@@ -1,29 +1,31 @@
package updater
import (
"bytes"
"net"
"net/netip"
"sort"
)
func uniqueSortedIPs(ips []net.IP) []net.IP {
func uniqueSortedIPs(ips []netip.Addr) []netip.Addr {
uniqueIPs := make(map[string]struct{}, len(ips))
for _, ip := range ips {
key := ip.String()
uniqueIPs[key] = struct{}{}
}
ips = make([]net.IP, 0, len(uniqueIPs))
ips = make([]netip.Addr, 0, len(uniqueIPs))
for key := range uniqueIPs {
ip := net.ParseIP(key)
if ipv4 := ip.To4(); ipv4 != nil {
ip = ipv4
ip, err := netip.ParseAddr(key)
if err != nil {
panic(err)
}
if ip.Is4In6() {
ip = netip.AddrFrom4(ip.As4())
}
ips = append(ips, ip)
}
sort.Slice(ips, func(i, j int) bool {
return bytes.Compare(ips[i], ips[j]) < 0
return ips[i].Compare(ips[j]) < 0
})
return ips

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
@@ -10,24 +10,24 @@ import (
func Test_uniqueSortedIPs(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
inputIPs []net.IP
outputIPs []net.IP
inputIPs []netip.Addr
outputIPs []netip.Addr
}{
"nil": {
inputIPs: nil,
outputIPs: []net.IP{},
outputIPs: []netip.Addr{},
},
"empty": {
inputIPs: []net.IP{},
outputIPs: []net.IP{},
inputIPs: []netip.Addr{},
outputIPs: []netip.Addr{},
},
"single IPv4": {
inputIPs: []net.IP{{1, 1, 1, 1}},
outputIPs: []net.IP{{1, 1, 1, 1}},
inputIPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
outputIPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
},
"two IPv4s": {
inputIPs: []net.IP{{1, 1, 2, 1}, {1, 1, 1, 1}},
outputIPs: []net.IP{{1, 1, 1, 1}, {1, 1, 2, 1}},
inputIPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 2, 1}), netip.AddrFrom4([4]byte{1, 1, 1, 1})},
outputIPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{1, 1, 2, 1})},
},
}
for name, testCase := range testCases {

View File

@@ -2,15 +2,16 @@ package updater
import (
"fmt"
"net"
"net/netip"
)
func parseIPv4(s string) (ipv4 net.IP, err error) {
ip := net.ParseIP(s)
if ip == nil {
return nil, fmt.Errorf("%w: %q", ErrParseIP, s)
} else if ip.To4() == nil {
return nil, fmt.Errorf("%w: %s", ErrNotIPv4, ip)
func parseIPv4(s string) (ipv4 netip.Addr, err error) {
ipv4, err = netip.ParseAddr(s)
if err != nil {
return ipv4, err
}
return ip, nil
if !ipv4.Is4() {
return ipv4, fmt.Errorf("%w: %s", ErrNotIPv4, ipv4)
}
return ipv4, nil
}

View File

@@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"sort"
"github.com/qdm12/gluetun/internal/constants/vpn"
@@ -13,7 +13,6 @@ import (
)
var (
ErrParseIP = errors.New("cannot parse IP address")
ErrNotIPv4 = errors.New("IP address is not IPv4")
)
@@ -47,7 +46,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
Region: jsonServer.Country,
Hostname: jsonServer.Domain,
Number: number,
IPs: []net.IP{ip},
IPs: []netip.Addr{ip},
TCP: jsonServer.Features.TCP,
UDP: jsonServer.Features.UDP,
}

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -9,7 +9,7 @@ import (
type cityToServer map[string]models.Server
func (cts cityToServer) add(city string, ips []net.IP) {
func (cts cityToServer) add(city string, ips []netip.Addr) {
server, ok := cts[city]
if !ok {
server.VPN = vpn.OpenVPN

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -28,7 +28,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -2,7 +2,7 @@ package updater
import (
"context"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common"
@@ -10,7 +10,7 @@ import (
func setLocationInfo(ctx context.Context, fetcher common.IPFetcher, servers []models.Server) (err error) {
// Get public IP address information
ipsToGetInfo := make([]net.IP, 0, len(servers))
ipsToGetInfo := make([]netip.Addr, 0, len(servers))
for _, server := range servers {
ipsToGetInfo = append(ipsToGetInfo, server.IPs...)
}

View File

@@ -9,6 +9,7 @@ import (
"io"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"strconv"
@@ -22,13 +23,13 @@ import (
var (
ErrServerNameNotFound = errors.New("server name not found in servers")
ErrGatewayIPIsNil = errors.New("gateway IP address is nil")
ErrGatewayIPIsNotValid = errors.New("gateway IP address is not valid")
ErrServerNameEmpty = errors.New("server name is empty")
)
// PortForward obtains a VPN server side port forwarded from PIA.
func (p *Provider) PortForward(ctx context.Context, client *http.Client,
logger utils.Logger, gateway net.IP, serverName string) (
logger utils.Logger, gateway netip.Addr, serverName string) (
port uint16, err error) {
server, ok := p.storage.GetServerByName(providers.PrivateInternetAccess, serverName)
if !ok {
@@ -40,8 +41,8 @@ func (p *Provider) PortForward(ctx context.Context, client *http.Client,
" (region " + server.Region + ") does not support port forwarding")
return 0, nil
}
if gateway == nil {
return 0, ErrGatewayIPIsNil
if !gateway.IsValid() {
return 0, fmt.Errorf("%w: %s", ErrGatewayIPIsNotValid, gateway)
} else if serverName == "" {
return 0, ErrServerNameEmpty
}
@@ -91,7 +92,7 @@ var (
)
func (p *Provider) KeepPortForward(ctx context.Context,
gateway net.IP, serverName string) (err error) {
gateway netip.Addr, serverName string) (err error) {
privateIPClient, err := newHTTPClient(serverName)
if err != nil {
return fmt.Errorf("creating custom HTTP client: %w", err)
@@ -132,7 +133,7 @@ func (p *Provider) KeepPortForward(ctx context.Context,
}
func refreshPIAPortForwardData(ctx context.Context, client, privateIPClient *http.Client,
gateway net.IP, portForwardPath, authFilePath string) (data piaPortForwardData, err error) {
gateway netip.Addr, portForwardPath, authFilePath string) (data piaPortForwardData, err error) {
data.Token, err = fetchToken(ctx, client, authFilePath)
if err != nil {
return data, fmt.Errorf("fetching token: %w", err)
@@ -314,7 +315,7 @@ func getOpenvpnCredentials(authFilePath string) (
return username, password, nil
}
func fetchPortForwardData(ctx context.Context, client *http.Client, gateway net.IP, token string) (
func fetchPortForwardData(ctx context.Context, client *http.Client, gateway netip.Addr, token string) (
port uint16, signature string, expiration time.Time, err error) {
errSubstitutions := map[string]string{url.QueryEscape(token): "<token>"}
@@ -368,7 +369,7 @@ var (
ErrBadResponse = errors.New("bad response received")
)
func bindPort(ctx context.Context, client *http.Client, gateway net.IP, data piaPortForwardData) (err error) {
func bindPort(ctx context.Context, client *http.Client, gateway netip.Addr, data piaPortForwardData) (err error) {
payload, err := packPayload(data.Port, data.Token, data.Expiration)
if err != nil {
return fmt.Errorf("serializing payload: %w", err)

View File

@@ -7,8 +7,8 @@ import (
"errors"
"fmt"
"io"
"net"
"net/http"
"net/netip"
)
var (
@@ -31,7 +31,7 @@ type regionData struct {
}
type serverData struct {
IP net.IP `json:"ip"`
IP netip.Addr `json:"ip"`
CN string `json:"cn"`
}

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -10,7 +10,7 @@ import (
type nameToServer map[string]models.Server
func (nts nameToServer) add(name, hostname, region string,
tcp, udp, portForward bool, ip net.IP) (change bool) {
tcp, udp, portForward bool, ip netip.Addr) (change bool) {
server, ok := nts[name]
if !ok {
change = true
@@ -32,7 +32,7 @@ func (nts nameToServer) add(name, hostname, region string,
ipFound := false
for _, existingIP := range server.IPs {
if ip.Equal(existingIP) {
if ip == existingIP {
ipFound = true
break
}

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -32,7 +32,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -5,8 +5,8 @@ import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
)
var (
@@ -26,8 +26,8 @@ type logicalServer struct {
}
type physicalServer struct {
EntryIP net.IP
ExitIP net.IP
EntryIP netip.Addr
ExitIP netip.Addr
Domain string
Status uint8
}

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -10,7 +10,7 @@ import (
type ipToServer map[string]models.Server
func (its ipToServer) add(country, region, city, name, hostname string,
free bool, entryIP net.IP) {
free bool, entryIP netip.Addr) {
key := entryIP.String()
server, ok := its[key]
@@ -27,7 +27,7 @@ func (its ipToServer) add(country, region, city, name, hostname string,
server.Free = free
server.UDP = true
server.TCP = true
server.IPs = []net.IP{entryIP}
server.IPs = []netip.Addr{entryIP}
its[key] = server
}

View File

@@ -2,8 +2,8 @@ package provider
import (
"context"
"net"
"net/http"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/models"
@@ -22,8 +22,8 @@ type Provider interface {
type PortForwarder interface {
PortForward(ctx context.Context, client *http.Client,
logger utils.Logger, gateway net.IP, serverName string) (
logger utils.Logger, gateway netip.Addr, serverName string) (
port uint16, err error)
KeepPortForward(ctx context.Context, gateway net.IP,
KeepPortForward(ctx context.Context, gateway netip.Addr,
serverName string) (err error)
}

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -32,7 +32,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -3,7 +3,7 @@ package updater
import (
"context"
"fmt"
"net"
"net/netip"
"sort"
"strings"
@@ -76,7 +76,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers = hts.toServersSlice()
// Get public IP address information
ipsToGetInfo := make([]net.IP, len(servers))
ipsToGetInfo := make([]netip.Addr, len(servers))
for i := range servers {
ipsToGetInfo[i] = servers[i].IPs[0]
}

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -75,7 +75,7 @@ func (hts hostToServers) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServers) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServers) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
servers := hts[host]
for i := range servers {

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -10,7 +10,7 @@ import (
type hostToServer map[string]models.Server
func (hts hostToServer) add(host, country, city string,
tcp, udp bool, ips []net.IP) {
tcp, udp bool, ips []netip.Addr) {
server, ok := hts[host]
if !ok {
server.VPN = vpn.OpenVPN
@@ -39,7 +39,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -48,7 +48,7 @@ func GetConnection(provider string,
connections := make([]models.Connection, 0, len(servers))
for _, server := range servers {
for _, ip := range server.IPs {
if !ipv6Supported && ip.To4() == nil {
if !ipv6Supported && ip.Is6() {
continue
}

View File

@@ -3,7 +3,7 @@ package utils
import (
"errors"
"math/rand"
"net"
"net/netip"
"testing"
"github.com/golang/mock/gomock"
@@ -58,7 +58,7 @@ func Test_GetConnection(t *testing.T) {
{
VPN: vpn.OpenVPN,
UDP: true,
IPs: []net.IP{net.IPv4(1, 1, 1, 1)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
Hostname: "name",
},
},
@@ -68,7 +68,7 @@ func Test_GetConnection(t *testing.T) {
randSource: rand.NewSource(0),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Protocol: constants.UDP,
Port: 1194,
Hostname: "name",
@@ -79,7 +79,7 @@ func Test_GetConnection(t *testing.T) {
{
VPN: vpn.OpenVPN,
UDP: true,
IPs: []net.IP{net.IPv4(1, 1, 1, 1)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
Hostname: "hostname",
OvpnX509: "x509",
},
@@ -90,7 +90,7 @@ func Test_GetConnection(t *testing.T) {
randSource: rand.NewSource(0),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Protocol: constants.UDP,
Port: 1194,
Hostname: "x509",
@@ -101,14 +101,14 @@ func Test_GetConnection(t *testing.T) {
{
VPN: vpn.OpenVPN,
UDP: true,
IPs: []net.IP{
net.IPv4(1, 1, 1, 1),
IPs: []netip.Addr{
netip.AddrFrom4([4]byte{1, 1, 1, 1}),
// All IPv6 is ignored
net.IPv6zero,
net.IPv6zero,
net.IPv6zero,
net.IPv6zero,
net.IPv6zero,
netip.IPv6Unspecified(),
netip.IPv6Unspecified(),
netip.IPv6Unspecified(),
netip.IPv6Unspecified(),
netip.IPv6Unspecified(),
},
},
},
@@ -118,7 +118,7 @@ func Test_GetConnection(t *testing.T) {
randSource: rand.NewSource(0),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Protocol: constants.UDP,
Port: 1194,
},
@@ -128,9 +128,9 @@ func Test_GetConnection(t *testing.T) {
{
VPN: vpn.OpenVPN,
UDP: true,
IPs: []net.IP{
net.IPv6zero,
net.IPv4(1, 1, 1, 1),
IPs: []netip.Addr{
netip.IPv6Unspecified(),
netip.AddrFrom4([4]byte{1, 1, 1, 1}),
},
},
},
@@ -141,7 +141,7 @@ func Test_GetConnection(t *testing.T) {
randSource: rand.NewSource(0),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv6zero,
IP: netip.IPv6Unspecified(),
Protocol: constants.UDP,
Port: 1194,
},
@@ -151,21 +151,21 @@ func Test_GetConnection(t *testing.T) {
{
VPN: vpn.OpenVPN,
UDP: true,
IPs: []net.IP{net.IPv4(1, 1, 1, 1)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
OvpnX509: "ovpnx509",
},
{
VPN: vpn.Wireguard,
UDP: true,
IPs: []net.IP{net.IPv4(2, 2, 2, 2)},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{2, 2, 2, 2})},
OvpnX509: "ovpnx509",
},
{
VPN: vpn.OpenVPN,
UDP: true,
IPs: []net.IP{
net.IPv4(3, 3, 3, 3),
{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, // ipv6 ignored
IPs: []netip.Addr{
netip.AddrFrom4([4]byte{3, 3, 3, 3}),
netip.AddrFrom16([16]byte{1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}), // ipv6 ignored
},
Hostname: "hostname",
},
@@ -176,7 +176,7 @@ func Test_GetConnection(t *testing.T) {
randSource: rand.NewSource(0),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Protocol: constants.UDP,
Port: 1194,
Hostname: "ovpnx509",

View File

@@ -4,15 +4,15 @@ import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
)
type NoPortForwarder interface {
PortForward(ctx context.Context, client *http.Client,
logger Logger, gateway net.IP, serverName string) (
logger Logger, gateway netip.Addr, serverName string) (
port uint16, err error)
KeepPortForward(ctx context.Context, gateway net.IP,
KeepPortForward(ctx context.Context, gateway netip.Addr,
serverName string) (err error)
}
@@ -29,10 +29,10 @@ func NewNoPortForwarding(providerName string) *NoPortForwarding {
var ErrPortForwardingNotSupported = errors.New("custom port forwarding obtention is not supported")
func (n *NoPortForwarding) PortForward(context.Context, *http.Client,
Logger, net.IP, string) (port uint16, err error) {
Logger, netip.Addr, string) (port uint16, err error) {
return 0, fmt.Errorf("%w: for %s", ErrPortForwardingNotSupported, n.providerName)
}
func (n *NoPortForwarding) KeepPortForward(context.Context, net.IP, string) (err error) {
func (n *NoPortForwarding) KeepPortForward(context.Context, netip.Addr, string) (err error) {
return fmt.Errorf("%w: for %s", ErrPortForwardingNotSupported, n.providerName)
}

View File

@@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"math/rand"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/constants/vpn"
@@ -25,13 +25,15 @@ func pickConnection(connections []models.Connection,
return connection, ErrNoConnectionToPickFrom
}
if len(selection.TargetIP) > 0 && selection.VPN == vpn.Wireguard {
targetIPSet := selection.TargetIP.IsValid() && !selection.TargetIP.IsUnspecified()
if targetIPSet && selection.VPN == vpn.Wireguard {
// we need the right public key
return getTargetIPConnection(connections, selection.TargetIP)
}
connection = pickRandomConnection(connections, randSource)
if len(selection.TargetIP) > 0 {
if targetIPSet {
connection.IP = selection.TargetIP
}
@@ -46,9 +48,9 @@ func pickRandomConnection(connections []models.Connection,
var errTargetIPNotFound = errors.New("target IP address not found")
func getTargetIPConnection(connections []models.Connection,
targetIP net.IP) (connection models.Connection, err error) {
targetIP netip.Addr) (connection models.Connection, err error) {
for _, connection := range connections {
if targetIP.Equal(connection.IP) {
if targetIP == connection.IP {
return connection, nil
}
}

View File

@@ -22,8 +22,7 @@ func BuildWireguardSettings(connection models.Connection,
settings.RulePriority = rulePriority
settings.Endpoint = new(net.UDPAddr)
settings.Endpoint.IP = make(net.IP, len(connection.IP))
copy(settings.Endpoint.IP, connection.IP)
settings.Endpoint.IP = connection.IP.AsSlice()
settings.Endpoint.Port = int(connection.Port)
settings.Addresses = make([]netip.Prefix, 0, len(userSettings.Addresses))

View File

@@ -24,7 +24,7 @@ func Test_BuildWireguardSettings(t *testing.T) {
}{
"some settings": {
connection: models.Connection{
IP: net.IPv4(1, 2, 3, 4),
IP: netip.AddrFrom4([4]byte{1, 2, 3, 4}),
Port: 51821,
PubKey: "public",
},
@@ -44,7 +44,7 @@ func Test_BuildWireguardSettings(t *testing.T) {
PublicKey: "public",
PreSharedKey: "pre-shared",
Endpoint: &net.UDPAddr{
IP: net.IPv4(1, 2, 3, 4),
IP: net.IP{1, 2, 3, 4},
Port: 51821,
},
Addresses: []netip.Prefix{

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/models"
)
@@ -16,7 +16,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/models"
)
@@ -16,7 +16,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -1,7 +1,7 @@
package updater
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models"
@@ -33,7 +33,7 @@ func (hts hostToServer) toHostsSlice() (hosts []string) {
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]netip.Addr) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs

View File

@@ -3,7 +3,7 @@ package wevpn
import (
"errors"
"math/rand"
"net"
"net/netip"
"testing"
"github.com/golang/mock/gomock"
@@ -41,7 +41,7 @@ func Test_Provider_GetConnection(t *testing.T) {
},
"default OpenVPN TCP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -50,14 +50,14 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 1195,
Protocol: constants.TCP,
},
},
"default OpenVPN UDP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -66,14 +66,14 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 1194,
Protocol: constants.UDP,
},
},
"default Wireguard port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, WgPubKey: "x"},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}, WgPubKey: "x"},
},
selection: settings.ServerSelection{
VPN: vpn.Wireguard,

View File

@@ -3,8 +3,8 @@ package windscribe
import (
"errors"
"math/rand"
"net"
"net/http"
"net/netip"
"testing"
"github.com/golang/mock/gomock"
@@ -42,7 +42,7 @@ func Test_Provider_GetConnection(t *testing.T) {
},
"default OpenVPN TCP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -51,14 +51,14 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 443,
Protocol: constants.TCP,
},
},
"default OpenVPN UDP port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
},
selection: settings.ServerSelection{
OpenVPN: settings.OpenVPNSelection{
@@ -67,21 +67,21 @@ func Test_Provider_GetConnection(t *testing.T) {
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.OpenVPN,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 1194,
Protocol: constants.UDP,
},
},
"default Wireguard port": {
filteredServers: []models.Server{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, WgPubKey: "x"},
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}, WgPubKey: "x"},
},
selection: settings.ServerSelection{
VPN: vpn.Wireguard,
}.WithDefaults(provider),
connection: models.Connection{
Type: vpn.Wireguard,
IP: net.IPv4(1, 1, 1, 1),
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
Port: 1194,
Protocol: constants.UDP,
PubKey: "x",

View File

@@ -5,8 +5,8 @@ import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
"strconv"
"time"
)
@@ -33,9 +33,9 @@ type groupData struct {
type serverData struct {
Hostname string `json:"hostname"`
IP net.IP `json:"ip"`
IP2 net.IP `json:"ip2"`
IP3 net.IP `json:"ip3"`
IP netip.Addr `json:"ip"`
IP2 netip.Addr `json:"ip2"`
IP3 netip.Addr `json:"ip3"`
}
func fetchAPI(ctx context.Context, client *http.Client) (

View File

@@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"sort"
"github.com/qdm12/gluetun/internal/constants/vpn"
@@ -30,11 +30,11 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
x5090Name := group.OvpnX509
wgPubKey := group.WgPubKey
for _, node := range group.Nodes {
ips := make([]net.IP, 0, 2) //nolint:gomnd
if node.IP != nil {
ips := make([]netip.Addr, 0, 2) //nolint:gomnd
if node.IP.IsValid() {
ips = append(ips, node.IP)
}
if node.IP2 != nil {
if node.IP2.IsValid() {
ips = append(ips, node.IP2)
}
server := models.Server{
@@ -49,7 +49,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
}
servers = append(servers, server)
if node.IP3 == nil { // Wireguard + Stealth
if !node.IP3.IsValid() { // Wireguard + Stealth
continue
} else if wgPubKey == "" {
return nil, fmt.Errorf("%w: for node %s", ErrNoWireguardKey, node.Hostname)
@@ -60,7 +60,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
server.TCP = false
server.OvpnX509 = ""
server.WgPubKey = wgPubKey
server.IPs = []net.IP{node.IP3}
server.IPs = []netip.Addr{node.IP3}
servers = append(servers, server)
}
}

View File

@@ -2,12 +2,12 @@ package publicip
import (
"context"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/publicip/ipinfo"
)
type Fetcher interface {
FetchInfo(ctx context.Context, ip net.IP) (
FetchInfo(ctx context.Context, ip netip.Addr) (
result ipinfo.Response, err error)
}

View File

@@ -5,8 +5,8 @@ import (
"encoding/json"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
"strings"
"github.com/qdm12/gluetun/internal/constants"
@@ -28,13 +28,13 @@ var (
)
// FetchInfo obtains information on the ip address provided
// using the ipinfo.io API. If the ip is nil, the public IP address
// using the ipinfo.io API. If the ip is the zero value, the public IP address
// of the machine is used as the IP.
func (f *Fetch) FetchInfo(ctx context.Context, ip net.IP) (
func (f *Fetch) FetchInfo(ctx context.Context, ip netip.Addr) (
result Response, err error) {
const baseURL = "https://ipinfo.io/"
url := baseURL
if ip != nil {
if ip.IsValid() {
url += ip.String()
}

View File

@@ -1,13 +1,13 @@
package ipinfo
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/models"
)
type Response struct {
IP net.IP `json:"ip,omitempty"`
IP netip.Addr `json:"ip,omitempty"`
Region string `json:"region,omitempty"`
Country string `json:"country,omitempty"`
City string `json:"city,omitempty"`
@@ -19,8 +19,8 @@ type Response struct {
}
func (r *Response) ToPublicIPModel() (model models.PublicIP) {
model = models.PublicIP{
IP: make(net.IP, len(r.IP)),
return models.PublicIP{
IP: r.IP,
Region: r.Region,
Country: r.Country,
City: r.City,
@@ -30,6 +30,4 @@ func (r *Response) ToPublicIPModel() (model models.PublicIP) {
PostalCode: r.Postal,
Timezone: r.Timezone,
}
copy(model.IP, r.IP)
return model
}

View File

@@ -2,7 +2,7 @@ package ipinfo
import (
"context"
"net"
"net/netip"
)
// FetchMultiInfo obtains the public IP address information for every IP
@@ -11,7 +11,7 @@ import (
// If an error is encountered, all the operations are canceled and
// an error is returned, so the results returned should be considered
// incomplete in this case.
func (f *Fetch) FetchMultiInfo(ctx context.Context, ips []net.IP) (
func (f *Fetch) FetchMultiInfo(ctx context.Context, ips []netip.Addr) (
results []Response, err error) {
ctx, cancel := context.WithCancel(ctx)
@@ -23,7 +23,7 @@ func (f *Fetch) FetchMultiInfo(ctx context.Context, ips []net.IP) (
resultsCh := make(chan asyncResult)
for i, ip := range ips {
go func(index int, ip net.IP) {
go func(index int, ip netip.Addr) {
aResult := asyncResult{
index: index,
}

View File

@@ -3,6 +3,7 @@ package publicip
import (
"context"
"errors"
"net/netip"
"os"
"github.com/qdm12/gluetun/internal/constants"
@@ -26,7 +27,7 @@ func (l *Loop) Run(ctx context.Context, done chan<- struct{}) {
resultCh := make(chan models.PublicIP)
errorCh := make(chan error)
go func() {
result, err := l.fetcher.FetchInfo(getCtx, nil)
result, err := l.fetcher.FetchInfo(getCtx, netip.Addr{})
if err != nil {
if getCtx.Err() == nil {
errorCh <- err

View File

@@ -3,7 +3,7 @@ package routing
import (
"errors"
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/netlink"
)
@@ -14,8 +14,8 @@ var (
type DefaultRoute struct {
NetInterface string
Gateway net.IP
AssignedIP net.IP
Gateway netip.Addr
AssignedIP netip.Addr
Family int
}
@@ -35,7 +35,7 @@ func (r *Routing) DefaultRoutes() (defaultRoutes []DefaultRoute, err error) {
continue
}
defaultRoute := DefaultRoute{
Gateway: route.Gw,
Gateway: netIPToNetipAddress(route.Gw),
Family: route.Family,
}
linkIndex := route.LinkIndex

View File

@@ -62,7 +62,7 @@ func (r *Routing) unrouteInboundFromDefault(defaultRoutes []DefaultRoute) (err e
func (r *Routing) addRuleInboundFromDefault(table int, defaultRoutes []DefaultRoute) (err error) {
for _, defaultRoute := range defaultRoutes {
assignedIP := netIPToNetipAddress(defaultRoute.AssignedIP)
assignedIP := defaultRoute.AssignedIP
bits := 32
if assignedIP.Is6() {
bits = 128
@@ -82,7 +82,7 @@ func (r *Routing) addRuleInboundFromDefault(table int, defaultRoutes []DefaultRo
func (r *Routing) delRuleInboundFromDefault(table int, defaultRoutes []DefaultRoute) (err error) {
for _, defaultRoute := range defaultRoutes {
assignedIP := netIPToNetipAddress(defaultRoute.AssignedIP)
assignedIP := defaultRoute.AssignedIP
bits := 32
if assignedIP.Is6() {
bits = 128

View File

@@ -4,11 +4,12 @@ import (
"errors"
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/netlink"
)
func IPIsPrivate(ip net.IP) bool {
func IPIsPrivate(ip netip.Addr) bool {
return ip.IsPrivate() || ip.IsLoopback() ||
ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast()
}
@@ -17,38 +18,26 @@ var (
errInterfaceIPNotFound = errors.New("IP address not found for interface")
)
func ipMatchesFamily(ip net.IP, family int) bool {
return (family == netlink.FAMILY_V6 && ip.To4() == nil) ||
(family == netlink.FAMILY_V4 && ip.To4() != nil)
func ipMatchesFamily(ip netip.Addr, family int) bool {
return (family == netlink.FAMILY_V6 && ip.Is6()) ||
(family == netlink.FAMILY_V4 && (ip.Is4() || ip.Is4In6()))
}
func ensureNoIPv6WrappedIPv4(candidateIP net.IP) (resultIP net.IP) {
const ipv4Size = 4
if candidateIP.To4() == nil || len(candidateIP) == ipv4Size { // ipv6 or ipv4
return candidateIP
}
// ipv6-wrapped ipv4
resultIP = make(net.IP, ipv4Size)
copy(resultIP, candidateIP[12:16])
return resultIP
}
func (r *Routing) assignedIP(interfaceName string, family int) (ip net.IP, err error) {
func (r *Routing) assignedIP(interfaceName string, family int) (ip netip.Addr, err error) {
iface, err := net.InterfaceByName(interfaceName)
if err != nil {
return nil, fmt.Errorf("network interface %s not found: %w", interfaceName, err)
return ip, fmt.Errorf("network interface %s not found: %w", interfaceName, err)
}
addresses, err := iface.Addrs()
if err != nil {
return nil, fmt.Errorf("listing interface %s addresses: %w", interfaceName, err)
return ip, fmt.Errorf("listing interface %s addresses: %w", interfaceName, err)
}
for _, address := range addresses {
switch value := address.(type) {
case *net.IPAddr:
ip = value.IP
ip = netIPToNetipAddress(value.IP)
case *net.IPNet:
ip = value.IP
ip = netIPToNetipAddress(value.IP)
default:
continue
}
@@ -60,9 +49,8 @@ func (r *Routing) assignedIP(interfaceName string, family int) (ip net.IP, err e
// Ensure we don't return an IPv6-wrapped IPv4 address
// since netip.Address String method works differently than
// net.IP String method for this kind of addresses.
ip = ensureNoIPv6WrappedIPv4(ip)
return ip, nil
return ip.Unmap(), nil
}
return nil, fmt.Errorf("%w: interface %s in %d addresses",
return ip, fmt.Errorf("%w: interface %s in %d addresses",
errInterfaceIPNotFound, interfaceName, len(addresses))
}

View File

@@ -1,7 +1,7 @@
package routing
import (
"net"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
@@ -87,8 +87,8 @@ func Test_IPIsPrivate(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()
ip := net.ParseIP(testCase.ipString)
require.NotNil(t, ip)
ip, err := netip.ParseAddr(testCase.ipString)
require.NoError(t, err)
isPrivate := IPIsPrivate(ip)
@@ -96,35 +96,3 @@ func Test_IPIsPrivate(t *testing.T) {
})
}
}
func Test_ensureNoIPv6WrappedIPv4(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
candidateIP net.IP
resultIP net.IP
}{
"nil": {},
"ipv6": {
candidateIP: net.IPv6loopback,
resultIP: net.IPv6loopback,
},
"ipv4": {
candidateIP: net.IP{1, 2, 3, 4},
resultIP: net.IP{1, 2, 3, 4},
},
"ipv6_wrapped_ipv4": {
candidateIP: net.IPv4(1, 2, 3, 4),
resultIP: net.IP{1, 2, 3, 4},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
resultIP := ensureNoIPv6WrappedIPv4(testCase.candidateIP)
assert.Equal(t, testCase.resultIP, resultIP)
})
}
}

View File

@@ -3,7 +3,6 @@ package routing
import (
"errors"
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/netlink"
@@ -18,7 +17,7 @@ var (
type LocalNetwork struct {
IPNet netip.Prefix
InterfaceName string
IP net.IP
IP netip.Addr
}
func (r *Routing) LocalNetworks() (localNetworks []LocalNetwork, err error) {

View File

@@ -2,14 +2,13 @@ package routing
import (
"fmt"
"net"
"net/netip"
"strconv"
"github.com/qdm12/gluetun/internal/netlink"
)
func (r *Routing) addRouteVia(destination netip.Prefix, gateway net.IP,
func (r *Routing) addRouteVia(destination netip.Prefix, gateway netip.Addr,
iface string, table int) error {
destinationStr := destination.String()
r.logger.Info("adding route for " + destinationStr)
@@ -25,7 +24,7 @@ func (r *Routing) addRouteVia(destination netip.Prefix, gateway net.IP,
route := netlink.Route{
Dst: NetipPrefixToIPNet(&destination),
Gw: gateway,
Gw: gateway.AsSlice(),
LinkIndex: link.Attrs().Index,
Table: table,
}
@@ -37,7 +36,7 @@ func (r *Routing) addRouteVia(destination netip.Prefix, gateway net.IP,
return nil
}
func (r *Routing) deleteRouteVia(destination netip.Prefix, gateway net.IP,
func (r *Routing) deleteRouteVia(destination netip.Prefix, gateway netip.Addr,
iface string, table int) (err error) {
destinationStr := destination.String()
r.logger.Info("deleting route for " + destinationStr)
@@ -53,7 +52,7 @@ func (r *Routing) deleteRouteVia(destination netip.Prefix, gateway net.IP,
route := netlink.Route{
Dst: NetipPrefixToIPNet(&destination),
Gw: gateway,
Gw: gateway.AsSlice(),
LinkIndex: link.Attrs().Index,
Table: table,
}

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/netlink"
)
@@ -14,10 +15,10 @@ var (
ErrVPNLocalGatewayIPNotFound = errors.New("VPN local gateway IP address not found")
)
func (r *Routing) VPNDestinationIP() (ip net.IP, err error) {
func (r *Routing) VPNDestinationIP() (ip netip.Addr, err error) {
routes, err := r.netLinker.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return nil, fmt.Errorf("listing routes: %w", err)
return ip, fmt.Errorf("listing routes: %w", err)
}
defaultLinkIndex := -1
@@ -28,36 +29,36 @@ func (r *Routing) VPNDestinationIP() (ip net.IP, err error) {
}
}
if defaultLinkIndex == -1 {
return nil, fmt.Errorf("%w: in %d route(s)", ErrLinkDefaultNotFound, len(routes))
return ip, fmt.Errorf("%w: in %d route(s)", ErrLinkDefaultNotFound, len(routes))
}
for _, route := range routes {
if route.LinkIndex == defaultLinkIndex &&
route.Dst != nil &&
!IPIsPrivate(route.Dst.IP) &&
!IPIsPrivate(netIPToNetipAddress(route.Dst.IP)) &&
bytes.Equal(route.Dst.Mask, net.IPMask{255, 255, 255, 255}) {
return route.Dst.IP, nil
return netIPToNetipAddress(route.Dst.IP), nil
}
}
return nil, fmt.Errorf("%w: in %d routes", ErrVPNDestinationIPNotFound, len(routes))
return ip, fmt.Errorf("%w: in %d routes", ErrVPNDestinationIPNotFound, len(routes))
}
func (r *Routing) VPNLocalGatewayIP(vpnIntf string) (ip net.IP, err error) {
func (r *Routing) VPNLocalGatewayIP(vpnIntf string) (ip netip.Addr, err error) {
routes, err := r.netLinker.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return nil, fmt.Errorf("listing routes: %w", err)
return ip, fmt.Errorf("listing routes: %w", err)
}
for _, route := range routes {
link, err := r.netLinker.LinkByIndex(route.LinkIndex)
if err != nil {
return nil, fmt.Errorf("finding link at index %d: %w", route.LinkIndex, err)
return ip, fmt.Errorf("finding link at index %d: %w", route.LinkIndex, err)
}
interfaceName := link.Attrs().Name
if interfaceName == vpnIntf &&
route.Dst != nil &&
route.Dst.IP.Equal(net.IP{0, 0, 0, 0}) {
return route.Gw, nil
return netIPToNetipAddress(route.Gw), nil
}
}
return nil, fmt.Errorf("%w: in %d routes", ErrVPNLocalGatewayIPNotFound, len(routes))
return ip, fmt.Errorf("%w: in %d routes", ErrVPNLocalGatewayIPNotFound, len(routes))
}

View File

@@ -1,7 +1,7 @@
package storage
import (
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/models"
)
@@ -12,21 +12,12 @@ func copyServer(server models.Server) (serverCopy models.Server) {
return serverCopy
}
func copyIPs(toCopy []net.IP) (copied []net.IP) {
func copyIPs(toCopy []netip.Addr) (copied []netip.Addr) {
if toCopy == nil {
return nil
}
copied = make([]net.IP, len(toCopy))
for i := range toCopy {
copied[i] = copyIP(toCopy[i])
}
return copied
}
func copyIP(toCopy net.IP) (copied net.IP) {
copied = make(net.IP, len(toCopy))
copied = make([]netip.Addr, len(toCopy))
copy(copied, toCopy)
return copied
}

View File

@@ -1,12 +1,11 @@
package storage
import (
"net"
"net/netip"
"testing"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_copyServer(t *testing.T) {
@@ -14,14 +13,14 @@ func Test_copyServer(t *testing.T) {
server := models.Server{
Country: "a",
IPs: []net.IP{{1, 2, 3, 4}},
IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 2, 3, 4})},
}
serverCopy := copyServer(server)
assert.Equal(t, server, serverCopy)
// Check for mutation
serverCopy.IPs[0][0] = 9
serverCopy.IPs[0] = netip.AddrFrom4([4]byte{9, 9, 9, 9})
assert.NotEqual(t, server, serverCopy)
}
@@ -29,21 +28,21 @@ func Test_copyIPs(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
toCopy []net.IP
copied []net.IP
toCopy []netip.Addr
copied []netip.Addr
}{
"nil": {},
"empty": {
toCopy: []net.IP{},
copied: []net.IP{},
toCopy: []netip.Addr{},
copied: []netip.Addr{},
},
"single IP": {
toCopy: []net.IP{{1, 1, 1, 1}},
copied: []net.IP{{1, 1, 1, 1}},
toCopy: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
copied: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
},
"two IPs": {
toCopy: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}},
copied: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}},
toCopy: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})},
copied: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})},
},
}
@@ -52,23 +51,13 @@ func Test_copyIPs(t *testing.T) {
t.Run(name, func(t *testing.T) {
t.Parallel()
// Reserver leading 9 for copy modifications below
for _, ipToCopy := range testCase.toCopy {
require.NotEqual(t, 9, ipToCopy[0])
}
copied := copyIPs(testCase.toCopy)
assert.Equal(t, testCase.copied, copied)
if len(copied) > 0 {
original := testCase.toCopy[0][0]
testCase.toCopy[0][0] = 9
assert.NotEqual(t, 9, copied[0][0])
testCase.toCopy[0][0] = original
copied[0][0] = 9
assert.NotEqual(t, 9, testCase.toCopy[0][0])
testCase.toCopy[0] = netip.AddrFrom4([4]byte{9, 9, 9, 9})
assert.NotEqual(t, testCase.toCopy[0], testCase.copied[0])
}
})
}

View File

@@ -3,7 +3,7 @@ package openvpn
import (
"errors"
"fmt"
"net"
"net/netip"
"sort"
"strings"
)
@@ -54,7 +54,7 @@ func ExtractHost(b []byte) (host, warning string, err error) {
return hosts[0], warning, nil
}
func ExtractIPs(b []byte) (ips []net.IP, err error) {
func ExtractIPs(b []byte) (ips []netip.Addr, err error) {
const rejectIP, rejectDomain = false, true
ipStrings := extractRemoteHosts(b, rejectIP, rejectDomain)
if len(ipStrings) == 0 {
@@ -65,9 +65,12 @@ func ExtractIPs(b []byte) (ips []net.IP, err error) {
return ipStrings[i] < ipStrings[j]
})
ips = make([]net.IP, len(ipStrings))
ips = make([]netip.Addr, len(ipStrings))
for i := range ipStrings {
ips[i] = net.ParseIP(ipStrings[i])
ips[i], err = netip.ParseAddr(ipStrings[i])
if err != nil {
return nil, fmt.Errorf("parsing IP address: %w", err)
}
}
return ips, nil
@@ -85,9 +88,9 @@ func extractRemoteHosts(content []byte, rejectIP, rejectDomain bool) (hosts []st
continue
}
host := fields[1]
parsedIP := net.ParseIP(host)
if (rejectIP && parsedIP != nil) ||
(rejectDomain && parsedIP == nil) {
_, err := netip.ParseAddr(host)
if (rejectIP && err == nil) ||
(rejectDomain && err != nil) {
continue
}
hosts = append(hosts, host)

View File

@@ -1,15 +1,20 @@
package resolver
import "net"
import (
"net/netip"
)
func uniqueIPsToSlice(uniqueIPs map[string]struct{}) (ips []net.IP) {
ips = make([]net.IP, 0, len(uniqueIPs))
func uniqueIPsToSlice(uniqueIPs map[string]struct{}) (ips []netip.Addr) {
ips = make([]netip.Addr, 0, len(uniqueIPs))
for key := range uniqueIPs {
IP := net.ParseIP(key)
if IPv4 := IP.To4(); IPv4 != nil {
IP = IPv4
ip, err := netip.ParseAddr(key)
if err != nil {
panic(err)
}
ips = append(ips, IP)
if ip.Is4In6() {
ip = netip.AddrFrom4(ip.As4())
}
ips = append(ips, ip)
}
return ips
}

View File

@@ -1,7 +1,7 @@
package resolver
import (
"net"
"net/netip"
"testing"
"github.com/stretchr/testify/assert"
@@ -11,23 +11,23 @@ func Test_uniqueIPsToSlice(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
inputIPs map[string]struct{}
outputIPs []net.IP
outputIPs []netip.Addr
}{
"nil": {
inputIPs: nil,
outputIPs: []net.IP{},
outputIPs: []netip.Addr{},
},
"empty": {
inputIPs: map[string]struct{}{},
outputIPs: []net.IP{},
outputIPs: []netip.Addr{},
},
"single IPv4": {
inputIPs: map[string]struct{}{"1.1.1.1": {}},
outputIPs: []net.IP{{1, 1, 1, 1}},
outputIPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
},
"two IPv4s": {
inputIPs: map[string]struct{}{"1.1.1.1": {}, "1.1.2.1": {}},
outputIPs: []net.IP{{1, 1, 1, 1}, {1, 1, 2, 1}},
outputIPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{1, 1, 2, 1})},
},
}
for name, testCase := range testCases {

View File

@@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"net"
"net/netip"
)
type Parallel struct {
@@ -31,7 +31,7 @@ type ParallelSettings struct {
type parallelResult struct {
host string
IPs []net.IP
IPs []netip.Addr
}
var (
@@ -40,7 +40,7 @@ var (
)
func (pr *Parallel) Resolve(ctx context.Context, settings ParallelSettings) (
hostToIPs map[string][]net.IP, warnings []string, err error) {
hostToIPs map[string][]netip.Addr, warnings []string, err error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
@@ -53,7 +53,7 @@ func (pr *Parallel) Resolve(ctx context.Context, settings ParallelSettings) (
go pr.resolveAsync(ctx, host, settings.Repeat, results, errors)
}
hostToIPs = make(map[string][]net.IP, len(settings.Hosts))
hostToIPs = make(map[string][]netip.Addr, len(settings.Hosts))
maxFails := int(settings.MaxFailRatio * float64(len(settings.Hosts)))
for range settings.Hosts {

View File

@@ -1,11 +1,11 @@
package resolver
import (
"bytes"
"context"
"errors"
"fmt"
"net"
"net/netip"
"sort"
"time"
)
@@ -31,7 +31,7 @@ type RepeatSettings struct {
}
func (r *Repeat) Resolve(ctx context.Context, host string, settings RepeatSettings) (
ips []net.IP, err error) {
ips []netip.Addr, err error) {
timedCtx, cancel := context.WithTimeout(ctx, settings.MaxDuration)
defer cancel()
@@ -54,7 +54,7 @@ func (r *Repeat) Resolve(ctx context.Context, host string, settings RepeatSettin
if settings.SortIPs {
sort.Slice(ips, func(i, j int) bool {
return bytes.Compare(ips[i], ips[j]) < 1
return ips[i].Compare(ips[j]) < 1
})
}
@@ -121,15 +121,15 @@ func (r *Repeat) resolveOnce(ctx, timedCtx context.Context, host string,
}
}
func (r *Repeat) lookupIPs(ctx context.Context, host string) (ips []net.IP, err error) {
func (r *Repeat) lookupIPs(ctx context.Context, host string) (ips []netip.Addr, err error) {
addresses, err := r.resolver.LookupIPAddr(ctx, host)
if err != nil {
return nil, err
}
ips = make([]net.IP, 0, len(addresses))
ips = make([]netip.Addr, 0, len(addresses))
for i := range addresses {
ip := addresses[i].IP
if ip == nil {
ip, ok := netip.AddrFromSlice(addresses[i].IP)
if !ok {
continue
}
ips = append(ips, ip)

View File

@@ -2,7 +2,7 @@ package vpn
import (
"context"
"net"
"net/netip"
"github.com/qdm12/gluetun/internal/configuration/settings"
"github.com/qdm12/gluetun/internal/models"
@@ -18,7 +18,7 @@ type Firewall interface {
}
type Routing interface {
VPNLocalGatewayIP(vpnInterface string) (gateway net.IP, err error)
VPNLocalGatewayIP(vpnInterface string) (gateway netip.Addr, err error)
}
type PortForward interface {