Source file src/net/unixsock_posix.go

     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 string, laddr, raddr sockaddr, mode string, ctxCtrlFn func(context.Context, string, string, 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) string {
    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 string) 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(cxt context.Context, network, address string, 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(cxt context.Context, network, address string, c syscall.RawConn) error
   221  	if sl.ListenConfig.Control != nil {
   222  		ctrlCtxFn = func(cxt context.Context, network, address string, 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(cxt context.Context, network, address string, c syscall.RawConn) error
   235  	if sl.ListenConfig.Control != nil {
   236  		ctrlCtxFn = func(cxt context.Context, network, address string, 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  

View as plain text