chore(all): migrate from net.IP* to net/netip

This commit is contained in:
Quentin McGaw
2023-06-12 09:48:43 +00:00
parent 3e6fb5ead4
commit 9a4a268926
68 changed files with 775 additions and 712 deletions

View File

@@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"github.com/miekg/dns"
)
@@ -18,7 +18,7 @@ var (
)
func fetch(ctx context.Context, client Client, providerData providerData) (
publicIP net.IP, err error) {
publicIP netip.Addr, err error) {
message := &dns.Msg{
MsgHdr: dns.MsgHdr{
Opcode: dns.OpcodeQuery,
@@ -34,34 +34,34 @@ func fetch(ctx context.Context, client Client, providerData providerData) (
r, _, err := client.ExchangeContext(ctx, message, providerData.nameserver)
if err != nil {
return nil, err
return netip.Addr{}, err
}
L := len(r.Answer)
if L == 0 {
return nil, ErrNoTXTRecordFound
return netip.Addr{}, fmt.Errorf("%w", ErrNoTXTRecordFound)
} else if L > 1 {
return nil, fmt.Errorf("%w: %d instead of 1", ErrTooManyAnswers, L)
return netip.Addr{}, fmt.Errorf("%w: %d instead of 1", ErrTooManyAnswers, L)
}
answer := r.Answer[0]
txt, ok := answer.(*dns.TXT)
if !ok {
return nil, fmt.Errorf("%w: %T instead of *dns.TXT",
return netip.Addr{}, fmt.Errorf("%w: %T instead of *dns.TXT",
ErrInvalidAnswerType, answer)
}
L = len(txt.Txt)
if L == 0 {
return nil, ErrNoTXTRecordFound
return netip.Addr{}, fmt.Errorf("%w", ErrNoTXTRecordFound)
} else if L > 1 {
return nil, fmt.Errorf("%w: %d instead of 1", ErrTooManyTXTRecords, L)
return netip.Addr{}, fmt.Errorf("%w: %d instead of 1", ErrTooManyTXTRecords, L)
}
ipString := txt.Txt[0]
publicIP = net.ParseIP(ipString)
if publicIP == nil {
return nil, fmt.Errorf("%w: %q", ErrIPMalformed, ipString)
publicIP, err = netip.ParseAddr(ipString)
if err != nil {
return netip.Addr{}, fmt.Errorf("%w: %w", ErrIPMalformed, err)
}
return publicIP, nil

View File

@@ -3,7 +3,7 @@ package dns
import (
"context"
"errors"
"net"
"net/netip"
"testing"
"time"
@@ -39,7 +39,7 @@ func Test_fetch(t *testing.T) {
testCases := map[string]struct {
response *dns.Msg
exchangeErr error
publicIP net.IP
publicIP netip.Addr
err error
}{
"success": {
@@ -50,7 +50,7 @@ func Test_fetch(t *testing.T) {
},
},
},
publicIP: net.IP{55, 55, 55, 55},
publicIP: netip.AddrFrom4([4]byte{55, 55, 55, 55}),
},
"exchange error": {
exchangeErr: errors.New("dummy"),
@@ -92,7 +92,7 @@ func Test_fetch(t *testing.T) {
Txt: []string{"invalid"},
}},
},
err: errors.New(`IP address malformed: "invalid"`),
err: errors.New(`IP address malformed: ParseAddr("invalid"): unable to parse IP`),
},
}
@@ -118,7 +118,7 @@ func Test_fetch(t *testing.T) {
assert.NoError(t, err)
}
if !testCase.publicIP.Equal(publicIP) {
if testCase.publicIP.Compare(publicIP) != 0 {
t.Errorf("IP address mismatch: expected %s and got %s", testCase.publicIP, publicIP)
}
})

View File

@@ -2,24 +2,24 @@ package dns
import (
"context"
"net"
"net/netip"
"sync/atomic"
)
func (f *Fetcher) IP(ctx context.Context) (publicIP net.IP, err error) {
func (f *Fetcher) IP(ctx context.Context) (publicIP netip.Addr, err error) {
return f.ip(ctx, f.client)
}
func (f *Fetcher) IP4(ctx context.Context) (publicIP net.IP, err error) {
func (f *Fetcher) IP4(ctx context.Context) (publicIP netip.Addr, err error) {
return f.ip(ctx, f.client4)
}
func (f *Fetcher) IP6(ctx context.Context) (publicIP net.IP, err error) {
func (f *Fetcher) IP6(ctx context.Context) (publicIP netip.Addr, err error) {
return f.ip(ctx, f.client6)
}
func (f *Fetcher) ip(ctx context.Context, client Client) (
publicIP net.IP, err error) {
publicIP netip.Addr, err error) {
index := int(atomic.AddUint32(f.ring.counter, 1)) % len(f.ring.providers)
provider := f.ring.providers[index]
return fetch(ctx, client, provider.data())

View File

@@ -5,8 +5,8 @@ import (
"errors"
"fmt"
"io"
"net"
"net/http"
"net/netip"
"regexp"
"strings"
@@ -26,33 +26,33 @@ var (
)
func fetch(ctx context.Context, client *http.Client, url string, version ipversion.IPVersion) (
publicIP net.IP, err error) {
publicIP netip.Addr, err error) {
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
return netip.Addr{}, err
}
response, err := client.Do(request)
if err != nil {
return nil, err
return netip.Addr{}, err
}
defer response.Body.Close()
switch response.StatusCode {
case http.StatusOK:
case http.StatusForbidden, http.StatusTooManyRequests:
return nil, fmt.Errorf("%w: %d (%s)", ErrBanned,
return netip.Addr{}, fmt.Errorf("%w: %d (%s)", ErrBanned,
response.StatusCode, bodyToSingleLine(response.Body))
}
b, err := io.ReadAll(response.Body)
if err != nil {
return nil, err
return netip.Addr{}, err
}
err = response.Body.Close()
if err != nil {
return nil, err
return netip.Addr{}, err
}
s := string(b)
@@ -69,39 +69,39 @@ func fetch(ctx context.Context, client *http.Client, url string, version ipversi
case len(ipv6Strings) == 1:
ipString = ipv6Strings[0]
case len(ipv4Strings) > 1:
return nil, fmt.Errorf("%w: found %d IPv4 addresses instead of 1",
return netip.Addr{}, fmt.Errorf("%w: found %d IPv4 addresses instead of 1",
ErrTooManyIPs, len(ipv4Strings))
case len(ipv6Strings) > 1:
return nil, fmt.Errorf("%w: found %d IPv6 addresses instead of 1",
return netip.Addr{}, fmt.Errorf("%w: found %d IPv6 addresses instead of 1",
ErrTooManyIPs, len(ipv6Strings))
default:
return nil, fmt.Errorf("%w: from %q", ErrNoIPFound, url)
return netip.Addr{}, fmt.Errorf("%w: from %q", ErrNoIPFound, url)
}
case ipversion.IP4:
switch len(ipv4Strings) {
case 0:
return nil, fmt.Errorf("%w: from %q for version %s", ErrNoIPFound, url, version)
return netip.Addr{}, fmt.Errorf("%w: from %q for version %s", ErrNoIPFound, url, version)
case 1:
ipString = ipv4Strings[0]
default:
return nil, fmt.Errorf("%w: found %d IPv4 addresses instead of 1",
return netip.Addr{}, fmt.Errorf("%w: found %d IPv4 addresses instead of 1",
ErrTooManyIPs, len(ipv4Strings))
}
case ipversion.IP6:
switch len(ipv6Strings) {
case 0:
return nil, fmt.Errorf("%w: from %q for version %s", ErrNoIPFound, url, version)
return netip.Addr{}, fmt.Errorf("%w: from %q for version %s", ErrNoIPFound, url, version)
case 1:
ipString = ipv6Strings[0]
default:
return nil, fmt.Errorf("%w: found %d IPv6 addresses instead of 1",
return netip.Addr{}, fmt.Errorf("%w: found %d IPv6 addresses instead of 1",
ErrTooManyIPs, len(ipv6Strings))
}
}
publicIP = net.ParseIP(ipString)
if publicIP == nil {
return nil, fmt.Errorf("%w: %s", ErrIPMalformed, ipString)
publicIP, err = netip.ParseAddr(ipString)
if err != nil {
return netip.Addr{}, fmt.Errorf("%w: %w", ErrIPMalformed, err)
}
return publicIP, nil

View File

@@ -5,8 +5,8 @@ import (
"context"
"errors"
"io"
"net"
"net/http"
"net/netip"
"testing"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
@@ -28,7 +28,7 @@ func Test_fetch(t *testing.T) {
version ipversion.IPVersion
httpContent []byte
httpErr error
publicIP net.IP
publicIP netip.Addr
err error
}{
"canceled context": {
@@ -62,24 +62,24 @@ func Test_fetch(t *testing.T) {
url: "https://opendns.com/ip",
version: ipversion.IP4or6,
httpContent: []byte(`1.67.201.251`),
publicIP: net.IP{1, 67, 201, 251},
publicIP: netip.AddrFrom4([4]byte{1, 67, 201, 251}),
},
"single IPv6 for IP4or6": {
ctx: context.Background(),
url: "https://opendns.com/ip",
version: ipversion.IP4or6,
httpContent: []byte(`::1`),
publicIP: net.IP{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
},
publicIP: netip.AddrFrom16([16]byte{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1,
}),
},
"IPv4 and IPv6 for IP4or6": {
ctx: context.Background(),
url: "https://opendns.com/ip",
version: ipversion.IP4or6,
httpContent: []byte(`1.67.201.251 ::1`),
publicIP: net.IP{1, 67, 201, 251},
publicIP: netip.AddrFrom4([4]byte{1, 67, 201, 251}),
},
"too many IPv4s for IP4or6": {
ctx: context.Background(),
@@ -107,7 +107,7 @@ func Test_fetch(t *testing.T) {
url: "https://opendns.com/ip",
version: ipversion.IP4,
httpContent: []byte(`1.67.201.251`),
publicIP: net.IP{1, 67, 201, 251},
publicIP: netip.AddrFrom4([4]byte{1, 67, 201, 251}),
},
"too many IPv4s for IP4": {
ctx: context.Background(),
@@ -128,10 +128,10 @@ func Test_fetch(t *testing.T) {
url: "https://opendns.com/ip",
version: ipversion.IP6,
httpContent: []byte(`::1`),
publicIP: net.IP{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
},
publicIP: netip.AddrFrom16([16]byte{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1,
}),
},
"too many IPv6s for IP6": {
ctx: context.Background(),
@@ -171,7 +171,7 @@ func Test_fetch(t *testing.T) {
assert.NoError(t, err)
}
if !tc.publicIP.Equal(publicIP) {
if tc.publicIP.Compare(publicIP) != 0 {
t.Errorf("IP address mismatch: expected %s and got %s", tc.publicIP, publicIP)
}
})

View File

@@ -4,26 +4,26 @@ import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"strings"
"github.com/qdm12/ddns-updater/pkg/publicip/ipversion"
)
func (f *Fetcher) IP(ctx context.Context) (publicIP net.IP, err error) {
func (f *Fetcher) IP(ctx context.Context) (publicIP netip.Addr, err error) {
return f.ip(ctx, f.ip4or6, ipversion.IP4or6)
}
func (f *Fetcher) IP4(ctx context.Context) (publicIP net.IP, err error) {
func (f *Fetcher) IP4(ctx context.Context) (publicIP netip.Addr, err error) {
return f.ip(ctx, f.ip4, ipversion.IP4)
}
func (f *Fetcher) IP6(ctx context.Context) (publicIP net.IP, err error) {
func (f *Fetcher) IP6(ctx context.Context) (publicIP netip.Addr, err error) {
return f.ip(ctx, f.ip6, ipversion.IP6)
}
func (f *Fetcher) ip(ctx context.Context, ring *urlsRing, version ipversion.IPVersion) (
publicIP net.IP, err error) {
publicIP netip.Addr, err error) {
ring.mutex.Lock()
var index int
@@ -39,7 +39,7 @@ func (f *Fetcher) ip(ctx context.Context, ring *urlsRing, version ipversion.IPVe
if banned == len(ring.urls) {
banString := ring.banString()
ring.mutex.Unlock()
return nil, fmt.Errorf("%w: %s", ErrBanned, banString)
return netip.Addr{}, fmt.Errorf("%w: %s", ErrBanned, banString)
}
}
@@ -57,7 +57,7 @@ func (f *Fetcher) ip(ctx context.Context, ring *urlsRing, version ipversion.IPVe
ring.banned[index] = strings.ReplaceAll(err.Error(), ErrBanned.Error()+": ", "")
ring.mutex.Unlock()
}
return nil, err
return netip.Addr{}, err
}
return publicIP, nil
}

View File

@@ -4,8 +4,8 @@ import (
"bytes"
"context"
"io"
"net"
"net/http"
"net/netip"
"testing"
"time"
@@ -19,7 +19,7 @@ func Test_fetcher_IP(t *testing.T) {
ctx := context.Background()
const url = "c"
httpBytes := []byte(`55.55.55.55`)
expectedPublicIP := net.IP{55, 55, 55, 55}
expectedPublicIP := netip.AddrFrom4([4]byte{55, 55, 55, 55})
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
@@ -51,7 +51,7 @@ func Test_fetcher_IP(t *testing.T) {
publicIP, err := initialFetcher.IP(ctx)
assert.NoError(t, err)
if !expectedPublicIP.Equal(publicIP) {
if expectedPublicIP.Compare(publicIP) != 0 {
t.Errorf("IP address mismatch: expected %s and got %s", expectedPublicIP, publicIP)
}
assert.Equal(t, expectedFetcher, initialFetcher)
@@ -63,7 +63,7 @@ func Test_fetcher_IP4(t *testing.T) {
ctx := context.Background()
const url = "c"
httpBytes := []byte(`55.55.55.55`)
expectedPublicIP := net.IP{55, 55, 55, 55}
expectedPublicIP := netip.AddrFrom4([4]byte{55, 55, 55, 55})
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
@@ -95,7 +95,7 @@ func Test_fetcher_IP4(t *testing.T) {
publicIP, err := initialFetcher.IP4(ctx)
assert.NoError(t, err)
if !expectedPublicIP.Equal(publicIP) {
if expectedPublicIP.Compare(publicIP) != 0 {
t.Errorf("IP address mismatch: expected %s and got %s", expectedPublicIP, publicIP)
}
assert.Equal(t, expectedFetcher, initialFetcher)
@@ -107,10 +107,9 @@ func Test_fetcher_IP6(t *testing.T) {
ctx := context.Background()
const url = "c"
httpBytes := []byte(`::1`)
expectedPublicIP := net.IP{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
}
expectedPublicIP := netip.AddrFrom16([16]byte{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1})
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
@@ -142,7 +141,7 @@ func Test_fetcher_IP6(t *testing.T) {
publicIP, err := initialFetcher.IP6(ctx)
assert.NoError(t, err)
if !expectedPublicIP.Equal(publicIP) {
if expectedPublicIP.Compare(publicIP) != 0 {
t.Errorf("IP address mismatch: expected %s and got %s", expectedPublicIP, publicIP)
}
assert.Equal(t, expectedFetcher, initialFetcher)
@@ -176,7 +175,7 @@ func Test_fetcher_ip(t *testing.T) {
testCases := map[string]struct {
initialFetcher *Fetcher
ctx context.Context
publicIP net.IP
publicIP netip.Addr
err error
errMessage string
finalFetcher *Fetcher // client is ignored when comparing the two
@@ -191,7 +190,7 @@ func Test_fetcher_ip(t *testing.T) {
urls: []string{"a", "b"},
},
},
publicIP: net.IP{55, 55, 55, 55},
publicIP: netip.AddrFrom4([4]byte{55, 55, 55, 55}),
finalFetcher: &Fetcher{
timeout: time.Hour,
ip4or6: &urlsRing{
@@ -210,7 +209,7 @@ func Test_fetcher_ip(t *testing.T) {
urls: []string{"a", "b"},
},
},
publicIP: net.IP{55, 55, 55, 55},
publicIP: netip.AddrFrom4([4]byte{55, 55, 55, 55}),
finalFetcher: &Fetcher{
timeout: time.Hour,
ip4or6: &urlsRing{
@@ -276,7 +275,7 @@ func Test_fetcher_ip(t *testing.T) {
banned: map[int]string{1: "banned"},
},
},
publicIP: net.IP{55, 55, 55, 55},
publicIP: netip.AddrFrom4([4]byte{55, 55, 55, 55}),
},
"all banned": {
ctx: context.Background(),
@@ -335,7 +334,7 @@ func Test_fetcher_ip(t *testing.T) {
assert.EqualError(t, err, testCase.errMessage)
}
if !testCase.publicIP.Equal(publicIP) {
if testCase.publicIP.Compare(publicIP) != 0 {
t.Errorf("IP address mismatch: expected %s and got %s", testCase.publicIP, publicIP)
}

View File

@@ -5,8 +5,8 @@ import (
"errors"
"fmt"
"math/rand"
"net"
"net/http"
"net/netip"
"sync"
)
@@ -47,7 +47,7 @@ func New(client *http.Client, options ...Option) (info *Info, err error) {
// Get finds IP information for the given IP address using one of
// the ip data provider picked at random. A `nil` IP address can be
// given to signal to fetch information on the current public IP address.
func (i *Info) Get(ctx context.Context, ip net.IP) (result Result, err error) {
func (i *Info) Get(ctx context.Context, ip netip.Addr) (result Result, err error) {
if len(i.providers) == 1 {
return i.providers[0].get(ctx, ip)
}
@@ -80,7 +80,7 @@ func (i *Info) Get(ctx context.Context, ip net.IP) (result Result, err error) {
// GetMultiple finds IP information for the given IP addresses, each using
// one of the ip data provider picked at random. It returns a slice of results
// matching the order of the IP addresses given as argument.
func (i *Info) GetMultiple(ctx context.Context, ips []net.IP) (results []Result, err error) {
func (i *Info) GetMultiple(ctx context.Context, ips []netip.Addr) (results []Result, err error) {
type resultWithError struct {
index int
result Result
@@ -92,7 +92,7 @@ func (i *Info) GetMultiple(ctx context.Context, ips []net.IP) (results []Result,
channel := make(chan resultWithError)
for index, ip := range ips {
go func(ctx context.Context, index int, ip net.IP) {
go func(ctx context.Context, index int, ip netip.Addr) {
result := resultWithError{
index: index,
}

View File

@@ -4,8 +4,8 @@ import (
"context"
"encoding/json"
"fmt"
"net"
"net/http"
"net/netip"
)
func newIpinfo(client *http.Client) *ipinfo {
@@ -18,12 +18,12 @@ type ipinfo struct {
client *http.Client
}
func (p *ipinfo) get(ctx context.Context, ip net.IP) (
func (p *ipinfo) get(ctx context.Context, ip netip.Addr) (
result Result, err error) {
result.Source = string(Ipinfo)
url := "https://ipinfo.io/"
if ip != nil {
if ip.IsValid() {
url += ip.String()
}
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
@@ -51,10 +51,10 @@ func (p *ipinfo) get(ctx context.Context, ip net.IP) (
decoder := json.NewDecoder(response.Body)
var data struct {
IP net.IP `json:"ip"`
Region string `json:"region"`
Country string `json:"country"`
City string `json:"city"`
IP netip.Addr `json:"ip"`
Region string `json:"region"`
Country string `json:"country"`
City string `json:"city"`
}
err = decoder.Decode(&data)
if err != nil {

View File

@@ -4,8 +4,8 @@ import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/netip"
)
type Provider string
@@ -32,7 +32,7 @@ func ValidateProvider(provider Provider) error {
}
type provider interface {
get(ctx context.Context, ip net.IP) (result Result, err error)
get(ctx context.Context, ip netip.Addr) (result Result, err error)
}
func newProvider(providerName Provider, client *http.Client) provider { //nolint:ireturn

View File

@@ -1,9 +1,9 @@
package info
import "net"
import "net/netip"
type Result struct {
IP net.IP
IP netip.Addr
Country *string
Region *string
City *string

View File

@@ -3,16 +3,16 @@ package publicip
import (
"context"
"errors"
"net"
"net/netip"
"github.com/qdm12/ddns-updater/pkg/publicip/dns"
"github.com/qdm12/ddns-updater/pkg/publicip/http"
)
type ipFetcher interface {
IP(ctx context.Context) (ip net.IP, err error)
IP4(ctx context.Context) (ipv4 net.IP, err error)
IP6(ctx context.Context) (ipv6 net.IP, err error)
IP(ctx context.Context) (ip netip.Addr, err error)
IP4(ctx context.Context) (ipv4 netip.Addr, err error)
IP6(ctx context.Context) (ipv6 netip.Addr, err error)
}
type Fetcher struct {
@@ -58,14 +58,14 @@ func NewFetcher(dnsSettings DNSSettings, httpSettings HTTPSettings) (f *Fetcher,
return fetcher, nil
}
func (f *Fetcher) IP(ctx context.Context) (ip net.IP, err error) {
func (f *Fetcher) IP(ctx context.Context) (ip netip.Addr, err error) {
return f.getSubFetcher().IP(ctx)
}
func (f *Fetcher) IP4(ctx context.Context) (ipv4 net.IP, err error) {
func (f *Fetcher) IP4(ctx context.Context) (ipv4 netip.Addr, err error) {
return f.getSubFetcher().IP4(ctx)
}
func (f *Fetcher) IP6(ctx context.Context) (ipv6 net.IP, err error) {
func (f *Fetcher) IP6(ctx context.Context) (ipv6 netip.Addr, err error) {
return f.getSubFetcher().IP6(ctx)
}