| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build unix || (js && wasm) || windows |
| |
| 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 { |
| // The operating system doesn't clean up |
| // the file that announcing created, so |
| // we have to clean it up ourselves. |
| // There's a race here--we can't know for |
| // sure whether someone else has come along |
| // and replaced our socket name already-- |
| // but this sequence (remove then close) |
| // is at least compatible with the auto-remove |
| // sequence in ListenUnix. It's only non-Go |
| // programs that can mess us up. |
| // Even if there are racy calls to Close, we want to unlink only for the first one. |
| 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 |
| } |
| |
| // SetUnlinkOnClose sets whether the underlying socket file should be removed |
| // from the file system when the listener is closed. |
| // |
| // The default behavior is to unlink the socket file only when package net created it. |
| // That is, when the listener and the underlying socket file were created by a call to |
| // Listen or ListenUnix, then by default closing the listener will remove the socket file. |
| // but if the listener was created by a call to FileListener to use an already existing |
| // socket file, then by default closing the listener will not remove the socket file. |
| 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 |
| } |