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 := spliceFrom (c .fd , r ); handled {
return n , err
}
if n , err , handled := sendFile (c .fd , r ); handled {
return n , err
}
return genericReadFrom (c , r )
}
func (c *TCPConn ) writeTo (w io .Writer ) (int64 , error ) {
if n , err , handled := spliceTo (w , c .fd ); handled {
return n , err
}
return genericWriteTo (c , w )
}
func (sd *sysDialer ) dialTCP (ctx context .Context , laddr , raddr *TCPAddr ) (*TCPConn , error ) {
if h := sd .testHookDialTCP ; h != nil {
return h (ctx , sd .network , laddr , raddr )
}
if h := testHookDialTCP ; h != nil {
return h (ctx , sd .network , laddr , raddr )
}
return sd .doDialTCP (ctx , laddr , raddr )
}
func (sd *sysDialer ) doDialTCP (ctx context .Context , laddr , raddr *TCPAddr ) (*TCPConn , error ) {
return sd .doDialTCPProto (ctx , laddr , raddr , 0 )
}
func (sd *sysDialer ) doDialTCPProto (ctx context .Context , laddr , raddr *TCPAddr , proto int ) (*TCPConn , error ) {
ctrlCtxFn := sd .Dialer .ControlContext
if ctrlCtxFn == nil && sd .Dialer .Control != nil {
ctrlCtxFn = func (ctx context .Context , network , address string , c syscall .RawConn ) error {
return sd .Dialer .Control (network , address , c )
}
}
fd , err := internetSocket (ctx , sd .network , laddr , raddr , syscall .SOCK_STREAM , proto , "dial" , ctrlCtxFn )
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 , proto , "dial" , ctrlCtxFn )
}
if err != nil {
return nil , err
}
return newTCPConn (fd , sd .Dialer .KeepAlive , sd .Dialer .KeepAliveConfig , testPreHookSetKeepAlive , testHookSetKeepAlive ), 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
}
return newTCPConn (fd , ln .lc .KeepAlive , ln .lc .KeepAliveConfig , testPreHookSetKeepAlive , testHookSetKeepAlive ), 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 ) {
return sl .listenTCPProto (ctx , laddr , 0 )
}
func (sl *sysListener ) listenTCPProto (ctx context .Context , laddr *TCPAddr , proto int ) (*TCPListener , error ) {
var ctrlCtxFn func (ctx context .Context , network , address string , c syscall .RawConn ) error
if sl .ListenConfig .Control != nil {
ctrlCtxFn = func (ctx context .Context , network , address string , c syscall .RawConn ) error {
return sl .ListenConfig .Control (network , address , c )
}
}
fd , err := internetSocket (ctx , sl .network , laddr , nil , syscall .SOCK_STREAM , proto , "listen" , ctrlCtxFn )
if err != nil {
return nil , err
}
return &TCPListener {fd : fd , lc : sl .ListenConfig }, nil
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux 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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds .