blob: 6b658e3c8a29327051c9cefa2de876b1320d6895 [file] [log] [blame]
// 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
}