-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
util: copy go1.16 ParseIP into separate file
go1.17 is changing how IPs are parsed, leading 0s will not be accepted. This change ensures that IP addresses with leading 0s are accepted (which is the case in Postgres) Release justification: only moved code out of go package to it's own file. Release note: None
- Loading branch information
1 parent
4fa6185
commit f44ccb0
Showing
5 changed files
with
297 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
// 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. | ||
|
||
// See the go license at https://golang.org/LICENSE. | ||
|
||
// 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. | ||
|
||
// This code was copied out of Go 1.16 as Go 1.17 removed the ability to | ||
// parse leading 0s in IP addresses. We copy the Go 1.16 to keep the ParseIP | ||
// function's capability to parse leading 0s and hence be compatible with | ||
// Postgres. | ||
// Example: '10.1.1.017' should be parsed as '10.1.1.17'. | ||
|
||
package ipaddr | ||
|
||
// An IP is a single IP address, a slice of bytes. | ||
// Functions in this package accept either 4-byte (IPv4) | ||
// or 16-byte (IPv6) slices as input. | ||
// | ||
// Note that in this documentation, referring to an | ||
// IP address as an IPv4 address or an IPv6 address | ||
// is a semantic property of the address, not just the | ||
// length of the byte slice: a 16-byte slice can still | ||
// be an IPv4 address. | ||
type IP []byte | ||
|
||
// IP address lengths (bytes). | ||
const ( | ||
IPv4len = 4 | ||
IPv6len = 16 | ||
) | ||
|
||
// 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 | ||
} | ||
|
||
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 | ||
} | ||
s = s[c:] | ||
p[i] = byte(n) | ||
} | ||
if len(s) != 0 { | ||
return nil | ||
} | ||
return makeIPv4(p[0], p[1], p[2], p[3]) | ||
} | ||
|
||
var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff} | ||
|
||
// makeIPv4 returns the IP address (in 16-byte form) of the | ||
// IPv4 address a.b.c.d. | ||
func makeIPv4(a, b, c, d byte) IP { | ||
p := make(IP, IPv6len) | ||
copy(p, v4InV6Prefix) | ||
p[12] = a | ||
p[13] = b | ||
p[14] = c | ||
p[15] = d | ||
return p | ||
} | ||
|
||
// 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 | ||
} | ||
|
||
// Bigger than we need, not too big to worry about overflow | ||
const big = 0xFFFFFF | ||
|
||
// Decimal to integer. | ||
// Returns number, characters consumed, success. | ||
func dtoi(s string) (n int, i int, ok bool) { | ||
n = 0 | ||
for i = 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { | ||
n = n*10 + int(s[i]-'0') | ||
if n >= big { | ||
return big, i, false | ||
} | ||
} | ||
if i == 0 { | ||
return 0, 0, false | ||
} | ||
return n, i, true | ||
} | ||
|
||
// Hexadecimal to integer. | ||
// Returns number, characters consumed, success. | ||
func xtoi(s string) (n int, i int, ok bool) { | ||
n = 0 | ||
for i = 0; i < len(s); i++ { | ||
if '0' <= s[i] && s[i] <= '9' { | ||
n *= 16 | ||
n += int(s[i] - '0') | ||
} else if 'a' <= s[i] && s[i] <= 'f' { | ||
n *= 16 | ||
n += int(s[i]-'a') + 10 | ||
} else if 'A' <= s[i] && s[i] <= 'F' { | ||
n *= 16 | ||
n += int(s[i]-'A') + 10 | ||
} else { | ||
break | ||
} | ||
if n >= big { | ||
return 0, i, false | ||
} | ||
} | ||
if i == 0 { | ||
return 0, i, false | ||
} | ||
return n, i, true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters