Skip to content

Commit

Permalink
net: make {TCP,Unix}Listener implement syscall.Conn
Browse files Browse the repository at this point in the history
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 <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Ian Lance Taylor <[email protected]>
  • Loading branch information
lmb authored and ianlancetaylor committed Oct 25, 2017
1 parent ff4ee88 commit eed308d
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 0 deletions.
16 changes: 16 additions & 0 deletions src/net/rawconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
50 changes: 50 additions & 0 deletions src/net/rawconn_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
53 changes: 53 additions & 0 deletions src/net/rawconn_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package net
import (
"syscall"
"testing"
"unsafe"
)

func TestRawConn(t *testing.T) {
Expand Down Expand Up @@ -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")
}
}
12 changes: 12 additions & 0 deletions src/net/tcpsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
12 changes: 12 additions & 0 deletions src/net/unixsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit eed308d

Please sign in to comment.