This change adds the syscall.Conn interface to Listener types, with the caveat that only RawConn.Control is supported. Custom socket options can now be set safely.
Updates #19435
Fixes #22065
Change-Id: I7e74780d00318dc54a923d1c628a18a36009acab
Reviewed-on: https://go-review.googlesource.com/71651
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
func newRawConn(fd *netFD) (*rawConn, error) {
return &rawConn{fd: fd}, nil
}
+
+type rawListener struct {
+ rawConn
+}
+
+func (l *rawListener) Read(func(uintptr) bool) error {
+ return syscall.EINVAL
+}
+
+func (l *rawListener) Write(func(uintptr) bool) error {
+ return syscall.EINVAL
+}
+
+func newRawListener(fd *netFD) (*rawListener, error) {
+ return &rawListener{rawConn{fd: fd}}, nil
+}
t.Fatal("should fail")
}
}
+
+func TestRawConnListener(t *testing.T) {
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ cc, err := ln.(*TCPListener).SyscallConn()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ called := false
+ op := func(uintptr) bool {
+ called = true
+ return true
+ }
+
+ err = cc.Write(op)
+ if err == nil {
+ t.Error("Write should return an error")
+ }
+ if called {
+ t.Error("Write shouldn't call op")
+ }
+
+ called = false
+ err = cc.Read(op)
+ if err == nil {
+ t.Error("Read should return an error")
+ }
+ if called {
+ t.Error("Read shouldn't call op")
+ }
+
+ var operr error
+ fn := func(s uintptr) {
+ _, operr = syscall.GetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR)
+ }
+ err = cc.Control(fn)
+ if err != nil || operr != nil {
+ t.Fatal(err, operr)
+ }
+ ln.Close()
+ err = cc.Control(fn)
+ if err == nil {
+ t.Fatal("Control after Close should fail")
+ }
+}
import (
"syscall"
"testing"
+ "unsafe"
)
func TestRawConn(t *testing.T) {
t.Fatal("should fail")
}
}
+
+func TestRawConnListener(t *testing.T) {
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ cc, err := ln.(*TCPListener).SyscallConn()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ called := false
+ op := func(uintptr) bool {
+ called = true
+ return true
+ }
+
+ err = cc.Write(op)
+ if err == nil {
+ t.Error("Write should return an error")
+ }
+ if called {
+ t.Error("Write shouldn't call op")
+ }
+
+ called = false
+ err = cc.Read(op)
+ if err == nil {
+ t.Error("Read should return an error")
+ }
+ if called {
+ t.Error("Read shouldn't call op")
+ }
+
+ var operr error
+ fn := func(s uintptr) {
+ var v, l int32
+ l = int32(unsafe.Sizeof(v))
+ operr = syscall.Getsockopt(syscall.Handle(s), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, (*byte)(unsafe.Pointer(&v)), &l)
+ }
+ err = cc.Control(fn)
+ if err != nil || operr != nil {
+ t.Fatal(err, operr)
+ }
+ ln.Close()
+ err = cc.Control(fn)
+ if err == nil {
+ t.Fatal("Control after Close should fail")
+ }
+}
fd *netFD
}
+// SyscallConn returns a raw network connection.
+// This implements the syscall.Conn interface.
+//
+// The returned RawConn only supports calling Control. Read and
+// Write return an error.
+func (l *TCPListener) SyscallConn() (syscall.RawConn, error) {
+ if !l.ok() {
+ return nil, syscall.EINVAL
+ }
+ return newRawListener(l.fd)
+}
+
// AcceptTCP accepts the next incoming call and returns the new
// connection.
func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
func (ln *UnixListener) ok() bool { return ln != nil && ln.fd != nil }
+// SyscallConn returns a raw network connection.
+// This implements the syscall.Conn interface.
+//
+// The returned RawConn only supports calling Control. Read and
+// Write return an error.
+func (l *UnixListener) SyscallConn() (syscall.RawConn, error) {
+ if !l.ok() {
+ return nil, syscall.EINVAL
+ }
+ return newRawListener(l.fd)
+}
+
// AcceptUnix accepts the next incoming call and returns the new
// connection.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) {