-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a counterpart of https://go-review.googlesource.com/37039. This change introduces a package that provides a portable interface for the manipulation of sockets using either syscall.Conn and syscall.RawConn interfaces or the internal/netreflect package appropriately. The package ensures that a package using this works with all supported versions of the Go standard library. Updates golang/go#19051. Change-Id: Ib72ea369e6839e77fed6e35b9aedc364e73c51cb Reviewed-on: https://go-review.googlesource.com/37035 Reviewed-by: Ian Lance Taylor <iant@golang.org>
- Loading branch information
Showing
14 changed files
with
464 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// 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 socket | ||
|
||
import "syscall" | ||
|
||
var ( | ||
errEAGAIN error = syscall.EAGAIN | ||
errEINVAL error = syscall.EINVAL | ||
errENOENT error = syscall.ENOENT | ||
) | ||
|
||
// errnoErr returns common boxed Errno values, to prevent allocations | ||
// at runtime. | ||
func errnoErr(errno syscall.Errno) error { | ||
switch errno { | ||
case 0: | ||
return nil | ||
case syscall.EAGAIN: | ||
return errEAGAIN | ||
case syscall.EINVAL: | ||
return errEINVAL | ||
case syscall.ENOENT: | ||
return errENOENT | ||
} | ||
return errno | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// 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 socket | ||
|
||
import "syscall" | ||
|
||
var ( | ||
errERROR_IO_PENDING error = syscall.ERROR_IO_PENDING | ||
errEINVAL error = syscall.EINVAL | ||
) | ||
|
||
// errnoErr returns common boxed Errno values, to prevent allocations | ||
// at runtime. | ||
func errnoErr(errno syscall.Errno) error { | ||
switch errno { | ||
case 0: | ||
return nil | ||
case syscall.ERROR_IO_PENDING: | ||
return errERROR_IO_PENDING | ||
case syscall.EINVAL: | ||
return errEINVAL | ||
} | ||
return errno | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// 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 go1.9 | ||
|
||
package socket | ||
|
||
import ( | ||
"errors" | ||
"net" | ||
"os" | ||
"syscall" | ||
) | ||
|
||
// A Conn represents a raw connection. | ||
type Conn struct { | ||
network string | ||
c syscall.RawConn | ||
} | ||
|
||
// NewConn returns a new raw connection. | ||
func NewConn(c net.Conn) (*Conn, error) { | ||
var err error | ||
var cc Conn | ||
switch c := c.(type) { | ||
case *net.TCPConn: | ||
cc.network = "tcp" | ||
cc.c, err = c.SyscallConn() | ||
case *net.UDPConn: | ||
cc.network = "udp" | ||
cc.c, err = c.SyscallConn() | ||
case *net.IPConn: | ||
cc.network = "ip" | ||
cc.c, err = c.SyscallConn() | ||
default: | ||
return nil, errors.New("unknown connection type") | ||
} | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &cc, nil | ||
} | ||
|
||
func (o *Option) get(c *Conn, b []byte) (int, error) { | ||
var operr error | ||
var n int | ||
fn := func(s uintptr) { | ||
n, operr = getsockopt(s, o.Level, o.Name, b) | ||
} | ||
if err := c.c.Control(fn); err != nil { | ||
return 0, err | ||
} | ||
return n, os.NewSyscallError("getsockopt", operr) | ||
} | ||
|
||
func (o *Option) set(c *Conn, b []byte) error { | ||
var operr error | ||
fn := func(s uintptr) { | ||
operr = setsockopt(s, o.Level, o.Name, b) | ||
} | ||
if err := c.c.Control(fn); err != nil { | ||
return err | ||
} | ||
return os.NewSyscallError("setsockopt", operr) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// 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 !go1.9 | ||
|
||
package socket | ||
|
||
import ( | ||
"net" | ||
"os" | ||
|
||
"golang.org/x/net/internal/netreflect" | ||
) | ||
|
||
// A Conn represents a raw connection. | ||
type Conn struct { | ||
c net.Conn | ||
} | ||
|
||
// NewConn returns a new raw connection. | ||
func NewConn(c net.Conn) (*Conn, error) { | ||
return &Conn{c: c}, nil | ||
} | ||
|
||
func (o *Option) get(c *Conn, b []byte) (int, error) { | ||
s, err := netreflect.SocketOf(c.c) | ||
if err != nil { | ||
return 0, err | ||
} | ||
n, err := getsockopt(s, o.Level, o.Name, b) | ||
return n, os.NewSyscallError("getsockopt", err) | ||
} | ||
|
||
func (o *Option) set(c *Conn, b []byte) error { | ||
s, err := netreflect.SocketOf(c.c) | ||
if err != nil { | ||
return err | ||
} | ||
return os.NewSyscallError("setsockopt", setsockopt(s, o.Level, o.Name, b)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// 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 socket provides a portable interface for socket system | ||
// calls. | ||
package socket // import "golang.org/x/net/internal/socket" | ||
|
||
import "errors" | ||
|
||
// An Option represents a sticky socket option. | ||
type Option struct { | ||
Level int // level | ||
Name int // name; must be equal or greater than 1 | ||
Len int // length of value in bytes; must be equal or greater than 1 | ||
} | ||
|
||
// Get reads a value for the option from the kernel. | ||
// It returns the number of bytes written into b. | ||
func (o *Option) Get(c *Conn, b []byte) (int, error) { | ||
if o.Name < 1 || o.Len < 1 { | ||
return 0, errors.New("invalid option") | ||
} | ||
if len(b) < o.Len { | ||
return 0, errors.New("short buffer") | ||
} | ||
return o.get(c, b) | ||
} | ||
|
||
// GetInt returns an integer value for the option. | ||
// | ||
// The Len field of Option must be either 1 or 4. | ||
func (o *Option) GetInt(c *Conn) (int, error) { | ||
if o.Len != 1 && o.Len != 4 { | ||
return 0, errors.New("invalid option") | ||
} | ||
var b []byte | ||
var bb [4]byte | ||
if o.Len == 1 { | ||
b = bb[:1] | ||
} else { | ||
b = bb[:4] | ||
} | ||
n, err := o.get(c, b) | ||
if err != nil { | ||
return 0, err | ||
} | ||
if n != o.Len { | ||
return 0, errors.New("invalid option length") | ||
} | ||
if o.Len == 1 { | ||
return int(b[0]), nil | ||
} | ||
return int(NativeEndian.Uint32(b[:4])), nil | ||
} | ||
|
||
// Set writes the option and value to the kernel. | ||
func (o *Option) Set(c *Conn, b []byte) error { | ||
if o.Name < 1 || o.Len < 1 { | ||
return errors.New("invalid option") | ||
} | ||
if len(b) < o.Len { | ||
return errors.New("short buffer") | ||
} | ||
return o.set(c, b) | ||
} | ||
|
||
// SetInt writes the option and value to the kernel. | ||
// | ||
// The Len field of Option must be either 1 or 4. | ||
func (o *Option) SetInt(c *Conn, v int) error { | ||
if o.Len != 1 && o.Len != 4 { | ||
return errors.New("invalid option") | ||
} | ||
var b []byte | ||
if o.Len == 1 { | ||
b = []byte{byte(v)} | ||
} else { | ||
var bb [4]byte | ||
NativeEndian.PutUint32(bb[:o.Len], uint32(v)) | ||
b = bb[:4] | ||
} | ||
return o.set(c, b) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// 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 windows | ||
|
||
package socket_test | ||
|
||
import ( | ||
"net" | ||
"runtime" | ||
"syscall" | ||
"testing" | ||
|
||
"golang.org/x/net/internal/nettest" | ||
"golang.org/x/net/internal/socket" | ||
) | ||
|
||
func TestSocket(t *testing.T) { | ||
t.Run("Option", func(t *testing.T) { | ||
testSocketOption(t, &socket.Option{Level: syscall.SOL_SOCKET, Name: syscall.SO_RCVBUF, Len: 4}) | ||
}) | ||
} | ||
|
||
func testSocketOption(t *testing.T, so *socket.Option) { | ||
c, err := nettest.NewLocalPacketListener("udp") | ||
if err != nil { | ||
t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err) | ||
} | ||
defer c.Close() | ||
cc, err := socket.NewConn(c.(net.Conn)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
const N = 2048 | ||
if err := so.SetInt(cc, N); err != nil { | ||
t.Fatal(err) | ||
} | ||
n, err := so.GetInt(cc) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if n < N { | ||
t.Fatalf("got %d; want greater than or equal to %d", n, N) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// 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 socket | ||
|
||
import ( | ||
"encoding/binary" | ||
"unsafe" | ||
) | ||
|
||
// NativeEndian is the machine native endian implementation of | ||
// ByteOrder. | ||
var NativeEndian binary.ByteOrder | ||
|
||
func init() { | ||
i := uint32(1) | ||
b := (*[4]byte)(unsafe.Pointer(&i)) | ||
if b[0] == 1 { | ||
NativeEndian = binary.LittleEndian | ||
} else { | ||
NativeEndian = binary.BigEndian | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// 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 socket | ||
|
||
import ( | ||
"syscall" | ||
"unsafe" | ||
) | ||
|
||
const ( | ||
sysSETSOCKOPT = 0xe | ||
sysGETSOCKOPT = 0xf | ||
) | ||
|
||
func socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) | ||
func rawsocketcall(call, a0, a1, a2, a3, a4, a5 uintptr) (uintptr, syscall.Errno) | ||
|
||
func getsockopt(s uintptr, level, name int, b []byte) (int, error) { | ||
l := uint32(len(b)) | ||
_, errno := socketcall(sysGETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(unsafe.Pointer(&l)), 0) | ||
return int(l), errnoErr(errno) | ||
} | ||
|
||
func setsockopt(s uintptr, level, name int, b []byte) error { | ||
_, errno := socketcall(sysSETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0) | ||
return errnoErr(errno) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright 2014 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. | ||
|
||
#include "textflag.h" | ||
|
||
TEXT ·socketcall(SB),NOSPLIT,$0-36 | ||
JMP syscall·socketcall(SB) | ||
|
||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36 | ||
JMP syscall·rawsocketcall(SB) |
Oops, something went wrong.