net: make parseProcNetIGMP more robust

Suggested by Paul Borman.

Fixes #2826.

R=rsc, borman
CC=golang-dev
https://golang.org/cl/5689048
diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go
index 21038c6..15c2f37 100644
--- a/src/pkg/net/interface_linux.go
+++ b/src/pkg/net/interface_linux.go
@@ -166,13 +166,13 @@
 			return nil, err
 		}
 	}
-	ifmat4 := parseProcNetIGMP(ifi)
-	ifmat6 := parseProcNetIGMP6(ifi)
+	ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
+	ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
 	return append(ifmat4, ifmat6...), nil
 }
 
-func parseProcNetIGMP(ifi *Interface) []Addr {
-	fd, err := open("/proc/net/igmp")
+func parseProcNetIGMP(path string, ifi *Interface) []Addr {
+	fd, err := open(path)
 	if err != nil {
 		return nil
 	}
@@ -185,23 +185,26 @@
 	fd.readLine() // skip first line
 	b := make([]byte, IPv4len)
 	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
-		f := getFields(l)
-		switch len(f) {
-		case 4:
+		f := splitAtBytes(l, " :\r\t\n")
+		if len(f) < 4 {
+			continue
+		}
+		switch {
+		case l[0] != ' ' && l[0] != '\t': // new interface line
+			name = f[1]
+		case len(f[0]) == 8:
 			if ifi == nil || name == ifi.Name {
 				fmt.Sscanf(f[0], "%08x", &b)
 				ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])}
 				ifmat = append(ifmat, ifma.toAddr())
 			}
-		case 5:
-			name = f[1]
 		}
 	}
 	return ifmat
 }
 
