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