unixsock_posix.mx raw

   1  // Copyright 2009 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  //go:build unix || js || wasip1 || windows
   6  
   7  package net
   8  
   9  import (
  10  	"context"
  11  	"errors"
  12  	"os"
  13  	"syscall"
  14  )
  15  
  16  func unixSocket(ctx context.Context, net []byte, laddr, raddr sockaddr, mode []byte, ctxCtrlFn func(context.Context, []byte, []byte, syscall.RawConn) error) (*netFD, error) {
  17  	var sotype int
  18  	switch net {
  19  	case "unix":
  20  		sotype = syscall.SOCK_STREAM
  21  	case "unixgram":
  22  		sotype = syscall.SOCK_DGRAM
  23  	case "unixpacket":
  24  		sotype = syscall.SOCK_SEQPACKET
  25  	default:
  26  		return nil, UnknownNetworkError(net)
  27  	}
  28  
  29  	switch mode {
  30  	case "dial":
  31  		if laddr != nil && laddr.isWildcard() {
  32  			laddr = nil
  33  		}
  34  		if raddr != nil && raddr.isWildcard() {
  35  			raddr = nil
  36  		}
  37  		if raddr == nil && (sotype != syscall.SOCK_DGRAM || laddr == nil) {
  38  			return nil, errMissingAddress
  39  		}
  40  	case "listen":
  41  	default:
  42  		return nil, errors.New("unknown mode: " + mode)
  43  	}
  44  
  45  	fd, err := socket(ctx, net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, ctxCtrlFn)
  46  	if err != nil {
  47  		return nil, err
  48  	}
  49  	return fd, nil
  50  }
  51  
  52  func sockaddrToUnix(sa syscall.Sockaddr) Addr {
  53  	if s, ok := sa.(*syscall.SockaddrUnix); ok {
  54  		return &UnixAddr{Name: s.Name, Net: "unix"}
  55  	}
  56  	return nil
  57  }
  58  
  59  func sockaddrToUnixgram(sa syscall.Sockaddr) Addr {
  60  	if s, ok := sa.(*syscall.SockaddrUnix); ok {
  61  		return &UnixAddr{Name: s.Name, Net: "unixgram"}
  62  	}
  63  	return nil
  64  }
  65  
  66  func sockaddrToUnixpacket(sa syscall.Sockaddr) Addr {
  67  	if s, ok := sa.(*syscall.SockaddrUnix); ok {
  68  		return &UnixAddr{Name: s.Name, Net: "unixpacket"}
  69  	}
  70  	return nil
  71  }
  72  
  73  func sotypeToNet(sotype int) []byte {
  74  	switch sotype {
  75  	case syscall.SOCK_STREAM:
  76  		return "unix"
  77  	case syscall.SOCK_DGRAM:
  78  		return "unixgram"
  79  	case syscall.SOCK_SEQPACKET:
  80  		return "unixpacket"
  81  	default:
  82  		panic("sotypeToNet unknown socket type")
  83  	}
  84  }
  85  
  86  func (a *UnixAddr) family() int {
  87  	return syscall.AF_UNIX
  88  }
  89  
  90  func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) {
  91  	if a == nil {
  92  		return nil, nil
  93  	}
  94  	return &syscall.SockaddrUnix{Name: a.Name}, nil
  95  }
  96  
  97  func (a *UnixAddr) toLocal(net []byte) sockaddr {
  98  	return a
  99  }
 100  
 101  func (c *UnixConn) readFrom(b []byte) (int, *UnixAddr, error) {
 102  	var addr *UnixAddr
 103  	n, sa, err := c.fd.readFrom(b)
 104  	switch sa := sa.(type) {
 105  	case *syscall.SockaddrUnix:
 106  		if sa.Name != "" {
 107  			addr = &UnixAddr{Name: sa.Name, Net: sotypeToNet(c.fd.sotype)}
 108  		}
 109  	}
 110  	return n, addr, err
 111  }
 112  
 113  func (c *UnixConn) readMsg(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
 114  	var sa syscall.Sockaddr
 115  	n, oobn, flags, sa, err = c.fd.readMsg(b, oob, readMsgFlags)
 116  	if readMsgFlags == 0 && err == nil && oobn > 0 {
 117  		setReadMsgCloseOnExec(oob[:oobn])
 118  	}
 119  
 120  	switch sa := sa.(type) {
 121  	case *syscall.SockaddrUnix:
 122  		if sa.Name != "" {
 123  			addr = &UnixAddr{Name: sa.Name, Net: sotypeToNet(c.fd.sotype)}
 124  		}
 125  	}
 126  	return
 127  }
 128  
 129  func (c *UnixConn) writeTo(b []byte, addr *UnixAddr) (int, error) {
 130  	if c.fd.isConnected {
 131  		return 0, ErrWriteToConnected
 132  	}
 133  	if addr == nil {
 134  		return 0, errMissingAddress
 135  	}
 136  	if addr.Net != sotypeToNet(c.fd.sotype) {
 137  		return 0, syscall.EAFNOSUPPORT
 138  	}
 139  	sa := &syscall.SockaddrUnix{Name: addr.Name}
 140  	return c.fd.writeTo(b, sa)
 141  }
 142  
 143  func (c *UnixConn) writeMsg(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
 144  	if c.fd.sotype == syscall.SOCK_DGRAM && c.fd.isConnected {
 145  		return 0, 0, ErrWriteToConnected
 146  	}
 147  	var sa syscall.Sockaddr
 148  	if addr != nil {
 149  		if addr.Net != sotypeToNet(c.fd.sotype) {
 150  			return 0, 0, syscall.EAFNOSUPPORT
 151  		}
 152  		sa = &syscall.SockaddrUnix{Name: addr.Name}
 153  	}
 154  	return c.fd.writeMsg(b, oob, sa)
 155  }
 156  
 157  func (sd *sysDialer) dialUnix(ctx context.Context, laddr, raddr *UnixAddr) (*UnixConn, error) {
 158  	ctrlCtxFn := sd.Dialer.ControlContext
 159  	if ctrlCtxFn == nil && sd.Dialer.Control != nil {
 160  		ctrlCtxFn = func(ctx context.Context, network, address []byte, c syscall.RawConn) error {
 161  			return sd.Dialer.Control(network, address, c)
 162  		}
 163  	}
 164  	fd, err := unixSocket(ctx, sd.network, laddr, raddr, "dial", ctrlCtxFn)
 165  	if err != nil {
 166  		return nil, err
 167  	}
 168  	return newUnixConn(fd), nil
 169  }
 170  
 171  func (ln *UnixListener) accept() (*UnixConn, error) {
 172  	fd, err := ln.fd.accept()
 173  	if err != nil {
 174  		return nil, err
 175  	}
 176  	return newUnixConn(fd), nil
 177  }
 178  
 179  func (ln *UnixListener) close() error {
 180  	// The operating system doesn't clean up
 181  	// the file that announcing created, so
 182  	// we have to clean it up ourselves.
 183  	// There's a race here--we can't know for
 184  	// sure whether someone else has come along
 185  	// and replaced our socket name already--
 186  	// but this sequence (remove then close)
 187  	// is at least compatible with the auto-remove
 188  	// sequence in ListenUnix. It's only non-Go
 189  	// programs that can mess us up.
 190  	// Even if there are racy calls to Close, we want to unlink only for the first one.
 191  	ln.unlinkOnce.Do(func() {
 192  		if ln.path[0] != '@' && ln.unlink {
 193  			syscall.Unlink(ln.path)
 194  		}
 195  	})
 196  	return ln.fd.Close()
 197  }
 198  
 199  func (ln *UnixListener) file() (*os.File, error) {
 200  	f, err := ln.fd.dup()
 201  	if err != nil {
 202  		return nil, err
 203  	}
 204  	return f, nil
 205  }
 206  
 207  // SetUnlinkOnClose sets whether the underlying socket file should be removed
 208  // from the file system when the listener is closed.
 209  //
 210  // The default behavior is to unlink the socket file only when package net created it.
 211  // That is, when the listener and the underlying socket file were created by a call to
 212  // Listen or ListenUnix, then by default closing the listener will remove the socket file.
 213  // but if the listener was created by a call to FileListener to use an already existing
 214  // socket file, then by default closing the listener will not remove the socket file.
 215  func (l *UnixListener) SetUnlinkOnClose(unlink bool) {
 216  	l.unlink = unlink
 217  }
 218  
 219  func (sl *sysListener) listenUnix(ctx context.Context, laddr *UnixAddr) (*UnixListener, error) {
 220  	var ctrlCtxFn func(ctx context.Context, network, address []byte, c syscall.RawConn) error
 221  	if sl.ListenConfig.Control != nil {
 222  		ctrlCtxFn = func(ctx context.Context, network, address []byte, c syscall.RawConn) error {
 223  			return sl.ListenConfig.Control(network, address, c)
 224  		}
 225  	}
 226  	fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen", ctrlCtxFn)
 227  	if err != nil {
 228  		return nil, err
 229  	}
 230  	return &UnixListener{fd: fd, path: fd.laddr.String(), unlink: true}, nil
 231  }
 232  
 233  func (sl *sysListener) listenUnixgram(ctx context.Context, laddr *UnixAddr) (*UnixConn, error) {
 234  	var ctrlCtxFn func(ctx context.Context, network, address []byte, c syscall.RawConn) error
 235  	if sl.ListenConfig.Control != nil {
 236  		ctrlCtxFn = func(ctx context.Context, network, address []byte, c syscall.RawConn) error {
 237  			return sl.ListenConfig.Control(network, address, c)
 238  		}
 239  	}
 240  	fd, err := unixSocket(ctx, sl.network, laddr, nil, "listen", ctrlCtxFn)
 241  	if err != nil {
 242  		return nil, err
 243  	}
 244  	return newUnixConn(fd), nil
 245  }
 246