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 }