Skip to content

Commit

Permalink
net, internal/syscall/unix: add SocketConn, SocketPacketConn
Browse files Browse the repository at this point in the history
FileConn and FilePacketConn APIs accept user-configured socket
descriptors to make them work together with runtime-integrated network
poller, but there's a limitation. The APIs reject protocol sockets that
are not supported by standard library. It's very hard for the net,
syscall packages to look after all platform, feature-specific sockets.

This change allows various platform, feature-specific socket descriptors
to use runtime-integrated network poller by using SocketConn,
SocketPacketConn APIs that bridge between the net, syscall packages and
platforms.

New exposed APIs:
pkg net, func SocketConn(*os.File, SocketAddr) (Conn, error)
pkg net, func SocketPacketConn(*os.File, SocketAddr) (PacketConn, error)
pkg net, type SocketAddr interface { Addr, Raw }
pkg net, type SocketAddr interface, Addr([]uint8) Addr
pkg net, type SocketAddr interface, Raw(Addr) []uint8

Fixes #10565.

Change-Id: Iec57499b3d84bb5cb0bcf3f664330c535eec11e3
Reviewed-on: https://go-review.googlesource.com/9275
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
cixtor committed May 13, 2015
1 parent 08ba7db commit 6f7961d
Show file tree
Hide file tree
Showing 14 changed files with 618 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/go/build/deps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ var pkgDeps = map[string][]string{
// Basic networking.
// Because net must be used by any package that wants to
// do networking portably, it must have a small dependency set: just L1+basic os.
"net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows", "internal/singleflight"},
"net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/unix", "internal/syscall/windows", "internal/singleflight"},

// NET enables use of basic network-related packages.
"NET": {
Expand Down
39 changes: 39 additions & 0 deletions src/internal/syscall/unix/socket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2015 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 nacl netbsd openbsd solaris

package unix

// Getsockname copies the binary encoding of the current address for s
// into addr.
func Getsockname(s int, addr []byte) error {
return getsockname(s, addr)
}

// Getpeername copies the binary encoding of the peer address for s
// into addr.
func Getpeername(s int, addr []byte) error {
return getpeername(s, addr)
}

var emptyPayload uintptr

// Recvfrom receives a message from s, copying the message into b.
// The socket address addr must be large enough for storing the source
// address of the message.
// Flags must be operation control flags or 0.
// It retunrs the number of bytes copied into b.
func Recvfrom(s int, b []byte, flags int, addr []byte) (int, error) {
return recvfrom(s, b, flags, addr)
}

// Sendto sends a message to the socket address addr, copying the
// message from b.
// The socket address addr must be suitable for s.
// Flags must be operation control flags or 0.
// It retunrs the number of bytes copied from b.
func Sendto(s int, b []byte, flags int, addr []byte) (int, error) {
return sendto(s, b, flags, addr)
}
67 changes: 67 additions & 0 deletions src/internal/syscall/unix/socket_linux_386.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2015 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 unix

import (
"syscall"
"unsafe"
)

const (
sysGETSOCKNAME = 0x6
sysGETPEERNAME = 0x7
sysSENDTO = 0xb
sysRECVFROM = 0xc
)

func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)

func getsockname(s int, addr []byte) error {
l := uint32(len(addr))
_, errno := rawsocketcall(sysGETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0)
if errno != 0 {
return error(errno)
}
return nil
}

func getpeername(s int, addr []byte) error {
l := uint32(len(addr))
_, errno := rawsocketcall(sysGETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0)
if errno != 0 {
return error(errno)
}
return nil
}

func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
var p unsafe.Pointer
if len(b) > 0 {
p = unsafe.Pointer(&b[0])
} else {
p = unsafe.Pointer(&emptyPayload)
}
l := uint32(len(from))
n, errno := socketcall(sysRECVFROM, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&from[0])), uintptr(unsafe.Pointer(&l)))
if errno != 0 {
return int(n), error(errno)
}
return int(n), nil
}

func sendto(s int, b []byte, flags int, to []byte) (int, error) {
var p unsafe.Pointer
if len(b) > 0 {
p = unsafe.Pointer(&b[0])
} else {
p = unsafe.Pointer(&emptyPayload)
}
n, errno := socketcall(sysSENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to)))
if errno != 0 {
return int(n), error(errno)
}
return int(n), nil
}
11 changes: 11 additions & 0 deletions src/internal/syscall/unix/socket_linux_386.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2015 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·socketcall(SB)
25 changes: 25 additions & 0 deletions src/internal/syscall/unix/socket_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2015 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 solaris

package unix

import "syscall"

func getsockname(s int, addr []byte) error {
return syscall.EOPNOTSUPP
}

func getpeername(s int, addr []byte) error {
return syscall.EOPNOTSUPP
}

