From ef6806fb13b1db44a57e4f26908803d55ed28e81 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Thu, 8 Nov 2012 10:35:16 -0600 Subject: [PATCH] net: close fds eagerly in DialTimeout Integrates with the pollserver now. Uses the old implementation on windows and plan9. Fixes #2631 R=paul, iant, adg, bendaglish, rsc CC=golang-dev https://golang.org/cl/6815049 --- src/pkg/net/dial.go | 77 ++++++++++++++++++++------------- src/pkg/net/dial_test.go | 78 ++++++++++++++++++++++++++++++++++ src/pkg/net/fd_unix.go | 12 ++++-- src/pkg/net/iprawsock.go | 14 ++++-- src/pkg/net/iprawsock_plan9.go | 4 ++ src/pkg/net/iprawsock_posix.go | 9 +++- src/pkg/net/ipsock.go | 11 ++--- src/pkg/net/ipsock_posix.go | 9 ++-- src/pkg/net/lookup.go | 41 ++++++++++++++++++ src/pkg/net/net.go | 2 + src/pkg/net/sock_posix.go | 7 ++- src/pkg/net/tcpsock.go | 8 +++- src/pkg/net/tcpsock_plan9.go | 12 +++++- src/pkg/net/tcpsock_posix.go | 10 +++-- src/pkg/net/udpsock.go | 11 ++++- src/pkg/net/udpsock_plan9.go | 8 ++++ src/pkg/net/udpsock_posix.go | 15 +++++-- src/pkg/net/unixsock_plan9.go | 4 ++ src/pkg/net/unixsock_posix.go | 14 +++--- 19 files changed, 281 insertions(+), 65 deletions(-) diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go index 752f81b618c..a85e3c673bf 100644 --- a/src/pkg/net/dial.go +++ b/src/pkg/net/dial.go @@ -5,6 +5,7 @@ package net import ( + "runtime" "time" ) @@ -36,7 +37,7 @@ func parseDialNetwork(net string) (afnet string, proto int, err error) { return "", 0, UnknownNetworkError(net) } -func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) { +func resolveNetAddr(op, net, addr string, deadline time.Time) (afnet string, a Addr, err error) { afnet, _, err = parseDialNetwork(net) if err != nil { return "", nil, &OpError{op, net, nil, err} @@ -44,25 +45,25 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) { if op == "dial" && addr == "" { return "", nil, &OpError{op, net, nil, errMissingAddress} } + a, err = resolveAfnetAddr(afnet, addr, deadline) + return +} + +func resolveAfnetAddr(afnet, addr string, deadline time.Time) (Addr, error) { + if addr == "" { + return nil, nil + } switch afnet { case "tcp", "tcp4", "tcp6": - if addr != "" { - a, err = ResolveTCPAddr(afnet, addr) - } + return resolveTCPAddr(afnet, addr, deadline) case "udp", "udp4", "udp6": - if addr != "" { - a, err = ResolveUDPAddr(afnet, addr) - } + return resolveUDPAddr(afnet, addr, deadline) case "ip", "ip4", "ip6": - if addr != "" { - a, err = ResolveIPAddr(afnet, addr) - } + return resolveIPAddr(afnet, addr, deadline) case "unix", "unixgram", "unixpacket": - if addr != "" { - a, err = ResolveUnixAddr(afnet, addr) - } + return ResolveUnixAddr(afnet, addr) } - return + return nil, nil } // Dial connects to the address addr on the network net. @@ -89,23 +90,23 @@ func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) { // Dial("ip6:ospf", "::1") // func Dial(net, addr string) (Conn, error) { - _, addri, err := resolveNetAddr("dial", net, addr) + _, addri, err := resolveNetAddr("dial", net, addr, noDeadline) if err != nil { return nil, err } - return dialAddr(net, addr, addri) + return dialAddr(net, addr, addri, noDeadline) } -func dialAddr(net, addr string, addri Addr) (c Conn, err error) { +func dialAddr(net, addr string, addri Addr, deadline time.Time) (c Conn, err error) { switch ra := addri.(type) { case *TCPAddr: - c, err = DialTCP(net, nil, ra) + c, err = dialTCP(net, nil, ra, deadline) case *UDPAddr: - c, err = DialUDP(net, nil, ra) + c, err = dialUDP(net, nil, ra, deadline) case *IPAddr: - c, err = DialIP(net, nil, ra) + c, err = dialIP(net, nil, ra, deadline) case *UnixAddr: - c, err = DialUnix(net, nil, ra) + c, err = dialUnix(net, nil, ra, deadline) default: err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)} } @@ -115,13 +116,31 @@ func dialAddr(net, addr string, addri Addr) (c Conn, err error) { return } +const useDialTimeoutRace = runtime.GOOS == "windows" || runtime.GOOS == "plan9" + // DialTimeout acts like Dial but takes a timeout. // The timeout includes name resolution, if required. func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) { - // TODO(bradfitz): the timeout should be pushed down into the - // net package's event loop, so on timeout to dead hosts we - // don't have a goroutine sticking around for the default of - // ~3 minutes. + if useDialTimeoutRace { + // On windows and plan9, use the relatively inefficient + // goroutine-racing implementation of DialTimeout that + // doesn't push down deadlines to the pollster. + // TODO: remove this once those are implemented. + return dialTimeoutRace(net, addr, timeout) + } + deadline := time.Now().Add(timeout) + _, addri, err := resolveNetAddr("dial", net, addr, deadline) + if err != nil { + return nil, err + } + return dialAddr(net, addr, addri, deadline) +} + +// dialTimeoutRace is the old implementation of DialTimeout, still used +// on operating systems where the deadline hasn't been pushed down +// into the pollserver. +// TODO: fix this on Windows and plan9. +func dialTimeoutRace(net, addr string, timeout time.Duration) (Conn, error) { t := time.NewTimer(timeout) defer t.Stop() type pair struct { @@ -131,13 +150,13 @@ func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) { ch := make(chan pair, 1) resolvedAddr := make(chan Addr, 1) go func() { - _, addri, err := resolveNetAddr("dial", net, addr) + _, addri, err := resolveNetAddr("dial", net, addr, noDeadline) if err != nil { ch <- pair{nil, err} return } resolvedAddr <- addri // in case we need it for OpError - c, err := dialAddr(net, addr, addri) + c, err := dialAddr(net, addr, addri, noDeadline) ch <- pair{c, err} }() select { @@ -175,7 +194,7 @@ func (a stringAddr) String() string { return a.addr } // The network string net must be a stream-oriented network: // "tcp", "tcp4", "tcp6", "unix" or "unixpacket". func Listen(net, laddr string) (Listener, error) { - afnet, a, err := resolveNetAddr("listen", net, laddr) + afnet, a, err := resolveNetAddr("listen", net, laddr, noDeadline) if err != nil { return nil, err } @@ -200,7 +219,7 @@ func Listen(net, laddr string) (Listener, error) { // The network string net must be a packet-oriented network: // "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram". func ListenPacket(net, addr string) (PacketConn, error) { - afnet, a, err := resolveNetAddr("listen", net, addr) + afnet, a, err := resolveNetAddr("listen", net, addr, noDeadline) if err != nil { return nil, err } diff --git a/src/pkg/net/dial_test.go b/src/pkg/net/dial_test.go index 869c3cb966e..34a15f27b1a 100644 --- a/src/pkg/net/dial_test.go +++ b/src/pkg/net/dial_test.go @@ -7,6 +7,8 @@ package net import ( "flag" "fmt" + "io" + "os" "regexp" "runtime" "testing" @@ -222,3 +224,79 @@ func TestDialError(t *testing.T) { } } } + +func TestDialTimeoutFDLeak(t *testing.T) { + if runtime.GOOS != "linux" { + // TODO(bradfitz): test on other platforms + t.Logf("skipping test on %s", runtime.GOOS) + return + } + + ln := newLocalListener(t) + defer ln.Close() + + type connErr struct { + conn Conn + err error + } + dials := listenerBacklog + 100 + maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows? + resc := make(chan connErr) + for i := 0; i < dials; i++ { + go func() { + conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond) + resc <- connErr{conn, err} + }() + } + + var firstErr string + var ngood int + var toClose []io.Closer + for i := 0; i < dials; i++ { + ce := <-resc + if ce.err == nil { + ngood++ + if ngood > maxGoodConnect { + t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect) + } + toClose = append(toClose, ce.conn) + continue + } + err := ce.err + if firstErr == "" { + firstErr = err.Error() + } else if err.Error() != firstErr { + t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err) + } + } + for _, c := range toClose { + c.Close() + } + for i := 0; i < 100; i++ { + if got := numFD(); got < dials { + // Test passes. + return + } + time.Sleep(10 * time.Millisecond) + } + if got := numFD(); got >= dials { + t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials) + } +} + +func numFD() int { + if runtime.GOOS == "linux" { + f, err := os.Open("/proc/self/fd") + if err != nil { + panic(err) + } + defer f.Close() + names, err := f.Readdirnames(0) + if err != nil { + panic(err) + } + return len(names) + } + // All tests using this should be skipped anyway, but: + panic("numFDs not implemented on " + runtime.GOOS) +} diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go index e1d1256fa1d..7f82f203e1d 100644 --- a/src/pkg/net/fd_unix.go +++ b/src/pkg/net/fd_unix.go @@ -162,7 +162,7 @@ func (s *pollServer) CheckDeadlines() { // TODO(rsc): This will need to be handled more efficiently, // probably with a heap indexed by wakeup time. - var next_deadline int64 + var nextDeadline int64 for key, fd := range s.pending { var t int64 var mode int @@ -187,12 +187,12 @@ func (s *pollServer) CheckDeadlines() { fd.wdeadline = -1 } s.WakeFD(fd, mode, nil) - } else if next_deadline == 0 || t < next_deadline { - next_deadline = t + } else if nextDeadline == 0 || t < nextDeadline { + nextDeadline = t } } } - s.deadline = next_deadline + s.deadline = nextDeadline } func (s *pollServer) Run() { @@ -332,10 +332,14 @@ func (fd *netFD) name() string { func (fd *netFD) connect(ra syscall.Sockaddr) error { err := syscall.Connect(fd.sysfd, ra) + hadTimeout := fd.wdeadline > 0 if err == syscall.EINPROGRESS { if err = fd.pollServer.WaitWrite(fd); err != nil { return err } + if hadTimeout && fd.wdeadline < 0 { + return errTimeout + } var e int e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR) if err != nil { diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go index ae21b3c3dde..d7bffc69e95 100644 --- a/src/pkg/net/iprawsock.go +++ b/src/pkg/net/iprawsock.go @@ -6,6 +6,10 @@ package net +import ( + "time" +) + // IPAddr represents the address of an IP end point. type IPAddr struct { IP IP @@ -26,7 +30,11 @@ func (a *IPAddr) String() string { // "ip", "ip4" or "ip6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]". func ResolveIPAddr(net, addr string) (*IPAddr, error) { - ip, err := hostToIP(net, addr) + return resolveIPAddr(net, addr, noDeadline) +} + +func resolveIPAddr(net, addr string, deadline time.Time) (*IPAddr, error) { + ip, err := hostToIP(net, addr, deadline) if err != nil { return nil, err } @@ -34,7 +42,7 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) { } // Convert "host" into IP address. -func hostToIP(net, host string) (ip IP, err error) { +func hostToIP(net, host string, deadline time.Time) (ip IP, err error) { var addr IP // Try as an IP address. addr = ParseIP(host) @@ -47,7 +55,7 @@ func hostToIP(net, host string) (ip IP, err error) { filter = ipv6only } // Not an IP address. Try as a DNS name. - addrs, err1 := LookupHost(host) + addrs, err1 := lookupHostDeadline(host, deadline) if err1 != nil { err = err1 goto Error diff --git a/src/pkg/net/iprawsock_plan9.go b/src/pkg/net/iprawsock_plan9.go index 6de2ee33d84..e77c5476afa 100644 --- a/src/pkg/net/iprawsock_plan9.go +++ b/src/pkg/net/iprawsock_plan9.go @@ -130,6 +130,10 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error // netProto, which must be "ip", "ip4", or "ip6" followed by a colon // and a protocol number or name. func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { + return dialIP(netProto, laddr, raddr, noDeadline) +} + +func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) { return nil, syscall.EPLAN9 } diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go index d0f0b567ac8..4d8b5341d97 100644 --- a/src/pkg/net/iprawsock_posix.go +++ b/src/pkg/net/iprawsock_posix.go @@ -10,6 +10,7 @@ package net import ( "syscall" + "time" ) func sockaddrToIP(sa syscall.Sockaddr) Addr { @@ -163,6 +164,10 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error // DialIP connects to the remote address raddr on the network protocol netProto, // which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name. func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { + return dialIP(netProto, laddr, raddr, noDeadline) +} + +func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) { net, proto, err := parseDialNetwork(netProto) if err != nil { return nil, err @@ -175,7 +180,7 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { if raddr == nil { return nil, &OpError{"dial", netProto, nil, errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP) + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) if err != nil { return nil, err } @@ -196,7 +201,7 @@ func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) { default: return nil, UnknownNetworkError(net) } - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "listen", sockaddrToIP) + fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP) if err != nil { return nil, err } diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go index b9b2a9b81ec..d1fcb4852f7 100644 --- a/src/pkg/net/ipsock.go +++ b/src/pkg/net/ipsock.go @@ -6,12 +6,9 @@ package net -var supportsIPv6, supportsIPv4map bool +import "time" -func init() { - sysInit() - supportsIPv6, supportsIPv4map = probeIPv6Stack() -} +var supportsIPv6, supportsIPv4map = probeIPv6Stack() func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { if filter == nil { @@ -103,7 +100,7 @@ func JoinHostPort(host, port string) string { } // Convert "host:port" into IP address and port. -func hostPortToIP(net, hostport string) (ip IP, iport int, err error) { +func hostPortToIP(net, hostport string, deadline time.Time) (ip IP, iport int, err error) { host, port, err := SplitHostPort(hostport) if err != nil { return nil, 0, err @@ -122,7 +119,7 @@ func hostPortToIP(net, hostport string) (ip IP, iport int, err error) { filter = ipv6only } // Not an IP address. Try as a DNS name. - addrs, err := LookupHost(host) + addrs, err := lookupHostDeadline(host, deadline) if err != nil { return nil, 0, err } diff --git a/src/pkg/net/ipsock_posix.go b/src/pkg/net/ipsock_posix.go index 171889207d7..87a2288973b 100644 --- a/src/pkg/net/ipsock_posix.go +++ b/src/pkg/net/ipsock_posix.go @@ -6,7 +6,10 @@ package net -import "syscall" +import ( + "syscall" + "time" +) // Should we try to use the IPv4 socket interface if we're // only dealing with IPv4 sockets? As long as the host system @@ -125,7 +128,7 @@ type sockaddr interface { sockaddr(family int) (syscall.Sockaddr, error) } -func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { +func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { var la, ra syscall.Sockaddr family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) if laddr != nil { @@ -138,7 +141,7 @@ func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode s goto Error } } - fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, toAddr) + fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, deadline, toAddr) if err != nil { goto Error } diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go index 533b3511a22..bec93ec08cd 100644 --- a/src/pkg/net/lookup.go +++ b/src/pkg/net/lookup.go @@ -4,12 +4,53 @@ package net +import ( + "time" +) + // LookupHost looks up the given host using the local resolver. // It returns an array of that host's addresses. func LookupHost(host string) (addrs []string, err error) { return lookupHost(host) } +func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err error) { + if deadline.IsZero() { + return lookupHost(host) + } + + // TODO(bradfitz): consider pushing the deadline down into the + // name resolution functions. But that involves fixing it for + // the native Go resolver, cgo, Windows, etc. + // + // In the meantime, just use a goroutine. Most users affected + // by http://golang.org/issue/2631 are due to TCP connections + // to unresponsive hosts, not DNS. + timeout := deadline.Sub(time.Now()) + if timeout <= 0 { + err = errTimeout + return + } + t := time.NewTimer(timeout) + defer t.Stop() + type res struct { + addrs []string + err error + } + resc := make(chan res, 1) + go func() { + a, err := lookupHost(host) + resc <- res{a, err} + }() + select { + case <-t.C: + err = errTimeout + case r := <-resc: + addrs, err = r.addrs, r.err + } + return +} + // LookupIP looks up host using the local resolver. // It returns an array of that host's IPv4 and IPv6 addresses. func LookupIP(host string) (addrs []IP, err error) { diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go index d6563e0a230..4f0edd4d29a 100644 --- a/src/pkg/net/net.go +++ b/src/pkg/net/net.go @@ -204,6 +204,8 @@ func (e *OpError) Temporary() bool { return ok && t.Temporary() } +var noDeadline = time.Time{} + type timeout interface { Timeout() bool } diff --git a/src/pkg/net/sock_posix.go b/src/pkg/net/sock_posix.go index dc5247a7f38..a3354eacb1b 100644 --- a/src/pkg/net/sock_posix.go +++ b/src/pkg/net/sock_posix.go @@ -11,12 +11,13 @@ package net import ( "io" "syscall" + "time" ) var listenerBacklog = maxListenerBacklog() // Generic socket creation. -func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { +func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { // See ../syscall/exec_unix.go for description of ForkLock. syscall.ForkLock.RLock() s, err := syscall.Socket(f, t, p) @@ -50,12 +51,16 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, } if ursa != nil { + if !deadline.IsZero() { + fd.wdeadline = deadline.UnixNano() + } if err = fd.connect(ursa); err != nil { closesocket(s) fd.Close() return nil, err } fd.isConnected = true + fd.wdeadline = 0 } lsa, _ := syscall.Getsockname(s) diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go index 47fbf29198c..6aba1f89fc8 100644 --- a/src/pkg/net/tcpsock.go +++ b/src/pkg/net/tcpsock.go @@ -6,6 +6,8 @@ package net +import "time" + // TCPAddr represents the address of a TCP end point. type TCPAddr struct { IP IP @@ -28,7 +30,11 @@ func (a *TCPAddr) String() string { // "tcp4" or "tcp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". func ResolveTCPAddr(net, addr string) (*TCPAddr, error) { - ip, port, err := hostPortToIP(net, addr) + return resolveTCPAddr(net, addr, noDeadline) +} + +func resolveTCPAddr(net, addr string, deadline time.Time) (*TCPAddr, error) { + ip, port, err := hostPortToIP(net, addr, deadline) if err != nil { return nil, err } diff --git a/src/pkg/net/tcpsock_plan9.go b/src/pkg/net/tcpsock_plan9.go index 4121dd89e9a..a77633b355e 100644 --- a/src/pkg/net/tcpsock_plan9.go +++ b/src/pkg/net/tcpsock_plan9.go @@ -6,7 +6,10 @@ package net -import "syscall" +import ( + "syscall" + "time" +) // TCPConn is an implementation of the Conn interface for TCP network // connections. @@ -36,6 +39,13 @@ func (c *TCPConn) CloseWrite() error { // which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is // used as the local address for the connection. func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) { + return dialTCP(net, laddr, raddr, noDeadline) +} + +func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (c *TCPConn, err error) { + if !deadline.IsZero() { + panic("net.dialTCP: deadline not implemented on Plan 9") + } switch net { case "tcp", "tcp4", "tcp6": default: diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go index 2c34d2fda7d..09654a4b580 100644 --- a/src/pkg/net/tcpsock_posix.go +++ b/src/pkg/net/tcpsock_posix.go @@ -143,11 +143,15 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error { // which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used // as the local address for the connection. func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { + return dialTCP(net, laddr, raddr, noDeadline) +} + +func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) { if raddr == nil { return nil, &OpError{"dial", net, nil, errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) // TCP has a rarely used mechanism called a 'simultaneous connection' in // which Dial("tcp", addr1, addr2) run on the machine at addr1 can @@ -177,7 +181,7 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { if err == nil { fd.Close() } - fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) } if err != nil { @@ -225,7 +229,7 @@ type TCPListener struct { // If laddr has a port of 0, it means to listen on some available port. // The caller can use l.Addr() to retrieve the chosen address. func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) { - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) + fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) if err != nil { return nil, err } diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go index 62b27d95e9c..bf2107b03a2 100644 --- a/src/pkg/net/udpsock.go +++ b/src/pkg/net/udpsock.go @@ -6,7 +6,10 @@ package net -import "errors" +import ( + "errors" + "time" +) var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP") @@ -32,7 +35,11 @@ func (a *UDPAddr) String() string { // "udp4" or "udp6". A literal IPv6 host address must be // enclosed in square brackets, as in "[::]:80". func ResolveUDPAddr(net, addr string) (*UDPAddr, error) { - ip, port, err := hostPortToIP(net, addr) + return resolveUDPAddr(net, addr, noDeadline) +} + +func resolveUDPAddr(net, addr string, deadline time.Time) (*UDPAddr, error) { + ip, port, err := hostPortToIP(net, addr, deadline) if err != nil { return nil, err } diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go index aaa7e5b28cb..c04660baa27 100644 --- a/src/pkg/net/udpsock_plan9.go +++ b/src/pkg/net/udpsock_plan9.go @@ -10,6 +10,7 @@ import ( "errors" "os" "syscall" + "time" ) // UDPConn is the implementation of the Conn and PacketConn @@ -122,6 +123,13 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er // which must be "udp", "udp4", or "udp6". If laddr is not nil, it is // used as the local address for the connection. func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) { + return dialUDP(net, laddr, raddr, noDeadline) +} + +func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (c *UDPConn, err error) { + if !deadline.IsZero() { + panic("net.dialUDP: deadline not implemented on Plan 9") + } switch net { case "udp", "udp4", "udp6": default: diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go index e075380c8e3..f6e2c17c3cb 100644 --- a/src/pkg/net/udpsock_posix.go +++ b/src/pkg/net/udpsock_posix.go @@ -8,7 +8,10 @@ package net -import "syscall" +import ( + "syscall" + "time" +) func sockaddrToUDP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { @@ -160,6 +163,10 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er // which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used // as the local address for the connection. func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { + return dialUDP(net, laddr, raddr, noDeadline) +} + +func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) { switch net { case "udp", "udp4", "udp6": default: @@ -168,7 +175,7 @@ func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { if raddr == nil { return nil, &OpError{"dial", net, nil, errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) if err != nil { return nil, err } @@ -188,7 +195,7 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { if laddr == nil { return nil, &OpError{"listen", net, nil, errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) if err != nil { return nil, err } @@ -208,7 +215,7 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e if gaddr == nil || gaddr.IP == nil { return nil, &OpError{"listenmulticast", net, nil, errMissingAddress} } - fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + fd, err := internetSocket(net, gaddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) if err != nil { return nil, err } diff --git a/src/pkg/net/unixsock_plan9.go b/src/pkg/net/unixsock_plan9.go index 21403754a7a..342e26fce02 100644 --- a/src/pkg/net/unixsock_plan9.go +++ b/src/pkg/net/unixsock_plan9.go @@ -139,6 +139,10 @@ func (c *UnixConn) CloseWrite() error { // which must be "unix" or "unixgram". If laddr is not nil, it is // used as the local address for the connection. func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) { + return dialUnix(net, laddr, raddr, noDeadline) +} + +func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) { return nil, syscall.EPLAN9 } diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go index 2bef5eaaf12..f7cc0746f22 100644 --- a/src/pkg/net/unixsock_posix.go +++ b/src/pkg/net/unixsock_posix.go @@ -14,7 +14,7 @@ import ( "time" ) -func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) { +func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (fd *netFD, err error) { var sotype int switch net { default: @@ -59,7 +59,7 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err f = sockaddrToUnixpacket } - fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, f) + fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f) if err != nil { goto Error } @@ -229,7 +229,11 @@ func (c *UnixConn) CloseWrite() error { // which must be "unix" or "unixgram". If laddr is not nil, it is used // as the local address for the connection. func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) { - fd, err := unixSocket(net, laddr, raddr, "dial") + return dialUnix(net, laddr, raddr, noDeadline) +} + +func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) { + fd, err := unixSocket(net, laddr, raddr, "dial", deadline) if err != nil { return nil, err } @@ -253,7 +257,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { if laddr != nil { laddr = &UnixAddr{laddr.Name, net} // make our own copy } - fd, err := unixSocket(net, laddr, nil, "listen") + fd, err := unixSocket(net, laddr, nil, "listen", noDeadline) if err != nil { return nil, err } @@ -344,7 +348,7 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UDPConn, error) { if laddr == nil { return nil, &OpError{"listen", net, nil, errMissingAddress} } - fd, err := unixSocket(net, laddr, nil, "listen") + fd, err := unixSocket(net, laddr, nil, "listen", noDeadline) if err != nil { return nil, err }