// 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 }