Congratulations on opening your first change. Thank you for your contribution!
Next steps:
Within the next week or so, a maintainer will review your change and provide
feedback. See https://golang.org/doc/contribute.html#review for more info and
tips to get your patch through code review.
Most changes in the Go project go through a few rounds of revision. This can be
surprising to people new to the project. The careful, iterative review process
is our way of helping mentor contributors and ensuring that their contributions
have a lasting impact.
During May-July and Nov-Jan the Go project is in a code freeze, during which
little code gets reviewed or merged. If a reviewer responds with a comment like
R=go1.11, it means that this CL will be reviewed as part of the next development
cycle. See https://golang.org/s/release for more details.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Audrius Butkevicius has uploaded this change for review.
net: allow setting socket options before bind and connect
Existing implementation does not provide a way to set options such as
SO_REUSEPORT. Exposes ListenControl function and a new Control field on the
Dialer struct.
Fixes #9661
Change-Id: If4d275711f823df72d3ac5cc3858651a6a57cccb
---
M src/net/dial.go
M src/net/dial_test.go
M src/net/hook.go
M src/net/iprawsock.go
M src/net/iprawsock_posix.go
M src/net/ipsock_posix.go
M src/net/net.go
M src/net/sock_posix.go
M src/net/tcpsock.go
M src/net/tcpsock_plan9.go
M src/net/tcpsock_posix.go
M src/net/udpsock.go
M src/net/udpsock_posix.go
M src/net/unixsock.go
M src/net/unixsock_plan9.go
M src/net/unixsock_posix.go
16 files changed, 192 insertions(+), 55 deletions(-)
diff --git a/src/net/dial.go b/src/net/dial.go
index f8b4aa2..2b2e331 100644
--- a/src/net/dial.go
+++ b/src/net/dial.go
@@ -64,6 +64,10 @@
// Resolver optionally specifies an alternate resolver to use.
Resolver *Resolver
+ // Control optionally specifies a control function that is called after
+ // initialization of the socket, but before dialing.
+ Control ControlFunc
+
// Cancel is an optional channel whose closure indicates that
// the dial should be canceled. Not all types of dials support
// cancelation.
@@ -544,16 +548,16 @@
switch ra := ra.(type) {
case *TCPAddr:
la, _ := la.(*TCPAddr)
- c, err = dialTCP(ctx, dp.network, la, ra)
+ c, err = dialTCP(ctx, dp.network, la, ra, dp.Control)
case *UDPAddr:
la, _ := la.(*UDPAddr)
- c, err = dialUDP(ctx, dp.network, la, ra)
+ c, err = dialUDP(ctx, dp.network, la, ra, dp.Control)
case *IPAddr:
la, _ := la.(*IPAddr)
- c, err = dialIP(ctx, dp.network, la, ra)
+ c, err = dialIP(ctx, dp.network, la, ra, dp.Control)
case *UnixAddr:
la, _ := la.(*UnixAddr)
- c, err = dialUnix(ctx, dp.network, la, ra)
+ c, err = dialUnix(ctx, dp.network, la, ra, dp.Control)
default:
return nil, &OpError{Op: "dial", Net: dp.network, Source: la, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: dp.address}}
}
@@ -601,6 +605,29 @@
return l, nil
}
+// ListenControl announces on the local network address.
+//
+// See func Listen and type ControlFunc for a description of parameters.
+func ListenControl(network, address string, control ControlFunc) (Listener, error) {
+ addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil)
+ if err != nil {
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
+ }
+ var l Listener
+ switch la := addrs.first(isIPv4).(type) {
+ case *TCPAddr:
+ l, err = listenTCP(context.Background(), network, la, control)
+ case *UnixAddr:
+ l, err = listenUnix(context.Background(), network, la, control)
+ default:
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
+ }
+ if err != nil {
+ return nil, err // l is non-nil interface containing nil pointer
+ }
+ return l, nil
+}
+
// ListenPacket announces on the local network address.
//
// The network must be "udp", "udp4", "udp6", "unixgram", or an IP
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 13fa9fa..15ecf19 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -7,11 +7,13 @@
import (
"bufio"
"context"
+ "errors"
"internal/poll"
"internal/testenv"
"io"
"runtime"
"sync"
+ "syscall"
"testing"
"time"
)
@@ -146,8 +148,8 @@
// In some environments, the slow IPs may be explicitly unreachable, and fail
// more quickly than expected. This test hook prevents dialTCP from returning
// before the deadline.
-func slowDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
- c, err := doDialTCP(ctx, net, laddr, raddr)
+func slowDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr, control ControlFunc) (*TCPConn, error) {
+ c, err := doDialTCP(ctx, net, laddr, raddr, control)
if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) {
// Wait for the deadline, or indefinitely if none exists.
<-ctx.Done()
@@ -463,7 +465,7 @@
origTestHookDialTCP := testHookDialTCP
defer func() { testHookDialTCP = origTestHookDialTCP }()
- testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr, control ControlFunc) (*TCPConn, error) {
// Sleep long enough for Happy Eyeballs to kick in, and inhibit cancelation.
// This forces dialParallel to juggle two successful connections.
time.Sleep(fallbackDelay * 2)
@@ -471,7 +473,7 @@
// Now ignore the provided context (which will be canceled) and use a
// different one to make sure this completes with a valid connection,
// which we hope to be closed below:
- return doDialTCP(context.Background(), net, laddr, raddr)
+ return doDialTCP(context.Background(), net, laddr, raddr, control)
}
d := Dialer{
@@ -910,3 +912,89 @@
}
c.Close()
}
+
+func TestListenControl(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ var called bool
+ l, err := ListenControl("tcp", ":0", func(syscall.RawConn) error {
+ called = true
+ return nil
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ l.Close()
+ if !called {
+ t.Fatalf("Control not called")
+ }
+
+ // Test that error propagates
+ sentinel := errors.New("sentinel error")
+ l, err = ListenControl("tcp", ":0", func(syscall.RawConn) error {
+ return sentinel
+ })
+ if err == nil {
+ l.Close()
+ t.Fatalf("expected missing error")
+ }
+ if l != nil {
+ l.Close()
+ t.Fatalf("unexpecetd listener")
+ }
+ if oe, ok := err.(*OpError); !ok || oe.Err != sentinel {
+ t.Fatalf("listen error = %v (%T); want OpError with Err == sentinel", err, err)
+ }
+}
+
+func TestDialControl(t *testing.T) {
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ ln, err := newLocalListener("tcp")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer ln.Close()
+
+ var called bool
+ d := Dialer{
+ Control: func(syscall.RawConn) error {
+ called = true
+ return nil
+ },
+ }
+ c, err := d.Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ c.Close()
+ if !called {
+ t.Fatalf("Control not called")
+ }
+
+ // Test that error propagates
+ sentinel := errors.New("sentinel error")
+ d = Dialer{
+ Control: func(syscall.RawConn) error {
+ return sentinel
+ },
+ }
+ c, err = d.Dial("tcp", ln.Addr().String())
+ if err == nil {
+ c.Close()
+ t.Fatalf("expected missing error")
+ }
+ if c != nil {
+ c.Close()
+ t.Fatalf("unexpecetd connection")
+ }
+ if oe, ok := err.(*OpError); !ok || oe.Err != sentinel {
+ t.Fatalf("dial error = %v (%T); want OpError with Err == sentinel", err, err)
+ }
+}
diff --git a/src/net/hook.go b/src/net/hook.go
index d7316ea..ec79f5c 100644
--- a/src/net/hook.go
+++ b/src/net/hook.go
@@ -8,7 +8,7 @@
var (
// if non-nil, overrides dialTCP.
- testHookDialTCP func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error)
+ testHookDialTCP func(ctx context.Context, net string, laddr, raddr *TCPAddr, control ControlFunc) (*TCPConn, error)
testHookHostsPath = "/etc/hosts"
testHookLookupIP = func(
diff --git a/src/net/iprawsock.go b/src/net/iprawsock.go
index c4b54f0..06a75d8 100644
--- a/src/net/iprawsock.go
+++ b/src/net/iprawsock.go
@@ -209,7 +209,7 @@
// If the IP field of raddr is nil or an unspecified IP address, the
// local system is assumed.
func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error) {
- c, err := dialIP(context.Background(), network, laddr, raddr)
+ c, err := dialIP(context.Background(), network, laddr, raddr, nil)
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -224,7 +224,7 @@
// ListenIP listens on all available IP addresses of the local system
// except multicast IP addresses.
func ListenIP(network string, laddr *IPAddr) (*IPConn, error) {
- c, err := listenIP(context.Background(), network, laddr)
+ c, err := listenIP(context.Background(), network, laddr, nil)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
}
diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go
index 64c6016..8d1a506 100644
--- a/src/net/iprawsock_posix.go
+++ b/src/net/iprawsock_posix.go
@@ -112,7 +112,7 @@
return c.fd.writeMsg(b, oob, sa)
}
-func dialIP(ctx context.Context, netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
+func dialIP(ctx context.Context, netProto string, laddr, raddr *IPAddr, control ControlFunc) (*IPConn, error) {
network, proto, err := parseNetwork(ctx, netProto, true)
if err != nil {
return nil, err
@@ -125,14 +125,14 @@
if raddr == nil {
return nil, errMissingAddress
}
- fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial")
+ fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial", control)
if err != nil {
return nil, err
}
return newIPConn(fd), nil
}
-func listenIP(ctx context.Context, netProto string, laddr *IPAddr) (*IPConn, error) {
+func listenIP(ctx context.Context, netProto string, laddr *IPAddr, control ControlFunc) (*IPConn, error) {
network, proto, err := parseNetwork(ctx, netProto, true)
if err != nil {
return nil, err
@@ -142,7 +142,7 @@
default:
return nil, UnknownNetworkError(netProto)
}
- fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen")
+ fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen", control)
if err != nil {
return nil, err
}
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index d659bf0..67679f2 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -133,12 +133,12 @@
return syscall.AF_INET6, false
}
-func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) {
+func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, control ControlFunc) (fd *netFD, err error) {
if (runtime.GOOS == "windows" || runtime.GOOS == "openbsd" || runtime.GOOS == "nacl") && mode == "dial" && raddr.isWildcard() {
raddr = raddr.toLocal(net)
}
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
- return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr)
+ return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, control)
}
func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
diff --git a/src/net/net.go b/src/net/net.go
index 91ec048..fbe9d47 100644
--- a/src/net/net.go
+++ b/src/net/net.go
@@ -106,6 +106,11 @@
String() string // string form of address (for example, "192.0.2.1:25", "[2001:db8::1]:80")
}
+// ControlFunc is a control function that allows setting up/overriding socket
+// options. It is called immediately after default socket options are setup, but
+// before the socket is used for binding or dialing.
+type ControlFunc func(syscall.RawConn) error
+
// Conn is a generic stream-oriented network connection.
//
// Multiple goroutines may invoke methods on a Conn simultaneously.
diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go
index 8cfc42e..2f886ea 100644
--- a/src/net/sock_posix.go
+++ b/src/net/sock_posix.go
@@ -38,7 +38,7 @@
// socket returns a network file descriptor that is ready for
// asynchronous I/O using the network poller.
-func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr) (fd *netFD, err error) {
+func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, control ControlFunc) (fd *netFD, err error) {
s, err := sysSocket(family, sotype, proto)
if err != nil {
return nil, err
@@ -51,6 +51,15 @@
poll.CloseFunc(s)
return nil, err
}
+ if control != nil {
+ if rawConn, err := newRawConn(fd); err != nil {
+ fd.Close()
+ return nil, err
+ } else if err := control(rawConn); err != nil {
+ fd.Close()
+ return nil, err
+ }
+ }
// This function makes a network file descriptor for the
// following applications:
diff --git a/src/net/tcpsock.go b/src/net/tcpsock.go
index e957aa3..caca028 100644
--- a/src/net/tcpsock.go
+++ b/src/net/tcpsock.go
@@ -212,7 +212,7 @@
if raddr == nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
}
- c, err := dialTCP(context.Background(), network, laddr, raddr)
+ c, err := dialTCP(context.Background(), network, laddr, raddr, nil)
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -308,6 +308,10 @@
// If the Port field of laddr is 0, a port number is automatically
// chosen.
func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error) {
+ return listenTCP(context.Background(), network, laddr, nil)
+}
+
+func listenTCP(ctx context.Context, network string, laddr *TCPAddr, control ControlFunc) (*TCPListener, error) {
switch network {
case "tcp", "tcp4", "tcp6":
default:
@@ -316,7 +320,7 @@
if laddr == nil {
laddr = &TCPAddr{}
}
- ln, err := listenTCP(context.Background(), network, laddr)
+ ln, err := doListenTCP(ctx, network, laddr, control)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
}
diff --git a/src/net/tcpsock_plan9.go b/src/net/tcpsock_plan9.go
index e37f065..7901259 100644
--- a/src/net/tcpsock_plan9.go
+++ b/src/net/tcpsock_plan9.go
@@ -14,14 +14,14 @@
return genericReadFrom(c, r)
}
-func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr, control ControlFunc) (*TCPConn, error) {
if testHookDialTCP != nil {
- return testHookDialTCP(ctx, net, laddr, raddr)
+ return testHookDialTCP(ctx, net, laddr, raddr, control)
}
- return doDialTCP(ctx, net, laddr, raddr)
+ return doDialTCP(ctx, net, laddr, raddr, control)
}
-func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr, control ControlFunc) (*TCPConn, error) {
switch net {
case "tcp", "tcp4", "tcp6":
default:
@@ -69,7 +69,7 @@
return f, nil
}
-func listenTCP(ctx context.Context, network string, laddr *TCPAddr) (*TCPListener, error) {
+func doListenTCP(ctx context.Context, network string, laddr *TCPAddr, control ControlFunc) (*TCPListener, error) {
fd, err := listenPlan9(ctx, network, laddr)
if err != nil {
return nil, err
diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go
index 58c7e49..e5f0dab 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -51,15 +51,15 @@
return genericReadFrom(c, r)
}
-func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+func dialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr, control ControlFunc) (*TCPConn, error) {
if testHookDialTCP != nil {
- return testHookDialTCP(ctx, net, laddr, raddr)
+ return testHookDialTCP(ctx, net, laddr, raddr, control)
}
- return doDialTCP(ctx, net, laddr, raddr)
+ return doDialTCP(ctx, net, laddr, raddr, control)
}
-func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
- fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial")
+func doDialTCP(ctx context.Context, net string, laddr, raddr *TCPAddr, control ControlFunc) (*TCPConn, error) {
+ fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", control)
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@@ -89,7 +89,7 @@
if err == nil {
fd.Close()
}
- fd, err = internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial")
+ fd, err = internetSocket(ctx, net, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", control)
}
if err != nil {
@@ -152,8 +152,8 @@
return f, nil
}
-func listenTCP(ctx context.Context, network string, laddr *TCPAddr) (*TCPListener, error) {
- fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_STREAM, 0, "listen")
+func doListenTCP(ctx context.Context, network string, laddr *TCPAddr, control ControlFunc) (*TCPListener, error) {
+ fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", control)
if err != nil {
return nil, err
}
diff --git a/src/net/udpsock.go b/src/net/udpsock.go
index 2c0f74f..6cf79b5 100644
--- a/src/net/udpsock.go
+++ b/src/net/udpsock.go
@@ -208,7 +208,7 @@
if raddr == nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
}
- c, err := dialUDP(context.Background(), network, laddr, raddr)
+ c, err := dialUDP(context.Background(), network, laddr, raddr, nil)
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -233,7 +233,7 @@
if laddr == nil {
laddr = &UDPAddr{}
}
- c, err := listenUDP(context.Background(), network, laddr)
+ c, err := listenUDP(context.Background(), network, laddr, nil)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
}
@@ -266,7 +266,7 @@
if gaddr == nil || gaddr.IP == nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: errMissingAddress}
}
- c, err := listenMulticastUDP(context.Background(), network, ifi, gaddr)
+ c, err := listenMulticastUDP(context.Background(), network, ifi, gaddr, nil)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr.opAddr(), Err: err}
}
diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go
index a126506..ebe6dd9 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -94,24 +94,24 @@
return c.fd.writeMsg(b, oob, sa)
}
-func dialUDP(ctx context.Context, net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial")
+func dialUDP(ctx context.Context, net string, laddr, raddr *UDPAddr, control ControlFunc) (*UDPConn, error) {
+ fd, err := internetSocket(ctx, net, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial", control)
if err != nil {
return nil, err
}
return newUDPConn(fd), nil
}
-func listenUDP(ctx context.Context, network string, laddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_DGRAM, 0, "listen")
+func listenUDP(ctx context.Context, network string, laddr *UDPAddr, control ControlFunc) (*UDPConn, error) {
+ fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_DGRAM, 0, "listen", control)
if err != nil {
return nil, err
}
return newUDPConn(fd), nil
}
-func listenMulticastUDP(ctx context.Context, network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(ctx, network, gaddr, nil, syscall.SOCK_DGRAM, 0, "listen")
+func listenMulticastUDP(ctx context.Context, network string, ifi *Interface, gaddr *UDPAddr, control ControlFunc) (*UDPConn, error) {
+ fd, err := internetSocket(ctx, network, gaddr, nil, syscall.SOCK_DGRAM, 0, "listen", control)
if err != nil {
return nil, err
}
diff --git a/src/net/unixsock.go b/src/net/unixsock.go
index 057940a..969d52c 100644
--- a/src/net/unixsock.go
+++ b/src/net/unixsock.go
@@ -200,7 +200,7 @@
default:
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(network)}
}
- c, err := dialUnix(context.Background(), network, laddr, raddr)
+ c, err := dialUnix(context.Background(), network, laddr, raddr, nil)
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
}
@@ -296,6 +296,10 @@
//
// The network must be "unix" or "unixpacket".
func ListenUnix(network string, laddr *UnixAddr) (*UnixListener, error) {
+ return listenUnix(context.Background(), network, laddr, nil)
+}
+
+func listenUnix(ctx context.Context, network string, laddr *UnixAddr, control ControlFunc) (*UnixListener, error) {
switch network {
case "unix", "unixpacket":
default:
@@ -304,7 +308,7 @@
if laddr == nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: errMissingAddress}
}
- ln, err := listenUnix(context.Background(), network, laddr)
+ ln, err := doListenUnix(context.Background(), network, laddr, control)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
}
@@ -323,7 +327,7 @@
if laddr == nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: errMissingAddress}
}
- c, err := listenUnixgram(context.Background(), network, laddr)
+ c, err := listenUnixgram(context.Background(), network, laddr, nil)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
}
diff --git a/src/net/unixsock_plan9.go b/src/net/unixsock_plan9.go
index e70eb21..764d7d7 100644
--- a/src/net/unixsock_plan9.go
+++ b/src/net/unixsock_plan9.go
@@ -42,10 +42,10 @@
return nil, syscall.EPLAN9
}
-func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
+func doListenUnix(ctx context.Context, network string, laddr *UnixAddr, control ControlFunc) (*UnixListener, error) {
return nil, syscall.EPLAN9
}
-func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) {
+func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr, control ControlFunc) (*UnixConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go
index a8f892e..bd356f4 100644
--- a/src/net/unixsock_posix.go
+++ b/src/net/unixsock_posix.go
@@ -13,7 +13,7 @@
"syscall"
)
-func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string) (*netFD, error) {
+func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string, control ControlFunc) (*netFD, error) {
var sotype int
switch net {
case "unix":
@@ -42,7 +42,7 @@
return nil, errors.New("unknown mode: " + mode)
}
- fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr)
+ fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, control)
if err != nil {
return nil, err
}
@@ -150,8 +150,8 @@
return c.fd.writeMsg(b, oob, sa)
}
-func dialUnix(ctx context.Context, net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(ctx, net, laddr, raddr, "dial")
+func dialUnix(ctx context.Context, net string, laddr, raddr *UnixAddr, control ControlFunc) (*UnixConn, error) {
+ fd, err := unixSocket(ctx, net, laddr, raddr, "dial", control)
if err != nil {
return nil, err
}
@@ -206,16 +206,16 @@
l.unlink = unlink
}
-func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
- fd, err := unixSocket(ctx, network, laddr, nil, "listen")
+func doListenUnix(ctx context.Context, network string, laddr *UnixAddr, control ControlFunc) (*UnixListener, error) {
+ fd, err := unixSocket(ctx, network, laddr, nil, "listen", control)
if err != nil {
return nil, err
}
return &UnixListener{fd: fd, path: fd.laddr.String(), unlink: true}, nil
}
-func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(ctx, network, laddr, nil, "listen")
+func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr, control ControlFunc) (*UnixConn, error) {
+ fd, err := unixSocket(ctx, network, laddr, nil, "listen", control)
if err != nil {
return nil, err
}
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
RELNOTE=yes
5 comments:
Patch Set #1, Line 588: func Listen(network, address string) (Listener, error) {
May as well just change Listen to be just
return ListenControl(network, address, nil)
Patch Set #1, Line 942: expected missing
How about "missing expected error".
Patch Set #1, Line 946: t.Fatalf("unexpecetd listener")
s/unexpecetd/unexpected/
Patch Set #1, Line 110: setup
s/setup/set/
Patch Set #1, Line 111: binding or dialing
You're mixing our terms with standard socket terms. I think either "listening or dialing" or "binding or connecting".
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
I think that it might be better to discuss this stuff on golang-dev before moving forward, because #9661 is a long-standing issue. Some people want this for the sake of convenience, but it's still not clear to me what's the best API surface for representing the border of responsibility.
For example, Dial and Listen functions are complexes; Should the user-defined control function be applied to derived sockets such as DNS transport for dialing? Or should we have a separate control function for such stuff? Should we need to expose internal functionality that is now part of dialing/listening for supporting user-defined control functions?
Audrius Butkevicius uploaded patch set #2 to this change.
net: allow setting socket options before listening or dialing
Existing implementation does not provide a way to set options such as
SO_REUSEPORT. Exposes ListenControl function and a new Control field on the
Dialer struct.
Fixes #9661
Change-Id: If4d275711f823df72d3ac5cc3858651a6a57cccb
---
M src/net/dial.go
M src/net/dial_test.go
M src/net/hook.go
M src/net/iprawsock.go
M src/net/iprawsock_posix.go
M src/net/ipsock_posix.go
M src/net/net.go
M src/net/sock_posix.go
M src/net/tcpsock.go
M src/net/tcpsock_plan9.go
M src/net/tcpsock_posix.go
M src/net/udpsock.go
M src/net/udpsock_posix.go
M src/net/unixsock.go
M src/net/unixsock_plan9.go
M src/net/unixsock_posix.go
16 files changed, 178 insertions(+), 57 deletions(-)
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Address the comments.
I've raised the thread on golang-dev, but it had no responses so far.
I really want this to happen, so I really hope this does not get stuck due to lack of feedback.
5 comments:
Patch Set #1, Line 588: func Listen(network, address string) (Listener, error) {
May as well just change Listen to be just […]
Done
Patch Set #1, Line 942: missing expected
How about "missing expected error".
Done
Patch Set #1, Line 946: t.Fatalf("unexpected listener")
s/unexpecetd/unexpected/
Done
s/setup/set/
Done
Patch Set #1, Line 111: listening or diali
You're mixing our terms with standard socket terms. […]
Done
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
I'm pretty interested in this as well.
Audrius Butkevicius uploaded patch set #3 to this change.
net: allow setting socket options before listening or dialing
Existing implementation does not provide a way to set options such as
SO_REUSEPORT. Exposes ListenControl function and a new Control field on the
Dialer struct.
Fixes #9661
Change-Id: If4d275711f823df72d3ac5cc3858651a6a57cccb
---
M src/net/dial.go
M src/net/dial_test.go
M src/net/hook.go
M src/net/iprawsock.go
M src/net/iprawsock_posix.go
M src/net/ipsock_posix.go
M src/net/net.go
M src/net/sock_posix.go
M src/net/tcpsock.go
M src/net/tcpsock_plan9.go
M src/net/tcpsock_posix.go
M src/net/udpsock.go
M src/net/udpsock_posix.go
M src/net/unixsock.go
M src/net/unixsock_plan9.go
M src/net/unixsock_posix.go
16 files changed, 178 insertions(+), 57 deletions(-)
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
R=go1.11
Thanks for working on this. We're not going to be able to review it properly until the Go 1.11 cycle opens. It does look approximately right to me though.
Patch Set 3:
R=go1.11
Thanks for working on this. We're not going to be able to review it properly until the Go 1.11 cycle opens. It does look approximately right to me though.
1 comment:
The SO_REUSEPORT option is both available on TCP and UDP, so looks like net.ListenUDPControl is also needed.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Patch Set 3:
R=go1.11
Thanks for working on this. We're not going to be able to review it properly until the Go 1.11 cycle opens. It does look approximately right to me though.
Given the tree is now open, what is left to do here?
i have a few comments on api design and implementation, but have no time right now.
will review this weekend.
Sorry for being late.
I created https://go-review.googlesource.com/c/go/+/95035 and played it last weekend for reviewing this CL.
I think your approach is basically fine, but probably need multiple rounds.
Thank you for tackling this.
12 comments:
/ Control optionally specifies a control function that is called after
// initialization of the socket, but before dialing.
The word "dialing" doesn't describe anything useful.
How about this?
// Control specifies an optional callback function that is
// called after a connection is created and before the
// connection is bound to the operating system.
Patch Set #3, Line 69: ControlFunc
No need to have a new type. I don't see any benefit to having methods on it.
Please replace "ControlFunc" with "func(network, address string, c syscall.RawConn) error".
Patch Set #3, Line 551: dp.Control
While we are here, let's rename dialParam with sysDialer and put dialParallel, dialSerial and dialSingle into it as its methods.
The non-mutable fields "network" and "address" are very important not only for returning error values but for giving precise hints to the user-defined callback function.
For example,
// sysDialer contains a Dial's parameters and configuration.
type sysDialer struct {
Dialer
network, address string
}
func (sd *sysDialer) dialParallel(ctx context.Context, primaries, fallbacks addrList) (Conn, error)
func (sd *sysDialer) dialSerial(ctx context.Context, ras addrList) (Conn, error)
func (sd *sysDialer) dialSingle(ctx context.Context, ra Addr) (c Conn, err error)
func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error)
func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error)
func (sd *sysDialer) dialIP(ctx context.Context, laddr, raddr *IPAddr) (*IPConn, error)
func (sd *sysDialer) dialUnix(ctx context.Context, laddr, raddr *UnixAddr) (*UnixConn, error)
also for stateful and stateless listeners,
// sysAnnouncer contains a Listen's parameters and configuration.
type sysAnnouncer struct {
Announcer
network, address string
}
func (sa *sysAnnouncer) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error)
func (sa *sysAnnouncer) listenUDP(ctx context.Context, laddr *UDPAddr) (*UDPConn, error)
func (sa *sysAnnouncer) listenMulticastUDP(ctx context.Context, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error)
func (sa *sysAnnouncer) listenIP(ctx context.Context, laddr *IPAddr) (*IPConn, error)
func (sa *sysAnnouncer) listenUnix(ctx context.Context, laddr *UnixAddr) (*UnixListener, error)
func (sa *sysAnnouncer) listenUnixgram(ctx context.Context, laddr *UnixAddr) (*UnixConn, error)
// ListenControl announces on the local network address.
//
// See func Listen and type ControlFunc for a description of parameters.
Your CL doesn't support listening for stateless transport protocols.
Also, there's no space for future uses.
I'd like to propose to change the exposed API surface as follows:
// An Announcer contains options for listening to an address.
type Announcer struct {
// Control specifies an optional callback function that is
// called after a connection is created and before the
// connection is bound to the operating system.
Control func(network, address string, c syscall.RawConn) error
}
// Listen announces on the local network address.
//
// See func Listen for a description of the network and address
// parameters.
func (an *Announcer) Listen(network, address string) (Listener, error)
/ ListenPacket announces on the local network address.
//
// See func ListenPacket for a description of the network and address
// parameters.
func (an *Announcer) ListenPacket(network, address string) (PacketConn, error)
Patch Set #3, Line 916: TestListenControl
I think both Test{Listen,Dial}Control functions don't test the functionality enough.
Please take a look at https://go-review.googlesource.com/c/go/+/95035/5/src/net/dial_test.go#918 and https://go-review.googlesource.com/c/go/+/95035/5/src/net/listen_test.go#733, feel free to pick anything for the CL.
Patch Set #3, Line 11: control ControlFunc
No need to modify this placeholder.
Please revert this.
Please do s/net/network/ while we are here.
Patch Set #3, Line 136: control
s/control/ctrlFn or controlFn or controlFunc/g
I don't think that you want to allow people to modify the destination identifier during the connection setup process.
The signature must be "func(network, address string, c syscall.RawConn) error".
Also, there's no benefit to making this type. Please drop it.
Patch Set #3, Line 54: if control != nil {
I don't think this is the right place to call back user-defined functions.
Please move this block to netFD.dial, netFD.listenStream and netFD.listenDatagram.
Please take a look at https://go-review.googlesource.com/c/go/+/95035/5/src/net/sock_posix.go
You need to give a useful hint to users; e.g., "tcp4" or "tcp6" instead of ambiguous​ "tcp".
Patch Set #3, Line 58: raddr
Please be careful not to leak unexposed types that satisfy net.Addr or net.sockaddr interface.
So this must be a string, not a net.Addr.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Patch Set 3:
(12 comments)
Sorry for being late.
I created https://go-review.googlesource.com/c/go/+/95035 and played it last weekend for reviewing this CL.
I think your approach is basically fine, but probably need multiple rounds.
Thank you for tackling this.
Hey,
So I reviewed your review CL, and it seems you have everything covered.
Is there a reason why we don't just kill this CL and get your CL merged instead?
If anything needs doing, it's probably best I scrap what's here and carry on from all the awesome work you've done, as I think it's a much better approach to the problem, which I simply couldn't see myself as I didn't know my way around the code as well.
I guess the only thing that looks suspicious to me is testing whether the control function was called, as it essentially relies on some syscall not failing yet does not do any assertions
on whether the control function was called at all (in which case the syscall wouldn't fail and the tests would still pass), but perhaps I am just missing something.
Thanks.
Yay/Nay? I know Go 1.11 is miles away, but I don't want to let this die.
Ian, thoughts?
Does this need to go through the proposal process for the new API?
The new API should probably be highlighted more in the commit message, though.
I'd make the subject say which symbol is being added.
2 comments:
/ Control optionally specifies a control function that is called after
// initialization of the socket, but before dialing.
The word "dialing" doesn't describe anything useful. […]
Mikio, that proposed wording doesn't help the average person understand this.
But I'm not even sure why the word "control" is used. Is that a term used elsewhere?
What about just:
// PreDial permits modification of a socket before it's used to Dial.
PreDial func(network, addr string, c syscall.RawConn) error.
Patch Set #3, Line 69: ControlFunc
No need to have a new type. I don't see any benefit to having methods on it. […]
Agreed.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Does this need to go through the proposal process for the new API?
The new API should probably be highlighted more in the commit message, though.
I'd make the subject say which symbol is being added.
I haven't looked through the CL yet.
If this follows the API that Russ outlined at #9661 then I don't think this needs to go through the proposal process. It's just a small extension of the existing RawConn support.
I agree that the new API should be in the commit message.
I guess the definition of "small" varies, but I think yes, it's the API that Russ proposed, which introduces a new Announcer type (Russ named it ListenConfig) which has a Listen() and ListenPacket() receivers.
Should we close this CL, and focus on the other one, or should I shamelessly copy what Mikio did? What's the usual scenario in this case?
I guess the definition of "small" varies, but I think yes, it's the API that Russ proposed, which introduces a new Announcer type (Russ named it ListenConfig) which has a Listen() and ListenPacket() receivers.
Should we close this CL, and focus on the other one, or should I shamelessly copy what Mikio did? What's the usual scenario in this case?
Sorry, which CL is the "other one"?
Sorry, which CL is the "other one"?
Thanks. It's up to Mikio.
sorry for being late. i'm on vacation and thus, i'm probably fine to allocate a spare time for reviewing this cl passing through the eyes of my wife.
i still prefer landing this cl, because 1) first-come-first-serve, 2) once this cl lands, the community has a new contributor. perhaps they might be able to take some burden on maintaining the net package and packages in x/net repo. i don't want to lose such opportunity.
I am now waiting on the decision on what happens with CL 95335, as it seems that it's good to go by itself, and I don't want to bring the test rewrites from CL 95035 into this CL.
Audrius Butkevicius uploaded patch set #4 to this change.
net: allow setting socket options before listening or dialing
Existing implementation does not provide a way to set options such as
SO_REUSEPORT, that has to be set prior the socket being bound.
Fixes #9661
New exposed API:
pkg net, method (*Announcer) Listen(string, string) (Listener, error)
pkg net, method (*Announcer) ListenPacket(string, string) (PacketConn, error)
pkg net, type Announcer struct
pkg net, type Announcer struct, Control func(string, string, syscall.RawConn) error
pkg net, type Dialer struct, Control func(string, string, syscall.RawConn) error
Change-Id: If4d275711f823df72d3ac5cc3858651a6a57cccb
---
M src/net/dial.go
M src/net/dial_test.go
M src/net/iprawsock.go
M src/net/iprawsock_plan9.go
M src/net/iprawsock_posix.go
M src/net/ipsock_posix.go
M src/net/listen_test.go
M src/net/rawconn_stub_test.go
M src/net/rawconn_unix_test.go
M src/net/rawconn_windows_test.go
M src/net/sock_posix.go
M src/net/tcpsock.go
M src/net/tcpsock_plan9.go
M src/net/tcpsock_posix.go
M src/net/udpsock.go
M src/net/udpsock_plan9.go
M src/net/udpsock_posix.go
M src/net/unixsock.go
M src/net/unixsock_plan9.go
M src/net/unixsock_posix.go
20 files changed, 453 insertions(+), 162 deletions(-)
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
So this now shamelessly cherry-picks Mikio's change.
Looking for feedback on what's left here.
Hi,
What are the next steps to get this merged?
Thanks.
Bump...
Audrius Butkevicius uploaded patch set #5 to this change.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
4 comments:
Patch Set #6, Line 7: allow setting socket options before listening or dialing
I like the API note below (keep it) but mention major new API surface here too
net: add Foo, Bar to permit socket opts before listen/dial
Patch Set #6, Line 12: Fixes #9661
put this after the new API (but before Change-Id)
// Control specifies an optional callback function that is
// called after a connection is created and before the
// connection is bound to the operating system.
I assume only c.Control is valid, and not c.Read/c.Write?
What happens if Control returns an error? Does caller close the socket?
// An Announcer contains options for listening to an address.
type Announcer
is this a term of art? why not, say, OptionListener? or ControlListener?
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Audrius Butkevicius uploaded patch set #7 to this change.
net: Add Announcer, Dialer.Control to permit socket opts before listen/dial
Existing implementation does not provide a way to set options such as
SO_REUSEPORT, that has to be set prior the socket being bound.
New exposed API:
pkg net, method (*Announcer) Listen(string, string) (Listener, error)
pkg net, method (*Announcer) ListenPacket(string, string) (PacketConn, error)
pkg net, type Announcer struct
pkg net, type Announcer struct, Control func(string, string, syscall.RawConn) error
pkg net, type Dialer struct, Control func(string, string, syscall.RawConn) error
Fixes #9661
Change-Id: If4d275711f823df72d3ac5cc3858651a6a57cccb
---
M src/net/dial.go
M src/net/dial_test.go
M src/net/iprawsock.go
M src/net/iprawsock_plan9.go
M src/net/iprawsock_posix.go
M src/net/ipsock_posix.go
M src/net/listen_test.go
M src/net/rawconn_stub_test.go
M src/net/rawconn_unix_test.go
M src/net/rawconn_windows_test.go
M src/net/sock_posix.go
M src/net/tcpsock.go
M src/net/tcpsock_plan9.go
M src/net/tcpsock_posix.go
M src/net/udpsock.go
M src/net/udpsock_plan9.go
M src/net/udpsock_posix.go
M src/net/unixsock.go
M src/net/unixsock_plan9.go
M src/net/unixsock_posix.go
20 files changed, 453 insertions(+), 162 deletions(-)
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
17 comments:
Patch Set #6, Line 7: Add Announcer, Dialer.Control to permit socket opts befo
I like the API note below (keep it) but mention major new API surface here too […]
Done
Patch Set #6, Line 12: New exposed API:
put this after the new API (but before Change-Id)
// Cancel is an optional channel whose closure indic
Mikio, that proposed wording doesn't help the average person understand this. […]
Done
Patch Set #3, Line 69: ial should
Agreed.
Done
While we are here, let's rename dialParam with sysDialer and put dialParallel, dialSerial and dialSi […]
Done
addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
if err != nil {
return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: ni
Your CL doesn't support listening for stateless transport protocols. […]
Done
// Control specifies an optional callback function that is
// called after a connection is created and before the
// connection is bound to the operating system.
I assume only c.Control is valid, and not c.Read/c.Write? […]
Yes, c.R/W would not be valid at that point.
Control returning an error would result in the socket being closed, and the original Dialer.Dial call returning the same error.
// An Announcer contains options for listening to an address.
type Announcer
is this a term of art? why not, say, OptionListener? or ControlListener?
I envision this potentially growing in the future in the things is provides, so ControlListener does not suit that. The ideal term would be Listener, but obviously that is not available.
I don't mind Announcer versus OptionListener to be honest.
Patch Set #3, Line 916: ch runtime.GOOS {
I think both Test{Listen,Dial}Control functions don't test the functionality enough. […]
Done
Patch Set #3, Line 11: (*TCPConn, error)
No need to modify this placeholder. […]
Done
Please do s/net/network/ while we are here.
Done
Patch Set #3, Line 136: ng, ctr
s/control/ctrlFn or controlFn or controlFunc/g
I don't think that you want to allow people to modify the destination identifier during the connecti […]
Done
I don't think this is the right place to call back user-defined functions. […]
Done
You need to give a useful hint to users; e.g., "tcp4" or "tcp6" instead of ambiguous​ "tcp".
Done
Patch Set #3, Line 58: ens a
Please be careful not to leak unexposed types that satisfy net.Addr or net.sockaddr interface. […]
Done
The SO_REUSEPORT option is both available on TCP and UDP, so looks like net. […]
Done
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
I think I am supposed to mark things as done once I've replied, so doing that.
2 comments:
// Control specifies an optional callback function that is
// called after a connection is created and before the
// connection is bound to the operating system.
Yes, c.R/W would not be valid at that point. […]
Done
// An Announcer contains options for listening to an address.
type Announcer
I envision this potentially growing in the future in the things is provides, so ControlListener does […]
Done
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
10 comments:
Patch Set #7, Line 75: // Control specifies an optional callback function that is
If Control is not nil, it is called after creating the network connection but before actually dialing.
Patch Set #7, Line 78: Control func(network, address string, c syscall.RawConn) error
Doesn't seem like we should pass network and address here.
Patch Set #7, Line 315: // sysDialer contains a Dial's parameters and configuration.
Why change the name of this type?
Even if there is a reason to change the name, it seems unrelated to the point of the change, and it's not clear why it should be done at the same time. Better to separate name changes from functional changes.
Patch Set #7, Line 580: // Control specifies an optional callback function that is
Same comments as above.
Patch Set #7, Line 590: func (an *Announcer) Listen(network, address string) (Listener, error) {
As long as we are introducing a new method anyhow, we should consider giving it a Context argument.
Patch Set #7, Line 463: testHookDialTCP = func(ctx context.Context, network string, laddr, raddr *TCPAddr) (*TCPConn, error) {
Move naming changes like this to a separate CL.
Patch Set #7, Line 921: StatefulDial
I don't see other uses of "stateful" and "stateless" in the package. The distinction between these tests seems to be streaming vs. packet. The test name should use those terms.
Patch Set #7, Line 928: t.Log(err)
Seems like this should be t.Error.
Patch Set #7, Line 948: t.Log(err)
t.Error.
Patch Set #7, Line 213: return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
As far as I can see the current code will simply return errMissingAddress in this case, not an OpError. We shouldn't change that.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
s/Add/add/
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Audrius Butkevicius uploaded patch set #8 to this change.
net: add Announcer, Dialer.Control to permit socket opts before listen/dial
20 files changed, 450 insertions(+), 161 deletions(-)
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Audrius Butkevicius uploaded patch set #9 to this change.
11 comments:
s/Add/add/
Done
Patch Set #7, Line 75: // If Control is not nil, it is called after creating the network
If Control is not nil, it is called after creating the network connection but before actually dialin […]
Done
Doesn't seem like we should pass network and address here.
This relates back to the original discussion in:
https://groups.google.com/d/msg/golang-dev/HAEaU5TakTU/hSqO-WJ8AAAJ
Patch Set #7, Line 315: type sysDialer struct {
Why change the name of this type? […]
It's not just a name change, this also puts all the dialing functions under one roof, and reduces the amount of "visual" arguments we have to pass around (even if it's just sd.dialSerial(ctx, primaries) vs dialSerial(ctx, dp, primaries)).
This also complements the new sysAnnouncer which does the same thing for listening functions.
If I were to revert the name, do you want me to make the dialing functions not part of the struct? Or can they be part of the struct just with a different name?
Also, should I remove sysAnnouncer too and just leave loose listening functions?
Patch Set #7, Line 580: // connection but before binding it to the operating system.
Same comments as above.
Done
Patch Set #7, Line 590: addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
As long as we are introducing a new method anyhow, we should consider giving it a Context argument.
Should I name this ListenContext to match DialContext, or keep it as Listen?
If it's former, should I add a context'less version too?
Patch Set #7, Line 463: testHookDialTCP = func(ctx context.Context, net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
Move naming changes like this to a separate CL.
Done
Patch Set #7, Line 921: StreamDial",
I don't see other uses of "stateful" and "stateless" in the package. […]
Done
Patch Set #7, Line 928: t.Error(err)
Seems like this should be t.Error.
Done
Patch Set #7, Line 948: t.Error(err)
t.Error.
Done
Patch Set #7, Line 213: return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
As far as I can see the current code will simply return errMissingAddress in this case, not an OpErr […]
In the existing code, errMissingAddress error returned from dialIP is wrapped on line 214 of the same function.
This change just maintains that wrapping, in order to pass the tests.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
3 comments:
This relates back to the original discussion in: […]
Thanks. In that case I think the comment needs to clarify that the network and address passed to Control method are not necessarily the ones passed to Dial.
Patch Set #7, Line 315: type sysDialer struct {
It's not just a name change, this also puts all the dialing functions under one roof, and reduces th […]
I guess I'm OK with the name change and with using methods, but I would like to see that in a separate CL that makes no functional changes. This CL could then be based on that one.
Patch Set #7, Line 213: return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: nil, Err: errMissingAddress}
In the existing code, errMissingAddress error returned from dialIP is wrapped on line 214 of the sam […]
Ah, right. Thanks.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Part of this change is now in https://go-review.googlesource.com/#/c/go/+/115175
1 comment:
Patch Set #7, Line 315: type sysDialer struct {
I guess I'm OK with the name change and with using methods, but I would like to see that in a separa […]
I've created this: https://go-review.googlesource.com/#/c/go/+/115175
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Audrius Butkevicius uploaded patch set #10 to this change.
net: add ListenConfig, Dialer.Control to permit socket opts before listen/dial
Existing implementation does not provide a way to set options such as
SO_REUSEPORT, that has to be set prior the socket being bound.
New exposed API:
pkg net, method (*ListenConfig) Listen(context.Context, string, string) (Listener, error)
pkg net, method (*ListenConfig) ListenPacket(context.Context, string, string) (PacketConn, error)
pkg net, type ListenConfig struct
pkg net, type ListenConfig struct, Control func(string, string, syscall.RawConn) error
pkg net, type Dialer struct, Control func(string, string, syscall.RawConn) error
Fixes #9661
Change-Id: If4d275711f823df72d3ac5cc3858651a6a57cccb
---
M src/net/dial.go
M src/net/dial_test.go
M src/net/iprawsock_posix.go
M src/net/ipsock_posix.go
M src/net/listen_test.go
M src/net/rawconn_stub_test.go
M src/net/rawconn_unix_test.go
M src/net/rawconn_windows_test.go
M src/net/sock_posix.go
M src/net/tcpsock_posix.go
M src/net/udpsock_posix.go
M src/net/unixsock_posix.go
12 files changed, 348 insertions(+), 74 deletions(-)
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
This is now rebased on the other CL that had the sysListener introduced, also, renamed Announcer to ListenConfig as it seems that was the consensus.
1 comment:
Patch Set #7, Line 78: // Network and address parameters passed to Control method are not
Thanks. […]
Done
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
4 comments:
Patch Set #10, Line 79: necesserily
s/necesserily/necessarily/
I think we should add something like `For example, passing "tcp" to Dial will cause the Control function to be called with "tcp4" or "tcp6".`
Patch Set #10, Line 580: // necesserily the ones passed to Listen.
Same changes as above.
Patch Set #10, Line 600: switch la.(type) {
Write this as
switch la := la.(type) {
and then you don't need the explicit type assertions in the TCPAddr and UnixAddr cases.
Patch Set #10, Line 630: switch la.(type) {
la := la.(type) and omit type assertions.
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Audrius Butkevicius uploaded patch set #11 to this change.
12 files changed, 350 insertions(+), 74 deletions(-)
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
4 comments:
Patch Set #10, Line 79: necessarily
s/necesserily/necessarily/ […]
Done
Patch Set #10, Line 580: // Network and address parameters passed to Control method are not
Same changes as above.
Done
Patch Set #10, Line 600: var l Listener
Write this as […]
Done
Patch Set #10, Line 630: var c PacketConn
la := la.(type) and omit type assertions.
Done
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.
Patch set 11:Run-TryBot +1
TryBots beginning. Status page: https://farmer.golang.org/try?commit=31145f23
TryBots are happy.
Patch set 11:TryBot-Result +1
Thanks for your patience with this.
RELNOTE=yes
Patch set 11:Code-Review +2
Ian Lance Taylor merged this change.
net: add ListenConfig, Dialer.Control to permit socket opts before listen/dial
Existing implementation does not provide a way to set options such as
SO_REUSEPORT, that has to be set prior the socket being bound.
New exposed API:
pkg net, method (*ListenConfig) Listen(context.Context, string, string) (Listener, error)
pkg net, method (*ListenConfig) ListenPacket(context.Context, string, string) (PacketConn, error)
pkg net, type ListenConfig struct
pkg net, type ListenConfig struct, Control func(string, string, syscall.RawConn) error
pkg net, type Dialer struct, Control func(string, string, syscall.RawConn) error
Fixes #9661
Change-Id: If4d275711f823df72d3ac5cc3858651a6a57cccb
Reviewed-on: https://go-review.googlesource.com/72810
Run-TryBot: Ian Lance Taylor <ia...@golang.org>
TryBot-Result: Gobot Gobot <go...@golang.org>
Reviewed-by: Ian Lance Taylor <ia...@golang.org>
---
M src/net/dial.go
M src/net/dial_test.go
M src/net/iprawsock_posix.go
M src/net/ipsock_posix.go
M src/net/listen_test.go
M src/net/rawconn_stub_test.go
M src/net/rawconn_unix_test.go
M src/net/rawconn_windows_test.go
M src/net/sock_posix.go
M src/net/tcpsock_posix.go
M src/net/udpsock_posix.go
M src/net/unixsock_posix.go
12 files changed, 350 insertions(+), 74 deletions(-)
diff --git a/src/net/dial.go b/src/net/dial.go
index 3ea049c..b1a5ca7 100644
--- a/src/net/dial.go
+++ b/src/net/dial.go
@@ -8,6 +8,7 @@
"context"
"internal/nettrace"
"internal/poll"
+ "syscall"
"time"
)
@@ -70,6 +71,14 @@
//
// Deprecated: Use DialContext instead.
Cancel <-chan struct{}
+
+ // If Control is not nil, it is called after creating the network
+ // connection but before actually dialing.
+ //
+ // Network and address parameters passed to Control method are not
+ // necessarily the ones passed to Dial. For example, passing "tcp" to Dial
+ // will cause the Control function to be called with "tcp4" or "tcp6".
+ Control func(network, address string, c syscall.RawConn) error
}
func minNonzeroTime(a, b time.Time) time.Time {
@@ -563,8 +572,82 @@
return c, nil
}
+// ListenConfig contains options for listening to an address.
+type ListenConfig struct {
+ // If Control is not nil, it is called after creating the network
+ // connection but before binding it to the operating system.
+ //
+ // Network and address parameters passed to Control method are not
+ // necessarily the ones passed to Listen. For example, passing "tcp" to
+ // Listen will cause the Control function to be called with "tcp4" or "tcp6".
+ Control func(network, address string, c syscall.RawConn) error
+}
+
+// Listen announces on the local network address.
+//
+// See func Listen for a description of the network and address
+// parameters.
+func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (Listener, error) {
+ addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
+ if err != nil {
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
+ }
+ sl := &sysListener{
+ ListenConfig: *lc,
+ network: network,
+ address: address,
+ }
+ var l Listener
+ la := addrs.first(isIPv4)
+ switch la := la.(type) {
+ case *TCPAddr:
+ l, err = sl.listenTCP(ctx, la)
+ case *UnixAddr:
+ l, err = sl.listenUnix(ctx, la)
+ default:
+ return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
+ }
+ if err != nil {
+ return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: err} // l is non-nil interface containing nil pointer
+ }
+ return l, nil
+}
+
+// ListenPacket announces on the local network address.
+//
+// See func ListenPacket for a description of the network and address
+// parameters.
+func (lc *ListenConfig) ListenPacket(ctx context.Context, network, address string) (PacketConn, error) {
+ addrs, err := DefaultResolver.resolveAddrList(ctx, "listen", network, address, nil)
+ if err != nil {
+ return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
+ }
+ sl := &sysListener{
+ ListenConfig: *lc,
+ network: network,
+ address: address,
+ }
+ var c PacketConn
+ la := addrs.first(isIPv4)
+ switch la := la.(type) {
+ case *UDPAddr:
+ c, err = sl.listenUDP(ctx, la)
+ case *IPAddr:
+ c, err = sl.listenIP(ctx, la)
+ case *UnixAddr:
+ c, err = sl.listenUnixgram(ctx, la)
+ default:
+ return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
+ }
+ if err != nil {
+ return nil, &OpError{Op: "listen", Net: sl.network, Source: nil, Addr: la, Err: err} // c is non-nil interface containing nil pointer
+ }
+ return c, nil
+}
+
// sysListener contains a Listen's parameters and configuration.
type sysListener struct {
+ ListenConfig
network, address string
}
@@ -587,23 +670,8 @@
// See func Dial for a description of the network and address
// parameters.
func Listen(network, address string) (Listener, error) {
- addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil)
- if err != nil {
- return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
- }
- var l Listener
- switch la := addrs.first(isIPv4).(type) {
- case *TCPAddr:
- l, err = ListenTCP(network, la)
- case *UnixAddr:
- l, err = ListenUnix(network, la)
- default:
- return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
- }
- if err != nil {
- return nil, err // l is non-nil interface containing nil pointer
- }
- return l, nil
+ var lc ListenConfig
+ return lc.Listen(context.Background(), network, address)
}
// ListenPacket announces on the local network address.
@@ -629,23 +697,6 @@
// See func Dial for a description of the network and address
// parameters.
func ListenPacket(network, address string) (PacketConn, error) {
- addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil)
- if err != nil {
- return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
- }
- var l PacketConn
- switch la := addrs.first(isIPv4).(type) {
- case *UDPAddr:
- l, err = ListenUDP(network, la)
- case *IPAddr:
- l, err = ListenIP(network, la)
- case *UnixAddr:
- l, err = ListenUnixgram(network, la)
- default:
- return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
- }
- if err != nil {
- return nil, err // l is non-nil interface containing nil pointer
- }
- return l, nil
+ var lc ListenConfig
+ return lc.ListenPacket(context.Background(), network, address)
}
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 96d8921..3934ad8 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -912,6 +912,57 @@
c.Close()
}
+func TestDialerControl(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ t.Run("StreamDial", func(t *testing.T) {
+ for _, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} {
+ if !testableNetwork(network) {
+ continue
+ }
+ ln, err := newLocalListener(network)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ defer ln.Close()
+ d := Dialer{Control: controlOnConnSetup}
+ c, err := d.Dial(network, ln.Addr().String())
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ c.Close()
+ }
+ })
+ t.Run("PacketDial", func(t *testing.T) {
+ for _, network := range []string{"udp", "udp4", "udp6", "unixgram"} {
+ if !testableNetwork(network) {
+ continue
+ }
+ c1, err := newLocalPacketListener(network)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ if network == "unixgram" {
+ defer os.Remove(c1.LocalAddr().String())
+ }
+ defer c1.Close()
+ d := Dialer{Control: controlOnConnSetup}
+ c2, err := d.Dial(network, c1.LocalAddr().String())
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ c2.Close()
+ }
+ })
+}
+
// mustHaveExternalNetwork is like testenv.MustHaveExternalNetwork
// except that it won't skip testing on non-iOS builders.
func mustHaveExternalNetwork(t *testing.T) {
diff --git a/src/net/iprawsock_posix.go b/src/net/iprawsock_posix.go
index 7dafd20..b2f5791 100644
--- a/src/net/iprawsock_posix.go
+++ b/src/net/iprawsock_posix.go
@@ -122,7 +122,7 @@
default:
return nil, UnknownNetworkError(sd.network)
}
- fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial")
+ fd, err := internetSocket(ctx, network, laddr, raddr, syscall.SOCK_RAW, proto, "dial", sd.Dialer.Control)
if err != nil {
return nil, err
}
@@ -139,7 +139,7 @@
default:
return nil, UnknownNetworkError(sl.network)
}
- fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen")
+ fd, err := internetSocket(ctx, network, laddr, nil, syscall.SOCK_RAW, proto, "listen", sl.ListenConfig.Control)
if err != nil {
return nil, err
}
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index 8372aaa..eddd411 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -133,12 +133,12 @@
return syscall.AF_INET6, false
}
-func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string) (fd *netFD, err error) {
+func internetSocket(ctx context.Context, net string, laddr, raddr sockaddr, sotype, proto int, mode string, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) {
if (runtime.GOOS == "windows" || runtime.GOOS == "openbsd" || runtime.GOOS == "nacl") && mode == "dial" && raddr.isWildcard() {
raddr = raddr.toLocal(net)
}
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
- return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr)
+ return socket(ctx, net, family, sotype, proto, ipv6only, laddr, raddr, ctrlFn)
}
func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
diff --git a/src/net/listen_test.go b/src/net/listen_test.go
index 96624f9..ffd38d7 100644
--- a/src/net/listen_test.go
+++ b/src/net/listen_test.go
@@ -7,6 +7,7 @@
package net
import (
+ "context"
"fmt"
"internal/testenv"
"os"
@@ -729,3 +730,56 @@
}
ln2.Close()
}
+
+func TestListenConfigControl(t *testing.T) {
+ switch runtime.GOOS {
+ case "nacl", "plan9":
+ t.Skipf("not supported on %s", runtime.GOOS)
+ }
+
+ t.Run("StreamListen", func(t *testing.T) {
+ for _, network := range []string{"tcp", "tcp4", "tcp6", "unix", "unixpacket"} {
+ if !testableNetwork(network) {
+ continue
+ }
+ ln, err := newLocalListener(network)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ address := ln.Addr().String()
+ ln.Close()
+ lc := ListenConfig{Control: controlOnConnSetup}
+ ln, err = lc.Listen(context.Background(), network, address)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ ln.Close()
+ }
+ })
+ t.Run("PacketListen", func(t *testing.T) {
+ for _, network := range []string{"udp", "udp4", "udp6", "unixgram"} {
+ if !testableNetwork(network) {
+ continue
+ }
+ c, err := newLocalPacketListener(network)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ address := c.LocalAddr().String()
+ c.Close()
+ if network == "unixgram" {
+ os.Remove(address)
+ }
+ lc := ListenConfig{Control: controlOnConnSetup}
+ c, err = lc.ListenPacket(context.Background(), network, address)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+ c.Close()
+ }
+ })
+}
diff --git a/src/net/rawconn_stub_test.go b/src/net/rawconn_stub_test.go
index 391b4d1..3e3b6bf 100644
--- a/src/net/rawconn_stub_test.go
+++ b/src/net/rawconn_stub_test.go
@@ -22,3 +22,7 @@
func controlRawConn(c syscall.RawConn, addr Addr) error {
return errors.New("not supported")
}
+
+func controlOnConnSetup(network string, address string, c syscall.RawConn) error {
+ return nil
+}
diff --git a/src/net/rawconn_unix_test.go b/src/net/rawconn_unix_test.go
index 2fe4d2c..a720a8a 100644
--- a/src/net/rawconn_unix_test.go
+++ b/src/net/rawconn_unix_test.go
@@ -6,7 +6,10 @@
package net
-import "syscall"
+import (
+ "errors"
+ "syscall"
+)
func readRawConn(c syscall.RawConn, b []byte) (int, error) {
var operr error
@@ -89,3 +92,36 @@
}
return nil
}
+
+func controlOnConnSetup(network string, address string, c syscall.RawConn) error {
+ var operr error
+ var fn func(uintptr)
+ switch network {
+ case "tcp", "udp", "ip":
+ return errors.New("ambiguous network: " + network)
+ case "unix", "unixpacket", "unixgram":
+ fn = func(s uintptr) {
+ _, operr = syscall.GetsockoptInt(int(s), syscall.SOL_SOCKET, syscall.SO_ERROR)
+ }
+ default:
+ switch network[len(network)-1] {
+ case '4':
+ fn = func(s uintptr) {
+ operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1)
+ }
+ case '6':
+ fn = func(s uintptr) {
+ operr = syscall.SetsockoptInt(int(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1)
+ }
+ default:
+ return errors.New("unknown network: " + network)
+ }
+ }
+ if err := c.Control(fn); err != nil {
+ return err
+ }
+ if operr != nil {
+ return operr
+ }
+ return nil
+}
diff --git a/src/net/rawconn_windows_test.go b/src/net/rawconn_windows_test.go
index 6df101e..2774c97 100644
--- a/src/net/rawconn_windows_test.go
+++ b/src/net/rawconn_windows_test.go
@@ -5,6 +5,7 @@
package net
import (
+ "errors"
"syscall"
"unsafe"
)
@@ -96,3 +97,32 @@
}
return nil
}
+
+func controlOnConnSetup(network string, address string, c syscall.RawConn) error {
+ var operr error
+ var fn func(uintptr)
+ switch network {
+ case "tcp", "udp", "ip":
+ return errors.New("ambiguous network: " + network)
+ default:
+ switch network[len(network)-1] {
+ case '4':
+ fn = func(s uintptr) {
+ operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IP, syscall.IP_TTL, 1)
+ }
+ case '6':
+ fn = func(s uintptr) {
+ operr = syscall.SetsockoptInt(syscall.Handle(s), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, 1)
+ }
+ default:
+ return errors.New("unknown network: " + network)
+ }
+ }
+ if err := c.Control(fn); err != nil {
+ return err
+ }
+ if operr != nil {
+ return operr
+ }
+ return nil
+}
diff --git a/src/net/sock_posix.go b/src/net/sock_posix.go
index 8cfc42e..00ff3fd 100644
--- a/src/net/sock_posix.go
+++ b/src/net/sock_posix.go
@@ -38,7 +38,7 @@
// socket returns a network file descriptor that is ready for
// asynchronous I/O using the network poller.
-func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr) (fd *netFD, err error) {
+func socket(ctx context.Context, net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) (fd *netFD, err error) {
s, err := sysSocket(family, sotype, proto)
if err != nil {
return nil, err
@@ -77,26 +77,41 @@
if laddr != nil && raddr == nil {
switch sotype {
case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
- if err := fd.listenStream(laddr, listenerBacklog); err != nil {
+ if err := fd.listenStream(laddr, listenerBacklog, ctrlFn); err != nil {
fd.Close()
return nil, err
}
return fd, nil
case syscall.SOCK_DGRAM:
- if err := fd.listenDatagram(laddr); err != nil {
+ if err := fd.listenDatagram(laddr, ctrlFn); err != nil {
fd.Close()
return nil, err
}
return fd, nil
}
}
- if err := fd.dial(ctx, laddr, raddr); err != nil {
+ if err := fd.dial(ctx, laddr, raddr, ctrlFn); err != nil {
fd.Close()
return nil, err
}
return fd, nil
}
+func (fd *netFD) ctrlNetwork() string {
+ switch fd.net {
+ case "unix", "unixgram", "unixpacket":
+ return fd.net
+ }
+ switch fd.net[len(fd.net)-1] {
+ case '4', '6':
+ return fd.net
+ }
+ if fd.family == syscall.AF_INET {
+ return fd.net + "4"
+ }
+ return fd.net + "6"
+}
+
func (fd *netFD) addrFunc() func(syscall.Sockaddr) Addr {
switch fd.family {
case syscall.AF_INET, syscall.AF_INET6:
@@ -121,14 +136,29 @@
return func(syscall.Sockaddr) Addr { return nil }
}
-func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr) error {
+func (fd *netFD) dial(ctx context.Context, laddr, raddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) error {
+ if ctrlFn != nil {
+ c, err := newRawConn(fd)
+ if err != nil {
+ return err
+ }
+ var ctrlAddr string
+ if raddr != nil {
+ ctrlAddr = raddr.String()
+ } else if laddr != nil {
+ ctrlAddr = laddr.String()
+ }
+ if err := ctrlFn(fd.ctrlNetwork(), ctrlAddr, c); err != nil {
+ return err
+ }
+ }
var err error
var lsa syscall.Sockaddr
if laddr != nil {
if lsa, err = laddr.sockaddr(fd.family); err != nil {
return err
} else if lsa != nil {
- if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
+ if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
return os.NewSyscallError("bind", err)
}
}
@@ -165,29 +195,39 @@
return nil
}
-func (fd *netFD) listenStream(laddr sockaddr, backlog int) error {
- if err := setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil {
+func (fd *netFD) listenStream(laddr sockaddr, backlog int, ctrlFn func(string, string, syscall.RawConn) error) error {
+ var err error
+ if err = setDefaultListenerSockopts(fd.pfd.Sysfd); err != nil {
return err
}
- if lsa, err := laddr.sockaddr(fd.family); err != nil {
+ var lsa syscall.Sockaddr
+ if lsa, err = laddr.sockaddr(fd.family); err != nil {
return err
- } else if lsa != nil {
- if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
- return os.NewSyscallError("bind", err)
+ }
+ if ctrlFn != nil {
+ c, err := newRawConn(fd)
+ if err != nil {
+ return err
+ }
+ if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil {
+ return err
}
}
- if err := listenFunc(fd.pfd.Sysfd, backlog); err != nil {
+ if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
+ }
+ if err = listenFunc(fd.pfd.Sysfd, backlog); err != nil {
return os.NewSyscallError("listen", err)
}
- if err := fd.init(); err != nil {
+ if err = fd.init(); err != nil {
return err
}
- lsa, _ := syscall.Getsockname(fd.pfd.Sysfd)
+ lsa, _ = syscall.Getsockname(fd.pfd.Sysfd)
fd.setAddr(fd.addrFunc()(lsa), nil)
return nil
}
-func (fd *netFD) listenDatagram(laddr sockaddr) error {
+func (fd *netFD) listenDatagram(laddr sockaddr, ctrlFn func(string, string, syscall.RawConn) error) error {
switch addr := laddr.(type) {
case *UDPAddr:
// We provide a socket that listens to a wildcard
@@ -211,17 +251,27 @@
laddr = &addr
}
}
- if lsa, err := laddr.sockaddr(fd.family); err != nil {
+ var err error
+ var lsa syscall.Sockaddr
+ if lsa, err = laddr.sockaddr(fd.family); err != nil {
return err
- } else if lsa != nil {
- if err := syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
- return os.NewSyscallError("bind", err)
+ }
+ if ctrlFn != nil {
+ c, err := newRawConn(fd)
+ if err != nil {
+ return err
+ }
+ if err := ctrlFn(fd.ctrlNetwork(), laddr.String(), c); err != nil {
+ return err
}
}
- if err := fd.init(); err != nil {
+ if err = syscall.Bind(fd.pfd.Sysfd, lsa); err != nil {
+ return os.NewSyscallError("bind", err)
+ }
+ if err = fd.init(); err != nil {
return err
}
- lsa, _ := syscall.Getsockname(fd.pfd.Sysfd)
+ lsa, _ = syscall.Getsockname(fd.pfd.Sysfd)
fd.setAddr(fd.addrFunc()(lsa), nil)
return nil
}
diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go
index 6061c16..bcf7592 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -62,7 +62,7 @@
}
func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) {
- fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial")
+ fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control)
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@@ -92,7 +92,7 @@
if err == nil {
fd.Close()
}
- fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial")
+ fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control)
}
if err != nil {
@@ -156,7 +156,7 @@
}
func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) {
- fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen")
+ fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control)
if err != nil {
return nil, err
}
diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go
index 4e96f47..8f4b71c 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -95,7 +95,7 @@
}
func (sd *sysDialer) dialUDP(ctx context.Context, laddr, raddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial")
+ fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_DGRAM, 0, "dial", sd.Dialer.Control)
if err != nil {
return nil, err
}
@@ -103,7 +103,7 @@
}
func (sl *sysListener) listenUDP(ctx context.Context, laddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_DGRAM, 0, "listen")
+ fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_DGRAM, 0, "listen", sl.ListenConfig.Control)
if err != nil {
return nil, err
}
@@ -111,7 +111,7 @@
}
func (sl *sysListener) listenMulticastUDP(ctx context.Context, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
- fd, err := internetSocket(ctx, sl.network, gaddr, nil, syscall.SOCK_DGRAM, 0, "listen")
+ fd, err := internetSocket(ctx, sl.network, gaddr, nil, syscall.SOCK_DGRAM, 0, "listen", sl.ListenConfig.Control)
if err != nil {
return nil, err
}
diff --git a/src/net/unixsock_posix.go b/src/net/unixsock_posix.go
index f627567..2495da1 100644
--- a/src/net/unixsock_posix.go
+++ b/src/net/unixsock_posix.go
@@ -13,7 +13,7 @@
"syscall"
)
-func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string) (*netFD, error) {
+func unixSocket(ctx context.Context, net string, laddr, raddr sockaddr, mode string, ctrlFn func(string, string, syscall.RawConn) error) (*netFD, error) {
var sotype int
switch net {
case "unix":
@@ -42,7 +42,7 @@
return nil, errors.New("unknown mode: " + mode)
}
- fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr)
+ fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, ctrlFn)
if err != nil {
return nil, err
}
@@ -151,7 +151,7 @@
}
func (sd *sysDialer) dialUnix(ctx context.Context, laddr, raddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(ctx, sd.network, laddr, raddr, "dial")
+ fd, err := unixSocket(ctx, sd.network, laddr, raddr, "dial", sd.Dialer.Control)
if err != nil {
return nil, err
}
@@ -207,7 +207,7 @@
}
func (sl *sysListener) listenUnix(ctx context.Context, laddr *UnixAddr) (*UnixListener, error) {
- fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen")
+ fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen", sl.ListenConfig.Control)
if err != nil {
return nil, err
}
@@ -215,7 +215,7 @@
}
func (sl *sysListener) listenUnixgram(ctx context.Context, laddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen")
+ fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen", sl.ListenConfig.Control)
if err != nil {
return nil, err
}
To view, visit change 72810. To unsubscribe, or for help writing mail filters, visit settings.