package net
import (
"context"
"io"
"os"
"syscall"
)
func sockaddrToTCP (sa syscall .Sockaddr ) Addr {
switch sa := sa .(type ) {
case *syscall .SockaddrInet4 :
return &TCPAddr {IP : sa .Addr [0 :], Port : sa .Port }
case *syscall .SockaddrInet6 :
return &TCPAddr {IP : sa .Addr [0 :], Port : sa .Port , Zone : zoneCache .name (int (sa .ZoneId ))}
}
return nil
}
func (a *TCPAddr ) family () int {
if a == nil || len (a .IP ) <= IPv4len {
return syscall .AF_INET
}
if a .IP .To4 () != nil {
return syscall .AF_INET
}
return syscall .AF_INET6
}
func (a *TCPAddr ) sockaddr (family int ) (syscall .Sockaddr , error ) {
if a == nil {
return nil , nil
}
return ipToSockaddr (family , a .IP , a .Port , a .Zone )
}
func (a *TCPAddr ) toLocal (net string ) sockaddr {
return &TCPAddr {loopbackIP (net ), a .Port , a .Zone }
}
func (c *TCPConn ) readFrom (r io .Reader ) (int64 , error ) {
if n , err , handled := splice (c .fd , r ); handled {
return n , err
}
if n , err , handled := sendFile (c .fd , r ); handled {
return n , err
}
return genericReadFrom (c , r )
}
func (sd *sysDialer ) dialTCP (ctx context .Context , laddr , raddr *TCPAddr ) (*TCPConn , error ) {
if testHookDialTCP != nil {
return testHookDialTCP (ctx , sd .network , laddr , raddr )
}
return sd .doDialTCP (ctx , laddr , raddr )
}
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" , sd .Dialer .Control )
for i := 0 ; i < 2 && (laddr == nil || laddr .Port == 0 ) && (selfConnect (fd , err ) || spuriousENOTAVAIL (err )); i ++ {
if err == nil {
fd .Close ()
}
fd , err = internetSocket (ctx , sd .network , laddr , raddr , syscall .SOCK_STREAM , 0 , "dial" , sd .Dialer .Control )
}
if err != nil {
return nil , err
}
return newTCPConn (fd ), nil
}
func selfConnect (fd *netFD , err error ) bool {
if err != nil {
return false
}
if fd .laddr == nil || fd .raddr == nil {
return true
}
l := fd .laddr .(*TCPAddr )
r := fd .raddr .(*TCPAddr )
return l .Port == r .Port && l .IP .Equal (r .IP )
}
func spuriousENOTAVAIL (err error ) bool {
if op , ok := err .(*OpError ); ok {
err = op .Err
}
if sys , ok := err .(*os .SyscallError ); ok {
err = sys .Err
}
return err == syscall .EADDRNOTAVAIL
}
func (ln *TCPListener ) ok () bool { return ln != nil && ln .fd != nil }
func (ln *TCPListener ) accept () (*TCPConn , error ) {
fd , err := ln .fd .accept ()
if err != nil {
return nil , err
}
tc := newTCPConn (fd )
if ln .lc .KeepAlive >= 0 {
setKeepAlive (fd , true )
ka := ln .lc .KeepAlive
if ln .lc .KeepAlive == 0 {
ka = defaultTCPKeepAlive
}
setKeepAlivePeriod (fd , ka )
}
return tc , nil
}
func (ln *TCPListener ) close () error {
return ln .fd .Close ()
}
func (ln *TCPListener ) file () (*os .File , error ) {
f , err := ln .fd .dup ()
if err != nil {
return nil , err
}
return f , nil
}
func (sl *sysListener ) listenTCP (ctx context .Context , laddr *TCPAddr ) (*TCPListener , error ) {
fd , err := internetSocket (ctx , sl .network , laddr , nil , syscall .SOCK_STREAM , 0 , "listen" , sl .ListenConfig .Control )
if err != nil {
return nil , err
}
return &TCPListener {fd : fd , lc : sl .ListenConfig }, nil
}
The pages are generated with Golds v0.3.6 . (GOOS=darwin GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .