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  }