summaryrefslogtreecommitdiff
path: root/src/pkg/net/tcpsock_posix.go
diff options
context:
space:
mode:
authorMichael Stapelberg <stapelberg@debian.org>2013-03-04 21:27:36 +0100
committerMichael Stapelberg <michael@stapelberg.de>2013-03-04 21:27:36 +0100
commit04b08da9af0c450d645ab7389d1467308cfc2db8 (patch)
treedb247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/pkg/net/tcpsock_posix.go
parent917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff)
downloadgolang-upstream/1.1_hg20130304.tar.gz
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/pkg/net/tcpsock_posix.go')
-rw-r--r--src/pkg/net/tcpsock_posix.go189
1 files changed, 63 insertions, 126 deletions
diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go
index e6b1937fb..bd5a2a287 100644
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -23,14 +23,9 @@ import (
func sockaddrToTCP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
case *syscall.SockaddrInet4:
- return &TCPAddr{sa.Addr[0:], sa.Port}
+ return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port}
case *syscall.SockaddrInet6:
- return &TCPAddr{sa.Addr[0:], sa.Port}
- default:
- if sa != nil {
- // Diagnose when we will turn a non-nil sockaddr into a nil.
- panic("unexpected type in sockaddrToTCP")
- }
+ return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
}
return nil
}
@@ -53,7 +48,7 @@ func (a *TCPAddr) isWildcard() bool {
}
func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
- return ipToSockaddr(family, a.IP, a.Port)
+ return ipToSockaddr(family, a.IP, a.Port, a.Zone)
}
func (a *TCPAddr) toAddr() sockaddr {
@@ -66,27 +61,15 @@ func (a *TCPAddr) toAddr() sockaddr {
// TCPConn is an implementation of the Conn interface
// for TCP network connections.
type TCPConn struct {
- fd *netFD
+ conn
}
func newTCPConn(fd *netFD) *TCPConn {
- c := &TCPConn{fd}
+ c := &TCPConn{conn{fd}}
c.SetNoDelay(true)
return c
}
-func (c *TCPConn) ok() bool { return c != nil && c.fd != nil }
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *TCPConn) Read(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Read(b)
-}
-
// ReadFrom implements the io.ReaderFrom ReadFrom method.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
if n, err, handled := sendFile(c.fd, r); handled {
@@ -95,22 +78,6 @@ func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
return genericReadFrom(c, r)
}
-// Write implements the Conn Write method.
-func (c *TCPConn) Write(b []byte) (n int, err error) {
- if !c.ok() {
- return 0, syscall.EINVAL
- }
- return c.fd.Write(b)
-}
-
-// Close closes the TCP connection.
-func (c *TCPConn) Close() error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return c.fd.Close()
-}
-
// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
@@ -129,64 +96,6 @@ func (c *TCPConn) CloseWrite() error {
return c.fd.CloseWrite()
}
-// LocalAddr returns the local network address, a *TCPAddr.
-func (c *TCPConn) LocalAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.laddr
-}
-
-// RemoteAddr returns the remote network address, a *TCPAddr.
-func (c *TCPConn) RemoteAddr() Addr {
- if !c.ok() {
- return nil
- }
- return c.fd.raddr
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *TCPConn) SetDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setDeadline(c.fd, t)
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *TCPConn) SetReadDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadDeadline(c.fd, t)
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *TCPConn) SetWriteDeadline(t time.Time) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteDeadline(c.fd, t)
-}
-
-// SetReadBuffer sets the size of the operating system's
-// receive buffer associated with the connection.
-func (c *TCPConn) SetReadBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setReadBuffer(c.fd, bytes)
-}
-
-// SetWriteBuffer sets the size of the operating system's
-// transmit buffer associated with the connection.
-func (c *TCPConn) SetWriteBuffer(bytes int) error {
- if !c.ok() {
- return syscall.EINVAL
- }
- return setWriteBuffer(c.fd, bytes)
-}
-
// SetLinger sets the behavior of Close() on a connection
// which still has data waiting to be sent or to be acknowledged.
//
@@ -225,20 +134,23 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
return setNoDelay(c.fd, noDelay)
}
-// File returns a copy of the underlying os.File, set to blocking mode.
-// It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *TCPConn) File() (f *os.File, err error) { return c.fd.dup() }
-
// DialTCP connects to the remote address raddr on the network net,
// 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) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
+ return dialTCP(net, laddr, raddr, noDeadline)
+}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
+ 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
@@ -257,9 +169,18 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
// use the result. See also:
// http://golang.org/issue/2690
// http://stackoverflow.com/questions/4949858/
- for i := 0; i < 2 && err == nil && laddr == nil && selfConnect(fd); i++ {
- fd.Close()
- fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ //
+ // The opposite can also happen: if we ask the kernel to pick an appropriate
+ // originating local address, sometimes it picks one that is already in use.
+ // So if the error is EADDRNOTAVAIL, we have to try again too, just for
+ // a different reason.
+ //
+ // The kernel socket code is no doubt enjoying watching us squirm.
+ for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
+ if err == nil {
+ fd.Close()
+ }
+ fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
}
if err != nil {
@@ -268,7 +189,12 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
return newTCPConn(fd), nil
}
-func selfConnect(fd *netFD) bool {
+func selfConnect(fd *netFD, err error) bool {
+ // If the connect failed, we clearly didn't connect to ourselves.
+ if err != nil {
+ return false
+ }
+
// The socket constructor can return an fd with raddr nil under certain
// unknown conditions. The errors in the calls there to Getpeername
// are discarded, but we can't catch the problem there because those
@@ -285,6 +211,11 @@ func selfConnect(fd *netFD) bool {
return l.Port == r.Port && l.IP.Equal(r.IP)
}
+func spuriousENOTAVAIL(err error) bool {
+ e, ok := err.(*OpError)
+ return ok && e.Err == syscall.EADDRNOTAVAIL
+}
+
// TCPListener is a TCP network listener.
// Clients should typically use variables of type Listener
// instead of assuming TCP.
@@ -292,29 +223,10 @@ type TCPListener struct {
fd *netFD
}
-// ListenTCP announces on the TCP address laddr and returns a TCP listener.
-// Net must be "tcp", "tcp4", or "tcp6".
-// 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)
- if err != nil {
- return nil, err
- }
- err = syscall.Listen(fd.sysfd, listenerBacklog)
- if err != nil {
- closesocket(fd.sysfd)
- return nil, &OpError{"listen", net, laddr, err}
- }
- l := new(TCPListener)
- l.fd = fd
- return l, nil
-}
-
// AcceptTCP accepts the next incoming call and returns the new connection
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
- if l == nil || l.fd == nil || l.fd.sysfd < 0 {
+ if l == nil || l.fd == nil {
return nil, syscall.EINVAL
}
fd, err := l.fd.accept(sockaddrToTCP)
@@ -359,3 +271,28 @@ func (l *TCPListener) SetDeadline(t time.Time) error {
// It is the caller's responsibility to close f when finished.
// Closing l does not affect f, and closing f does not affect l.
func (l *TCPListener) File() (f *os.File, err error) { return l.fd.dup() }
+
+// ListenTCP announces on the TCP address laddr and returns a TCP listener.
+// Net must be "tcp", "tcp4", or "tcp6".
+// 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) {
+ switch net {
+ case "tcp", "tcp4", "tcp6":
+ default:
+ return nil, UnknownNetworkError(net)
+ }
+ if laddr == nil {
+ laddr = &TCPAddr{}
+ }
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
+ if err != nil {
+ return nil, err
+ }
+ err = syscall.Listen(fd.sysfd, listenerBacklog)
+ if err != nil {
+ closesocket(fd.sysfd)
+ return nil, &OpError{"listen", net, laddr, err}
+ }
+ return &TCPListener{fd}, nil
+}