Skip to content

Commit 6f7961d

Browse files
committed
net, internal/syscall/unix: add SocketConn, SocketPacketConn
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 <[email protected]>
1 parent 08ba7db commit 6f7961d

14 files changed

+618
-54
lines changed

src/go/build/deps_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ var pkgDeps = map[string][]string{
244244
// Basic networking.
245245
// Because net must be used by any package that wants to
246246
// do networking portably, it must have a small dependency set: just L1+basic os.
247-
"net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows", "internal/singleflight"},
247+
"net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/unix", "internal/syscall/windows", "internal/singleflight"},
248248

249249
// NET enables use of basic network-related packages.
250250
"NET": {

src/internal/syscall/unix/socket.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
6+
7+
package unix
8+
9+
// Getsockname copies the binary encoding of the current address for s
10+
// into addr.
11+
func Getsockname(s int, addr []byte) error {
12+
return getsockname(s, addr)
13+
}
14+
15+
// Getpeername copies the binary encoding of the peer address for s
16+
// into addr.
17+
func Getpeername(s int, addr []byte) error {
18+
return getpeername(s, addr)
19+
}
20+
21+
var emptyPayload uintptr
22+
23+
// Recvfrom receives a message from s, copying the message into b.
24+
// The socket address addr must be large enough for storing the source
25+
// address of the message.
26+
// Flags must be operation control flags or 0.
27+
// It retunrs the number of bytes copied into b.
28+
func Recvfrom(s int, b []byte, flags int, addr []byte) (int, error) {
29+
return recvfrom(s, b, flags, addr)
30+
}
31+
32+
// Sendto sends a message to the socket address addr, copying the
33+
// message from b.
34+
// The socket address addr must be suitable for s.
35+
// Flags must be operation control flags or 0.
36+
// It retunrs the number of bytes copied from b.
37+
func Sendto(s int, b []byte, flags int, addr []byte) (int, error) {
38+
return sendto(s, b, flags, addr)
39+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package unix
6+
7+
import (
8+
"syscall"
9+
"unsafe"
10+
)
11+
12+
const (
13+
sysGETSOCKNAME = 0x6
14+
sysGETPEERNAME = 0x7
15+
sysSENDTO = 0xb
16+
sysRECVFROM = 0xc
17+
)
18+
19+
func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
20+
func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno)
21+
22+
func getsockname(s int, addr []byte) error {
23+
l := uint32(len(addr))
24+
_, errno := rawsocketcall(sysGETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0)
25+
if errno != 0 {
26+
return error(errno)
27+
}
28+
return nil
29+
}
30+
31+
func getpeername(s int, addr []byte) error {
32+
l := uint32(len(addr))
33+
_, errno := rawsocketcall(sysGETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0)
34+
if errno != 0 {
35+
return error(errno)
36+
}
37+
return nil
38+
}
39+
40+
func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
41+
var p unsafe.Pointer
42+
if len(b) > 0 {
43+
p = unsafe.Pointer(&b[0])
44+
} else {
45+
p = unsafe.Pointer(&emptyPayload)
46+
}
47+
l := uint32(len(from))
48+
n, errno := socketcall(sysRECVFROM, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&from[0])), uintptr(unsafe.Pointer(&l)))
49+
if errno != 0 {
50+
return int(n), error(errno)
51+
}
52+
return int(n), nil
53+
}
54+
55+
func sendto(s int, b []byte, flags int, to []byte) (int, error) {
56+
var p unsafe.Pointer
57+
if len(b) > 0 {
58+
p = unsafe.Pointer(&b[0])
59+
} else {
60+
p = unsafe.Pointer(&emptyPayload)
61+
}
62+
n, errno := socketcall(sysSENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to)))
63+
if errno != 0 {
64+
return int(n), error(errno)
65+
}
66+
return int(n), nil
67+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
#include "textflag.h"
6+
7+
TEXT ·socketcall(SB),NOSPLIT,$0-36
8+
JMP syscall·socketcall(SB)
9+
10+
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
11+
JMP syscall·socketcall(SB)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build nacl solaris
6+
7+
package unix
8+
9+
import "syscall"
10+
11+
func getsockname(s int, addr []byte) error {
12+
return syscall.EOPNOTSUPP
13+
}
14+
15+
func getpeername(s int, addr []byte) error {
16+
return syscall.EOPNOTSUPP
17+
}
18+
19+
func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
20+
return 0, syscall.EOPNOTSUPP
21+
}
22+
23+
func sendto(s int, b []byte, flags int, to []byte) (int, error) {
24+
return 0, syscall.EOPNOTSUPP
25+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build darwin dragonfly freebsd linux,!386 netbsd openbsd
6+
7+
package unix
8+
9+
import (
10+
"syscall"
11+
"unsafe"
12+
)
13+
14+
func getsockname(s int, addr []byte) error {
15+
l := uint32(len(addr))
16+
_, _, errno := syscall.RawSyscall(syscall.SYS_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)))
17+
if errno != 0 {
18+
return error(errno)
19+
}
20+
return nil
21+
}
22+
23+
func getpeername(s int, addr []byte) error {
24+
l := uint32(len(addr))
25+
_, _, errno := syscall.RawSyscall(syscall.SYS_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)))
26+
if errno != 0 {
27+
return error(errno)
28+
}
29+
return nil
30+
}
31+
32+
func recvfrom(s int, b []byte, flags int, from []byte) (int, error) {
33+
var p unsafe.Pointer
34+
if len(b) > 0 {
35+
p = unsafe.Pointer(&b[0])
36+
} else {
37+
p = unsafe.Pointer(&emptyPayload)
38+
}
39+
l := uint32(len(from))
40+
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)))
41+
if errno != 0 {
42+
return int(n), error(errno)
43+
}
44+
return int(n), nil
45+
}
46+
47+
func sendto(s int, b []byte, flags int, to []byte) (int, error) {
48+
var p unsafe.Pointer
49+
if len(b) > 0 {
50+
p = unsafe.Pointer(&b[0])
51+
} else {
52+
p = unsafe.Pointer(&emptyPayload)
53+
}
54+
n, _, errno := syscall.Syscall6(syscall.SYS_SENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to)))
55+
if errno != 0 {
56+
return int(n), error(errno)
57+
}
58+
return int(n), nil
59+
}

