cuelang.org/go@v0.10.1/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/netip"
    21  
    22  	"cuelang.org/go/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 netip.Addr) {
    32  	switch ip.Kind() {
    33  	case cue.StringKind:
    34  		s, err := ip.String()
    35  		if err != nil {
    36  			return netip.Addr{}
    37  		}
    38  		goip, err := netip.ParseAddr(s)
    39  		if err != nil {
    40  			return netip.Addr{}
    41  		}
    42  		return goip
    43  
    44  	case cue.BytesKind:
    45  		b, err := ip.Bytes()
    46  		if err != nil {
    47  			return netip.Addr{}
    48  		}
    49  		goip, err := netip.ParseAddr(string(b))
    50  		if err != nil {
    51  			return netip.Addr{}
    52  		}
    53  		return goip
    54  
    55  	case cue.ListKind:
    56  		iter, err := ip.List()
    57  		if err != nil {
    58  			return netip.Addr{}
    59  		}
    60  		var bytes []byte
    61  		for iter.Next() {
    62  			v, err := iter.Value().Int64()
    63  			if err != nil {
    64  				return netip.Addr{}
    65  			}
    66  			if v < 0 || 255 < v {
    67  				return netip.Addr{}
    68  			}
    69  			bytes = append(bytes, byte(v))
    70  		}
    71  		goip, ok := netip.AddrFromSlice(bytes)
    72  		if !ok {
    73  			return netip.Addr{}
    74  		}
    75  		return goip
    76  
    77  	default:
    78  		// TODO: return canonical invalid type.
    79  		return netip.Addr{}
    80  	}
    81  }
    82  
    83  func netGetIPCIDR(ip cue.Value) (gonet *netip.Prefix, err error) {
    84  	switch ip.Kind() {
    85  	case cue.StringKind:
    86  		s, err := ip.String()
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		cidr, err := netip.ParsePrefix(s)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		return &cidr, nil
    95  
    96  	case cue.BytesKind:
    97  		b, err := ip.Bytes()
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  		cidr, err := netip.ParsePrefix(string(b))
   102  		if err != nil {
   103  			return nil, err
   104  		}
   105  		return &cidr, nil
   106  
   107  	default:
   108  		// TODO: return canonical invalid type.
   109  		return nil, nil
   110  	}
   111  }
   112  
   113  // ParseIP parses s as an IP address, returning the result.
   114  // The string s can be in dotted decimal ("192.0.2.1")
   115  // or IPv6 ("2001:db8::68") form.
   116  // If s is not a valid textual representation of an IP address,
   117  // ParseIP returns nil.
   118  func ParseIP(s string) ([]uint, error) {
   119  	goip, err := netip.ParseAddr(s)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("invalid IP address %q", s)
   122  	}
   123  	return netToList(goip.AsSlice()), nil
   124  }
   125  
   126  func netToList(ip []byte) []uint {
   127  	a := make([]uint, len(ip))
   128  	for i, p := range ip {
   129  		a[i] = uint(p)
   130  	}
   131  	return a
   132  }
   133  
   134  // IPv4 reports whether ip is a valid IPv4 address.
   135  //
   136  // The address may be a string or list of bytes.
   137  func IPv4(ip cue.Value) bool {
   138  	// TODO: convert to native CUE.
   139  	return netGetIP(ip).Is4()
   140  }
   141  
   142  // IPv6 reports whether ip is a valid IPv6 address.
   143  //
   144  // The address may be a string or list of bytes.
   145  func IPv6(ip cue.Value) bool {
   146  	return netGetIP(ip).Is6()
   147  }
   148  
   149  // IP reports whether ip is a valid IPv4 or IPv6 address.
   150  //
   151  // The address may be a string or list of bytes.
   152  func IP(ip cue.Value) bool {
   153  	// TODO: convert to native CUE.
   154  	return netGetIP(ip).IsValid()
   155  }
   156  
   157  // IPCIDR reports whether ip is a valid IPv4 or IPv6 address with CIDR subnet notation.
   158  //
   159  // The address may be a string or list of bytes.
   160  func IPCIDR(ip cue.Value) (bool, error) {
   161  	_, err := netGetIPCIDR(ip)
   162  	return err == nil, err
   163  }
   164  
   165  // LoopbackIP reports whether ip is a loopback address.
   166  func LoopbackIP(ip cue.Value) bool {
   167  	return netGetIP(ip).IsLoopback()
   168  }
   169  
   170  // MulticastIP reports whether ip is a multicast address.
   171  func MulticastIP(ip cue.Value) bool {
   172  	return netGetIP(ip).IsMulticast()
   173  }
   174  
   175  // InterfaceLocalMulticastIP reports whether ip is an interface-local multicast
   176  // address.
   177  func InterfaceLocalMulticastIP(ip cue.Value) bool {
   178  	return netGetIP(ip).IsInterfaceLocalMulticast()
   179  }
   180  
   181  // LinkLocalMulticast reports whether ip is a link-local multicast address.
   182  func LinkLocalMulticastIP(ip cue.Value) bool {
   183  	return netGetIP(ip).IsLinkLocalMulticast()
   184  }
   185  
   186  // LinkLocalUnicastIP reports whether ip is a link-local unicast address.
   187  func LinkLocalUnicastIP(ip cue.Value) bool {
   188  	return netGetIP(ip).IsLinkLocalUnicast()
   189  }
   190  
   191  // GlobalUnicastIP reports whether ip is a global unicast address.
   192  //
   193  // The identification of global unicast addresses uses address type
   194  // identification as defined in RFC 1122, RFC 4632 and RFC 4291 with the
   195  // exception of IPv4 directed broadcast addresses. It returns true even if ip is
   196  // in IPv4 private address space or local IPv6 unicast address space.
   197  func GlobalUnicastIP(ip cue.Value) bool {
   198  	return netGetIP(ip).IsGlobalUnicast()
   199  }
   200  
   201  // UnspecifiedIP reports whether ip is an unspecified address, either the IPv4
   202  // address "0.0.0.0" or the IPv6 address "::".
   203  func UnspecifiedIP(ip cue.Value) bool {
   204  	return netGetIP(ip).IsUnspecified()
   205  }
   206  
   207  // ToIP4 converts a given IP address, which may be a string or a list, to its
   208  // 4-byte representation.
   209  func ToIP4(ip cue.Value) ([]uint, error) {
   210  	ipdata := netGetIP(ip)
   211  	if !ipdata.IsValid() {
   212  		return nil, fmt.Errorf("invalid IP %q", ip)
   213  	}
   214  	if !ipdata.Is4() {
   215  		return nil, fmt.Errorf("cannot convert %q to IPv4", ipdata)
   216  	}
   217  	as4 := ipdata.As4()
   218  	return netToList(as4[:]), nil
   219  }
   220  
   221  // ToIP16 converts a given IP address, which may be a string or a list, to its
   222  // 16-byte representation.
   223  func ToIP16(ip cue.Value) ([]uint, error) {
   224  	ipdata := netGetIP(ip)
   225  	if !ipdata.IsValid() {
   226  		return nil, fmt.Errorf("invalid IP %q", ip)
   227  	}
   228  	as16 := ipdata.As16()
   229  	return netToList(as16[:]), nil
   230  }
   231  
   232  // IPString returns the string form of the IP address ip. It returns one of 4 forms:
   233  //
   234  // - "<nil>", if ip has length 0
   235  // - dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address
   236  // - IPv6 ("2001:db8::1"), if ip is a valid IPv6 address
   237  // - the hexadecimal form of ip, without punctuation, if no other cases apply
   238  func IPString(ip cue.Value) (string, error) {
   239  	ipdata := netGetIP(ip)
   240  	if !ipdata.IsValid() {
   241  		return "", fmt.Errorf("invalid IP %q", ip)
   242  	}
   243  	return ipdata.String(), nil
   244  }