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
5package net
6
7import (
8	"context"
9	"os"
10	"sync"
11	"syscall"
12	"time"
13)
14
15// BUG(mikio): On JS, WASIP1 and Plan 9, methods and functions related
16// to UnixConn and UnixListener are not implemented.
17
18// BUG(mikio): On Windows, methods and functions related to UnixConn
19// and UnixListener don't work for "unixgram" and "unixpacket".
20
21// UnixAddr represents the address of a Unix domain socket end point.
22type UnixAddr struct {
23	Name string
24	Net  string
25}
26
27// Network returns the address's network name, "unix", "unixgram" or
28// "unixpacket".
29func (a *UnixAddr) Network() string {
30	return a.Net
31}
32
33func (a *UnixAddr) String() string {
34	if a == nil {
35		return "<nil>"
36	}
37	return a.Name
38}
39
40func (a *UnixAddr) isWildcard() bool {
41	return a == nil || a.Name == ""
42}
43
44func (a *UnixAddr) opAddr() Addr {
45	if a == nil {
46		return nil
47	}
48	return a
49}
50
51// ResolveUnixAddr returns an address of Unix domain socket end point.
52//
53// The network must be a Unix network name.
54//
55// See func [Dial] for a description of the network and address
56// parameters.
57func ResolveUnixAddr(network, address string) (*UnixAddr, error) {
58	switch network {
59	case "unix", "unixgram", "unixpacket":
60		return &UnixAddr{Name: address, Net: network}, nil
61	default:
62		return nil, UnknownNetworkError(network)
63	}
64}
65
66// UnixConn is an implementation of the [Conn] interface for connections
67// to Unix domain sockets.
68type UnixConn struct {
69	conn
70}
71
72// SyscallConn returns a raw network connection.
73// This implements the [syscall.Conn] interface.
74func (c *UnixConn) SyscallConn() (syscall.RawConn, error) {
75	if !c.ok() {
76		return nil, syscall.EINVAL
77	}
78	return newRawConn(c.fd), nil
79}
80
81// CloseRead shuts down the reading side of the Unix domain connection.
82// Most callers should just use Close.
83func (c *UnixConn) CloseRead() error {
84	if !c.ok() {
85		return syscall.EINVAL
86	}
87	if err := c.fd.closeRead(); err != nil {
88		return &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
89	}
90	return nil
91}
92
93// CloseWrite shuts down the writing side of the Unix domain connection.
94// Most callers should just use Close.
95func (c *UnixConn) CloseWrite() error {
96	if !c.ok() {
97		return syscall.EINVAL
98	}
99	if err := c.fd.closeWrite(); err != nil {
100		return &OpError{Op: "close", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
101	}
102	return nil
103}
104
105// ReadFromUnix acts like [UnixConn.ReadFrom] but returns a [UnixAddr].
106func (c *UnixConn) ReadFromUnix(b []byte) (int, *UnixAddr, error) {
107	if !c.ok() {
108		return 0, nil, syscall.EINVAL
109	}
110	n, addr, err := c.readFrom(b)
111	if err != nil {
112		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
113	}
114	return n, addr, err
115}
116
117// ReadFrom implements the [PacketConn] ReadFrom method.
118func (c *UnixConn) ReadFrom(b []byte) (int, Addr, error) {
119	if !c.ok() {
120		return 0, nil, syscall.EINVAL
121	}
122	n, addr, err := c.readFrom(b)
123	if err != nil {
124		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
125	}
126	if addr == nil {
127		return n, nil, err
128	}
129	return n, addr, err
130}
131
132// ReadMsgUnix reads a message from c, copying the payload into b and
133// the associated out-of-band data into oob. It returns the number of
134// bytes copied into b, the number of bytes copied into oob, the flags
135// that were set on the message and the source address of the message.
136//
137// Note that if len(b) == 0 and len(oob) > 0, this function will still
138// read (and discard) 1 byte from the connection.
139func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
140	if !c.ok() {
141		return 0, 0, 0, nil, syscall.EINVAL
142	}
143	n, oobn, flags, addr, err = c.readMsg(b, oob)
144	if err != nil {
145		err = &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
146	}
147	return
148}
149
150// WriteToUnix acts like [UnixConn.WriteTo] but takes a [UnixAddr].
151func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (int, error) {
152	if !c.ok() {
153		return 0, syscall.EINVAL
154	}
155	n, err := c.writeTo(b, addr)
156	if err != nil {
157		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
158	}
159	return n, err
160}
161
162// WriteTo implements the [PacketConn] WriteTo method.
163func (c *UnixConn) WriteTo(b []byte, addr Addr) (int, error) {
164	if !c.ok() {
165		return 0, syscall.EINVAL
166	}
167	a, ok := addr.(*UnixAddr)
168	if !ok {
169		return 0, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr, Err: syscall.EINVAL}
170	}
171	n, err := c.writeTo(b, a)
172	if err != nil {
173		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: a.opAddr(), Err: err}
174	}
175	return n, err
176}
177
178// WriteMsgUnix writes a message to addr via c, copying the payload
179// from b and the associated out-of-band data from oob. It returns the
180// number of payload and out-of-band bytes written.
181//
182// Note that if len(b) == 0 and len(oob) > 0, this function will still
183// write 1 byte to the connection.
184func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
185	if !c.ok() {
186		return 0, 0, syscall.EINVAL
187	}
188	n, oobn, err = c.writeMsg(b, oob, addr)
189	if err != nil {
190		err = &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: addr.opAddr(), Err: err}
191	}
192	return
193}
194
195func newUnixConn(fd *netFD) *UnixConn { return &UnixConn{conn{fd}} }
196
197// DialUnix acts like [Dial] for Unix networks.
198//
199// The network must be a Unix network name; see func Dial for details.
200//
201// If laddr is non-nil, it is used as the local address for the
202// connection.
203func DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConn, error) {
204	switch network {
205	case "unix", "unixgram", "unixpacket":
206	default:
207		return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: UnknownNetworkError(network)}
208	}
209	sd := &sysDialer{network: network, address: raddr.String()}
210	c, err := sd.dialUnix(context.Background(), laddr, raddr)
211	if err != nil {
212		return nil, &OpError{Op: "dial", Net: network, Source: laddr.opAddr(), Addr: raddr.opAddr(), Err: err}
213	}
214	return c, nil
215}
216
217// UnixListener is a Unix domain socket listener. Clients should
218// typically use variables of type [Listener] instead of assuming Unix
219// domain sockets.
220type UnixListener struct {
221	fd         *netFD
222	path       string
223	unlink     bool
224	unlinkOnce sync.Once
225}
226
227func (ln *UnixListener) ok() bool { return ln != nil && ln.fd != nil }
228
229// SyscallConn returns a raw network connection.
230// This implements the [syscall.Conn] interface.
231//
232// The returned RawConn only supports calling Control. Read and
233// Write return an error.
234func (l *UnixListener) SyscallConn() (syscall.RawConn, error) {
235	if !l.ok() {
236		return nil, syscall.EINVAL
237	}
238	return newRawListener(l.fd), nil
239}
240
241// AcceptUnix accepts the next incoming call and returns the new
242// connection.
243func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
244	if !l.ok() {
245		return nil, syscall.EINVAL
246	}
247	c, err := l.accept()
248	if err != nil {
249		return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
250	}
251	return c, nil
252}
253
254// Accept implements the Accept method in the [Listener] interface.
255// Returned connections will be of type [*UnixConn].
256func (l *UnixListener) Accept() (Conn, error) {
257	if !l.ok() {
258		return nil, syscall.EINVAL
259	}
260	c, err := l.accept()
261	if err != nil {
262		return nil, &OpError{Op: "accept", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
263	}
264	return c, nil
265}
266
267// Close stops listening on the Unix address. Already accepted
268// connections are not closed.
269func (l *UnixListener) Close() error {
270	if !l.ok() {
271		return syscall.EINVAL
272	}
273	if err := l.close(); err != nil {
274		return &OpError{Op: "close", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
275	}
276	return nil
277}
278
279// Addr returns the listener's network address.
280// The Addr returned is shared by all invocations of Addr, so
281// do not modify it.
282func (l *UnixListener) Addr() Addr { return l.fd.laddr }
283
284// SetDeadline sets the deadline associated with the listener.
285// A zero time value disables the deadline.
286func (l *UnixListener) SetDeadline(t time.Time) error {
287	if !l.ok() {
288		return syscall.EINVAL
289	}
290	return l.fd.SetDeadline(t)
291}
292
293// File returns a copy of the underlying [os.File].
294// It is the caller's responsibility to close f when finished.
295// Closing l does not affect f, and closing f does not affect l.
296//
297// The returned os.File's file descriptor is different from the
298// connection's. Attempting to change properties of the original
299// using this duplicate may or may not have the desired effect.
300func (l *UnixListener) File() (f *os.File, err error) {
301	if !l.ok() {
302		return nil, syscall.EINVAL
303	}
304	f, err = l.file()
305	if err != nil {
306		err = &OpError{Op: "file", Net: l.fd.net, Source: nil, Addr: l.fd.laddr, Err: err}
307	}
308	return
309}
310
311// ListenUnix acts like [Listen] for Unix networks.
312//
313// The network must be "unix" or "unixpacket".
314func ListenUnix(network string, laddr *UnixAddr) (*UnixListener, error) {
315	switch network {
316	case "unix", "unixpacket":
317	default:
318		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(network)}
319	}
320	if laddr == nil {
321		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: errMissingAddress}
322	}
323	sl := &sysListener{network: network, address: laddr.String()}
324	ln, err := sl.listenUnix(context.Background(), laddr)
325	if err != nil {
326		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
327	}
328	return ln, nil
329}
330
331// ListenUnixgram acts like [ListenPacket] for Unix networks.
332//
333// The network must be "unixgram".
334func ListenUnixgram(network string, laddr *UnixAddr) (*UnixConn, error) {
335	switch network {
336	case "unixgram":
337	default:
338		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: UnknownNetworkError(network)}
339	}
340	if laddr == nil {
341		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: errMissingAddress}
342	}
343	sl := &sysListener{network: network, address: laddr.String()}
344	c, err := sl.listenUnixgram(context.Background(), laddr)
345	if err != nil {
346		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: laddr.opAddr(), Err: err}
347	}
348	return c, nil
349}
350