func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
return 0, syscall.EOPNOTSUPP
}

func sendto(s int, b []byte, flags int, to []byte) (int, error) {
return 0, syscall.EOPNOTSUPP
}
59 changes: 59 additions & 0 deletions src/internal/syscall/unix/socket_unix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2015 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 unix

import (
"syscall"
"unsafe"
)

func getsockname(s int, addr []byte) error {
l := uint32(len(addr))
_, _, errno := syscall.RawSyscall(syscall.SYS_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)))
if errno != 0 {
return error(errno)
}
return nil
}

func getpeername(s int, addr []byte) error {
l := uint32(len(addr))
_, _, errno := syscall.RawSyscall(syscall.SYS_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)))
if errno != 0 {
return error(errno)
}
return nil
}

func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
var p unsafe.Pointer
if len(b) > 0 {
p = unsafe.Pointer(&b[0])
} else {
p = unsafe.Pointer(&emptyPayload)
}
l := uint32(len(from))
n, _, errno := syscall.Syscall6(syscall.SYS_RECVFROM, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&from[0])), uintptr(unsafe.Pointer(&l)))
if errno != 0 {
return int(n), error(errno)
}
return int(n), nil
}

func sendto(s int, b []byte, flags int, to []byte) (int, error) {
var p unsafe.Pointer
if len(b) > 0 {
p = unsafe.Pointer(&b[0])
} else {
p = unsafe.Pointer(&emptyPayload)
}
n, _, errno := syscall.Syscall6(syscall.SYS_SENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to)))
if errno != 0 {
return int(n), error(errno)
}
return int(n), nil
}
51 changes: 51 additions & 0 deletions src/net/fd_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package net

import (
"internal/syscall/unix"
"io"
"os"
"runtime"
Expand Down Expand Up @@ -270,6 +271,33 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
return
}

func (fd *netFD) recvFrom(b []byte, flags int, from []byte) (n int, err error) {
if err := fd.readLock(); err != nil {
return 0, err
}
defer fd.readUnlock()
if err := fd.pd.PrepareRead(); err != nil {
return 0, err
}
for {
n, err = unix.Recvfrom(fd.sysfd, b, flags, from)
if err != nil {
n = 0
if err == syscall.EAGAIN {
if err = fd.pd.WaitRead(); err == nil {
continue
}
}
}
err = fd.eofError(n, err)
break
}
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("recvfrom", err)
}
return
}

func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
if err := fd.readLock(); err != nil {
return 0, 0, 0, nil, err
Expand Down Expand Up @@ -359,6 +387,29 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
return
}

func (fd *netFD) sendTo(b []byte, flags int, to []byte) (n int, err error) {
if err := fd.writeLock(); err != nil {
return 0, err
}
defer fd.writeUnlock()
if err := fd.pd.PrepareWrite(); err != nil {
return 0, err
}
for {
n, err = unix.Sendto(fd.sysfd, b, flags, to)
if err == syscall.EAGAIN {
if err = fd.pd.WaitWrite(); err == nil {
continue
}
}
break
}
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("sendto", err)
}
return
}

func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
if err := fd.writeLock(); err != nil {
return 0, 0, err
Expand Down
44 changes: 44 additions & 0 deletions src/net/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,47 @@ func FilePacketConn(f *os.File) (c PacketConn, err error) {
}
return
}

// A SocketAddr is used with SocketConn or SocketPacketConn to
// implement a user-configured socket address.
// The net package does not provide any implementations of SocketAddr;
// the caller of SocketConn or SocketPacketConn is expected to provide
// one.
type SocketAddr interface {
// Addr takes a platform-specific socket address and returns
// a net.Addr. The result may be nil when the syscall package,
// system call or underlying protocol does not support the
// socket address.
Addr([]byte) Addr

// Raw takes a net.Addr and returns a platform-specific socket
// address. The result may be nil when the syscall package,
// system call or underlying protocol does not support the
// socket address.
Raw(Addr) []byte
}

// SocketConn returns a copy of the network connection corresponding
// to the open file f and user-defined socket address sa.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
func SocketConn(f *os.File, sa SocketAddr) (c Conn, err error) {
c, err = socketConn(f, sa)
if err != nil {
err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
}
return
}

// SocketPacketConn returns a copy of the packet network connection
// corresponding to the open file f and user-defined socket address
// sa.
// It is the caller's responsibility to close f when finished.
// Closing c does not affect f, and closing f does not affect c.
func SocketPacketConn(f *os.File, sa SocketAddr) (c PacketConn, err error) {
c, err = socketPacketConn(f, sa)
if err != nil {
err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
}
return
}
Loading

0 comments on commit 6f7961d

Please sign in to comment.