Skip to content

Commit

Permalink
pkg/net: replace net with net/netip
Browse files Browse the repository at this point in the history
This replaces the go net package with net/netip as the
latter is a better alternative and supports IPv6 checks.

Updates #2614

Signed-off-by: Martin Greber <[email protected]>
  • Loading branch information
martingreber committed Dec 16, 2023
1 parent 80b0eee commit ee61020
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 38 deletions.
2 changes: 1 addition & 1 deletion pkg/net/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func JoinHostPort(host, port cue.Value) (string, error) {
switch host.Kind() {
case cue.ListKind:
ipdata := netGetIP(host)
if len(ipdata) != 4 && len(ipdata) != 16 {
if !ipdata.IsValid() {
err = fmt.Errorf("invalid host %s", host)
}
hostStr = ipdata.String()
Expand Down
81 changes: 46 additions & 35 deletions pkg/net/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@
package net

import (
"fmt"
"net"

"cuelang.org/go/cue"
"fmt"
"net/netip"
)

// IP address lengths (bytes).
Expand All @@ -28,76 +27,87 @@ const (
IPv6len = 16
)

func netGetIP(ip cue.Value) (goip net.IP) {
func netGetIP(ip cue.Value) (goip netip.Addr) {
switch ip.Kind() {
case cue.StringKind:
s, err := ip.String()
if err != nil {
return nil
return netip.Addr{}
}
goip := net.ParseIP(s)
if goip == nil {
return nil
goip, err := netip.ParseAddr(s)
if err != nil {
return netip.Addr{}
}
return goip

case cue.BytesKind:
b, err := ip.Bytes()
if err != nil {
return nil
return netip.Addr{}
}
goip := net.ParseIP(string(b))
if goip == nil {
return nil
goip, err := netip.ParseAddr(string(b))
if err != nil {
return netip.Addr{}
}
return goip

case cue.ListKind:
iter, err := ip.List()
if err != nil {
return nil
return netip.Addr{}
}
var bytes []byte
for iter.Next() {
v, err := iter.Value().Int64()
if err != nil {
return nil
return netip.Addr{}
}
if v < 0 || 255 < v {
return nil
return netip.Addr{}
}
goip = append(goip, byte(v))
bytes = append(bytes, byte(v))
}
goip, ok := netip.AddrFromSlice(bytes)
if !ok {
return netip.Addr{}
}
return goip

default:
// TODO: return canonical invalid type.
return nil
return netip.Addr{}
}
}

func netGetIPCIDR(ip cue.Value) (gonet *net.IPNet, err error) {
func netGetIPCIDR(ip cue.Value) (gonet *netip.Prefix, err error) {
switch ip.Kind() {
case cue.StringKind:
s, err := ip.String()
if err != nil {
return nil, err
}
_, gonet, err := net.ParseCIDR(s)
cidr, err := netip.ParsePrefix(s)
if err != nil {
return nil, err
}
return gonet, nil
if !cidr.IsValid() {
return nil, err
}
return &cidr, nil

case cue.BytesKind:
b, err := ip.Bytes()
if err != nil {
return nil, err
}
_, gonet, err := net.ParseCIDR(string(b))
cidr, err := netip.ParsePrefix(string(b))
if err != nil {
return nil, err
}
return gonet, nil
if !cidr.IsValid() {
return nil, err
}
return &cidr, nil

default:
// TODO: return canonical invalid type.
Expand All @@ -111,14 +121,14 @@ func netGetIPCIDR(ip cue.Value) (gonet *net.IPNet, err error) {
// If s is not a valid textual representation of an IP address,
// ParseIP returns nil.
func ParseIP(s string) ([]uint, error) {
goip := net.ParseIP(s)
if goip == nil {
goip, err := netip.ParseAddr(s)
if err != nil {
return nil, fmt.Errorf("invalid IP address %q", s)
}
return netToList(goip), nil
return netToList(goip.AsSlice()), nil
}

func netToList(ip net.IP) []uint {
func netToList(ip []byte) []uint {
a := make([]uint, len(ip))
for i, p := range ip {
a[i] = uint(p)
Expand All @@ -131,15 +141,15 @@ func netToList(ip net.IP) []uint {
// The address may be a string or list of bytes.
func IPv4(ip cue.Value) bool {
// TODO: convert to native CUE.
return netGetIP(ip).To4() != nil
return netGetIP(ip).Is4()
}

// IP reports whether s is a valid IPv4 or IPv6 address.
//
// The address may be a string or list of bytes.
func IP(ip cue.Value) bool {
// TODO: convert to native CUE.
return netGetIP(ip) != nil
return netGetIP(ip).IsValid()
}

// IPCIDR reports whether ip is a valid IPv4 or IPv6 address with CIDR subnet notation.
Expand Down Expand Up @@ -196,24 +206,25 @@ func UnspecifiedIP(ip cue.Value) bool {
// 4-byte representation.
func ToIP4(ip cue.Value) ([]uint, error) {
ipdata := netGetIP(ip)
if ipdata == nil {
if !ipdata.IsValid() {
return nil, fmt.Errorf("invalid IP %q", ip)
}
ipv4 := ipdata.To4()
if ipv4 == nil {
if !ipdata.Is4() {
return nil, fmt.Errorf("cannot convert %q to IPv4", ipdata)
}
return netToList(ipv4), nil
as4 := ipdata.As4()
return netToList(as4[:]), nil
}

// ToIP16 converts a given IP address, which may be a string or a list, to its
// 16-byte representation.
func ToIP16(ip cue.Value) ([]uint, error) {
ipdata := netGetIP(ip)
if ipdata == nil {
if !ipdata.IsValid() {
return nil, fmt.Errorf("invalid IP %q", ip)
}
return netToList(ipdata), nil
as16 := ipdata.As16()
return netToList(as16[:]), nil
}

// IPString returns the string form of the IP address ip. It returns one of 4 forms:
Expand All @@ -224,7 +235,7 @@ func ToIP16(ip cue.Value) ([]uint, error) {
// - the hexadecimal form of ip, without punctuation, if no other cases apply
func IPString(ip cue.Value) (string, error) {
ipdata := netGetIP(ip)
if ipdata == nil {
if !ipdata.IsValid() {
return "", fmt.Errorf("invalid IP %q", ip)
}
return ipdata.String(), nil
Expand Down
4 changes: 2 additions & 2 deletions pkg/net/testdata/gen.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ t7: error in call to net.JoinHostPort: invalid host [192, 30, 4]:
t13: invalid value "ff02::1:3" (does not satisfy net.IPv4):
./in.cue:15:6
./in.cue:15:19
t20: error in call to net.IPCIDR: invalid CIDR address: 172.16.12.3:
t20: error in call to net.IPCIDR: netip.ParsePrefix("172.16.12.3"): no '/':
./in.cue:22:6

Result:
Expand All @@ -60,7 +60,7 @@ t16: [127, 0, 0, 1]
t17: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1]
t18: true
t19: true
t20: _|_ // t20: error in call to net.IPCIDR: invalid CIDR address: 172.16.12.3
t20: _|_ // t20: error in call to net.IPCIDR: netip.ParsePrefix("172.16.12.3"): no '/'
t21: "foo%2Fbar"
t22: "foo/bar"
t23: "f%25o"
Expand Down

0 comments on commit ee61020

Please sign in to comment.