diff --git a/pkg/net/host.go b/pkg/net/host.go index aef0ad991..0d1634076 100644 --- a/pkg/net/host.go +++ b/pkg/net/host.go @@ -55,11 +55,12 @@ func JoinHostPort(host, port cue.Value) (string, error) { hostStr := "" switch host.Kind() { case cue.ListKind: - ipdata := netGetIP(host) - if !ipdata.IsValid() { + ipdata, ipErr := netGetIP(host) + if ipErr != nil { err = fmt.Errorf("invalid host %s", host) + } else { + hostStr = ipdata.String() } - hostStr = ipdata.String() case cue.BytesKind: var b []byte b, err = host.Bytes() diff --git a/pkg/net/ip.go b/pkg/net/ip.go index a4bf53f0f..20519eb81 100644 --- a/pkg/net/ip.go +++ b/pkg/net/ip.go @@ -30,55 +30,54 @@ const ( IPv6len = 16 ) -func netGetIP(ip cue.Value) (goip netip.Addr) { +func netGetIP(ip cue.Value) (netip.Addr, error) { switch ip.Kind() { case cue.StringKind: s, err := ip.String() if err != nil { - return netip.Addr{} + return netip.Addr{}, err } goip, err := netip.ParseAddr(s) if err != nil { - return netip.Addr{} + return netip.Addr{}, err } - return goip + return goip, nil case cue.BytesKind: b, err := ip.Bytes() if err != nil { - return netip.Addr{} + return netip.Addr{}, err } goip, err := netip.ParseAddr(string(b)) if err != nil { - return netip.Addr{} + return netip.Addr{}, err } - return goip + return goip, nil case cue.ListKind: iter, err := ip.List() if err != nil { - return netip.Addr{} + return netip.Addr{}, err } var bytes []byte for iter.Next() { v, err := iter.Value().Int64() if err != nil { - return netip.Addr{} + return netip.Addr{}, err } if v < 0 || 255 < v { - return netip.Addr{} + return netip.Addr{}, fmt.Errorf("invalid IP byte value %d", v) } bytes = append(bytes, byte(v)) } goip, ok := netip.AddrFromSlice(bytes) if !ok { - return netip.Addr{} + return netip.Addr{}, fmt.Errorf("invalid IP length %d", len(bytes)) } - return goip + return goip, nil default: - // TODO: return canonical invalid type. - return netip.Addr{} + return netip.Addr{}, fmt.Errorf("invalid type %v for IP address", ip.Kind()) } } @@ -107,8 +106,7 @@ func netGetIPCIDR(ip cue.Value) (gonet *netip.Prefix, err error) { return &cidr, nil default: - // TODO: return canonical invalid type. - return nil, nil + return nil, fmt.Errorf("invalid type %v for IP CIDR", ip.Kind()) } } @@ -136,24 +134,36 @@ func netToList(ip []byte) []uint { // IPv4 reports whether ip is a valid IPv4 address. // // The address may be a string or list of bytes. -func IPv4(ip cue.Value) bool { +func IPv4(ip cue.Value) (bool, error) { // TODO: convert to native CUE. - return netGetIP(ip).Is4() + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.Is4(), nil } // IPv6 reports whether ip is a valid IPv6 address. // // The address may be a string or list of bytes. -func IPv6(ip cue.Value) bool { - return netGetIP(ip).Is6() +func IPv6(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.Is6(), nil } // IP reports whether ip is a valid IPv4 or IPv6 address. // // The address may be a string or list of bytes. -func IP(ip cue.Value) bool { +func IP(ip cue.Value) (bool, error) { // TODO: convert to native CUE. - return netGetIP(ip).IsValid() + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsValid(), nil } // IPCIDR reports whether ip is a valid IPv4 or IPv6 address with CIDR subnet notation. @@ -165,29 +175,49 @@ func IPCIDR(ip cue.Value) (bool, error) { } // LoopbackIP reports whether ip is a loopback address. -func LoopbackIP(ip cue.Value) bool { - return netGetIP(ip).IsLoopback() +func LoopbackIP(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsLoopback(), nil } // MulticastIP reports whether ip is a multicast address. -func MulticastIP(ip cue.Value) bool { - return netGetIP(ip).IsMulticast() +func MulticastIP(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsMulticast(), nil } // InterfaceLocalMulticastIP reports whether ip is an interface-local multicast // address. -func InterfaceLocalMulticastIP(ip cue.Value) bool { - return netGetIP(ip).IsInterfaceLocalMulticast() +func InterfaceLocalMulticastIP(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsInterfaceLocalMulticast(), nil } // LinkLocalMulticastIP reports whether ip is a link-local multicast address. -func LinkLocalMulticastIP(ip cue.Value) bool { - return netGetIP(ip).IsLinkLocalMulticast() +func LinkLocalMulticastIP(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsLinkLocalMulticast(), nil } // LinkLocalUnicastIP reports whether ip is a link-local unicast address. -func LinkLocalUnicastIP(ip cue.Value) bool { - return netGetIP(ip).IsLinkLocalUnicast() +func LinkLocalUnicastIP(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsLinkLocalUnicast(), nil } // GlobalUnicastIP reports whether ip is a global unicast address. @@ -196,22 +226,30 @@ func LinkLocalUnicastIP(ip cue.Value) bool { // identification as defined in RFC 1122, RFC 4632 and RFC 4291 with the // exception of IPv4 directed broadcast addresses. It returns true even if ip is // in IPv4 private address space or local IPv6 unicast address space. -func GlobalUnicastIP(ip cue.Value) bool { - return netGetIP(ip).IsGlobalUnicast() +func GlobalUnicastIP(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsGlobalUnicast(), nil } // UnspecifiedIP reports whether ip is an unspecified address, either the IPv4 // address "0.0.0.0" or the IPv6 address "::". -func UnspecifiedIP(ip cue.Value) bool { - return netGetIP(ip).IsUnspecified() +func UnspecifiedIP(ip cue.Value) (bool, error) { + goip, err := netGetIP(ip) + if err != nil { + return false, err + } + return goip.IsUnspecified(), nil } // ToIP4 converts a given IP address, which may be a string or a list, to its // 4-byte representation. func ToIP4(ip cue.Value) ([]uint, error) { - ipdata := netGetIP(ip) - if !ipdata.IsValid() { - return nil, fmt.Errorf("invalid IP %q", ip) + ipdata, err := netGetIP(ip) + if err != nil { + return nil, err } if !ipdata.Is4() { return nil, fmt.Errorf("cannot convert %q to IPv4", ipdata) @@ -223,9 +261,9 @@ func ToIP4(ip cue.Value) ([]uint, error) { // 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.IsValid() { - return nil, fmt.Errorf("invalid IP %q", ip) + ipdata, err := netGetIP(ip) + if err != nil { + return nil, err } as16 := ipdata.As16() return netToList(as16[:]), nil @@ -238,9 +276,9 @@ func ToIP16(ip cue.Value) ([]uint, error) { // - IPv6 ("2001:db8::1"), if ip is a valid IPv6 address // - the hexadecimal form of ip, without punctuation, if no other cases apply func IPString(ip cue.Value) (string, error) { - ipdata := netGetIP(ip) - if !ipdata.IsValid() { - return "", fmt.Errorf("invalid IP %q", ip) + ipdata, err := netGetIP(ip) + if err != nil { + return "", err } return ipdata.String(), nil } @@ -279,9 +317,9 @@ func AddIP(ip cue.Value, offset *big.Int) (string, error) { } return netip.PrefixFrom(addr, prefix.Bits()).String(), nil } - ipdata := netGetIP(ip) - if !ipdata.IsValid() { - return "", fmt.Errorf("invalid IP %q", ip) + ipdata, err := netGetIP(ip) + if err != nil { + return "", err } addr, err := netIPAdd(ipdata, offset) if err != nil { @@ -357,9 +395,9 @@ func ParseCIDR(s string) (*ParsedCIDR, error) { // InCIDR reports whether an IP address is contained a CIDR subnet string. func InCIDR(ip, cidr cue.Value) (bool, error) { - ipAddr := netGetIP(ip) - if !ipAddr.IsValid() { - return false, fmt.Errorf("invalid IP %q", ip) + ipAddr, err := netGetIP(ip) + if err != nil { + return false, err } prefix, err := netGetIPCIDR(cidr) @@ -376,13 +414,13 @@ func InCIDR(ip, cidr cue.Value) (bool, error) { // // The addresses may be strings or lists of bytes. func CompareIP(ip1, ip2 cue.Value) (int, error) { - addr1 := netGetIP(ip1) - if !addr1.IsValid() { - return 0, fmt.Errorf("invalid IP %q", ip1) + addr1, err := netGetIP(ip1) + if err != nil { + return 0, err } - addr2 := netGetIP(ip2) - if !addr2.IsValid() { - return 0, fmt.Errorf("invalid IP %q", ip2) + addr2, err := netGetIP(ip2) + if err != nil { + return 0, err } return addr1.Compare(addr2), nil } diff --git a/pkg/net/pkg.go b/pkg/net/pkg.go index 71138afc1..9b7bf0ca9 100644 --- a/pkg/net/pkg.go +++ b/pkg/net/pkg.go @@ -78,7 +78,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = IPv4(ip) + c.Ret, c.Err = IPv4(ip) } }, }, { @@ -90,7 +90,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = IPv6(ip) + c.Ret, c.Err = IPv6(ip) } }, }, { @@ -102,7 +102,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = IP(ip) + c.Ret, c.Err = IP(ip) } }, }, { @@ -126,7 +126,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = LoopbackIP(ip) + c.Ret, c.Err = LoopbackIP(ip) } }, }, { @@ -138,7 +138,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = MulticastIP(ip) + c.Ret, c.Err = MulticastIP(ip) } }, }, { @@ -150,7 +150,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = InterfaceLocalMulticastIP(ip) + c.Ret, c.Err = InterfaceLocalMulticastIP(ip) } }, }, { @@ -162,7 +162,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = LinkLocalMulticastIP(ip) + c.Ret, c.Err = LinkLocalMulticastIP(ip) } }, }, { @@ -174,7 +174,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = LinkLocalUnicastIP(ip) + c.Ret, c.Err = LinkLocalUnicastIP(ip) } }, }, { @@ -186,7 +186,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = GlobalUnicastIP(ip) + c.Ret, c.Err = GlobalUnicastIP(ip) } }, }, { @@ -198,7 +198,7 @@ var p = &pkg.Package{ Func: func(c *pkg.CallCtxt) { ip := c.Value(0) if c.Do() { - c.Ret = UnspecifiedIP(ip) + c.Ret, c.Err = UnspecifiedIP(ip) } }, }, { diff --git a/pkg/net/testdata/compareip.txtar b/pkg/net/testdata/compareip.txtar index 382ba8d49..8db741de4 100644 --- a/pkg/net/testdata/compareip.txtar +++ b/pkg/net/testdata/compareip.txtar @@ -36,11 +36,11 @@ t19: net.CompareIP("192.168.1.1", "invalid") t20: net.CompareIP("invalid1", "invalid2") -- out/net-v3 -- Errors: -t18: error in call to net.CompareIP: invalid IP "invalid": +t18: error in call to net.CompareIP: ParseAddr("invalid"): unable to parse IP: ./in.cue:33:6 -t19: error in call to net.CompareIP: invalid IP "invalid": +t19: error in call to net.CompareIP: ParseAddr("invalid"): unable to parse IP: ./in.cue:34:6 -t20: error in call to net.CompareIP: invalid IP "invalid1": +t20: error in call to net.CompareIP: ParseAddr("invalid1"): unable to parse IP: ./in.cue:35:6 Result: @@ -74,6 +74,6 @@ t16: 1 t17: 0 // Error cases -t18: _|_ // t18: error in call to net.CompareIP: invalid IP "invalid" -t19: _|_ // t19: error in call to net.CompareIP: invalid IP "invalid" -t20: _|_ // t20: error in call to net.CompareIP: invalid IP "invalid1" +t18: _|_ // t18: error in call to net.CompareIP: ParseAddr("invalid"): unable to parse IP +t19: _|_ // t19: error in call to net.CompareIP: ParseAddr("invalid"): unable to parse IP +t20: _|_ // t20: error in call to net.CompareIP: ParseAddr("invalid1"): unable to parse IP diff --git a/pkg/net/testdata/gen.txtar b/pkg/net/testdata/gen.txtar index 27db5b5a8..07855d87c 100644 --- a/pkg/net/testdata/gen.txtar +++ b/pkg/net/testdata/gen.txtar @@ -56,13 +56,15 @@ t46: net.AddIPCIDR("10.0.0.0/8", -11) t47: net.AddIPCIDR("255.0.0.0/8", 1) -- out/net-v3 -- Errors: -t20c: invalid value 123 (does not satisfy net.IP): +t20b: invalid value 123 (does not satisfy net.IPCIDR): error in call to net.IPCIDR: invalid type int for IP CIDR: + ./in.cue:23:7 +t20c: invalid value 123 (does not satisfy net.IP): error in call to net.IP: invalid type int for IP address: ./in.cue:24:7 -t20d: invalid value 123 (does not satisfy net.IPv4): +t20d: invalid value 123 (does not satisfy net.IPv4): error in call to net.IPv4: invalid type int for IP address: ./in.cue:25:7 -t20e: invalid value 123 (does not satisfy net.IPv6): +t20e: invalid value 123 (does not satisfy net.IPv6): error in call to net.IPv6: invalid type int for IP address: ./in.cue:26:7 -t25: invalid value "2001:db8::1234567" (does not satisfy net.IPv6): +t25: invalid value "2001:db8::1234567" (does not satisfy net.IPv6): error in call to net.IPv6: ParseAddr("2001:db8::1234567"): each group must have 4 or less digits (at "1234567"): ./in.cue:31:6 ./in.cue:31:17 t30: invalid value "%" (does not satisfy net.URL): error in call to net.URL: parse "%": invalid URL escape "%": @@ -74,7 +76,7 @@ t32: invalid value "/foo/bar" (does not satisfy net.AbsURL): error in call to ne t34: invalid value "%" (does not satisfy net.AbsURL): error in call to net.AbsURL: parse "%": invalid URL escape "%": ./in.cue:40:6 ./in.cue:40:19 -t9: invalid value "23.23.23.2333" (does not satisfy net.IPv4): +t9: invalid value "23.23.23.2333" (does not satisfy net.IPv4): error in call to net.IPv4: ParseAddr("23.23.23.2333"): IPv4 field has value >255: ./in.cue:11:6 ./in.cue:11:17 t7: error in call to net.JoinHostPort: invalid host [192, 30, 4]: @@ -87,7 +89,7 @@ t20: error in call to net.IPCIDR: netip.ParsePrefix("172.16.12.3"): no '/': t27: invalid value "23.23.23.23" (does not satisfy net.IPv6): ./in.cue:33:6 ./in.cue:33:19 -t39: error in call to net.AddIP: invalid IP "invalid ip": +t39: error in call to net.AddIP: ParseAddr("invalid ip"): unable to parse IP: ./in.cue:45:6 t40: error in call to net.AddIP: IP address arithmetic resulted in out-of-range address (underflow): ./in.cue:46:6 @@ -109,7 +111,7 @@ t5: "[2001:db8::1]:80" t6: "192.30.4.2:80" t7: _|_ // t7: error in call to net.JoinHostPort: invalid host [192, 30, 4] t8: true -t9: _|_ // t9: invalid value "23.23.23.2333" (does not satisfy net.IPv4) +t9: _|_ // t9: invalid value "23.23.23.2333" (does not satisfy net.IPv4): t9: error in call to net.IPv4: ParseAddr("23.23.23.2333"): IPv4 field has value >255 t10: true t11: true t12: false @@ -121,15 +123,15 @@ 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: netip.ParsePrefix("172.16.12.3"): no '/' -t20b: 123 -t20c: _|_ // t20c: invalid value 123 (does not satisfy net.IP) -t20d: _|_ // t20d: invalid value 123 (does not satisfy net.IPv4) -t20e: _|_ // t20e: invalid value 123 (does not satisfy net.IPv6) +t20b: _|_ // t20b: invalid value 123 (does not satisfy net.IPCIDR): t20b: error in call to net.IPCIDR: invalid type int for IP CIDR +t20c: _|_ // t20c: invalid value 123 (does not satisfy net.IP): t20c: error in call to net.IP: invalid type int for IP address +t20d: _|_ // t20d: invalid value 123 (does not satisfy net.IPv4): t20d: error in call to net.IPv4: invalid type int for IP address +t20e: _|_ // t20e: invalid value 123 (does not satisfy net.IPv6): t20e: error in call to net.IPv6: invalid type int for IP address t21: "foo%2Fbar" t22: "foo/bar" t23: "f%25o" t24: "f%o" -t25: _|_ // t25: invalid value "2001:db8::1234567" (does not satisfy net.IPv6) +t25: _|_ // t25: invalid value "2001:db8::1234567" (does not satisfy net.IPv6): t25: error in call to net.IPv6: ParseAddr("2001:db8::1234567"): each group must have 4 or less digits (at "1234567") t26: true t27: _|_ // t27: invalid value "23.23.23.23" (does not satisfy net.IPv6) t28: true @@ -143,7 +145,7 @@ t35: "127.0.0.2" t36: "127.0.0.3/8" t37: "2001:db8::1" t38: "2001:db8::2/64" -t39: _|_ // t39: error in call to net.AddIP: invalid IP "invalid ip" +t39: _|_ // t39: error in call to net.AddIP: ParseAddr("invalid ip"): unable to parse IP t40: _|_ // t40: error in call to net.AddIP: IP address arithmetic resulted in out-of-range address (underflow) t41: _|_ // t41: error in call to net.AddIP: IP address arithmetic resulted in out-of-range address (overflow) t42: "::ffff:127.0.0.2" diff --git a/pkg/net/testdata/incidr.txtar b/pkg/net/testdata/incidr.txtar index 3e5367afe..c0baea554 100644 --- a/pkg/net/testdata/incidr.txtar +++ b/pkg/net/testdata/incidr.txtar @@ -38,13 +38,13 @@ v3: [192, 168, 1, 50] & net.InCIDR("192.168.1.0/24") v4: "2001:db8::1" & net.InCIDR("2001:db8::/32") -- out/net-v3 -- Errors: -t15: error in call to net.InCIDR: invalid IP "invalid": +t15: error in call to net.InCIDR: ParseAddr("invalid"): unable to parse IP: ./in.cue:28:6 t16: error in call to net.InCIDR: netip.ParsePrefix("invalid"): no '/': ./in.cue:29:6 t17: error in call to net.InCIDR: netip.ParsePrefix("192.168.1.0"): no '/': ./in.cue:30:6 -t18: error in call to net.InCIDR: invalid IP "192.168.1.0/24": +t18: error in call to net.InCIDR: ParseAddr("192.168.1.0/24"): unexpected character (at "/24"): ./in.cue:31:6 v2: invalid value "10.0.0.1" (does not satisfy net.InCIDR("192.168.1.0/24")): ./in.cue:35:18 @@ -77,10 +77,10 @@ t13: true t14: true // Error cases -t15: _|_ // t15: error in call to net.InCIDR: invalid IP "invalid" +t15: _|_ // t15: error in call to net.InCIDR: ParseAddr("invalid"): unable to parse IP t16: _|_ // t16: error in call to net.InCIDR: netip.ParsePrefix("invalid"): no '/' t17: _|_ // t17: error in call to net.InCIDR: netip.ParsePrefix("192.168.1.0"): no '/' -t18: _|_ // t18: error in call to net.InCIDR: invalid IP "192.168.1.0/24" +t18: _|_ // t18: error in call to net.InCIDR: ParseAddr("192.168.1.0/24"): unexpected character (at "/24") // Validator usage - IP is validated against a CIDR constraint v1: "192.168.1.50"