src/net/fd_unix.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package net
88

99
import (
10+
"internal/syscall/unix"
1011
"io"
1112
"os"
1213
"runtime"
@@ -270,6 +271,33 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
270271
return
271272
}
272273

274+
func (fd *netFD) recvFrom(b []byte, flags int, from []byte) (n int, err error) {
275+
if err := fd.readLock(); err != nil {
276+
return 0, err
277+
}
278+
defer fd.readUnlock()
279+
if err := fd.pd.PrepareRead(); err != nil {
280+
return 0, err
281+
}
282+
for {
283+
n, err = unix.Recvfrom(fd.sysfd, b, flags, from)
284+
if err != nil {
285+
n = 0
286+
if err == syscall.EAGAIN {
287+
if err = fd.pd.WaitRead(); err == nil {
288+
continue
289+
}
290+
}
291+
}
292+
err = fd.eofError(n, err)
293+
break
294+
}
295+
if _, ok := err.(syscall.Errno); ok {
296+
err = os.NewSyscallError("recvfrom", err)
297+
}
298+
return
299+
}
300+
273301
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
274302
if err := fd.readLock(); err != nil {
275303
return 0, 0, 0, nil, err
@@ -359,6 +387,29 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
359387
return
360388
}
361389

390+
func (fd *netFD) sendTo(b []byte, flags int, to []byte) (n int, err error) {
391+
if err := fd.writeLock(); err != nil {
392+
return 0, err
393+
}
394+
defer fd.writeUnlock()
395+
if err := fd.pd.PrepareWrite(); err != nil {
396+
return 0, err
397+
}
398+
for {
399+
n, err = unix.Sendto(fd.sysfd, b, flags, to)
400+
if err == syscall.EAGAIN {
401+
if err = fd.pd.WaitWrite(); err == nil {
402+
continue
403+
}
404+
}
405+
break
406+
}
407+
if _, ok := err.(syscall.Errno); ok {
408+
err = os.NewSyscallError("sendto", err)
409+
}
410+
return
411+
}
412+
362413
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
363414
if err := fd.writeLock(); err != nil {
364415
return 0, 0, err

src/net/file.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,47 @@ func FilePacketConn(f *os.File) (c PacketConn, err error) {
4646
}
4747
return
4848
}
49+
50+
// A SocketAddr is used with SocketConn or SocketPacketConn to
51+
// implement a user-configured socket address.
52+
// The net package does not provide any implementations of SocketAddr;
53+
// the caller of SocketConn or SocketPacketConn is expected to provide
54+
// one.
55+
type SocketAddr interface {
56+
// Addr takes a platform-specific socket address and returns
57+
// a net.Addr. The result may be nil when the syscall package,
58+
// system call or underlying protocol does not support the
59+
// socket address.
60+
Addr([]byte) Addr
61+
62+
// Raw takes a net.Addr and returns a platform-specific socket
63+
// address. The result may be nil when the syscall package,
64+
// system call or underlying protocol does not support the
65+
// socket address.
66+
Raw(Addr) []byte
67+
}
68+
69+
// SocketConn returns a copy of the network connection corresponding
70+
// to the open file f and user-defined socket address sa.
71+
// It is the caller's responsibility to close f when finished.
72+
// Closing c does not affect f, and closing f does not affect c.
73+
func SocketConn(f *os.File, sa SocketAddr) (c Conn, err error) {
74+
c, err = socketConn(f, sa)
75+
if err != nil {
76+
err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
77+
}
78+
return
79+
}
80+
81+
// SocketPacketConn returns a copy of the packet network connection
82+
// corresponding to the open file f and user-defined socket address
83+
// sa.
84+
// It is the caller's responsibility to close f when finished.
85+
// Closing c does not affect f, and closing f does not affect c.
86+
func SocketPacketConn(f *os.File, sa SocketAddr) (c PacketConn, err error) {
87+
c, err = socketPacketConn(f, sa)
88+
if err != nil {
89+
err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err}
90+
}
91+
return
92+
}

0 commit comments

Comments
 (0)