package net
import (
"context"
"internal/poll"
"os"
"syscall"
)
func socket (ctx context .Context , net string , family , sotype , proto int , ipv6only bool , laddr , raddr sockaddr , ctrlCtxFn func (context .Context , string , string , syscall .RawConn ) error ) (fd *netFD , err error ) {
s , err := sysSocket (family , sotype , proto )
if err != nil {
return nil , err
}
if err = setDefaultSockopts (s , family , sotype , ipv6only ); err != nil {
poll .CloseFunc (s )
return nil , err
}
if fd , err = newFD (s , family , sotype , net ); err != nil {
poll .CloseFunc (s )
return nil , err
}
if laddr != nil && raddr == nil {
switch sotype {
case syscall .SOCK_STREAM , syscall .SOCK_SEQPACKET :
if err := fd .listenStream (ctx , laddr , listenerBacklog (), ctrlCtxFn ); err != nil {
fd .Close ()
return nil , err
}
return fd , nil
case syscall .SOCK_DGRAM :
if err := fd .listenDatagram (ctx , laddr , ctrlCtxFn ); err != nil {
fd .Close ()
return nil , err
}
return fd , nil
}
}
if err := fd .dial (ctx , laddr , raddr , ctrlCtxFn ); err != nil {
fd .Close ()
return nil , err
}
return fd , nil
}
func (fd *netFD ) ctrlNetwork () string {
switch fd .net {
case "unix" , "unixgram" , "unixpacket" :
return fd .net
}
switch fd .net [len (fd .net )-1 ] {
case '4' , '6' :
return fd .net
}
if fd .family == syscall .AF_INET {
return fd .net + "4"
}
return fd .net + "6"
}
func (fd *netFD ) addrFunc () func (syscall .Sockaddr ) Addr {
switch fd .family {
case syscall .AF_INET , syscall .AF_INET6 :
switch fd .sotype {
case syscall .SOCK_STREAM :
return sockaddrToTCP
case syscall .SOCK_DGRAM :
return sockaddrToUDP
case syscall .SOCK_RAW :
return sockaddrToIP
}
case syscall .AF_UNIX :
switch fd .sotype {
case syscall .SOCK_STREAM :
return sockaddrToUnix
case syscall .SOCK_DGRAM :
return sockaddrToUnixgram
case syscall .SOCK_SEQPACKET :
return sockaddrToUnixpacket
}
}
return func (syscall .Sockaddr ) Addr { return nil }
}
func (fd *netFD ) dial (ctx context .Context , laddr , raddr sockaddr , ctrlCtxFn func (context .Context , string , string , syscall .RawConn ) error ) error {
var c *rawConn
var err error
if ctrlCtxFn != nil {
c , err = newRawConn (fd )
if err != nil {
return err
}
var ctrlAddr string
if raddr != nil {
ctrlAddr = raddr .String ()
} else if laddr != nil {
ctrlAddr = laddr .String ()
}
if err := ctrlCtxFn (ctx , fd .ctrlNetwork (), ctrlAddr , c ); err != nil {
return err
}
}
var lsa syscall .Sockaddr
if laddr != nil {
if lsa , err = laddr .sockaddr (fd .family ); err != nil {
return err
} else if lsa != nil {
if err = syscall .Bind (fd .pfd .Sysfd , lsa ); err != nil {
return os .NewSyscallError ("bind" , err )
}
}
}
var rsa syscall .Sockaddr
var crsa syscall .Sockaddr
if raddr != nil {
if rsa , err = raddr .sockaddr (fd .family ); err != nil {
return err
}
if crsa , err = fd .connect (ctx , lsa , rsa ); err != nil {
return err
}
fd .isConnected = true
} else {
if err := fd .init (); err != nil {
return err
}
}
lsa , _ = syscall .Getsockname (fd .pfd .Sysfd )
if crsa != nil {
fd .setAddr (fd .addrFunc ()(lsa ), fd .addrFunc ()(crsa ))
} else if rsa , _ = syscall .Getpeername (fd .pfd .Sysfd ); rsa != nil {
fd .setAddr (fd .addrFunc ()(lsa ), fd .addrFunc ()(rsa ))
} else {
fd .setAddr (fd .addrFunc ()(lsa ), raddr )
}
return nil
}
func (fd *netFD ) listenStream (ctx context .Context , laddr sockaddr , backlog int , ctrlCtxFn func (context .Context , string , string , syscall .RawConn ) error ) error {
var err error
if err = setDefaultListenerSockopts (fd .pfd .Sysfd ); err != nil {
return err
}
var lsa syscall .Sockaddr
if lsa , err = laddr .sockaddr (fd .family ); err != nil {
return err
}
if ctrlCtxFn != nil {
c , err := newRawConn (fd )
if err != nil {
return err
}
if err := ctrlCtxFn (ctx , fd .ctrlNetwork (), laddr .String (), c ); err != nil {
return err
}
}
if err = syscall .Bind (fd .pfd .Sysfd , lsa ); err != nil {
return os .NewSyscallError ("bind" , err )
}
if err = listenFunc (fd .pfd .Sysfd , backlog ); err != nil {
return os .NewSyscallError ("listen" , err )
}
if err = fd .init (); err != nil {
return err
}
lsa , _ = syscall .Getsockname (fd .pfd .Sysfd )
fd .setAddr (fd .addrFunc ()(lsa ), nil )
return nil
}
func (fd *netFD ) listenDatagram (ctx context .Context , laddr sockaddr , ctrlCtxFn func (context .Context , string , string , syscall .RawConn ) error ) error {
switch addr := laddr .(type ) {
case *UDPAddr :
if addr .IP != nil && addr .IP .IsMulticast () {
if err := setDefaultMulticastSockopts (fd .pfd .Sysfd ); err != nil {
return err
}
addr := *addr
switch fd .family {
case syscall .AF_INET :
addr .IP = IPv4zero
case syscall .AF_INET6 :
addr .IP = IPv6unspecified
}
laddr = &addr
}
}
var err error
var lsa syscall .Sockaddr
if lsa , err = laddr .sockaddr (fd .family ); err != nil {
return err
}
if ctrlCtxFn != nil {
c , err := newRawConn (fd )
if err != nil {
return err
}
if err := ctrlCtxFn (ctx , fd .ctrlNetwork (), laddr .String (), c ); err != nil {
return err
}
}
if err = syscall .Bind (fd .pfd .Sysfd , lsa ); err != nil {
return os .NewSyscallError ("bind" , err )
}
if err = fd .init (); err != nil {
return err
}
lsa , _ = syscall .Getsockname (fd .pfd .Sysfd )
fd .setAddr (fd .addrFunc ()(lsa ), nil )
return nil
}
The pages are generated with Golds v0.6.7 . (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 @Go100and1 (reachable from the left QR code) to get the latest news of Golds .