github.com/opentofu/opentofu@v1.7.1/internal/ipaddr/ip.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // IP address manipulations
     6  //
     7  // IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes.
     8  // An IPv4 address can be converted to an IPv6 address by
     9  // adding a canonical prefix (10 zeros, 2 0xFFs).
    10  // This library accepts either size of byte slice but always
    11  // returns 16-byte addresses.
    12  
    13  package ipaddr
    14  
    15  import (
    16  	stdnet "net"
    17  )
    18  
    19  //
    20  // Lean on the standard net lib as much as possible.
    21  //
    22  
    23  type IP = stdnet.IP
    24  type IPNet = stdnet.IPNet
    25  type ParseError = stdnet.ParseError
    26  
    27  const IPv4len = stdnet.IPv4len
    28  const IPv6len = stdnet.IPv6len
    29  
    30  var CIDRMask = stdnet.CIDRMask
    31  var IPv4 = stdnet.IPv4
    32  
    33  // Parse IPv4 address (d.d.d.d).
    34  func parseIPv4(s string) IP {
    35  	var p [IPv4len]byte
    36  	for i := 0; i < IPv4len; i++ {
    37  		if len(s) == 0 {
    38  			// Missing octets.
    39  			return nil
    40  		}
    41  		if i > 0 {
    42  			if s[0] != '.' {
    43  				return nil
    44  			}
    45  			s = s[1:]
    46  		}
    47  		n, c, ok := dtoi(s)
    48  		if !ok || n > 0xFF {
    49  			return nil
    50  		}
    51  		//
    52  		// NOTE: This correct check was added for go-1.17, but is a
    53  		// backwards-incompatible change for OpenTofu users, who might have
    54  		// already written modules with leading zeroes.
    55  		//
    56  		//if c > 1 && s[0] == '0' {
    57  		//	// Reject non-zero components with leading zeroes.
    58  		//	return nil
    59  		//}
    60  		s = s[c:]
    61  		p[i] = byte(n)
    62  	}
    63  	if len(s) != 0 {
    64  		return nil
    65  	}
    66  	return IPv4(p[0], p[1], p[2], p[3])
    67  }
    68  
    69  // parseIPv6 parses s as a literal IPv6 address described in RFC 4291
    70  // and RFC 5952.
    71  func parseIPv6(s string) (ip IP) {
    72  	ip = make(IP, IPv6len)
    73  	ellipsis := -1 // position of ellipsis in ip
    74  
    75  	// Might have leading ellipsis
    76  	if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
    77  		ellipsis = 0
    78  		s = s[2:]
    79  		// Might be only ellipsis
    80  		if len(s) == 0 {
    81  			return ip
    82  		}
    83  	}
    84  
    85  	// Loop, parsing hex numbers followed by colon.
    86  	i := 0
    87  	for i < IPv6len {
    88  		// Hex number.
    89  		n, c, ok := xtoi(s)
    90  		if !ok || n > 0xFFFF {
    91  			return nil
    92  		}
    93  
    94  		// If followed by dot, might be in trailing IPv4.
    95  		if c < len(s) && s[c] == '.' {
    96  			if ellipsis < 0 && i != IPv6len-IPv4len {
    97  				// Not the right place.
    98  				return nil
    99  			}
   100  			if i+IPv4len > IPv6len {
   101  				// Not enough room.
   102  				return nil
   103  			}
   104  			ip4 := parseIPv4(s)
   105  			if ip4 == nil {
   106  				return nil
   107  			}
   108  			ip[i] = ip4[12]
   109  			ip[i+1] = ip4[13]
   110  			ip[i+2] = ip4[14]
   111  			ip[i+3] = ip4[15]
   112  			s = ""
   113  			i += IPv4len
   114  			break
   115  		}
   116  
   117  		// Save this 16-bit chunk.
   118  		ip[i] = byte(n >> 8)
   119  		ip[i+1] = byte(n)
   120  		i += 2
   121  
   122  		// Stop at end of string.
   123  		s = s[c:]
   124  		if len(s) == 0 {
   125  			break
   126  		}
   127  
   128  		// Otherwise must be followed by colon and more.
   129  		if s[0] != ':' || len(s) == 1 {
   130  			return nil
   131  		}
   132  		s = s[1:]
   133  
   134  		// Look for ellipsis.
   135  		if s[0] == ':' {
   136  			if ellipsis >= 0 { // already have one
   137  				return nil
   138  			}
   139  			ellipsis = i
   140  			s = s[1:]
   141  			if len(s) == 0 { // can be at end
   142  				break
   143  			}
   144  		}
   145  	}
   146  
   147  	// Must have used entire string.
   148  	if len(s) != 0 {
   149  		return nil
   150  	}
   151  
   152  	// If didn't parse enough, expand ellipsis.
   153  	if i < IPv6len {
   154  		if ellipsis < 0 {
   155  			return nil
   156  		}
   157  		n := IPv6len - i
   158  		for j := i - 1; j >= ellipsis; j-- {
   159  			ip[j+n] = ip[j]
   160  		}
   161  		for j := ellipsis + n - 1; j >= ellipsis; j-- {
   162  			ip[j] = 0
   163  		}
   164  	} else if ellipsis >= 0 {
   165  		// Ellipsis must represent at least one 0 group.
   166  		return nil
   167  	}
   168  	return ip
   169  }
   170  
   171  // ParseIP parses s as an IP address, returning the result.
   172  // The string s can be in IPv4 dotted decimal ("192.0.2.1"), IPv6
   173  // ("2001:db8::68"), or IPv4-mapped IPv6 ("::ffff:192.0.2.1") form.
   174  // If s is not a valid textual representation of an IP address,
   175  // ParseIP returns nil.
   176  func ParseIP(s string) IP {
   177  	for i := 0; i < len(s); i++ {
   178  		switch s[i] {
   179  		case '.':
   180  			return parseIPv4(s)
   181  		case ':':
   182  			return parseIPv6(s)
   183  		}
   184  	}
   185  	return nil
   186  }
   187  
   188  // ParseCIDR parses s as a CIDR notation IP address and prefix length,
   189  // like "192.0.2.0/24" or "2001:db8::/32", as defined in
   190  // RFC 4632 and RFC 4291.
   191  //
   192  // It returns the IP address and the network implied by the IP and
   193  // prefix length.
   194  // For example, ParseCIDR("192.0.2.1/24") returns the IP address
   195  // 192.0.2.1 and the network 192.0.2.0/24.
   196  func ParseCIDR(s string) (IP, *IPNet, error) {
   197  	i := indexByteString(s, '/')
   198  	if i < 0 {
   199  		return nil, nil, &ParseError{Type: "CIDR address", Text: s}
   200  	}
   201  	addr, mask := s[:i], s[i+1:]
   202  	iplen := IPv4len
   203  	ip := parseIPv4(addr)
   204  	if ip == nil {
   205  		iplen = IPv6len
   206  		ip = parseIPv6(addr)
   207  	}
   208  	n, i, ok := dtoi(mask)
   209  	if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
   210  		return nil, nil, &ParseError{Type: "CIDR address", Text: s}
   211  	}
   212  	m := CIDRMask(n, 8*iplen)
   213  	return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil
   214  }
   215  
   216  // This is copied from go/src/internal/bytealg, which includes versions
   217  // optimized for various platforms.  Those optimizations are elided here so we
   218  // don't have to maintain them.
   219  func indexByteString(s string, c byte) int {
   220  	for i := 0; i < len(s); i++ {
   221  		if s[i] == c {
   222  			return i
   223  		}
   224  	}
   225  	return -1
   226  }