Skip to content

Commit

Permalink
util: copy go1.16 ParseIP into separate file
Browse files Browse the repository at this point in the history
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
RichardJCai committed Sep 1, 2021
1 parent 4fa6185 commit f44ccb0
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 39 deletions.
9 changes: 4 additions & 5 deletions pkg/sql/sem/tree/datum.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"fmt"
"math"
"math/big"
"net"
"regexp"
"sort"
"strconv"
Expand Down Expand Up @@ -1688,10 +1687,10 @@ func (d *DIPAddr) IsMin(_ *EvalContext) bool {
// dIPv4 and dIPv6 min and maxes use ParseIP because the actual byte constant is
// no equal to solely zeros or ones. For IPv4 there is a 0xffff prefix. Without
// this prefix this makes IP arithmetic invalid.
var dIPv4min = ipaddr.Addr(uint128.FromBytes([]byte(net.ParseIP("0.0.0.0"))))
var dIPv4max = ipaddr.Addr(uint128.FromBytes([]byte(net.ParseIP("255.255.255.255"))))
var dIPv6min = ipaddr.Addr(uint128.FromBytes([]byte(net.ParseIP("::"))))
var dIPv6max = ipaddr.Addr(uint128.FromBytes([]byte(net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))))
var dIPv4min = ipaddr.Addr(uint128.FromBytes([]byte(ipaddr.ParseIP("0.0.0.0"))))
var dIPv4max = ipaddr.Addr(uint128.FromBytes([]byte(ipaddr.ParseIP("255.255.255.255"))))
var dIPv6min = ipaddr.Addr(uint128.FromBytes([]byte(ipaddr.ParseIP("::"))))
var dIPv6max = ipaddr.Addr(uint128.FromBytes([]byte(ipaddr.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))))

// dMaxIPv4Addr and dMinIPv6Addr are used as global constants to prevent extra
// heap extra allocation
Expand Down
5 changes: 4 additions & 1 deletion pkg/util/ipaddr/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "ipaddr",
srcs = ["ipaddr.go"],
srcs = [
"ip.go",
"ipaddr.go",
],
importpath = "github.com/cockroachdb/cockroach/pkg/util/ipaddr",
visibility = ["//visibility:public"],
deps = [
Expand Down
253 changes: 253 additions & 0 deletions pkg/util/ipaddr/ip.go
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
}
4 changes: 2 additions & 2 deletions pkg/util/ipaddr/ipaddr.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func ParseINet(s string, dest *IPAddr) error {
} else {
maskSize = 128
}
ip := net.ParseIP(addr)
ip := ParseIP(addr)
if ip == nil {
return pgerror.WithCandidateCode(
errors.Errorf("could not parse %q as inet. invalid IP", s),
Expand Down Expand Up @@ -236,7 +236,7 @@ func ParseINet(s string, dest *IPAddr) error {

}

ip := net.ParseIP(addr)
ip := ParseIP(addr)
if ip == nil {
return pgerror.WithCandidateCode(
errors.Errorf("could not parse %q as inet. invalid IP", s),
Expand Down
65 changes: 34 additions & 31 deletions pkg/util/ipaddr/ipaddr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,22 @@ func TestIPAddrParseInet(t *testing.T) {
err string
}{
// Basic IPv4.
{"192.168.1.2", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 32}, ""},
{"192.168.1.2", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 32}, ""},
// Test we preserve masked bits.
{"192.168.1.2/16", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 16}, ""},
{"192.168.1.2/16", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 16}, ""},
// Test the ability to have following '.'.
{"192.168.1.2.", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 32}, ""},
{"192.168.1.2./10", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 10}, ""},
{"192.168.1.2.", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 32}, ""},
{"192.168.1.2./10", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 10}, ""},
// Basic IPv6.
{"2001:4f8:3:ba::/64", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("2001:4f8:3:ba::")))), Mask: 64}, ""},
{"2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("2001:4f8:3:ba:2e0:81ff:fe22:d1f1")))), Mask: 128}, ""},
{"::ffff:1.2.3.1/120", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 120}, ""},
{"::ffff:1.2.3.1/128", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 128}, ""},
{"::ffff:1.2.3.1/128", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 128}, ""},
{"::ffff:1.2.3.1/20", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 20}, ""},
{"::ffff:1.2.3.1/120", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 120}, ""},
{"::1", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::1")))), Mask: 128}, ""},
{"9ec6:78fc:c3ae:a65a:9ac7:2081:ac81:e0aa/101", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("9ec6:78fc:c3ae:a65a:9ac7:2081:ac81:e0aa")))), Mask: 101}, ""},
{"2001:4f8:3:ba::/64", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("2001:4f8:3:ba::")))), Mask: 64}, ""},
{"2001:4f8:3:ba:2e0:81ff:fe22:d1f1/128", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("2001:4f8:3:ba:2e0:81ff:fe22:d1f1")))), Mask: 128}, ""},
{"::ffff:1.2.3.1/120", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 120}, ""},
{"::ffff:1.2.3.1/128", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 128}, ""},
{"::ffff:1.2.3.1/128", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 128}, ""},
{"::ffff:1.2.3.1/20", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 20}, ""},
{"::ffff:1.2.3.1/120", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 120}, ""},
{"::1", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::1")))), Mask: 128}, ""},
{"9ec6:78fc:c3ae:a65a:9ac7:2081:ac81:e0aa/101", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("9ec6:78fc:c3ae:a65a:9ac7:2081:ac81:e0aa")))), Mask: 101}, ""},

