]> Cypherpunks repositories - gostls13.git/commitdiff
net: make {TCP,UDP,IP,Unix}Conn compliant of syscall.Conn interface
authorMikio Hara <mikioh.mikioh@gmail.com>
Mon, 13 Feb 2017 21:38:10 +0000 (06:38 +0900)
committerMikio Hara <mikioh.mikioh@gmail.com>
Fri, 19 May 2017 09:10:27 +0000 (09:10 +0000)
This change makes {TCP,UDP,IP,Unix}Conn types compliant of
syscall.Conn interface and adds type rawConn as an implementation of
syscall.RawConn interface.

By this change, the long-standing issues regarding unsupported socket
options and system calls can be solved partly and the broken x/net
packages due to https://go-review.googlesource.com/36799 can be
repaired.

Fixes #3661.
Updates #9661.
Updates #19051.
Updates #19435.

Change-Id: Ic996b040418b54f6d043bc70591789d5a5b23270
Reviewed-on: https://go-review.googlesource.com/37039
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/net/iprawsock.go
src/net/rawconn.go [new file with mode: 0644]
src/net/rawconn_test.go [new file with mode: 0644]
src/net/tcpsock.go
src/net/udpsock.go
src/net/unixsock.go

index d69a303d786b7784f9209cac12e6665894e4f91b..408d63fb346288956f031b9d4c1c46612d10b389 100644 (file)
@@ -93,6 +93,15 @@ type IPConn struct {
        conn
 }
 
+// SyscallConn returns a raw network connection.
+// This implements the syscall.Conn interface.
+func (c *IPConn) SyscallConn() (syscall.RawConn, error) {
+       if !c.ok() {
+               return nil, syscall.EINVAL
+       }
+       return newRawConn(c.fd)
+}
+
 // ReadFromIP reads an IP packet from c, copying the payload into b.
 // It returns the number of bytes copied into b and the return address
 // that was on the packet.
