mirror of
https://github.com/golang/go
synced 2024-11-18 10:04:43 -07:00
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 <mikioh.mikioh@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
0a09c72c2e
commit
366bb678aa
@ -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.
|
||||
|
59
src/net/rawconn.go
Normal file
59
src/net/rawconn.go
Normal file
@ -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
|
||||
}
|
94
src/net/rawconn_test.go
Normal file
94
src/net/rawconn_test.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user