-func parseProcNetIGMP6(ifi *Interface) []Addr {
-	fd, err := open("/proc/net/igmp6")
+func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
+	fd, err := open(path)
 	if err != nil {
 		return nil
 	}
@@ -210,7 +213,10 @@
 	var ifmat []Addr
 	b := make([]byte, IPv6len)
 	for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
-		f := getFields(l)
+		f := splitAtBytes(l, " \r\t\n")
+		if len(f) < 6 {
+			continue
+		}
 		if ifi == nil || f[1] == ifi.Name {
 			fmt.Sscanf(f[2], "%32x", &b)
 			ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
diff --git a/src/pkg/net/interface_linux_test.go b/src/pkg/net/interface_linux_test.go
new file mode 100644
index 0000000..f14d1fe
--- /dev/null
+++ b/src/pkg/net/interface_linux_test.go
@@ -0,0 +1,54 @@
+// Copyright 2012 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.
+
+package net
+
+import "testing"
+
+const (
+	numOfTestIPv4MCAddrs = 14
+	numOfTestIPv6MCAddrs = 18
+)
+
+var (
+	igmpInterfaceTable = []Interface{
+		{Name: "lo"},
+		{Name: "eth0"}, {Name: "eth1"}, {Name: "eth2"},
+		{Name: "eth0.100"}, {Name: "eth0.101"}, {Name: "eth0.102"}, {Name: "eth0.103"},
+		{Name: "device1tap2"},
+	}
+	igmp6InterfaceTable = []Interface{
+		{Name: "lo"},
+		{Name: "eth0"}, {Name: "eth1"}, {Name: "eth2"},
+		{Name: "eth0.100"}, {Name: "eth0.101"}, {Name: "eth0.102"}, {Name: "eth0.103"},
+		{Name: "device1tap2"},
+		{Name: "pan0"},
+	}
+)
+
+func TestParseProcNet(t *testing.T) {
+	defer func() {
+		if p := recover(); p != nil {
+			t.Fatalf("panicked")
+		}
+	}()
+
+	var ifmat4 []Addr
+	for _, ifi := range igmpInterfaceTable {
+		ifmat := parseProcNetIGMP("testdata/igmp", &ifi)
+		ifmat4 = append(ifmat4, ifmat...)
+	}
+	if len(ifmat4) != numOfTestIPv4MCAddrs {
+		t.Fatalf("parseProcNetIGMP returns %v addresses, expected %v", len(ifmat4), numOfTestIPv4MCAddrs)
+	}
+
+	var ifmat6 []Addr
+	for _, ifi := range igmp6InterfaceTable {
+		ifmat := parseProcNetIGMP6("testdata/igmp6", &ifi)
+		ifmat6 = append(ifmat6, ifmat...)
+	}
+	if len(ifmat6) != numOfTestIPv6MCAddrs {
+		t.Fatalf("parseProcNetIGMP6 returns %v addresses, expected %v", len(ifmat6), numOfTestIPv6MCAddrs)
+	}
+}
diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go
index 4ce01dc..769414e 100644
--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -31,17 +31,17 @@
 	for _, ifi := range ift {
 		ifxi, err := InterfaceByIndex(ifi.Index)
 		if err != nil {
-			t.Fatalf("InterfaceByIndex(%#q) failed: %v", ifi.Index, err)
+			t.Fatalf("InterfaceByIndex(%q) failed: %v", ifi.Index, err)
 		}
 		if !sameInterface(ifxi, &ifi) {
-			t.Fatalf("InterfaceByIndex(%#q) = %v, want %v", ifi.Index, *ifxi, ifi)
+			t.Fatalf("InterfaceByIndex(%q) = %v, want %v", ifi.Index, *ifxi, ifi)
 		}
 		ifxn, err := InterfaceByName(ifi.Name)
 		if err != nil {
-			t.Fatalf("InterfaceByName(%#q) failed: %v", ifi.Name, err)
+			t.Fatalf("InterfaceByName(%q) failed: %v", ifi.Name, err)
 		}
 		if !sameInterface(ifxn, &ifi) {
-			t.Fatalf("InterfaceByName(%#q) = %v, want %v", ifi.Name, *ifxn, ifi)
+			t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, *ifxn, ifi)
 		}
 		t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU)
 		t.Logf("\thardware address %q", ifi.HardwareAddr.String())
diff --git a/src/pkg/net/parse.go b/src/pkg/net/parse.go
index 4c4200a..7c87b42 100644
--- a/src/pkg/net/parse.go
+++ b/src/pkg/net/parse.go
@@ -67,7 +67,7 @@
 	if err != nil {
 		return nil, err
 	}
-	return &file{fd, make([]byte, 1024)[0:0], false}, nil
+	return &file{fd, make([]byte, os.Getpagesize())[0:0], false}, nil
 }
 
 func byteIndex(s string, c byte) int {
diff --git a/src/pkg/net/testdata/igmp b/src/pkg/net/testdata/igmp
new file mode 100644
index 0000000..5f380a2
--- /dev/null
+++ b/src/pkg/net/testdata/igmp
@@ -0,0 +1,24 @@
+Idx	Device    : Count Querier	Group    Users Timer	Reporter
+1	lo        :     1      V3
+				010000E0     1 0:00000000		0
+2	eth0      :     2      V2
+				FB0000E0     1 0:00000000		1
+				010000E0     1 0:00000000		0
+3	eth1      :     1      V3
+				010000E0     1 0:00000000		0
+4	eth2      :     1      V3
+				010000E0     1 0:00000000		0
+5	eth0.100  :     2      V3
+				FB0000E0     1 0:00000000		0
+				010000E0     1 0:00000000		0
+6	eth0.101  :     2      V3
+				FB0000E0     1 0:00000000		0
+				010000E0     1 0:00000000		0
+7	eth0.102  :     2      V3
+				FB0000E0     1 0:00000000		0
+				010000E0     1 0:00000000		0
+8	eth0.103  :     2      V3
+				FB0000E0     1 0:00000000		0
+				010000E0     1 0:00000000		0
+9	device1tap2:     1      V3
+				010000E0     1 0:00000000		0
diff --git a/src/pkg/net/testdata/igmp6 b/src/pkg/net/testdata/igmp6
new file mode 100644
index 0000000..6cd5a2d
--- /dev/null
+++ b/src/pkg/net/testdata/igmp6
@@ -0,0 +1,18 @@
+1    lo              ff020000000000000000000000000001     1 0000000C 0
+2    eth0            ff0200000000000000000001ffac891e     1 00000006 0
+2    eth0            ff020000000000000000000000000001     1 0000000C 0
+3    eth1            ff0200000000000000000001ffac8928     2 00000006 0
+3    eth1            ff020000000000000000000000000001     1 0000000C 0
+4    eth2            ff0200000000000000000001ffac8932     2 00000006 0
+4    eth2            ff020000000000000000000000000001     1 0000000C 0
+5    eth0.100        ff0200000000000000000001ffac891e     1 00000004 0
+5    eth0.100        ff020000000000000000000000000001     1 0000000C 0
+6    pan0            ff020000000000000000000000000001     1 0000000C 0
+7    eth0.101        ff0200000000000000000001ffac891e     1 00000004 0
+7    eth0.101        ff020000000000000000000000000001     1 0000000C 0
+8    eth0.102        ff0200000000000000000001ffac891e     1 00000004 0
+8    eth0.102        ff020000000000000000000000000001     1 0000000C 0
+9    eth0.103        ff0200000000000000000001ffac891e     1 00000004 0
+9    eth0.103        ff020000000000000000000000000001     1 0000000C 0
+10   device1tap2     ff0200000000000000000001ff4cc3a3     1 00000004 0
+10   device1tap2     ff020000000000000000000000000001     1 0000000C 0