]> Cypherpunks repositories - gostls13.git/commitdiff
net: make {TCP,Unix}Listener implement syscall.Conn
authorLorenz Bauer <lmb@cloudflare.com>
Wed, 18 Oct 2017 10:15:04 +0000 (11:15 +0100)
committerIan Lance Taylor <iant@golang.org>
Wed, 25 Oct 2017 20:12:14 +0000 (20:12 +0000)
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>
src/net/rawconn.go
src/net/rawconn_unix_test.go
src/net/rawconn_windows_test.go
src/net/tcpsock.go
src/net/unixsock.go

index d67be644a34d165f43959505af8d1c0f054f6b3d..2399c9f31dd12de96a2a682d1e9a709e20e4134a 100644 (file)
@@ -60,3 +60,19 @@ func (c *rawConn) Write(f func(uintptr) bool) error {
 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
+}
index 294249ba5d1b9726c92cb48058c72fac59bb63d8..913ad8659510f531a9f2d91381361a63b6b45f4c 100644 (file)
@@ -92,3 +92,53 @@ 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) {
+               _, 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")
+       }
+}
index 5fb6de7539398ce9da15cec882ba1d5c2b42bec5..2ee12c35963d8f62cfcd6224b5f4e1e5fda546db 100644 (file)
@@ -7,6 +7,7 @@ package net
 import (
        "syscall"
        "testing"
+       "unsafe"
 )
 
 func TestRawConn(t *testing.T) {
@@ -34,3 +35,55 @@ 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")
+       }
+}
index e957aa3005a173e5950451ed25ef51c5d3423415..9528140b940ee108b095f3b379940f18e4a54db1 100644 (file)
@@ -225,6 +225,18 @@ type TCPListener struct {
        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) {
index 057940acf65f56009c6f7c4f4b6bc87c02d4c654..20326dabeaa9cf72da15fc325358fe64bc600eb6 100644 (file)
@@ -219,6 +219,18 @@ type UnixListener struct {
 
 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) {