From 366bb678aa0281ca2920e38ace9d695474a61797 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Tue, 14 Feb 2017 06:38:10 +0900 Subject: [PATCH] net: make {TCP,UDP,IP,Unix}Conn compliant of syscall.Conn interface 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 TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/net/iprawsock.go | 9 ++++ src/net/rawconn.go | 59 ++++++++++++++++++++++++++ src/net/rawconn_test.go | 94 +++++++++++++++++++++++++++++++++++++++++ src/net/tcpsock.go | 9 ++++ src/net/udpsock.go | 9 ++++ src/net/unixsock.go | 9 ++++ 6 files changed, 189 insertions(+) create mode 100644 src/net/rawconn.go create mode 100644 src/net/rawconn_test.go diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go index d69a303d78..408d63fb34 100644 --- a/src/net/iprawsock.go +++ b/src/net/iprawsock.go @@ -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 index 0000000000..486a5e7d9d --- /dev/null +++ b/src/net/rawconn.go @@ -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 index 0000000000..294249ba5d --- /dev/null +++ b/src/net/rawconn_test.go @@ -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") + } +} diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go index a544a5b3c3..91571bd25c 100644 --- a/src/net/tcpsock.go +++ b/src/net/tcpsock.go @@ -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() { diff --git a/src/net/udpsock.go b/src/net/udpsock.go index 841ef53359..33959d5630 100644 --- a/src/net/udpsock.go +++ b/src/net/udpsock.go @@ -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. diff --git a/src/net/unixsock.go b/src/net/unixsock.go index d29514e47b..2485bab709 100644 --- a/src/net/unixsock.go +++ b/src/net/unixsock.go @@ -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 { -- 2.50.0