package net
import (
"context"
"errors"
"os"
"syscall"
)
func unixSocket (ctx context .Context , net string , laddr , raddr sockaddr , mode string , ctxCtrlFn func (context .Context , string , string , syscall .RawConn ) error ) (*netFD , error ) {
var sotype int
switch net {
case "unix" :
sotype = syscall .SOCK_STREAM
case "unixgram" :
sotype = syscall .SOCK_DGRAM
case "unixpacket" :
sotype = syscall .SOCK_SEQPACKET
default :
return nil , UnknownNetworkError (net )
}
switch mode {
case "dial" :
if laddr != nil && laddr .isWildcard () {
laddr = nil
}
if raddr != nil && raddr .isWildcard () {
raddr = nil
}
if raddr == nil && (sotype != syscall .SOCK_DGRAM || laddr == nil ) {
return nil , errMissingAddress
}
case "listen" :
default :
return nil , errors .New ("unknown mode: " + mode )
}
fd , err := socket (ctx , net , syscall .AF_UNIX , sotype , 0 , false , laddr , raddr , ctxCtrlFn )
if err != nil {
return nil , err
}
return fd , nil
}
func sockaddrToUnix (sa syscall .Sockaddr ) Addr {
if s , ok := sa .(*syscall .SockaddrUnix ); ok {
return &UnixAddr {Name : s .Name , Net : "unix" }
}
return nil
}
func sockaddrToUnixgram (sa syscall .Sockaddr ) Addr {
if s , ok := sa .(*syscall .SockaddrUnix ); ok {
return &UnixAddr {Name : s .Name , Net : "unixgram" }
}
return nil
}
func sockaddrToUnixpacket (sa syscall .Sockaddr ) Addr {
if s , ok := sa .(*syscall .SockaddrUnix ); ok {
return &UnixAddr {Name : s .Name , Net : "unixpacket" }
}
return nil
}
func sotypeToNet (sotype int ) string {
switch sotype {
case syscall .SOCK_STREAM :
return "unix"
case syscall .SOCK_DGRAM :
return "unixgram"
case syscall .SOCK_SEQPACKET :
return "unixpacket"
default :
panic ("sotypeToNet unknown socket type" )
}
}
func (a *UnixAddr ) family () int {
return syscall .AF_UNIX
}
func (a *UnixAddr ) sockaddr (family int ) (syscall .Sockaddr , error ) {
if a == nil {
return nil , nil
}
return &syscall .SockaddrUnix {Name : a .Name }, nil
}
func (a *UnixAddr ) toLocal (net string ) sockaddr {
return a
}
func (c *UnixConn ) readFrom (b []byte ) (int , *UnixAddr , error ) {
var addr *UnixAddr
n , sa , err := c .fd .readFrom (b )
switch sa := sa .(type ) {
case *syscall .SockaddrUnix :
if sa .Name != "" {
addr = &UnixAddr {Name : sa .Name , Net : sotypeToNet (c .fd .sotype )}
}
}
return n , addr , err
}
func (c *UnixConn ) readMsg (b , oob []byte ) (n , oobn , flags int , addr *UnixAddr , err error ) {
var sa syscall .Sockaddr
n , oobn , flags , sa , err = c .fd .readMsg (b , oob , readMsgFlags )
if readMsgFlags == 0 && err == nil && oobn > 0 {
setReadMsgCloseOnExec (oob [:oobn ])
}
switch sa := sa .(type ) {
case *syscall .SockaddrUnix :
if sa .Name != "" {
addr = &UnixAddr {Name : sa .Name , Net : sotypeToNet (c .fd .sotype )}
}
}
return
}
func (c *UnixConn ) writeTo (b []byte , addr *UnixAddr ) (int , error ) {
if c .fd .isConnected {
return 0 , ErrWriteToConnected
}
if addr == nil {
return 0 , errMissingAddress
}
if addr .Net != sotypeToNet (c .fd .sotype ) {
return 0 , syscall .EAFNOSUPPORT
}
sa := &syscall .SockaddrUnix {Name : addr .Name }
return c .fd .writeTo (b , sa )
}
func (c *UnixConn ) writeMsg (b , oob []byte , addr *UnixAddr ) (n , oobn int , err error ) {
if c .fd .sotype == syscall .SOCK_DGRAM && c .fd .isConnected {
return 0 , 0 , ErrWriteToConnected
}
var sa syscall .Sockaddr
if addr != nil {
if addr .Net != sotypeToNet (c .fd .sotype ) {
return 0 , 0 , syscall .EAFNOSUPPORT
}
sa = &syscall .SockaddrUnix {Name : addr .Name }
}
return c .fd .writeMsg (b , oob , sa )
}
func (sd *sysDialer ) dialUnix (ctx context .Context , laddr , raddr *UnixAddr ) (*UnixConn , error ) {
ctrlCtxFn := sd .Dialer .ControlContext
if ctrlCtxFn == nil && sd .Dialer .Control != nil {
ctrlCtxFn = func (cxt context .Context , network , address string , c syscall .RawConn ) error {
return sd .Dialer .Control (network , address , c )
}
}
fd , err := unixSocket (ctx , sd .network , laddr , raddr , "dial" , ctrlCtxFn )
if err != nil {
return nil , err
}
return newUnixConn (fd ), nil
}
func (ln *UnixListener ) accept () (*UnixConn , error ) {
fd , err := ln .fd .accept ()
if err != nil {
return nil , err
}
return newUnixConn (fd ), nil
}
func (ln *UnixListener ) close () error {
ln .unlinkOnce .Do (func () {
if ln .path [0 ] != '@' && ln .unlink {
syscall .Unlink (ln .path )
}
})
return ln .fd .Close ()
}
func (ln *UnixListener ) file () (*os .File , error ) {
f , err := ln .fd .dup ()
if err != nil {
return nil , err
}
return f , nil
}
func (l *UnixListener ) SetUnlinkOnClose (unlink bool ) {
l .unlink = unlink
}
func (sl *sysListener ) listenUnix (ctx context .Context , laddr *UnixAddr ) (*UnixListener , error ) {
var ctrlCtxFn func (cxt context .Context , network , address string , c syscall .RawConn ) error
if sl .ListenConfig .Control != nil {
ctrlCtxFn = func (cxt context .Context , network , address string , c syscall .RawConn ) error {
return sl .ListenConfig .Control (network , address , c )
}
}
fd , err := unixSocket (ctx , sl .network , laddr , nil , "listen" , ctrlCtxFn )
if err != nil {
return nil , err
}
return &UnixListener {fd : fd , path : fd .laddr .String (), unlink : true }, nil
}
func (sl *sysListener ) listenUnixgram (ctx context .Context , laddr *UnixAddr ) (*UnixConn , error ) {
var ctrlCtxFn func (cxt context .Context , network , address string , c syscall .RawConn ) error
if sl .ListenConfig .Control != nil {
ctrlCtxFn = func (cxt context .Context , network , address string , c syscall .RawConn ) error {
return sl .ListenConfig .Control (network , address , c )
}
}
fd , err := unixSocket (ctx , sl .network , laddr , nil , "listen" , ctrlCtxFn )
if err != nil {
return nil , err
}
return newUnixConn (fd ), 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 .