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
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, 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
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, 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
129func (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
143func (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
157func (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(ctx 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
171func (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
179func (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
199func (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.
215func (l *UnixListener) SetUnlinkOnClose(unlink bool) {
216	l.unlink = unlink
217}
218
219func (sl *sysListener) listenUnix(ctx context.Context, laddr *UnixAddr) (*UnixListener, error) {
220	var ctrlCtxFn func(ctx context.Context, network, address string, c syscall.RawConn) error
221	if sl.ListenConfig.Control != nil {
222		ctrlCtxFn = func(ctx 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
233func (sl *sysListener) listenUnixgram(ctx context.Context, laddr *UnixAddr) (*UnixConn, error) {
234	var ctrlCtxFn func(ctx context.Context, network, address string, c syscall.RawConn) error
235	if sl.ListenConfig.Control != nil {
236		ctrlCtxFn = func(ctx 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