diff --git a/src/net/rawconn.go b/src/net/rawconn.go
new file mode 100644 (file)
index 0000000..486a5e7
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright 2017 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 (
+       "runtime"
+       "syscall"
+)
+
+// BUG(mikio): On NaCl, Plan 9 and Windows, the Control, Read and
+// Write methods of syscall.RawConn are not implemented.
+
+type rawConn struct {
+       fd *netFD
+}
+
+func (c *rawConn) ok() bool { return c != nil && c.fd != nil }
+
+func (c *rawConn) Control(f func(uintptr)) error {
+       if !c.ok() {
+               return syscall.EINVAL
+       }
+       err := c.fd.pfd.RawControl(f)
+       runtime.KeepAlive(c.fd)
+       if err != nil {
+               err = &OpError{Op: "raw-control", Net: c.fd.net, Source: nil, Addr: c.fd.laddr, Err: err}
+       }
+       return err
+}
+
+func (c *rawConn) Read(f func(uintptr) bool) error {
+       if !c.ok() {
+               return syscall.EINVAL
+       }
+       err := c.fd.pfd.RawRead(f)
+       runtime.KeepAlive(c.fd)
+       if err != nil {
+               err = &OpError{Op: "raw-read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+       }
+       return err
+}
+
+func (c *rawConn) Write(f func(uintptr) bool) error {
+       if !c.ok() {
+               return syscall.EINVAL
+       }
+       err := c.fd.pfd.RawWrite(f)
+       runtime.KeepAlive(c.fd)
+       if err != nil {
+               err = &OpError{Op: "raw-write", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
+       }
+       return err
+}
+
+func newRawConn(fd *netFD) (*rawConn, error) {
+       return &rawConn{fd: fd}, nil
+}
diff --git a/src/net/rawconn_test.go b/src/net/rawconn_test.go
new file mode 100644 (file)
index 0000000..294249b
--- /dev/null
@@ -0,0 +1,94 @@
+// Copyright 2017 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.
+
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+
+package net
+
+import (
+       "bytes"
+       "syscall"
+       "testing"
+)
+
+func TestRawConn(t *testing.T) {
+       handler := func(ls *localServer, ln Listener) {
+               c, err := ln.Accept()
+               if err != nil {
+                       t.Error(err)
+                       return
+               }
+               defer c.Close()
+               var b [32]byte
+               n, err := c.Read(b[:])
+               if err != nil {
+                       t.Error(err)
+                       return
+               }
+               if _, err := c.Write(b[:n]); err != nil {
+                       t.Error(err)
+                       return
+               }
+       }
+       ls, err := newLocalServer("tcp")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer ls.teardown()
+       if err := ls.buildup(handler); err != nil {
+               t.Fatal(err)
+       }
+
+       c, err := Dial(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer c.Close()
+       cc, err := c.(*TCPConn).SyscallConn()
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       var operr error
+       data := []byte("HELLO-R-U-THERE")
+       err = cc.Write(func(s uintptr) bool {
+               _, operr = syscall.Write(int(s), data)
+               if operr == syscall.EAGAIN {
+                       return false
+               }
+               return true
+       })
+       if err != nil || operr != nil {
+               t.Fatal(err, operr)
+       }
+
+       var nr int
+       var b [32]byte
+       err = cc.Read(func(s uintptr) bool {
+               nr, operr = syscall.Read(int(s), b[:])
+               if operr == syscall.EAGAIN {
+                       return false
+               }
+               return true
+       })
+       if err != nil || operr != nil {
+               t.Fatal(err, operr)
+       }
+       if bytes.Compare(b[:nr], data) != 0 {
+               t.Fatalf("got %#v; want %#v", b[:nr], data)
+       }
+
+       fn := func(s uintptr) {
+               operr = syscall.SetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
+       }
+       err = cc.Control(fn)
+       if err != nil || operr != nil {
+               t.Fatal(err, operr)
+       }
+       c.Close()
+       err = cc.Control(fn)
+       if err == nil {
+               t.Fatal("should fail")
+       }
+}
index a544a5b3c32c27370018f3fc424cdec597583518..91571bd25c0bc28cfbcce44ede00e5d2f8089689 100644 (file)
@@ -80,6 +80,15 @@ type TCPConn struct {
        conn
 }
 
+// SyscallConn returns a raw network connection.
+// This implements the syscall.Conn interface.
+func (c *TCPConn) SyscallConn() (syscall.RawConn, error) {
+       if !c.ok() {
+               return nil, syscall.EINVAL
+       }
+       return newRawConn(c.fd)
+}
+
 // ReadFrom implements the io.ReaderFrom ReadFrom method.
 func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
        if !c.ok() {
index 841ef5335909d589b8e2479067d95edb77a6c384..33959d56307428a889348cb003631c05adc9905f 100644 (file)
@@ -83,6 +83,15 @@ type UDPConn struct {
        conn
 }
 
+// SyscallConn returns a raw network connection.
+// This implements the syscall.Conn interface.
+func (c *UDPConn) SyscallConn() (syscall.RawConn, error) {
+       if !c.ok() {
+               return nil, syscall.EINVAL
+       }
+       return newRawConn(c.fd)
+}
+
 // ReadFromUDP reads a UDP packet from c, copying the payload into b.
 // It returns the number of bytes copied into b and the return address
 // that was on the packet.
index d29514e47b32075d58b697752f444a0a432a8f65..2485bab709d69b9accf258cd1bbb8bcabe000ec3 100644 (file)
@@ -60,6 +60,15 @@ type UnixConn struct {
        conn
 }
 
+// SyscallConn returns a raw network connection.
+// This implements the syscall.Conn interface.
+func (c *UnixConn) SyscallConn() (syscall.RawConn, error) {
+       if !c.ok() {
+               return nil, syscall.EINVAL
+       }
+       return newRawConn(c.fd)
+}
+
 // CloseRead shuts down the reading side of the Unix domain connection.
 // Most callers should just use Close.
 func (c *UnixConn) CloseRead() error {