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
7package net
8
9import (
10	"context"
11	"errors"
12	"os"
13	"syscall"
14)
15
16func 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
52func 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
59func 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
66func 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
73func 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
86func (a *UnixAddr) family() int {
87	return syscall.AF_UNIX
88}
89
90func (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
97func (a *UnixAddr) toLocal(net string) sockaddr {
98	return a
99}
100
101func (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
113func (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
125func (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
139func (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
153func 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
161func (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
169func (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
189func (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.
205func (l *UnixListener) SetUnlinkOnClose(unlink bool) {
206	l.unlink = unlink
207}
208
209func 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
217func 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