// Test bad IPs.
{"abc", nil, "invalid IP"},
Expand Down Expand Up @@ -70,15 +70,18 @@ func TestIPAddrParseInet(t *testing.T) {
{"::0/0", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.IPv6zero))), Mask: 0}, ""},
{"::0/10", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.IPv6zero))), Mask: 10}, ""},

{"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))), Mask: 128}, ""},
{"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))), Mask: 0}, ""},
{"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/10", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))), Mask: 10}, ""},
{"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))), Mask: 128}, ""},
{"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))), Mask: 0}, ""},
{"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/10", &IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")))), Mask: 10}, ""},

// Postgres compatibility edge cases: IPv4 missing octets.
{"192.168/24", nil, "mask is larger than provided octets"},
{"192/10", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.0.0.0")))), Mask: 10}, ""},
{"192.168/23", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.0.0")))), Mask: 23}, ""},
{"192.168./10", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.0.0")))), Mask: 10}, ""},
{"192/10", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.0.0.0")))), Mask: 10}, ""},
{"192.168/23", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.0.0")))), Mask: 23}, ""},
{"192.168./10", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.0.0")))), Mask: 10}, ""},

// Postgres allows leading 0s, '10.0.0.017'::INET parses as 10.0.0.17
{"10.0.0.017", &IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("10.0.0.17")))), Mask: 32}, ""},
}
for i, testCase := range testCases {
var actual IPAddr
Expand All @@ -102,18 +105,18 @@ func TestIPAddrBinaryMarshalling(t *testing.T) {
testCases := []struct {
input *IPAddr
}{
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 32}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 16}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 32}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.1.2")))), Mask: 10}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("2001:4f8:3:ba::")))), Mask: 64}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("2001:4f8:3:ba:2e0:81ff:fe22:d1f1")))), Mask: 128}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 120}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 128}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::ffff:1.2.3.1")))), Mask: 128}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("::1")))), Mask: 128}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.0.0")))), Mask: 23}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(net.ParseIP("192.168.0.0")))), Mask: 10}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 32}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 16}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 32}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.1.2")))), Mask: 10}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("2001:4f8:3:ba::")))), Mask: 64}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("2001:4f8:3:ba:2e0:81ff:fe22:d1f1")))), Mask: 128}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 120}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 128}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::ffff:1.2.3.1")))), Mask: 128}},
{&IPAddr{Family: IPv6family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("::1")))), Mask: 128}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.0.0")))), Mask: 23}},
{&IPAddr{Family: IPv4family, Addr: Addr(uint128.FromBytes([]byte(ParseIP("192.168.0.0")))), Mask: 10}},
}
for i, testCase := range testCases {
var data []byte
Expand Down

0 comments on commit f44ccb0

Please sign in to comment.