Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/unixsock_posix.go

Documentation: net

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

View as plain text