github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/pkg/net/ip.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package net defines net-related types. 16 package net 17 18 import ( 19 "fmt" 20 "net" 21 22 "github.com/joomcode/cue/cue" 23 ) 24 25 // IP address lengths (bytes). 26 const ( 27 IPv4len = 4 28 IPv6len = 16 29 ) 30 31 func netGetIP(ip cue.Value) (goip net.IP) { 32 switch ip.Kind() { 33 case cue.StringKind: 34 s, err := ip.String() 35 if err != nil { 36 return nil 37 } 38 goip := net.ParseIP(s) 39 if goip == nil { 40 return nil 41 } 42 return goip 43 44 case cue.BytesKind: 45 b, err := ip.Bytes() 46 if err != nil { 47 return nil 48 } 49 goip := net.ParseIP(string(b)) 50 if goip == nil { 51 return nil 52 } 53 return goip 54 55 case cue.ListKind: 56 iter, err := ip.List() 57 if err != nil { 58 return nil 59 } 60 for iter.Next() { 61 v, err := iter.Value().Int64() 62 if err != nil { 63 return nil 64 } 65 if v < 0 || 255 < v { 66 return nil 67 } 68 goip = append(goip, byte(v)) 69 } 70 return goip 71 72 default: 73 // TODO: return canonical invalid type. 74 return nil 75 } 76 } 77 78 func netGetIPCIDR(ip cue.Value) (gonet *net.IPNet, err error) { 79 switch ip.Kind() { 80 case cue.StringKind: 81 s, err := ip.String() 82 if err != nil { 83 return nil, err 84 } 85 _, gonet, err := net.ParseCIDR(s) 86 if err != nil { 87 return nil, err 88 } 89 return gonet, nil 90 91 case cue.BytesKind: 92 b, err := ip.Bytes() 93 if err != nil { 94 return nil, err 95 } 96 _, gonet, err := net.ParseCIDR(string(b)) 97 if err != nil { 98 return nil, err 99 } 100 return gonet, nil 101 102 default: 103 // TODO: return canonical invalid type. 104 return nil, nil 105 } 106 } 107 108 // ParseIP parses s as an IP address, returning the result. 109 // The string s can be in dotted decimal ("192.0.2.1") 110 // or IPv6 ("2001:db8::68") form. 111 // If s is not a valid textual representation of an IP address, 112 // ParseIP returns nil. 113 func ParseIP(s string) ([]uint, error) { 114 goip := net.ParseIP(s) 115 if goip == nil { 116 return nil, fmt.Errorf("invalid IP address %q", s) 117 } 118 return netToList(goip), nil 119 } 120 121 func netToList(ip net.IP) []uint { 122 a := make([]uint, len(ip)) 123 for i, p := range ip { 124 a[i] = uint(p) 125 } 126 return a 127 } 128 129 // IPv4 reports whether s is a valid IPv4 address. 130 // 131 // The address may be a string or list of bytes. 132 func IPv4(ip cue.Value) bool { 133 // TODO: convert to native CUE. 134 return netGetIP(ip).To4() != nil 135 } 136 137 // IP reports whether s is a valid IPv4 or IPv6 address. 138 // 139 // The address may be a string or list of bytes. 140 func IP(ip cue.Value) bool { 141 // TODO: convert to native CUE. 142 return netGetIP(ip) != nil 143 } 144 145 // IPCIDR reports whether ip is a valid IPv4 or IPv6 address with CIDR subnet notation. 146 // 147 // The address may be a string or list of bytes. 148 func IPCIDR(ip cue.Value) (bool, error) { 149 _, err := netGetIPCIDR(ip) 150 return err == nil, err 151 } 152 153 // LoopbackIP reports whether ip is a loopback address. 154 func LoopbackIP(ip cue.Value) bool { 155 return netGetIP(ip).IsLoopback() 156 } 157 158 // MulticastIP reports whether ip is a multicast address. 159 func MulticastIP(ip cue.Value) bool { 160 return netGetIP(ip).IsMulticast() 161 } 162 163 // InterfaceLocalMulticastIP reports whether ip is an interface-local multicast 164 // address. 165 func InterfaceLocalMulticastIP(ip cue.Value) bool { 166 return netGetIP(ip).IsInterfaceLocalMulticast() 167 } 168 169 // LinkLocalMulticast reports whether ip is a link-local multicast address. 170 func LinkLocalMulticastIP(ip cue.Value) bool { 171 return netGetIP(ip).IsLinkLocalMulticast() 172 } 173 174 // LinkLocalUnicastIP reports whether ip is a link-local unicast address. 175 func LinkLocalUnicastIP(ip cue.Value) bool { 176 return netGetIP(ip).IsLinkLocalUnicast() 177 } 178 179 // GlobalUnicastIP reports whether ip is a global unicast address. 180 // 181 // The identification of global unicast addresses uses address type 182 // identification as defined in RFC 1122, RFC 4632 and RFC 4291 with the 183 // exception of IPv4 directed broadcast addresses. It returns true even if ip is 184 // in IPv4 private address space or local IPv6 unicast address space. 185 func GlobalUnicastIP(ip cue.Value) bool { 186 return netGetIP(ip).IsGlobalUnicast() 187 } 188 189 // UnspecifiedIP reports whether ip is an unspecified address, either the IPv4 190 // address "0.0.0.0" or the IPv6 address "::". 191 func UnspecifiedIP(ip cue.Value) bool { 192 return netGetIP(ip).IsUnspecified() 193 } 194 195 // ToIP4 converts a given IP address, which may be a string or a list, to its 196 // 4-byte representation. 197 func ToIP4(ip cue.Value) ([]uint, error) { 198 ipdata := netGetIP(ip) 199 if ipdata == nil { 200 return nil, fmt.Errorf("invalid IP %q", ip) 201 } 202 ipv4 := ipdata.To4() 203 if ipv4 == nil { 204 return nil, fmt.Errorf("cannot convert %q to IPv4", ipdata) 205 } 206 return netToList(ipv4), nil 207 } 208 209 // ToIP16 converts a given IP address, which may be a string or a list, to its 210 // 16-byte representation. 211 func ToIP16(ip cue.Value) ([]uint, error) { 212 ipdata := netGetIP(ip) 213 if ipdata == nil { 214 return nil, fmt.Errorf("invalid IP %q", ip) 215 } 216 return netToList(ipdata), nil 217 } 218 219 // IPString returns the string form of the IP address ip. It returns one of 4 forms: 220 // 221 // - "<nil>", if ip has length 0 222 // - dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address 223 // - IPv6 ("2001:db8::1"), if ip is a valid IPv6 address 224 // - the hexadecimal form of ip, without punctuation, if no other cases apply 225 func IPString(ip cue.Value) (string, error) { 226 ipdata := netGetIP(ip) 227 if ipdata == nil { 228 return "", fmt.Errorf("invalid IP %q", ip) 229 } 230 return ipdata.String(), nil 231 }