Commit 263405ea authored by Nicolas S. Dade's avatar Nicolas S. Dade Committed by Mikio Hara

net: permit WriteMsgUDP to connected UDP sockets

The sanity checks at the beginning of WriteMsgUDP were too
strict, and did not allow a case sendmsg(2) suppports: sending
to a connected UDP socket.

This fixes the sanity checks. Either the socket is unconnected,
and a destination addresses is required (what all existing callers
must have been doing), or the socket is connected and an explicit
destination address must not be used.

Fixes #9807

Change-Id: I08d4ec3c2bf830335c402acfc0680c841cfcec71
Reviewed-on: https://go-review.googlesource.com/3951Reviewed-by: 's avatarMikio Hara <mikioh.mikioh@gmail.com>
parent e810a079
...@@ -81,26 +81,26 @@ func TestWriteToUDP(t *testing.T) { ...@@ -81,26 +81,26 @@ func TestWriteToUDP(t *testing.T) {
t.Skipf("skipping test on %q", runtime.GOOS) t.Skipf("skipping test on %q", runtime.GOOS)
} }
l, err := ListenPacket("udp", "127.0.0.1:0") c, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil { if err != nil {
t.Fatalf("Listen failed: %v", err) t.Fatal(err)
} }
defer l.Close() defer c.Close()
testWriteToConn(t, l.LocalAddr().String()) testWriteToConn(t, c.LocalAddr().String())
testWriteToPacketConn(t, l.LocalAddr().String()) testWriteToPacketConn(t, c.LocalAddr().String())
} }
func testWriteToConn(t *testing.T, raddr string) { func testWriteToConn(t *testing.T, raddr string) {
c, err := Dial("udp", raddr) c, err := Dial("udp", raddr)
if err != nil { if err != nil {
t.Fatalf("Dial failed: %v", err) t.Fatal(err)
} }
defer c.Close() defer c.Close()
ra, err := ResolveUDPAddr("udp", raddr) ra, err := ResolveUDPAddr("udp", raddr)
if err != nil { if err != nil {
t.Fatalf("ResolveUDPAddr failed: %v", err) t.Fatal(err)
} }
_, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra) _, err = c.(*UDPConn).WriteToUDP([]byte("Connection-oriented mode socket"), ra)
...@@ -121,36 +121,60 @@ func testWriteToConn(t *testing.T, raddr string) { ...@@ -121,36 +121,60 @@ func testWriteToConn(t *testing.T, raddr string) {
_, err = c.Write([]byte("Connection-oriented mode socket")) _, err = c.Write([]byte("Connection-oriented mode socket"))
if err != nil { if err != nil {
t.Fatalf("Write failed: %v", err) t.Fatal(err)
}
_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, ra)
if err == nil {
t.Fatal("WriteMsgUDP should fail")
}
if err != nil && err.(*OpError).Err != ErrWriteToConnected {
t.Fatalf("WriteMsgUDP should fail as ErrWriteToConnected: %v", err)
}
_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-oriented mode socket"), nil, nil)
if err != nil {
t.Fatal(err)
} }
} }
func testWriteToPacketConn(t *testing.T, raddr string) { func testWriteToPacketConn(t *testing.T, raddr string) {
c, err := ListenPacket("udp", "127.0.0.1:0") c, err := ListenPacket("udp", "127.0.0.1:0")
if err != nil { if err != nil {
t.Fatalf("ListenPacket failed: %v", err) t.Fatal(err)
} }
defer c.Close() defer c.Close()
ra, err := ResolveUDPAddr("udp", raddr) ra, err := ResolveUDPAddr("udp", raddr)
if err != nil { if err != nil {
t.Fatalf("ResolveUDPAddr failed: %v", err) t.Fatal(err)
} }
_, err = c.(*UDPConn).WriteToUDP([]byte("Connection-less mode socket"), ra) _, err = c.(*UDPConn).WriteToUDP([]byte("Connection-less mode socket"), ra)
if err != nil { if err != nil {
t.Fatalf("WriteToUDP failed: %v", err) t.Fatal(err)
} }
_, err = c.WriteTo([]byte("Connection-less mode socket"), ra) _, err = c.WriteTo([]byte("Connection-less mode socket"), ra)
if err != nil { if err != nil {
t.Fatalf("WriteTo failed: %v", err) t.Fatal(err)
} }
_, err = c.(*UDPConn).Write([]byte("Connection-less mode socket")) _, err = c.(*UDPConn).Write([]byte("Connection-less mode socket"))
if err == nil { if err == nil {
t.Fatal("Write should fail") t.Fatal("Write should fail")
} }
_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, nil)
if err == nil {
t.Fatal("WriteMsgUDP should fail")
}
if err != nil && err.(*OpError).Err != errMissingAddress {
t.Fatalf("WriteMsgUDP should fail as errMissingAddress: %v", err)
}
_, _, err = c.(*UDPConn).WriteMsgUDP([]byte("Connection-less mode socket"), nil, ra)
if err != nil {
t.Fatal(err)
}
} }
var udpConnLocalNameTests = []struct { var udpConnLocalNameTests = []struct {
......
...@@ -139,17 +139,19 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) { ...@@ -139,17 +139,19 @@ func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
return c.WriteToUDP(b, a) return c.WriteToUDP(b, a)
} }
// WriteMsgUDP writes a packet to addr via c, copying the payload from // WriteMsgUDP writes a packet to addr via c if c isn't connected, or
// b and the associated out-of-band data from oob. It returns the // to c's remote destination address if c is connected (in which case
// number of payload and out-of-band bytes written. // addr must be nil). The payload is copied from b and the associated
// out-of-band data is copied from oob. It returns the number of
// payload and out-of-band bytes written.
func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) { func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err error) {
if !c.ok() { if !c.ok() {
return 0, 0, syscall.EINVAL return 0, 0, syscall.EINVAL
} }
if c.fd.isConnected { if c.fd.isConnected && addr != nil {
return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected} return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
} }
if addr == nil { if !c.fd.isConnected && addr == nil {
return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress} return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
} }
sa, err := addr.sockaddr(c.fd.family) sa, err := addr.sockaddr(c.fd.family)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment