github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/net/tcpsock_posix.go (about) 1 // Copyright 2009 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build unix || (js && wasm) || windows 6 7 package net 8 9 import ( 10 "context" 11 "io" 12 "os" 13 "syscall" 14 ) 15 16 func sockaddrToTCP(sa syscall.Sockaddr) Addr { 17 switch sa := sa.(type) { 18 case *syscall.SockaddrInet4: 19 return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port} 20 case *syscall.SockaddrInet6: 21 return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneCache.name(int(sa.ZoneId))} 22 } 23 return nil 24 } 25 26 func (a *TCPAddr) family() int { 27 if a == nil || len(a.IP) <= IPv4len { 28 return syscall.AF_INET 29 } 30 if a.IP.To4() != nil { 31 return syscall.AF_INET 32 } 33 return syscall.AF_INET6 34 } 35 36 func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) { 37 if a == nil { 38 return nil, nil 39 } 40 return ipToSockaddr(family, a.IP, a.Port, a.Zone) 41 } 42 43 func (a *TCPAddr) toLocal(net string) sockaddr { 44 return &TCPAddr{loopbackIP(net), a.Port, a.Zone} 45 } 46 47 func (c *TCPConn) readFrom(r io.Reader) (int64, error) { 48 if n, err, handled := splice(c.fd, r); handled { 49 return n, err 50 } 51 if n, err, handled := sendFile(c.fd, r); handled { 52 return n, err 53 } 54 return genericReadFrom(c, r) 55 } 56 57 func (sd *sysDialer) dialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { 58 if h := sd.testHookDialTCP; h != nil { 59 return h(ctx, sd.network, laddr, raddr) 60 } 61 if h := testHookDialTCP; h != nil { 62 return h(ctx, sd.network, laddr, raddr) 63 } 64 return sd.doDialTCP(ctx, laddr, raddr) 65 } 66 67 func (sd *sysDialer) doDialTCP(ctx context.Context, laddr, raddr *TCPAddr) (*TCPConn, error) { 68 fd, err := internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control) 69 70 // TCP has a rarely used mechanism called a 'simultaneous connection' in 71 // which Dial("tcp", addr1, addr2) run on the machine at addr1 can 72 // connect to a simultaneous Dial("tcp", addr2, addr1) run on the machine 73 // at addr2, without either machine executing Listen. If laddr == nil, 74 // it means we want the kernel to pick an appropriate originating local 75 // address. Some Linux kernels cycle blindly through a fixed range of 76 // local ports, regardless of destination port. If a kernel happens to 77 // pick local port 50001 as the source for a Dial("tcp", "", "localhost:50001"), 78 // then the Dial will succeed, having simultaneously connected to itself. 79 // This can only happen when we are letting the kernel pick a port (laddr == nil) 80 // and when there is no listener for the destination address. 81 // It's hard to argue this is anything other than a kernel bug. If we 82 // see this happen, rather than expose the buggy effect to users, we 83 // close the fd and try again. If it happens twice more, we relent and 84 // use the result. See also: 85 // https://golang.org/issue/2690 86 // https://stackoverflow.com/questions/4949858/ 87 // 88 // The opposite can also happen: if we ask the kernel to pick an appropriate 89 // originating local address, sometimes it picks one that is already in use. 90 // So if the error is EADDRNOTAVAIL, we have to try again too, just for 91 // a different reason. 92 // 93 // The kernel socket code is no doubt enjoying watching us squirm. 94 for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ { 95 if err == nil { 96 fd.Close() 97 } 98 fd, err = internetSocket(ctx, sd.network, laddr, raddr, syscall.SOCK_STREAM, 0, "dial", sd.Dialer.Control) 99 } 100 101 if err != nil { 102 return nil, err 103 } 104 return newTCPConn(fd), nil 105 } 106 107 func selfConnect(fd *netFD, err error) bool { 108 // If the connect failed, we clearly didn't connect to ourselves. 109 if err != nil { 110 return false 111 } 112 113 // The socket constructor can return an fd with raddr nil under certain 114 // unknown conditions. The errors in the calls there to Getpeername 115 // are discarded, but we can't catch the problem there because those 116 // calls are sometimes legally erroneous with a "socket not connected". 117 // Since this code (selfConnect) is already trying to work around 118 // a problem, we make sure if this happens we recognize trouble and 119 // ask the DialTCP routine to try again. 120 // TODO: try to understand what's really going on. 121 if fd.laddr == nil || fd.raddr == nil { 122 return true 123 } 124 l := fd.laddr.(*TCPAddr) 125 r := fd.raddr.(*TCPAddr) 126 return l.Port == r.Port && l.IP.Equal(r.IP) 127 } 128 129 func spuriousENOTAVAIL(err error) bool { 130 if op, ok := err.(*OpError); ok { 131 err = op.Err 132 } 133 if sys, ok := err.(*os.SyscallError); ok { 134 err = sys.Err 135 } 136 return err == syscall.EADDRNOTAVAIL 137 } 138 139 func (ln *TCPListener) ok() bool { return ln != nil && ln.fd != nil } 140 141 func (ln *TCPListener) accept() (*TCPConn, error) { 142 fd, err := ln.fd.accept() 143 if err != nil { 144 return nil, err 145 } 146 tc := newTCPConn(fd) 147 if ln.lc.KeepAlive >= 0 { 148 setKeepAlive(fd, true) 149 ka := ln.lc.KeepAlive 150 if ln.lc.KeepAlive == 0 { 151 ka = defaultTCPKeepAlive 152 } 153 setKeepAlivePeriod(fd, ka) 154 } 155 return tc, nil 156 } 157 158 func (ln *TCPListener) close() error { 159 return ln.fd.Close() 160 } 161 162 func (ln *TCPListener) file() (*os.File, error) { 163 f, err := ln.fd.dup() 164 if err != nil { 165 return nil, err 166 } 167 return f, nil 168 } 169 170 func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) { 171 fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control) 172 if err != nil { 173 return nil, err 174 } 175 return &TCPListener{fd: fd, lc: sl.ListenConfig}, nil 176 }