internal/socket: handle nil addr in sendMsgs

With the pool to reuse internal allocations for mmsghdrs introduced in
CL 315589, I've accidentally introduced a regression for sending
messages on dialed connections.
Calling sendMsgs with Messages with a nil address would panic during
(*msghdr).pack at msghdr_linux.go:18 when taking address of first
element of 0-sized buffer.
Handle this case by explicitly resetting the Name/Namelen.

Add test cases for writing to connected sockets.

Change-Id: I6d5dac696b7ab103a5290675c56002ede3e7b576
GitHub-Last-Rev: 4be010d03a0bcb86dda667952e3f60f096ebd3cc
GitHub-Pull-Request: golang/net#103
Reviewed-on: https://go-review.googlesource.com/c/net/+/316712
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
diff --git a/internal/socket/mmsghdr_unix.go b/internal/socket/mmsghdr_unix.go
index 42c4a0d..40ebeda 100644
--- a/internal/socket/mmsghdr_unix.go
+++ b/internal/socket/mmsghdr_unix.go
@@ -83,8 +83,10 @@
 			saRest = saRest[sizeofSockaddrInet6:]
 		} else if marshalFn != nil {
 			n := marshalFn(ms[i].Addr, saRest)
-			sa = saRest[:n]
-			saRest = saRest[n:]
+			if n > 0 {
+				sa = saRest[:n]
+				saRest = saRest[n:]
+			}
 		}
 		hs[i].Hdr.pack(vs, ms[i].Buffers, ms[i].OOB, sa)
 	}
diff --git a/internal/socket/msghdr_linux.go b/internal/socket/msghdr_linux.go
index 5a38798..c3c7cc4 100644
--- a/internal/socket/msghdr_linux.go
+++ b/internal/socket/msghdr_linux.go
@@ -17,6 +17,9 @@
 	if sa != nil {
 		h.Name = (*byte)(unsafe.Pointer(&sa[0]))
 		h.Namelen = uint32(len(sa))
+	} else {
+		h.Name = nil
+		h.Namelen = 0
 	}
 }
 
diff --git a/internal/socket/socket_test.go b/internal/socket/socket_test.go
index 96c6272..d37ca75 100644
--- a/internal/socket/socket_test.go
+++ b/internal/socket/socket_test.go
@@ -171,6 +171,16 @@
 		t.Fatal(err)
 	}
 
+	// create a dialed connection talking (only) to c/cc
+	cDialed, err := net.Dial("udp", c.LocalAddr().String())
+	if err != nil {
+		t.Fatal(err)
+	}
+	ccDialed, err := socket.NewConn(cDialed)
+	if err != nil {
+		t.Fatal(err)
+	}
+
 	t.Run("Message", func(t *testing.T) {
 		data := []byte("HELLO-R-U-THERE")
 		wm := socket.Message{
@@ -191,6 +201,27 @@
 			t.Fatalf("got %#v; want %#v", b[:rm.N], data)
 		}
 	})
+	t.Run("Message-dialed", func(t *testing.T) {
+		data := []byte("HELLO-R-U-THERE")
+		wm := socket.Message{
+			Buffers: bytes.SplitAfter(data, []byte("-")),
+			Addr:    nil,
+		}
+		if err := ccDialed.SendMsg(&wm, 0); err != nil {
+			t.Fatal(err)
+		}
+		b := make([]byte, 32)
+		rm := socket.Message{
+			Buffers: [][]byte{b[:1], b[1:3], b[3:7], b[7:11], b[11:]},
+		}
+		if err := cc.RecvMsg(&rm, 0); err != nil {
+			t.Fatal(err)
+		}
+		if !bytes.Equal(b[:rm.N], data) {
+			t.Fatalf("got %#v; want %#v", b[:rm.N], data)
+		}
+	})
+
 	switch runtime.GOOS {
 	case "android", "linux":
 		t.Run("Messages", func(t *testing.T) {
@@ -228,6 +259,55 @@
 				t.Fatalf("got %#v; want %#v", b[:nn], data)
 			}
 		})
+		t.Run("Messages-undialed-no-dst", func(t *testing.T) {
+			// sending without destination address should fail.
+			// This checks that the internally recycled buffers are reset correctly.
+			data := []byte("HELLO-R-U-THERE")
+			wmbs := bytes.SplitAfter(data, []byte("-"))
+			wms := []socket.Message{
+				{Buffers: wmbs[:1], Addr: nil},
+				{Buffers: wmbs[1:], Addr: nil},
+			}
+			n, err := cc.SendMsgs(wms, 0)
+			if n != 0 && err == nil {
+				t.Fatal("expected error, destination address required")
+			}
+		})
+		t.Run("Messages-dialed", func(t *testing.T) {
+			data := []byte("HELLO-R-U-THERE")
+			wmbs := bytes.SplitAfter(data, []byte("-"))
+			wms := []socket.Message{
+				{Buffers: wmbs[:1], Addr: nil},
+				{Buffers: wmbs[1:], Addr: nil},
+			}
+			n, err := ccDialed.SendMsgs(wms, 0)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if n != len(wms) {
+				t.Fatalf("got %d; want %d", n, len(wms))
+			}
+			b := make([]byte, 32)
+			rmbs := [][][]byte{{b[:len(wmbs[0])]}, {b[len(wmbs[0]):]}}
+			rms := []socket.Message{
+				{Buffers: rmbs[0]},
+				{Buffers: rmbs[1]},
+			}
+			n, err = cc.RecvMsgs(rms, 0)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if n != len(rms) {
+				t.Fatalf("got %d; want %d", n, len(rms))
+			}
+			nn := 0
+			for i := 0; i < n; i++ {
+				nn += rms[i].N
+			}
+			if !bytes.Equal(b[:nn], data) {
+				t.Fatalf("got %#v; want %#v", b[:nn], data)
+			}
+		})
 	}
 
 	// The behavior of transmission for zero byte paylaod depends