mirror of
https://github.com/opentffoundation/opentf.git
synced 2026-04-11 06:01:36 -04:00
The code in this package is all snapshot from the Go codebase in older versions, inlined here to allow OpenTofu's cidr-calculation-related functions to preserve their original behavior despite upstream changing the parsing rules in a breaking way. This code is intentionally modified as little as possible from the upstream code it was derived from. We are imposing on ourselves considerably stricter style conventions than the Go project follows and so we need to disable various linters for this package to allow this code to remain written in the Go idiomatic style, rather than in OpenTofu's stricter local style. In particular, we've chosen to prohibit ourselves from using named return values or package-global variables, despite those both being typical in the standard library and in other codebases. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
228 lines
5.0 KiB
Go
228 lines
5.0 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// IP address manipulations
|
|
//
|
|
// IPv4 addresses are 4 bytes; IPv6 addresses are 16 bytes.
|
|
// An IPv4 address can be converted to an IPv6 address by
|
|
// adding a canonical prefix (10 zeros, 2 0xFFs).
|
|
// This library accepts either size of byte slice but always
|
|
// returns 16-byte addresses.
|
|
|
|
//nolint:cyclop,funlen,gochecknoglobals,gocritic,nonamedreturns,mnd // This file is copied from the Go codebase and intended to remain close to the original in case we need to backport changes.
|
|
package ipaddr
|
|
|
|
import (
|
|
stdnet "net"
|
|
)
|
|
|
|
//
|
|
// Lean on the standard net lib as much as possible.
|
|
//
|
|
|
|
type IP = stdnet.IP
|
|
type IPNet = stdnet.IPNet
|
|
type ParseError = stdnet.ParseError
|
|
|
|
const IPv4len = stdnet.IPv4len
|
|
const IPv6len = stdnet.IPv6len
|
|
|
|
var CIDRMask = stdnet.CIDRMask
|
|
var IPv4 = stdnet.IPv4
|
|
|
|
// Parse IPv4 address (d.d.d.d).
|
|
func parseIPv4(s string) IP {
|
|
var p [IPv4len]byte
|
|
for i := 0; i < IPv4len; i++ {
|
|
if len(s) == 0 {
|
|
// Missing octets.
|
|
return nil
|
|
}
|
|
if i > 0 {
|
|
if s[0] != '.' {
|
|
return nil
|
|
}
|
|
s = s[1:]
|
|
}
|
|
n, c, ok := dtoi(s)
|
|
if !ok || n > 0xFF {
|
|
return nil
|
|
}
|
|
//
|
|
// NOTE: This correct check was added for go-1.17, but is a
|
|
// backwards-incompatible change for OpenTofu users, who might have
|
|
// already written modules with leading zeroes.
|
|
//
|
|
//if c > 1 && s[0] == '0' {
|
|
// // Reject non-zero components with leading zeroes.
|
|
// return nil
|
|
//}
|
|
s = s[c:]
|
|
p[i] = byte(n)
|
|
}
|
|
if len(s) != 0 {
|
|
return nil
|
|
}
|
|
return IPv4(p[0], p[1], p[2], p[3])
|
|
}
|
|
|
|
// parseIPv6 parses s as a literal IPv6 address described in RFC 4291
|
|
// and RFC 5952.
|
|
func parseIPv6(s string) (ip IP) {
|
|
ip = make(IP, IPv6len)
|
|
ellipsis := -1 // position of ellipsis in ip
|
|
|
|
// Might have leading ellipsis
|
|
if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
|
|
ellipsis = 0
|
|
s = s[2:]
|
|
// Might be only ellipsis
|
|
if len(s) == 0 {
|
|
return ip
|
|
}
|
|
}
|
|
|
|
// Loop, parsing hex numbers followed by colon.
|
|
i := 0
|
|
for i < IPv6len {
|
|
// Hex number.
|
|
n, c, ok := xtoi(s)
|
|
if !ok || n > 0xFFFF {
|
|
return nil
|
|
}
|
|
|
|
// If followed by dot, might be in trailing IPv4.
|
|
if c < len(s) && s[c] == '.' {
|
|
if ellipsis < 0 && i != IPv6len-IPv4len {
|
|
// Not the right place.
|
|
return nil
|
|
}
|
|
if i+IPv4len > IPv6len {
|
|
// Not enough room.
|
|
return nil
|
|
}
|
|
ip4 := parseIPv4(s)
|
|
if ip4 == nil {
|
|
return nil
|
|
}
|
|
ip[i] = ip4[12]
|
|
ip[i+1] = ip4[13]
|
|
ip[i+2] = ip4[14]
|
|
ip[i+3] = ip4[15]
|
|
s = ""
|
|
i += IPv4len
|
|
break
|
|
}
|
|
|
|
// Save this 16-bit chunk.
|
|
ip[i] = byte(n >> 8)
|
|
ip[i+1] = byte(n)
|
|
i += 2
|
|
|
|
// Stop at end of string.
|
|
s = s[c:]
|
|
if len(s) == 0 {
|
|
break
|
|
}
|
|
|
|
// Otherwise must be followed by colon and more.
|
|
if s[0] != ':' || len(s) == 1 {
|
|
return nil
|
|
}
|
|
s = s[1:]
|
|
|
|
// Look for ellipsis.
|
|
if s[0] == ':' {
|
|
if ellipsis >= 0 { // already have one
|
|
return nil
|
|
}
|
|
ellipsis = i
|
|
s = s[1:]
|
|
if len(s) == 0 { // can be at end
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Must have used entire string.
|
|
if len(s) != 0 {
|
|
return nil
|
|
}
|
|
|
|
// If didn't parse enough, expand ellipsis.
|
|
if i < IPv6len {
|
|
if ellipsis < 0 {
|
|
return nil
|
|
}
|
|
n := IPv6len - i
|
|
for j := i - 1; j >= ellipsis; j-- {
|
|
ip[j+n] = ip[j]
|
|
}
|
|
for j := ellipsis + n - 1; j >= ellipsis; j-- {
|
|
ip[j] = 0
|
|
}
|
|
} else if ellipsis >= 0 {
|
|
// Ellipsis must represent at least one 0 group.
|
|
return nil
|
|
}
|
|
return ip
|
|
}
|
|
|
|
// ParseIP parses s as an IP address, returning the result.
|
|
// The string s can be in IPv4 dotted decimal ("192.0.2.1"), IPv6
|
|
// ("2001:db8::68"), or IPv4-mapped IPv6 ("::ffff:192.0.2.1") form.
|
|
// If s is not a valid textual representation of an IP address,
|
|
// ParseIP returns nil.
|
|
func ParseIP(s string) IP {
|
|
for i := 0; i < len(s); i++ {
|
|
switch s[i] {
|
|
case '.':
|
|
return parseIPv4(s)
|
|
case ':':
|
|
return parseIPv6(s)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ParseCIDR parses s as a CIDR notation IP address and prefix length,
|
|
// like "192.0.2.0/24" or "2001:db8::/32", as defined in
|
|
// RFC 4632 and RFC 4291.
|
|
//
|
|
// It returns the IP address and the network implied by the IP and
|
|
// prefix length.
|
|
// For example, ParseCIDR("192.0.2.1/24") returns the IP address
|
|
// 192.0.2.1 and the network 192.0.2.0/24.
|
|
func ParseCIDR(s string) (IP, *IPNet, error) {
|
|
i := indexByteString(s, '/')
|
|
if i < 0 {
|
|
return nil, nil, &ParseError{Type: "CIDR address", Text: s}
|
|
}
|
|
addr, mask := s[:i], s[i+1:]
|
|
iplen := IPv4len
|
|
ip := parseIPv4(addr)
|
|
if ip == nil {
|
|
iplen = IPv6len
|
|
ip = parseIPv6(addr)
|
|
}
|
|
n, i, ok := dtoi(mask)
|
|
if ip == nil || !ok || i != len(mask) || n < 0 || n > 8*iplen {
|
|
return nil, nil, &ParseError{Type: "CIDR address", Text: s}
|
|
}
|
|
m := CIDRMask(n, 8*iplen)
|
|
return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil
|
|
}
|
|
|
|
// This is copied from go/src/internal/bytealg, which includes versions
|
|
// optimized for various platforms. Those optimizations are elided here so we
|
|
// don't have to maintain them.
|
|
func indexByteString(s string, c byte) int {
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] == c {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|