unixsock.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  package net
   6  
   7  import (
   8  	"context"
   9  	"os"
  10  	"sync"
  11  	"syscall"
  12  	"time"
  13  )
  14  
  15  // BUG(mikio): On JS, WASIP1 and Plan 9, methods and functions related
  16  // to UnixConn and UnixListener are not implemented.
  17  
  18  // BUG(mikio): On Windows, methods and functions related to UnixConn
  19  // and UnixListener don't work for "unixgram" and "unixpacket".
  20  
  21  // UnixAddr represents the address of a Unix domain socket end point.
  22  type UnixAddr struct {
  23  	Name []byte
  24  	Net  []byte
  25  }
  26  
  27  // Network returns the address's network name, "unix", "unixgram" or
  28  // "unixpacket".
  29  func (a *UnixAddr) Network() []byte {
  30  	return a.Net
  31  }
  32  
  33  func (a *UnixAddr) String() string {
  34  	if a == nil {
  35  		return "<nil>"
  36  	}
  37  	return a.Name
  38  }
  39  
  40  func (a *UnixAddr) isWildcard() bool {
  41  	return a == nil || a.Name == ""
  42  }
  43  
  44  func (a *UnixAddr) opAddr() Addr {
  45  	if a == nil {
  46  		return nil
  47  	}
  48  	return a
  49  }
  50  
  51  // ResolveUnixAddr returns an address of Unix domain socket end point.
  52  //
  53  // The network must be a Unix network name.
  54  //
  55  // See func [Dial] for a description of the network and address
  56  // parameters.
  57  func ResolveUnixAddr(network, address []byte) (*UnixAddr, error) {
  58  	switch network {
  59  	case "unix", "unixgram", "unixpacket":
  60  		return &UnixAddr{Name: address, Net: network}, nil
  61  	default:
  62  		return nil, UnknownNetworkError(network)
  63  	}
  64  }
  65  
  66  // UnixConn is an implementation of the [Conn] interface for connections
  67  // to Unix domain sockets.
  68  type UnixConn struct {
  69  	conn
  70  }
  71  
  72  // SyscallConn returns a raw network connection.
  73  // This implements the [syscall.Conn] interface.
  74  func (c *UnixConn) SyscallConn() (syscall.RawConn, error) {
  75  	if !c.ok() {
  76  		return nil, syscall.EINVAL
  77  	}
  78  	return newRawConn(c.fd), nil
  79  }
  80  
  81  // CloseRead shuts down the reading side of the Unix domain connection.
  82  // Most callers should just use [UnixConn.Close].
  83  func (c *UnixConn) CloseRead() error {
  84  	if !c.ok() {
  85  		return syscall.EINVAL
  86  	}
  87  	if err := c.fd.closeRead(); err != nil {
  88  		return &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
  89  	}
  90  	return nil
  91  }
  92  
  93  // CloseWrite shuts down the writing side of the Unix domain connection.
  94  // Most callers should just use [UnixConn.Close].
  95  func (c *UnixConn) CloseWrite() error {
  96  	if !c.ok() {
  97  		return syscall.EINVAL
  98  	}
  99  	if err := c.fd.closeWrite(); err != nil {
 100  		return &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
 101  	}
 102  	return nil
 103  }
 104  
 105  // ReadFromUnix acts like [UnixConn.ReadFrom] but returns a [UnixAddr].
 106  func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) {
 107  	if !c.ok() {
 108  		return 0, nil, syscall.EINVAL
 109  	}
 110  	n, addr, err := c.readFrom(b)
 111  	if err != nil {
 112  		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
 113  	}
 114  	return n, addr, err
 115  }
 116  
 117  // ReadFrom implements the [PacketConn].ReadFrom method.
 118  func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) {
 119  	if !c.ok() {
 120  		return 0, nil, syscall.EINVAL
 121  	}
 122  	n, addr, err := c.readFrom(b)
 123  	if err != nil {
 124  		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
 125  	}
 126  	if addr == nil {
 127  		return n, nil, err
 128  	}
 129  	return n, addr, err
 130  }
 131  
 132  // ReadMsgUnix reads a message from c, copying the payload into b and
 133  // the associated out-of-band data into oob. It returns the number of
 134  // bytes copied into b, the number of bytes copied into oob, the flags
 135  // that were set on the message and the source address of the message.
 136  //
 137  // Note that if len(b) == 0 and len(oob) > 0, this function will still
 138  // read (and discard) 1 byte from the connection.
 139  func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
 140  	if !c.ok() {
 141  		return 0, 0, 0, nil, syscall.EINVAL
 142  	}
 143  	n, oobn, flags, addr, err = c.readMsg(b, oob)
 144  	if err != nil {
 145  		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
 146  	}
 147  	return
 148  }
 149  
 150  // WriteToUnix acts like [UnixConn.WriteTo] but takes a [UnixAddr].
 151  func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) {
 152  	if !c.ok() {
 153  		return 0, syscall.EINVAL
 154  	}
 155  	n, err := c.writeTo(b, addr)
 156  	if err != nil {
 157  		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
 158  	}
 159  	return n, err
 160  }
 161  
 162  // WriteTo implements the [PacketConn].WriteTo method.
 163  func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) {
 164  	if !c.ok() {
 165  		return 0, syscall.EINVAL
 166  	}
 167  	a, ok := addr.(*UnixAddr)
 168  	if !ok {
 169  		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL}
 170  	}
 171  	n, err := c.writeTo(b, a)
 172  	if err != nil {
 173  		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: a.opAddr(), Err: err}
 174  	}
 175  	return n, err
 176  }
 177  
 178  // WriteMsgUnix writes a message to addr via c, copying the payload
 179  // from b and the associated out-of-band data from oob. It returns the
 180  // number of payload and out-of-band bytes written.
 181  //
 182  // Note that if len(b) == 0 and len(oob) > 0, this function will still
 183  // write 1 byte to the connection.
 184  func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
 185  	if !c.ok() {
 186  		return 0, 0, syscall.EINVAL
 187  	}
 188  	n, oobn, err = c.writeMsg(b, oob, addr)
 189  	if err != nil {
 190  		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
 191  	}
 192  	return
 193  }
 194  
 195  func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} }
 196  
 197  // DialUnix acts like [Dial] for Unix networks.
 198  //
 199  // The network must be a Unix network name; see func [Dial] for details.
 200  //
 201  // If laddr is non-nil, it is used as the local address for the
 202  // connection.
 203  func DialUnix(network []byte, laddr, raddr *UnixAddr) (*UnixConn, error) {
 204  	switch network {
 205  	case "unix", "unixgram", "unixpacket":
 206  	default:
 207  		return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(network)}
 208  	}
 209  	sd := &sysDialer{network: network, address: raddr.String()}
 210  	c, err := sd.dialUnix(context.Background(), laddr, raddr)
 211  	if err != nil {
 212  		return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
 213  	}
 214  	return c, nil
 215  }
 216  
 217  // UnixListener is a Unix domain socket listener. Clients should
 218  // typically use variables of type [Listener] instead of assuming Unix
 219  // domain sockets.
 220  type UnixListener struct {
 221  	fd         *netFD
 222  	path       []byte
 223  	unlink     bool
 224  	unlinkOnce sync.Once
 225  }
 226  
 227  func (ln *UnixListener) ok() bool { return ln != nil && ln.fd != nil }
 228  
 229  // SyscallConn returns a raw network connection.
 230  // This implements the [syscall.Conn] interface.
 231  //
 232  // The returned [syscall.RawConn] only supports calling Control. Read and
 233  // Write return an error.
 234  func (l *UnixListener) SyscallConn() (syscall.RawConn, error) {
 235  	if !l.ok() {
 236  		return nil, syscall.EINVAL
 237  	}
 238  	return newRawListener(l.fd), nil
 239  }
 240  
 241  // AcceptUnix accepts the next incoming call and returns the new
 242  // connection.
 243  func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
 244  	if !l.ok() {
 245  		return nil, syscall.EINVAL
 246  	}
 247  	c, err := l.accept()
 248  	if err != nil {
 249  		return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
 250  	}
 251  	return c, nil
 252  }
 253  
 254  // Accept implements the Accept method in the [Listener] interface.
 255  // Returned connections will be of type [*UnixConn].
 256  func (l *UnixListener) Accept() (Conn, error) {
 257  	if !l.ok() {
 258  		return nil, syscall.EINVAL
 259  	}
 260  	c, err := l.accept()
 261  	if err != nil {
 262  		return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
 263  	}
 264  	return c, nil
 265  }
 266  
 267  // Close stops listening on the Unix address. Already accepted
 268  // connections are not closed.
 269  func (l *UnixListener) Close() error {
 270  	if !l.ok() {
 271  		return syscall.EINVAL
 272  	}
 273  	if err := l.close(); err != nil {
 274  		return &OpError{Op: "close", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
 275  	}
 276  	return nil
 277  }
 278  
 279  // Addr returns the listener's network address.
 280  // The [Addr] returned is shared by all invocations of Addr, so
 281  // do not modify it.
 282  func (l *UnixListener) Addr() Addr { return l.fd.laddr }
 283  
 284  // SetDeadline sets the deadline associated with the listener.
 285  // A zero time value disables the deadline.
 286  func (l *UnixListener) SetDeadline(t time.Time) error {
 287  	if !l.ok() {
 288  		return syscall.EINVAL
 289  	}
 290  	return l.fd.SetDeadline(t)
 291  }
 292  
 293  // File returns a copy of the underlying [os.File].
 294  // It is the caller's responsibility to close f when finished.
 295  // Closing l does not affect f, and closing f does not affect l.
 296  //
 297  // The returned [os.File]'s file descriptor is different from the
 298  // connection's. Attempting to change properties of the original
 299  // using this duplicate may or may not have the desired effect.
 300  //
 301  // On Windows, the returned os.File's file descriptor is not
 302  // usable on other processes.
 303  func (l *UnixListener) File() (f *os.File, err error) {
 304  	if !l.ok() {
 305  		return nil, syscall.EINVAL
 306  	}
 307  	f, err = l.file()
 308  	if err != nil {
 309  		err = &OpError{Op: "file", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
 310  	}
 311  	return
 312  }
 313  
 314  // ListenUnix acts like [Listen] for Unix networks.
 315  //
 316  // The network must be "unix" or "unixpacket".
 317  func ListenUnix(network []byte, laddr *UnixAddr) (*UnixListener, error) {
 318  	switch network {
 319  	case "unix", "unixpacket":
 320  	default:
 321  		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(network)}
 322  	}
 323  	if laddr == nil {
 324  		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: errMissingAddress}
 325  	}
 326  	sl := &sysListener{network: network, address: laddr.String()}
 327  	ln, err := sl.listenUnix(context.Background(), laddr)
 328  	if err != nil {
 329  		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
 330  	}
 331  	return ln, nil
 332  }
 333  
 334  // ListenUnixgram acts like [ListenPacket] for Unix networks.
 335  //
 336  // The network must be "unixgram".
 337  func ListenUnixgram(network []byte, laddr *UnixAddr) (*UnixConn, error) {
 338  	switch network {
 339  	case "unixgram":
 340  	default:
 341  		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(network)}
 342  	}
 343  	if laddr == nil {
 344  		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: errMissingAddress}
 345  	}
 346  	sl := &sysListener{network: network, address: laddr.String()}
 347  	c, err := sl.listenUnixgram(context.Background(), laddr)
 348  	if err != nil {
 349  		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
 350  	}
 351  	return c, nil
 352  }
 353