| // Copyright 2019 CUE Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package net defines net-related types. |
| package net |
| |
| import ( |
| "fmt" |
| "net" |
| |
| "cuelang.org/go/cue" |
| ) |
| |
| // IP address lengths (bytes). |
| const ( |
| IPv4len = 4 |
| IPv6len = 16 |
| ) |
| |
| func netGetIP(ip cue.Value) (goip net.IP) { |
| switch ip.Kind() { |
| case cue.StringKind: |
| s, err := ip.String() |
| if err != nil { |
| return nil |
| } |
| goip := net.ParseIP(s) |
| if goip == nil { |
| return nil |
| } |
| return goip |
| |
| case cue.BytesKind: |
| b, err := ip.Bytes() |
| if err != nil { |
| return nil |
| } |
| goip := net.ParseIP(string(b)) |
| if goip == nil { |
| return nil |
| } |
| return goip |
| |
| case cue.ListKind: |
| iter, err := ip.List() |
| if err != nil { |
| return nil |
| } |
| for iter.Next() { |
| v, err := iter.Value().Int64() |
| if err != nil { |
| return nil |
| } |
| if v < 0 || 255 < v { |
| return nil |
| } |
| goip = append(goip, byte(v)) |
| } |
| return goip |
| |
| default: |
| // TODO: return canonical invalid type. |
| return nil |
| } |
| } |
| |
| // ParseIP parses s as an IP address, returning the result. |
| // The string s can be in dotted decimal ("192.0.2.1") |
| // or IPv6 ("2001:db8::68") form. |
| // 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 { |
| return nil, fmt.Errorf("invalid IP address %q", s) |
| } |
| return netToList(goip), nil |
| } |
| |
| func netToList(ip net.IP) []uint { |
| a := make([]uint, len(ip)) |
| for i, p := range ip { |
| a[i] = uint(p) |
| } |
| return a |
| } |
| |
| // IPv4 reports whether s is a valid IPv4 address. |
| // |
| // 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 |
| } |
| |
| // 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 |
| } |
| |
| // LoopbackIP reports whether ip is a loopback address. |
| func LoopbackIP(ip cue.Value) bool { |
| return netGetIP(ip).IsLoopback() |
| } |
| |
| // MulticastIP reports whether ip is a multicast address. |
| func MulticastIP(ip cue.Value) bool { |
| return netGetIP(ip).IsMulticast() |
| } |
| |
| // InterfaceLocalMulticastIP reports whether ip is an interface-local multicast |
| // address. |
| func InterfaceLocalMulticastIP(ip cue.Value) bool { |
| return netGetIP(ip).IsInterfaceLocalMulticast() |
| } |
| |
| // LinkLocalMulticast reports whether ip is a link-local multicast address. |
| func LinkLocalMulticastIP(ip cue.Value) bool { |
| return netGetIP(ip).IsLinkLocalMulticast() |
| } |
| |
| // LinkLocalUnicastIP reports whether ip is a link-local unicast address. |
| func LinkLocalUnicastIP(ip cue.Value) bool { |
| return netGetIP(ip).IsLinkLocalUnicast() |
| } |
| |
| // GlobalUnicastIP reports whether ip is a global unicast address. |
| // |
| // The identification of global unicast addresses uses address type |
| // 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() |
| } |
| |
| // 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() |
| } |
| |
| // 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 == nil { |
| return nil, fmt.Errorf("invalid IP %q", ip) |
| } |
| ipv4 := ipdata.To4() |
| if ipv4 == nil { |
| return nil, fmt.Errorf("cannot convert %q to IPv4", ipdata) |
| } |
| return netToList(ipv4), 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 { |
| return nil, fmt.Errorf("invalid IP %q", ip) |
| } |
| return netToList(ipdata), nil |
| } |
| |
| // IPString returns the string form of the IP address ip. It returns one of 4 forms: |
| // |
| // - "<nil>", if ip has length 0 |
| // - dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address |
| // - 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 == nil { |
| return "", fmt.Errorf("invalid IP %q", ip) |
| } |
| return ipdata.String(), nil |
| } |