Home | History | Annotate | Download | only in 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 // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris 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) (*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)
     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)
    116 	switch sa := sa.(type) {
    117 	case *syscall.SockaddrUnix:
    118 		if sa.Name != "" {
    119 			addr = &UnixAddr{Name: sa.Name, Net: sotypeToNet(c.fd.sotype)}
    120 		}
    121 	}
    122 	return
    123 }
    124 
    125 func (c *UnixConn) writeTo(b []byte, addr *UnixAddr) (int, error) {
    126 	if c.fd.isConnected {
    127 		return 0, ErrWriteToConnected
    128 	}
    129 	if addr == nil {
    130 		return 0, errMissingAddress
    131 	}
    132 	if addr.Net != sotypeToNet(c.fd.sotype) {
    133 		return 0, syscall.EAFNOSUPPORT
    134 	}
    135 	sa := &syscall.SockaddrUnix{Name: addr.Name}
    136 	return c.fd.writeTo(b, sa)
    137 }
    138 
    139 func (c *UnixConn) writeMsg(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
    140 	if c.fd.sotype == syscall.SOCK_DGRAM && c.fd.isConnected {
    141 		return 0, 0, ErrWriteToConnected
    142 	}
    143 	var sa syscall.Sockaddr
    144 	if addr != nil {
    145 		if addr.Net != sotypeToNet(c.fd.sotype) {
    146 			return 0, 0, syscall.EAFNOSUPPORT
    147 		}
    148 		sa = &syscall.SockaddrUnix{Name: addr.Name}
    149 	}
    150 	return c.fd.writeMsg(b, oob, sa)
    151 }
    152 
    153 func dialUnix(ctx context.Context, net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
    154 	fd, err := unixSocket(ctx, net, laddr, raddr, "dial")
    155 	if err != nil {
    156 		return nil, err
    157 	}
    158 	return newUnixConn(fd), nil
    159 }
    160 
    161 func (ln *UnixListener) accept() (*UnixConn, error) {
    162 	fd, err := ln.fd.accept()
    163 	if err != nil {
    164 		return nil, err
    165 	}
    166 	return newUnixConn(fd), nil
    167 }
    168 
    169 func (ln *UnixListener) close() error {
    170 	// The operating system doesn't clean up
    171 	// the file that announcing created, so
    172 	// we have to clean it up ourselves.
    173 	// There's a race here--we can't know for
    174 	// sure whether someone else has come along
    175 	// and replaced our socket name already--
    176 	// but this sequence (remove then close)
    177 	// is at least compatible with the auto-remove
    178 	// sequence in ListenUnix. It's only non-Go
    179 	// programs that can mess us up.
    180 	// Even if there are racy calls to Close, we want to unlink only for the first one.
    181 	ln.unlinkOnce.Do(func() {
    182 		if ln.path[0] != '@' && ln.unlink {
    183 			syscall.Unlink(ln.path)
    184 		}
    185 	})
    186 	return ln.fd.Close()
    187 }
    188 
    189 func (ln *UnixListener) file() (*os.File, error) {
    190 	f, err := ln.fd.dup()
    191 	if err != nil {
    192 		return nil, err
    193 	}
    194 	return f, nil
    195 }
    196 
    197 // SetUnlinkOnClose sets whether the underlying socket file should be removed
    198 // from the file system when the listener is closed.
    199 //
    200 // The default behavior is to unlink the socket file only when package net created it.
    201 // That is, when the listener and the underlying socket file were created by a call to
    202 // Listen or ListenUnix, then by default closing the listener will remove the socket file.
    203 // but if the listener was created by a call to FileListener to use an already existing
    204 // socket file, then by default closing the listener will not remove the socket file.
    205 func (l *UnixListener) SetUnlinkOnClose(unlink bool) {
    206 	l.unlink = unlink
    207 }
    208 
    209 func listenUnix(ctx context.Context, network string, laddr *UnixAddr) (*UnixListener, error) {
    210 	fd, err := unixSocket(ctx, network, laddr, nil, "listen")
    211 	if err != nil {
    212 		return nil, err
    213 	}
    214 	return &UnixListener{fd: fd, path: fd.laddr.String(), unlink: true}, nil
    215 }
    216 
    217 func listenUnixgram(ctx context.Context, network string, laddr *UnixAddr) (*UnixConn, error) {
    218 	fd, err := unixSocket(ctx, network, laddr, nil, "listen")
    219 	if err != nil {
    220 		return nil, err
    221 	}
    222 	return newUnixConn(fd), nil
    223 }
    224