1/*
2 * Copyright 2023, Trung Nguyen, trungnt282910@gmail.com.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "UnixDatagramEndpoint.h"
8
9#include <new>
10
11#include "unix.h"
12#include "UnixAddressManager.h"
13#include "UnixFifo.h"
14
15
16#define UNIX_DATAGRAM_ENDPOINT_DEBUG_LEVEL	0
17#define UNIX_DEBUG_LEVEL					UNIX_DATAGRAM_ENDPOINT_DEBUG_LEVEL
18#include "UnixDebug.h"
19
20
21typedef AutoLocker<UnixDatagramEndpoint> UnixDatagramEndpointLocker;
22
23
24UnixDatagramEndpoint::UnixDatagramEndpoint(net_socket* socket)
25	:
26	UnixEndpoint(socket),
27	fTargetEndpoint(NULL),
28	fReceiveFifo(NULL),
29	fShutdownWrite(false)
30{
31	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::UnixDatagramEndpoint()\n",
32		find_thread(NULL), this);
33}
34
35
36UnixDatagramEndpoint::~UnixDatagramEndpoint()
37{
38	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::~UnixDatagramEndpoint()\n",
39		find_thread(NULL), this);
40}
41
42
43status_t
44UnixDatagramEndpoint::Init()
45{
46	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Init()\n",
47		find_thread(NULL), this);
48
49	RETURN_ERROR(B_OK);
50}
51
52
53void
54UnixDatagramEndpoint::Uninit()
55{
56	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Uninit()\n",
57		find_thread(NULL), this);
58
59	ReleaseReference();
60}
61
62
63status_t
64UnixDatagramEndpoint::Open()
65{
66	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Open()\n",
67		find_thread(NULL), this);
68
69	status_t error = ProtocolSocket::Open();
70	if (error != B_OK)
71		RETURN_ERROR(error);
72
73	RETURN_ERROR(B_OK);
74}
75
76
77status_t
78UnixDatagramEndpoint::Close()
79{
80	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Close()\n",
81		find_thread(NULL), this);
82
83	UnixDatagramEndpointLocker endpointLocker(this);
84
85	fShutdownRead = fShutdownWrite = true;
86
87	if (IsBound()) {
88		status_t status = UnixEndpoint::_Unbind();
89		if (status != B_OK)
90			RETURN_ERROR(status);
91	}
92
93	_UnsetReceiveFifo();
94
95	RETURN_ERROR(_Disconnect());
96}
97
98
99status_t
100UnixDatagramEndpoint::Free()
101{
102	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Free()\n",
103		find_thread(NULL), this);
104
105	UnixDatagramEndpointLocker endpointLocker(this);
106
107	ASSERT(fReceiveFifo == NULL);
108	ASSERT(fTargetEndpoint == NULL);
109
110	return B_OK;
111}
112
113
114status_t
115UnixDatagramEndpoint::Bind(const struct sockaddr* _address)
116{
117	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Bind(\"%s\")\n",
118		find_thread(NULL), this,
119		ConstSocketAddress(&gAddressModule, _address).AsString().Data());
120
121	if (_address->sa_family != AF_UNIX)
122		RETURN_ERROR(EAFNOSUPPORT);
123
124	UnixDatagramEndpointLocker endpointLocker(this);
125
126	if (IsBound())
127		RETURN_ERROR(B_BAD_VALUE);
128
129	const sockaddr_un* address = (const sockaddr_un*)_address;
130
131	RETURN_ERROR(_Bind(address));
132}
133
134
135status_t
136UnixDatagramEndpoint::Unbind()
137{
138	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Unbind()\n",
139		find_thread(NULL), this);
140
141	UnixDatagramEndpointLocker endpointLocker(this);
142
143	if (IsBound())
144		RETURN_ERROR(UnixEndpoint::_Unbind());
145
146	RETURN_ERROR(B_OK);
147}
148
149
150status_t
151UnixDatagramEndpoint::Listen(int backlog)
152{
153	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Listen(%d)\n", find_thread(NULL),
154		this, backlog);
155
156	RETURN_ERROR(EOPNOTSUPP);
157}
158
159
160status_t
161UnixDatagramEndpoint::Connect(const struct sockaddr* _address)
162{
163	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Connect(\"%s\")\n",
164		find_thread(NULL), this,
165		ConstSocketAddress(&gAddressModule, _address).AsString().Data());
166
167	UnixDatagramEndpointLocker endpointLocker(this);
168
169	BReference<UnixDatagramEndpoint> targetEndpointReference;
170	status_t status = _InitializeEndpoint(_address, targetEndpointReference);
171	if (status != B_OK)
172		RETURN_ERROR(status);
173
174	endpointLocker.Unlock();
175
176	UnixDatagramEndpoint* targetEndpoint = targetEndpointReference.Get();
177	UnixDatagramEndpointLocker targetLocker(targetEndpoint);
178
179	if (targetEndpoint->fTargetEndpoint != NULL && targetEndpoint->fTargetEndpoint != this)
180		RETURN_ERROR(EPERM);
181
182	targetLocker.Unlock();
183	endpointLocker.Lock();
184
185	status = _Disconnect();
186	if (status != B_OK)
187		RETURN_ERROR(status);
188
189	fTargetEndpoint = targetEndpoint;
190	fTargetEndpoint->AcquireReference();
191
192	// Required by the socket layer.
193	PeerAddress().SetTo(&fTargetEndpoint->socket->address);
194	gSocketModule->set_connected(Socket());
195
196	RETURN_ERROR(B_OK);
197}
198
199
200status_t
201UnixDatagramEndpoint::Accept(net_socket** _acceptedSocket)
202{
203	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Accept()\n",
204		find_thread(NULL), this);
205
206	RETURN_ERROR(EOPNOTSUPP);
207}
208
209
210ssize_t
211UnixDatagramEndpoint::Send(const iovec* vecs, size_t vecCount,
212	ancillary_data_container* ancillaryData, const struct sockaddr* address,
213	socklen_t addressLength, int flags)
214{
215	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Send()\n",
216		find_thread(NULL), this);
217
218	if ((flags & ~(MSG_DONTWAIT)) != 0)
219		return EOPNOTSUPP;
220
221	bigtime_t timeout = 0;
222	if ((flags & MSG_DONTWAIT) == 0) {
223		timeout = absolute_timeout(socket->send.timeout);
224		if (gStackModule->is_restarted_syscall())
225			timeout = gStackModule->restore_syscall_restart_timeout();
226		else
227			gStackModule->store_syscall_restart_timeout(timeout);
228	}
229	UnixDatagramEndpointLocker endpointLocker(this);
230
231	if (fShutdownWrite)
232		RETURN_ERROR(EPIPE);
233
234	status_t status;
235
236	BReference<UnixDatagramEndpoint> targetEndpointReference;
237	if (address == NULL) {
238		if (fTargetEndpoint == NULL)
239			RETURN_ERROR(ENOTCONN);
240
241		targetEndpointReference.SetTo(fTargetEndpoint);
242	} else {
243		status = _InitializeEndpoint(address, targetEndpointReference);
244		if (status != B_OK)
245			RETURN_ERROR(status);
246	}
247
248	// Get the address before unlocking the sending endpoint.
249	struct sockaddr_storage sourceAddress;
250	memcpy(&sourceAddress, &socket->address, sizeof(struct sockaddr_storage));
251	endpointLocker.Unlock();
252
253	UnixDatagramEndpoint* targetEndpoint = targetEndpointReference.Get();
254	UnixDatagramEndpointLocker targetLocker(targetEndpoint);
255
256	if (targetEndpoint->fTargetEndpoint != NULL && targetEndpoint->fTargetEndpoint != this)
257		RETURN_ERROR(EPERM);
258
259	if (targetEndpoint->fShutdownRead)
260		RETURN_ERROR(EPIPE);
261
262	if (targetEndpoint->fReceiveFifo == NULL) {
263		targetEndpoint->fReceiveFifo
264			= new (std::nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT, UnixFifoType::Datagram);
265		if (targetEndpoint->fReceiveFifo == NULL)
266			RETURN_ERROR(B_NO_MEMORY);
267
268		status = targetEndpoint->fReceiveFifo->Init();
269		if (status != B_OK) {
270			targetEndpoint->_UnsetReceiveFifo();
271			RETURN_ERROR(status);
272		}
273	}
274
275	UnixFifo* targetFifo = targetEndpoint->fReceiveFifo;
276	BReference<UnixFifo> targetFifoReference(targetFifo);
277	UnixFifoLocker fifoLocker(targetFifo);
278
279	targetLocker.Unlock();
280
281	ssize_t result = targetFifo->Write(vecs, vecCount, ancillaryData, &sourceAddress,
282		timeout);
283
284	// Notify select()ing readers, if we successfully wrote anything.
285	size_t readable = targetFifo->Readable();
286	bool notifyRead = (readable > 0 && result >= 0);
287
288	// Notify select()ing writers, if we failed to write anything and there's
289	// still room to write.
290	size_t writable = targetFifo->Writable();
291	bool notifyWrite = (writable > 0 && result < 0);
292
293	fifoLocker.Unlock();
294	targetLocker.Lock();
295
296	// We must recheck fShutdownRead after reacquiring the lock, as it may have changed.
297	if (notifyRead && !targetEndpoint->fShutdownRead)
298		gSocketModule->notify(targetEndpoint->socket, B_SELECT_READ, readable);
299
300	targetLocker.Unlock();
301
302	if (notifyWrite) {
303		endpointLocker.Lock();
304		if (!fShutdownWrite)
305			gSocketModule->notify(socket, B_SELECT_WRITE, writable);
306	}
307
308	switch (result) {
309		case EPIPE:
310			// The socket module will generate SIGPIPE for us, if necessary.
311			break;
312		case B_TIMED_OUT:
313			if (timeout == 0)
314				result = B_WOULD_BLOCK;
315			break;
316	}
317
318	RETURN_ERROR(result);
319}
320
321
322ssize_t
323UnixDatagramEndpoint::Receive(const iovec* vecs, size_t vecCount,
324	ancillary_data_container** _ancillaryData, struct sockaddr* _address,
325	socklen_t* _addressLength, int flags)
326{
327	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Receive()\n",
328		find_thread(NULL), this);
329
330	if ((flags & ~(MSG_DONTWAIT)) != 0)
331		return EOPNOTSUPP;
332
333	bigtime_t timeout = 0;
334	if ((flags & MSG_DONTWAIT) == 0) {
335		timeout = absolute_timeout(socket->receive.timeout);
336		if (gStackModule->is_restarted_syscall())
337			timeout = gStackModule->restore_syscall_restart_timeout();
338		else
339			gStackModule->store_syscall_restart_timeout(timeout);
340	}
341
342	UnixDatagramEndpointLocker endpointLocker(this);
343
344	// It is not clearly specified in POSIX how to treat pending
345	// datagrams when a socket has been shut down for reading.
346	// On Linux, pending messages are still read.
347	if (fShutdownRead)
348		RETURN_ERROR(0);
349
350	status_t status;
351
352	if (fReceiveFifo == NULL) {
353		fReceiveFifo = new (std::nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT,
354			UnixFifoType::Datagram);
355		if (fReceiveFifo == NULL)
356			RETURN_ERROR(B_NO_MEMORY);
357
358		status = fReceiveFifo->Init();
359		if (status != B_OK) {
360			_UnsetReceiveFifo();
361			RETURN_ERROR(status);
362		}
363	}
364
365	UnixFifo* fifo = fReceiveFifo;
366	BReference<UnixFifo> fifoReference(fifo);
367	UnixFifoLocker fifoLocker(fifo);
368
369	endpointLocker.Unlock();
370
371	struct sockaddr_storage addressStorage;
372
373	ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, &addressStorage, timeout);
374
375	// Notify select()ing writers, if we successfully read anything.
376	size_t writable = fifo->Writable();
377	bool notifyWrite = (result >= 0 && writable > 0
378		&& !fifo->IsWriteShutdown());
379
380	// Notify select()ing readers, if we failed to read anything and there's
381	// still something left to read.
382	size_t readable = fifo->Readable();
383	bool notifyRead = (result < 0 && readable > 0
384		&& !fifo->IsReadShutdown());
385
386	// re-lock our endpoint (unlock FIFO to respect locking order)
387	fifoLocker.Unlock();
388	endpointLocker.Lock();
389
390	// send notifications
391	if (notifyRead)
392		gSocketModule->notify(socket, B_SELECT_READ, readable);
393
394	if (notifyWrite) {
395		BReference<UnixDatagramEndpoint> originEndpointReference;
396		status = _InitializeEndpoint((struct sockaddr*)&addressStorage,
397			originEndpointReference);
398		if (status == B_OK) {
399			UnixDatagramEndpoint* originEndpoint = originEndpointReference.Get();
400			endpointLocker.Unlock();
401			UnixDatagramEndpointLocker originLocker(originEndpoint);
402			gSocketModule->notify(originEndpoint->socket, B_SELECT_WRITE, writable);
403			originLocker.Unlock();
404			endpointLocker.Lock();
405		}
406	}
407
408	if (result < 0) {
409		switch (result) {
410			case B_TIMED_OUT:
411				if (timeout == 0)
412					result = B_WOULD_BLOCK;
413				break;
414		}
415	} else {
416		if (_address != NULL) {
417			if (_addressLength == NULL)
418				RETURN_ERROR(B_BAD_ADDRESS);
419			struct sockaddr_un* address = (struct sockaddr_un*)&addressStorage;
420			socklen_t memoryLength = min_c(*_addressLength, address->sun_len);
421			memcpy(_address, address, memoryLength);
422			*_addressLength = address->sun_len;
423		}
424	}
425
426	RETURN_ERROR(result);
427}
428
429
430ssize_t
431UnixDatagramEndpoint::Sendable()
432{
433	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Sendable()\n",
434		find_thread(NULL), this);
435
436	RETURN_ERROR(EOPNOTSUPP);
437}
438
439
440ssize_t
441UnixDatagramEndpoint::Receivable()
442{
443	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Receivable()\n",
444		find_thread(NULL), this);
445
446	UnixDatagramEndpointLocker locker(this);
447
448	if (fReceiveFifo == NULL)
449		RETURN_ERROR(0);
450
451	UnixFifoLocker fifoLocker(fReceiveFifo);
452	ssize_t readable = fReceiveFifo->Readable();
453
454	RETURN_ERROR(readable);
455}
456
457
458status_t
459UnixDatagramEndpoint::SetReceiveBufferSize(size_t size)
460{
461	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::SetReceiveBufferSize()\n",
462		find_thread(NULL), this);
463
464	UnixDatagramEndpointLocker locker(this);
465
466	if (fReceiveFifo == NULL)
467		RETURN_ERROR(0);
468
469	UnixFifoLocker fifoLocker(fReceiveFifo);
470	RETURN_ERROR(fReceiveFifo->SetBufferCapacity(size));
471}
472
473
474status_t
475UnixDatagramEndpoint::GetPeerCredentials(ucred* credentials)
476{
477	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::GetPeerCredentials()\n",
478		find_thread(NULL), this);
479
480	RETURN_ERROR(EOPNOTSUPP);
481}
482
483
484status_t
485UnixDatagramEndpoint::Shutdown(int direction)
486{
487	TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Shutdown()\n",
488		find_thread(NULL), this);
489
490	UnixDatagramEndpointLocker endpointLocker(this);
491
492	if (direction != SHUT_RD && direction != SHUT_WR && direction != SHUT_RDWR)
493		RETURN_ERROR(B_BAD_VALUE);
494
495	if (direction != SHUT_RD)
496		fShutdownWrite = true;
497
498	if (direction != SHUT_WR)
499		fShutdownRead = true;
500
501	RETURN_ERROR(B_OK);
502}
503
504
505status_t
506UnixDatagramEndpoint::_InitializeEndpoint(const struct sockaddr* _address,
507	BReference<UnixDatagramEndpoint>& outEndpoint)
508{
509	if (_address->sa_family != AF_UNIX)
510		RETURN_ERROR(EAFNOSUPPORT);
511
512	UnixAddress unixAddress;
513
514	const struct sockaddr_un* address = (const struct sockaddr_un*)_address;
515
516	if (address->sun_path[0] == '\0') {
517		// internal address space (or empty address)
518		int32 internalID;
519		if (UnixAddress::IsEmptyAddress(*address))
520			RETURN_ERROR(B_BAD_VALUE);
521
522		internalID = UnixAddress::InternalID(*address);
523		if (internalID < 0)
524			RETURN_ERROR(internalID);
525
526		unixAddress.SetTo(internalID);
527	} else {
528		// FS address space
529		size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path));
530		if (pathLen == 0 || pathLen == sizeof(address->sun_path))
531			RETURN_ERROR(B_BAD_VALUE);
532
533		struct stat st;
534		status_t error = vfs_read_stat(-1, address->sun_path, true, &st,
535			!gStackModule->is_syscall());
536		if (error != B_OK)
537			RETURN_ERROR(error);
538
539		if (!S_ISSOCK(st.st_mode))
540			RETURN_ERROR(B_BAD_VALUE);
541
542		unixAddress.SetTo(st.st_dev, st.st_ino, NULL);
543	}
544
545	UnixAddressManagerLocker addressLocker(gAddressManager);
546	UnixEndpoint* targetUnixEndpoint = gAddressManager.Lookup(unixAddress);
547	if (targetUnixEndpoint == NULL)
548		RETURN_ERROR(ECONNREFUSED);
549	UnixDatagramEndpoint* targetEndpoint
550		= dynamic_cast<UnixDatagramEndpoint*>(targetUnixEndpoint);
551	if (targetEndpoint == NULL)
552		RETURN_ERROR(EPROTOTYPE);
553
554	outEndpoint.SetTo(targetEndpoint);
555	addressLocker.Unlock();
556
557	RETURN_ERROR(B_OK);
558}
559
560
561status_t
562UnixDatagramEndpoint::_Disconnect()
563{
564	if (fTargetEndpoint != NULL)
565		fTargetEndpoint->ReleaseReference();
566
567	fTargetEndpoint = NULL;
568	RETURN_ERROR(B_OK);
569}
570
571
572void
573UnixDatagramEndpoint::_UnsetReceiveFifo()
574{
575	if (fReceiveFifo != NULL) {
576		fReceiveFifo->ReleaseReference();
577		fReceiveFifo = NULL;
578	}
579}
580