diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go index 57ac9a933a0d34..484dd4470ee9f3 100644 --- a/src/net/lookup_test.go +++ b/src/net/lookup_test.go @@ -481,7 +481,6 @@ var revAddrTests = []struct { {"1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.ip6.arpa.", ""}, {"1234:567::89a:bcde", "e.d.c.b.a.9.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.6.5.0.4.3.2.1.ip6.arpa.", ""}, {"1234:567:fefe:bcbc:adad:9e4a:89a:bcde", "e.d.c.b.a.9.8.0.a.4.e.9.d.a.d.a.c.b.c.b.e.f.e.f.7.6.5.0.4.3.2.1.ip6.arpa.", ""}, - {"1.2.3", "", "unrecognized address"}, {"1.2.3.4.5", "", "unrecognized address"}, {"1234:567:bcbca::89a:bcde", "", "unrecognized address"}, {"1234:567::bcbc:adad::89a:bcde", "", "unrecognized address"}, diff --git a/src/net/netip/netip.go b/src/net/netip/netip.go index 1d20a4aa7f616b..2c356c9d93aa6f 100644 --- a/src/net/netip/netip.go +++ b/src/net/netip/netip.go @@ -125,6 +125,13 @@ func ParseAddr(s string) (Addr, error) { return Addr{}, parseAddrError{in: s, msg: "missing IPv6 address"} } } + + // Single number IP + addr, err := parseIPv4(s) + if err == nil { + return addr, nil + } + return Addr{}, parseAddrError{in: s, msg: "unable to parse IP"} } @@ -154,19 +161,20 @@ func (err parseAddrError) Error() string { // parseIPv4 parses s as an IPv4 address (in form "192.168.0.1"). func parseIPv4(s string) (ip Addr, err error) { - var fields [4]uint8 - var val, pos int + if len(s) == 0 { + return Addr{}, parseAddrError{in: s, msg: "unable to parse IP"} + } + + var fields [4]uint + var val, pos uint var digLen int // number of digits in current octet for i := 0; i < len(s); i++ { if s[i] >= '0' && s[i] <= '9' { if digLen == 1 && val == 0 { return Addr{}, parseAddrError{in: s, msg: "IPv4 field has octet with leading zero"} } - val = val*10 + int(s[i]) - '0' + val = val*10 + uint(s[i]) - '0' digLen++ - if val > 255 { - return Addr{}, parseAddrError{in: s, msg: "IPv4 field has value >255"} - } } else if s[i] == '.' { // .1.2.3 // 1.2.3. @@ -178,7 +186,7 @@ func parseIPv4(s string) (ip Addr, err error) { if pos == 3 { return Addr{}, parseAddrError{in: s, msg: "IPv4 address too long"} } - fields[pos] = uint8(val) + fields[pos] = val pos++ val = 0 digLen = 0 @@ -186,11 +194,44 @@ func parseIPv4(s string) (ip Addr, err error) { return Addr{}, parseAddrError{in: s, msg: "unexpected character", at: s[i:]} } } - if pos < 3 { - return Addr{}, parseAddrError{in: s, msg: "IPv4 address too short"} + fields[pos] = val + + switch pos { + // a.b.c.d Each of the four numeric parts specifies a byte of the address + case 3: + // NOP + // a.b.c Parts a and b specify the first two bytes of the binary address. + // Part c is interpreted as a 16-bit value that defines the rightmost two bytes of the binary address + case 2: + c := fields[2] + fields[3] = c & 0xFF + fields[2] = (c >> 8) + // a.b Part a specifies the first byte of the binary address. + // Part b is interpreted as a 24-bit value that defines the rightmost three bytes of the binary address. + case 1: + b := fields[1] + fields[3] = b & 0xFF + fields[2] = (b >> 8) & 0xFF + fields[1] = b >> 16 + // a The value a is interpreted as a 32-bit value that is stored directly into the binary address + // without any byte rearrangement. + case 0: + a := fields[0] + fields[3] = a & 0xFF + fields[2] = (a >> 8) & 0xFF + fields[1] = (a >> 16) & 0xFF + fields[0] = a >> 24 + } + + var bFields [4]uint8 + for i, f := range fields { + if f > 0xFF { + return Addr{}, parseAddrError{in: s, msg: "IPv4 field has value >255"} + } + bFields[i] = uint8(f) } - fields[3] = uint8(val) - return AddrFrom4(fields), nil + + return AddrFrom4(bFields), nil } // parseIPv6 parses s as an IPv6 address (in form "2001:db8::68"). diff --git a/src/net/netip/netip_test.go b/src/net/netip/netip_test.go index a748ac34f13ccb..9c147f973d8c70 100644 --- a/src/net/netip/netip_test.go +++ b/src/net/netip/netip_test.go @@ -218,7 +218,7 @@ func TestParseAddr(t *testing.T) { "bad", // Single number. Some parsers accept this as an IPv4 address in // big-endian uint32 form, but we don't. - "1234", + // "1234", // IPv4 with a zone specifier "1.2.3.4%eth0", // IPv4 field must have at least one digit @@ -232,15 +232,12 @@ func TestParseAddr(t *testing.T) { // IPv4 in dotted hex form "0xc0.0xa8.0x8c.0xff", // IPv4 in class B form - "192.168.12345", + // "192.168.12345", // IPv4 in class B form, with a small enough number to be // parseable as a regular dotted decimal field. - "127.0.1", // IPv4 in class A form - "192.1234567", // IPv4 in class A form, with a small enough number to be // parseable as a regular dotted decimal field. - "127.1", // IPv4 field has value >255 "192.168.300.1", // IPv4 with too many fields @@ -302,6 +299,67 @@ func TestParseAddr(t *testing.T) { } } +type shortIPCase struct { + in string + out string +} + +func genShortIP(count, a, b, c, d int) shortIPCase { + test := shortIPCase{ + out: fmt.Sprintf("%d.%d.%d.%d", a, b, c, d), + } + + switch count { + case 3: + test.in = fmt.Sprintf("%d.%d.%d", a, b, (c<<8)|d) + case 2: + test.in = fmt.Sprintf("%d.%d", a, (b<<16)|(c<<8)|d) + case 1: + v := (a << 24) | (b << 16) | (c << 8) | d + test.in = fmt.Sprintf("%d", v) + } + + return test +} + +func TestParseIPV4Short(t *testing.T) { + + var shortIPCases = []shortIPCase{ + genShortIP(3, 10, 20, 30, 40), + genShortIP(2, 10, 20, 30, 40), + genShortIP(1, 10, 20, 30, 40), + } + + for _, test := range shortIPCases { + t.Run(test.in, func(t *testing.T) { + ip, err := ParseAddr(test.in) + if err != nil { + t.Errorf("unexpected error: %s", err) + return + } + + if got := ip.String(); got != test.out { + t.Errorf("ParseAddr(%q): got %s, expected %s", test.in, got, test.out) + } + }) + } + + var badIPCases = []string{ + "10.20.65536", + "10.16777216", + "4294967296", + } + + for _, s := range badIPCases { + t.Run(s, func(t *testing.T) { + ip, err := ParseAddr(s) + if err == nil { + t.Errorf("ParseAddr(%q): expected error, got %v", s, ip) + } + }) + } +} + func TestAddrFromSlice(t *testing.T) { tests := []struct { ip []byte