Commit 50e760fb authored by Mikio Hara's avatar Mikio Hara

internal/socket: new package

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/37035Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent d8bd24be
// 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
}
// 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
}
// 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)
}
// 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))
}
// 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)
}
// 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)
}
}
// 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
}
}
// 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)
}
// 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)
// 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"
)
//go:cgo_import_dynamic libc___xnet_getsockopt __xnet_getsockopt "libsocket.so"
//go:cgo_import_dynamic libc_setsockopt setsockopt "libsocket.so"
//go:linkname procGetsockopt libc___xnet_getsockopt
//go:linkname procSetsockopt libc_setsockopt
var (
procGetsockopt uintptr
procSetsockopt uintptr
)
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, uintptr, syscall.Errno)
func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (uintptr, uintptr, syscall.Errno)
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
l := uint32(len(b))
_, _, errno := sysvicall6(uintptr(unsafe.Pointer(&procGetsockopt)), 5, 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 := sysvicall6(uintptr(unsafe.Pointer(&procSetsockopt)), 5, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
return errnoErr(errno)
}
// Copyright 2016 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 ·sysvicall6(SB),NOSPLIT,$0-88
JMP syscall·sysvicall6(SB)
TEXT ·rawSysvicall6(SB),NOSPLIT,$0-88
JMP syscall·rawSysvicall6(SB)
// 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 nacl plan9
package socket
import "errors"
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
return 0, errors.New("not implemented")
}
func setsockopt(s uintptr, level, name int, b []byte) error {
return errors.New("not implemented")
}
// 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,!386 netbsd openbsd
package socket
import (
"syscall"
"unsafe"
)
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
l := uint32(len(b))
_, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, 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 := syscall.Syscall6(syscall.SYS_SETSOCKOPT, s, uintptr(level), uintptr(name), uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), 0)
return errnoErr(errno)
}
// 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"
)
func getsockopt(s uintptr, level, name int, b []byte) (int, error) {
l := uint32(len(b))
err := syscall.Getsockopt(syscall.Handle(s), int32(level), int32(name), (*byte)(unsafe.Pointer(&b[0])), (*int32)(unsafe.Pointer(&l)))
return int(l), err
}
func setsockopt(s uintptr, level, name int, b []byte) error {
return syscall.Setsockopt(syscall.Handle(s), int32(level), int32(name), (*byte)(unsafe.Pointer(&b[0])), int32(len(b)))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment