unix: Implement datagram sockets
Implement `SOCK_DGRAM` sockets for `AF_UNIX` family. Change-Id: If3d6f408a7d881635ccf04b080391905fdc94b13 Reviewed-on: https://review.haiku-os.org/c/haiku/+/6617 Reviewed-by: Jérôme Duval <[email protected]> Tested-by: Commit checker robot <[email protected]>
This commit is contained in:
committed by
Jérôme Duval
parent
5a86b40e33
commit
b7b57869e8
@@ -9,4 +9,7 @@ KernelAddon unix :
|
||||
UnixAddress.cpp
|
||||
UnixEndpoint.cpp
|
||||
UnixFifo.cpp
|
||||
|
||||
UnixDatagramEndpoint.cpp
|
||||
UnixStreamEndpoint.cpp
|
||||
;
|
||||
|
||||
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* Copyright 2023, Trung Nguyen, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "UnixDatagramEndpoint.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "unix.h"
|
||||
#include "UnixAddressManager.h"
|
||||
#include "UnixFifo.h"
|
||||
|
||||
|
||||
#define UNIX_DATAGRAM_ENDPOINT_DEBUG_LEVEL 0
|
||||
#define UNIX_DEBUG_LEVEL UNIX_DATAGRAM_ENDPOINT_DEBUG_LEVEL
|
||||
#include "UnixDebug.h"
|
||||
|
||||
|
||||
typedef AutoLocker<UnixDatagramEndpoint> UnixDatagramEndpointLocker;
|
||||
|
||||
|
||||
UnixDatagramEndpoint::UnixDatagramEndpoint(net_socket* socket)
|
||||
:
|
||||
UnixEndpoint(socket),
|
||||
fTargetEndpoint(NULL),
|
||||
fReceiveFifo(NULL),
|
||||
fShutdownWrite(false)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::UnixDatagramEndpoint()\n",
|
||||
find_thread(NULL), this);
|
||||
}
|
||||
|
||||
|
||||
UnixDatagramEndpoint::~UnixDatagramEndpoint()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::~UnixDatagramEndpoint()\n",
|
||||
find_thread(NULL), this);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Init()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Init()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixDatagramEndpoint::Uninit()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Uninit()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
ReleaseReference();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Open()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Open()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
status_t error = ProtocolSocket::Open();
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Close()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Close()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
if (IsBound())
|
||||
RETURN_ERROR(UnixEndpoint::_Unbind());
|
||||
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
RETURN_ERROR(_Disconnect());
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Free()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Free()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
RETURN_ERROR(_Disconnect());
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Bind(const struct sockaddr* _address)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Bind(\"%s\")\n",
|
||||
find_thread(NULL), this,
|
||||
ConstSocketAddress(&gAddressModule, _address).AsString().Data());
|
||||
|
||||
if (_address->sa_family != AF_UNIX)
|
||||
RETURN_ERROR(EAFNOSUPPORT);
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
if (IsBound())
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
const sockaddr_un* address = (const sockaddr_un*)_address;
|
||||
|
||||
RETURN_ERROR(_Bind(address));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Unbind()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Unbind()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
if (IsBound())
|
||||
RETURN_ERROR(UnixEndpoint::_Unbind());
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Listen(int backlog)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Listen(%d)\n", find_thread(NULL),
|
||||
this, backlog);
|
||||
|
||||
RETURN_ERROR(EOPNOTSUPP);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Connect(const struct sockaddr* _address)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Connect(\"%s\")\n",
|
||||
find_thread(NULL), this,
|
||||
ConstSocketAddress(&gAddressModule, _address).AsString().Data());
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
BReference<UnixDatagramEndpoint> targetEndpointReference;
|
||||
status_t status = _InitializeEndpoint(_address, targetEndpointReference);
|
||||
if (status != B_OK)
|
||||
RETURN_ERROR(status);
|
||||
|
||||
endpointLocker.Unlock();
|
||||
|
||||
UnixDatagramEndpoint* targetEndpoint = targetEndpointReference.Get();
|
||||
UnixDatagramEndpointLocker targetLocker(targetEndpoint);
|
||||
|
||||
if (targetEndpoint->fTargetEndpoint != NULL && targetEndpoint->fTargetEndpoint != this)
|
||||
RETURN_ERROR(EPERM);
|
||||
|
||||
targetLocker.Unlock();
|
||||
endpointLocker.Lock();
|
||||
|
||||
status = _Disconnect();
|
||||
if (status != B_OK)
|
||||
RETURN_ERROR(status);
|
||||
|
||||
fTargetEndpoint = targetEndpoint;
|
||||
fTargetEndpoint->AcquireReference();
|
||||
|
||||
// Required by the socket layer.
|
||||
PeerAddress().SetTo(&fTargetEndpoint->socket->address);
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Accept(net_socket** _acceptedSocket)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Accept()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
RETURN_ERROR(EOPNOTSUPP);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixDatagramEndpoint::Send(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container* ancillaryData, const struct sockaddr* address,
|
||||
socklen_t addressLength)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Send()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->send.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
if (fShutdownWrite)
|
||||
RETURN_ERROR(EPIPE);
|
||||
|
||||
status_t status;
|
||||
|
||||
BReference<UnixDatagramEndpoint> targetEndpointReference;
|
||||
if (address == NULL) {
|
||||
if (fTargetEndpoint == NULL)
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
|
||||
targetEndpointReference.SetTo(fTargetEndpoint);
|
||||
} else {
|
||||
status = _InitializeEndpoint(address, targetEndpointReference);
|
||||
if (status != B_OK)
|
||||
RETURN_ERROR(status);
|
||||
}
|
||||
|
||||
// Get the address before unlocking the sending endpoint.
|
||||
struct sockaddr_storage sourceAddress;
|
||||
memcpy(&sourceAddress, &socket->address, sizeof(struct sockaddr_storage));
|
||||
endpointLocker.Unlock();
|
||||
|
||||
UnixDatagramEndpoint* targetEndpoint = targetEndpointReference.Get();
|
||||
UnixDatagramEndpointLocker targetLocker(targetEndpoint);
|
||||
|
||||
if (targetEndpoint->fTargetEndpoint != NULL && targetEndpoint->fTargetEndpoint != this)
|
||||
RETURN_ERROR(EPERM);
|
||||
|
||||
if (targetEndpoint->fShutdownRead)
|
||||
RETURN_ERROR(EPIPE);
|
||||
|
||||
if (targetEndpoint->fReceiveFifo == NULL) {
|
||||
targetEndpoint->fReceiveFifo
|
||||
= new (std::nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT, UnixFifoType::Datagram);
|
||||
if (targetEndpoint->fReceiveFifo == NULL)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
|
||||
status = targetEndpoint->fReceiveFifo->Init();
|
||||
if (status != B_OK) {
|
||||
targetEndpoint->_UnsetReceiveFifo();
|
||||
RETURN_ERROR(status);
|
||||
}
|
||||
}
|
||||
|
||||
UnixFifo* targetFifo = targetEndpoint->fReceiveFifo;
|
||||
BReference<UnixFifo> targetFifoReference(targetFifo);
|
||||
UnixFifoLocker fifoLocker(targetFifo);
|
||||
|
||||
targetLocker.Unlock();
|
||||
|
||||
ssize_t result = targetFifo->Write(vecs, vecCount, ancillaryData, &sourceAddress,
|
||||
timeout);
|
||||
|
||||
// Notify select()ing readers, if we successfully wrote anything.
|
||||
size_t readable = targetFifo->Readable();
|
||||
bool notifyRead = (readable > 0 && result >= 0);
|
||||
|
||||
// Notify select()ing writers, if we failed to write anything and there's
|
||||
// still room to write.
|
||||
size_t writable = targetFifo->Writable();
|
||||
bool notifyWrite = (writable > 0 && result < 0);
|
||||
|
||||
fifoLocker.Unlock();
|
||||
targetLocker.Lock();
|
||||
|
||||
if (notifyRead)
|
||||
gSocketModule->notify(targetEndpoint->socket, B_SELECT_READ, readable);
|
||||
|
||||
targetLocker.Unlock();
|
||||
|
||||
if (notifyWrite) {
|
||||
endpointLocker.Lock();
|
||||
gSocketModule->notify(socket, B_SELECT_WRITE, writable);
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case EPIPE:
|
||||
if (gStackModule->is_syscall())
|
||||
send_signal(find_thread(NULL), SIGPIPE);
|
||||
break;
|
||||
case B_TIMED_OUT:
|
||||
if (timeout == 0)
|
||||
result = B_WOULD_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
RETURN_ERROR(result);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixDatagramEndpoint::Receive(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container** _ancillaryData, struct sockaddr* _address,
|
||||
socklen_t* _addressLength)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Receive()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
// It is not clearly specified in POSIX how to treat pending
|
||||
// datagrams when a socket has been shut down for reading.
|
||||
// On Linux, pending messages are still read.
|
||||
if (fShutdownRead)
|
||||
RETURN_ERROR(0);
|
||||
|
||||
status_t status;
|
||||
|
||||
if (fReceiveFifo == NULL) {
|
||||
fReceiveFifo = new (std::nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT,
|
||||
UnixFifoType::Datagram);
|
||||
if (fReceiveFifo == NULL)
|
||||
RETURN_ERROR(B_NO_MEMORY);
|
||||
|
||||
status = fReceiveFifo->Init();
|
||||
if (status != B_OK) {
|
||||
_UnsetReceiveFifo();
|
||||
RETURN_ERROR(status);
|
||||
}
|
||||
}
|
||||
|
||||
UnixFifo* fifo = fReceiveFifo;
|
||||
BReference<UnixFifo> fifoReference(fifo);
|
||||
UnixFifoLocker fifoLocker(fifo);
|
||||
|
||||
endpointLocker.Unlock();
|
||||
|
||||
struct sockaddr_storage addressStorage;
|
||||
|
||||
ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, &addressStorage, timeout);
|
||||
|
||||
// Notify select()ing writers, if we successfully read anything.
|
||||
size_t writable = fifo->Writable();
|
||||
bool notifyWrite = (result >= 0 && writable > 0
|
||||
&& !fifo->IsWriteShutdown());
|
||||
|
||||
// Notify select()ing readers, if we failed to read anything and there's
|
||||
// still something left to read.
|
||||
size_t readable = fifo->Readable();
|
||||
bool notifyRead = (result < 0 && readable > 0
|
||||
&& !fifo->IsReadShutdown());
|
||||
|
||||
// re-lock our endpoint (unlock FIFO to respect locking order)
|
||||
fifoLocker.Unlock();
|
||||
endpointLocker.Lock();
|
||||
|
||||
// send notifications
|
||||
if (notifyRead)
|
||||
gSocketModule->notify(socket, B_SELECT_READ, readable);
|
||||
|
||||
if (notifyWrite) {
|
||||
BReference<UnixDatagramEndpoint> originEndpointReference;
|
||||
status = _InitializeEndpoint((struct sockaddr*)&addressStorage,
|
||||
originEndpointReference);
|
||||
if (status == B_OK) {
|
||||
UnixDatagramEndpoint* originEndpoint = originEndpointReference.Get();
|
||||
endpointLocker.Unlock();
|
||||
UnixDatagramEndpointLocker originLocker(originEndpoint);
|
||||
gSocketModule->notify(originEndpoint->socket, B_SELECT_WRITE, writable);
|
||||
originLocker.Unlock();
|
||||
endpointLocker.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
switch (result) {
|
||||
case B_TIMED_OUT:
|
||||
if (timeout == 0)
|
||||
result = B_WOULD_BLOCK;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (_address != NULL) {
|
||||
if (_addressLength == NULL)
|
||||
RETURN_ERROR(B_BAD_ADDRESS);
|
||||
struct sockaddr_un* address = (struct sockaddr_un*)&addressStorage;
|
||||
socklen_t memoryLength = min_c(*_addressLength, address->sun_len);
|
||||
memcpy(_address, address, memoryLength);
|
||||
*_addressLength = address->sun_len;
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_ERROR(result);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixDatagramEndpoint::Sendable()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Sendable()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
RETURN_ERROR(EOPNOTSUPP);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixDatagramEndpoint::Receivable()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Receivable()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
UnixDatagramEndpointLocker locker(this);
|
||||
|
||||
if (fReceiveFifo == NULL)
|
||||
RETURN_ERROR(0);
|
||||
|
||||
UnixFifoLocker fifoLocker(fReceiveFifo);
|
||||
ssize_t readable = fReceiveFifo->Readable();
|
||||
|
||||
RETURN_ERROR(readable);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::SetReceiveBufferSize(size_t size)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::SetReceiveBufferSize()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
UnixDatagramEndpointLocker locker(this);
|
||||
|
||||
if (fReceiveFifo == NULL)
|
||||
RETURN_ERROR(0);
|
||||
|
||||
UnixFifoLocker fifoLocker(fReceiveFifo);
|
||||
RETURN_ERROR(fReceiveFifo->SetBufferCapacity(size));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::GetPeerCredentials(ucred* credentials)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::GetPeerCredentials()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
RETURN_ERROR(EOPNOTSUPP);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::Shutdown(int direction)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixDatagramEndpoint::Shutdown()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
UnixDatagramEndpointLocker endpointLocker(this);
|
||||
|
||||
if (direction != SHUT_RD && direction != SHUT_WR && direction != SHUT_RDWR)
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
if (direction != SHUT_RD)
|
||||
fShutdownWrite = true;
|
||||
|
||||
if (direction != SHUT_WR)
|
||||
fShutdownRead = true;
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::_InitializeEndpoint(const struct sockaddr* _address,
|
||||
BReference<UnixDatagramEndpoint>& outEndpoint)
|
||||
{
|
||||
if (_address->sa_family != AF_UNIX)
|
||||
RETURN_ERROR(EAFNOSUPPORT);
|
||||
|
||||
UnixAddress unixAddress;
|
||||
|
||||
const struct sockaddr_un* address = (const struct sockaddr_un*)_address;
|
||||
|
||||
if (address->sun_path[0] == '\0') {
|
||||
// internal address space (or empty address)
|
||||
int32 internalID;
|
||||
if (UnixAddress::IsEmptyAddress(*address))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
internalID = UnixAddress::InternalID(*address);
|
||||
if (internalID < 0)
|
||||
RETURN_ERROR(internalID);
|
||||
|
||||
unixAddress.SetTo(internalID);
|
||||
} else {
|
||||
// FS address space
|
||||
size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path));
|
||||
if (pathLen == 0 || pathLen == sizeof(address->sun_path))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
struct stat st;
|
||||
status_t error = vfs_read_stat(-1, address->sun_path, true, &st,
|
||||
!gStackModule->is_syscall());
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
if (!S_ISSOCK(st.st_mode))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
unixAddress.SetTo(st.st_dev, st.st_ino, NULL);
|
||||
}
|
||||
|
||||
UnixAddressManagerLocker addressLocker(gAddressManager);
|
||||
UnixEndpoint* targetUnixEndpoint = gAddressManager.Lookup(unixAddress);
|
||||
if (targetUnixEndpoint == NULL)
|
||||
RETURN_ERROR(ECONNREFUSED);
|
||||
UnixDatagramEndpoint* targetEndpoint
|
||||
= dynamic_cast<UnixDatagramEndpoint*>(targetUnixEndpoint);
|
||||
if (targetEndpoint == NULL)
|
||||
RETURN_ERROR(EPROTOTYPE);
|
||||
|
||||
outEndpoint.SetTo(targetEndpoint);
|
||||
addressLocker.Unlock();
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixDatagramEndpoint::_Disconnect()
|
||||
{
|
||||
if (fTargetEndpoint != NULL)
|
||||
fTargetEndpoint->ReleaseReference();
|
||||
|
||||
fTargetEndpoint = NULL;
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixDatagramEndpoint::_UnsetReceiveFifo()
|
||||
{
|
||||
if (fReceiveFifo != NULL) {
|
||||
fReceiveFifo->ReleaseReference();
|
||||
fReceiveFifo = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2023, Trung Nguyen, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef UNIX_DATAGRAM_ENDPOINT_H
|
||||
#define UNIX_DATAGRAM_ENDPOINT_H
|
||||
|
||||
|
||||
#include <Referenceable.h>
|
||||
|
||||
#include "UnixEndpoint.h"
|
||||
|
||||
|
||||
class UnixFifo;
|
||||
|
||||
|
||||
class UnixDatagramEndpoint : public UnixEndpoint, public BReferenceable {
|
||||
public:
|
||||
UnixDatagramEndpoint(net_socket* socket);
|
||||
virtual ~UnixDatagramEndpoint() override;
|
||||
|
||||
status_t Init() override;
|
||||
void Uninit() override;
|
||||
|
||||
status_t Open() override;
|
||||
status_t Close() override;
|
||||
status_t Free() override;
|
||||
|
||||
status_t Bind(const struct sockaddr* _address) override;
|
||||
status_t Unbind() override;
|
||||
status_t Listen(int backlog) override;
|
||||
status_t Connect(const struct sockaddr* address) override;
|
||||
status_t Accept(net_socket** _acceptedSocket) override;
|
||||
|
||||
ssize_t Send(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container* ancillaryData,
|
||||
const struct sockaddr* address,
|
||||
socklen_t addressLength) override;
|
||||
ssize_t Receive(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container** _ancillaryData,
|
||||
struct sockaddr* _address,
|
||||
socklen_t* _addressLength) override;
|
||||
|
||||
ssize_t Sendable() override;
|
||||
ssize_t Receivable() override;
|
||||
|
||||
status_t SetReceiveBufferSize(size_t size) override;
|
||||
status_t GetPeerCredentials(ucred* credentials) override;
|
||||
|
||||
status_t Shutdown(int direction) override;
|
||||
|
||||
bool IsBound() const
|
||||
{
|
||||
return fAddress.IsValid();
|
||||
}
|
||||
|
||||
private:
|
||||
static status_t _InitializeEndpoint(const struct sockaddr* _address,
|
||||
BReference<UnixDatagramEndpoint> &outEndpoint);
|
||||
|
||||
status_t _Disconnect();
|
||||
void _UnsetReceiveFifo();
|
||||
|
||||
private:
|
||||
UnixDatagramEndpoint* fTargetEndpoint;
|
||||
UnixFifo* fReceiveFifo;
|
||||
bool fShutdownWrite:1;
|
||||
bool fShutdownRead:1;
|
||||
};
|
||||
|
||||
|
||||
#endif // UNIX_DATAGRAM_ENDPOINT_H
|
||||
@@ -1,41 +1,46 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Copyright 2023, Trung Nguyen, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "UnixEndpoint.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include <vfs.h>
|
||||
|
||||
#include "UnixAddressManager.h"
|
||||
#include "UnixFifo.h"
|
||||
#include "UnixDatagramEndpoint.h"
|
||||
#include "UnixStreamEndpoint.h"
|
||||
|
||||
|
||||
#define UNIX_ENDPOINT_DEBUG_LEVEL 0
|
||||
#define UNIX_DEBUG_LEVEL UNIX_ENDPOINT_DEBUG_LEVEL
|
||||
#define UNIX_ENDPOINT_DEBUG_LEVEL 1
|
||||
#define UNIX_DEBUG_LEVEL UNIX_ENDPOINT_DEBUG_LEVEL
|
||||
#include "UnixDebug.h"
|
||||
|
||||
|
||||
// Note on locking order (outermost -> innermost):
|
||||
// UnixEndpoint: connecting -> listening -> child
|
||||
// -> UnixFifo (never lock more than one at a time)
|
||||
// -> UnixAddressManager
|
||||
|
||||
|
||||
static inline bigtime_t
|
||||
absolute_timeout(bigtime_t timeout)
|
||||
status_t
|
||||
UnixEndpoint::Create(net_socket* socket, UnixEndpoint** _endpoint)
|
||||
{
|
||||
if (timeout == 0 || timeout == B_INFINITE_TIMEOUT)
|
||||
return timeout;
|
||||
TRACE("[%" B_PRId32 "] UnixEndpoint::Create(%p, %p)\n", find_thread(NULL),
|
||||
socket, _endpoint);
|
||||
|
||||
// TODO: Make overflow safe!
|
||||
return timeout + system_time();
|
||||
if (socket == NULL || _endpoint == NULL)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
switch (socket->type) {
|
||||
case SOCK_STREAM:
|
||||
*_endpoint = new(std::nothrow) UnixStreamEndpoint(socket);
|
||||
break;
|
||||
case SOCK_DGRAM:
|
||||
*_endpoint = new(std::nothrow) UnixDatagramEndpoint(socket);
|
||||
break;
|
||||
default:
|
||||
return EPROTOTYPE;
|
||||
}
|
||||
|
||||
return *_endpoint == NULL ? B_NO_MEMORY : B_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +48,7 @@ UnixEndpoint::UnixEndpoint(net_socket* socket)
|
||||
:
|
||||
ProtocolSocket(socket),
|
||||
fAddress(),
|
||||
fAddressHashLink(),
|
||||
fPeerEndpoint(NULL),
|
||||
fReceiveFifo(NULL),
|
||||
fState(UNIX_ENDPOINT_CLOSED),
|
||||
fAcceptSemaphore(-1),
|
||||
fIsChild(false),
|
||||
fWasConnected(false)
|
||||
fAddressHashLink()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::UnixEndpoint()\n",
|
||||
find_thread(NULL), this);
|
||||
@@ -60,7 +59,7 @@ UnixEndpoint::UnixEndpoint(net_socket* socket)
|
||||
|
||||
UnixEndpoint::~UnixEndpoint()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::~UnixEndpoint()\n",
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::UnixEndpoint()\n",
|
||||
find_thread(NULL), this);
|
||||
|
||||
mutex_destroy(&fLock);
|
||||
@@ -68,110 +67,11 @@ UnixEndpoint::~UnixEndpoint()
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Init()
|
||||
UnixEndpoint::_Bind(const struct sockaddr_un* address)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Init()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixEndpoint::Uninit()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Uninit()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
// check whether we're closed
|
||||
UnixEndpointLocker locker(this);
|
||||
bool closed = (fState == UNIX_ENDPOINT_CLOSED);
|
||||
locker.Unlock();
|
||||
|
||||
if (!closed) {
|
||||
// That probably means, we're a child endpoint of a listener and
|
||||
// have been fully connected, but not yet accepted. Our Close()
|
||||
// hook isn't called in this case. Do it manually.
|
||||
Close();
|
||||
}
|
||||
|
||||
ReleaseReference();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Open()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Open()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
status_t error = ProtocolSocket::Open();
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
fState = UNIX_ENDPOINT_NOT_CONNECTED;
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Close()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Close()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
|
||||
if (fState == UNIX_ENDPOINT_CONNECTED) {
|
||||
UnixEndpointLocker peerLocker;
|
||||
if (_LockConnectedEndpoints(locker, peerLocker) == B_OK) {
|
||||
// We're still connected. Disconnect both endpoints!
|
||||
fPeerEndpoint->_Disconnect();
|
||||
_Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
if (fState == UNIX_ENDPOINT_LISTENING)
|
||||
_StopListening();
|
||||
|
||||
_Unbind();
|
||||
|
||||
fState = UNIX_ENDPOINT_CLOSED;
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Free()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Free()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Bind(const struct sockaddr *_address)
|
||||
{
|
||||
if (_address->sa_family != AF_UNIX)
|
||||
RETURN_ERROR(EAFNOSUPPORT);
|
||||
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Bind(\"%s\")\n",
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::_Bind(\"%s\")\n",
|
||||
find_thread(NULL), this,
|
||||
ConstSocketAddress(&gAddressModule, _address).AsString().Data());
|
||||
|
||||
const sockaddr_un* address = (const sockaddr_un*)_address;
|
||||
|
||||
UnixEndpointLocker endpointLocker(this);
|
||||
|
||||
if (fState != UNIX_ENDPOINT_NOT_CONNECTED || IsBound())
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
ConstSocketAddress(&gAddressModule, (struct sockaddr*)address).AsString().Data());
|
||||
|
||||
if (address->sun_path[0] == '\0') {
|
||||
UnixAddressManagerLocker addressLocker(gAddressManager);
|
||||
@@ -227,607 +127,6 @@ UnixEndpoint::Bind(const struct sockaddr *_address)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Unbind()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Unbind()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixEndpointLocker endpointLocker(this);
|
||||
|
||||
RETURN_ERROR(_Unbind());
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Listen(int backlog)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Listen(%d)\n", find_thread(NULL),
|
||||
this, backlog);
|
||||
|
||||
UnixEndpointLocker endpointLocker(this);
|
||||
|
||||
if (!IsBound())
|
||||
RETURN_ERROR(EDESTADDRREQ);
|
||||
if (fState != UNIX_ENDPOINT_NOT_CONNECTED
|
||||
&& fState != UNIX_ENDPOINT_LISTENING)
|
||||
RETURN_ERROR(EINVAL);
|
||||
|
||||
gSocketModule->set_max_backlog(socket, backlog);
|
||||
|
||||
if (fState == UNIX_ENDPOINT_NOT_CONNECTED) {
|
||||
fAcceptSemaphore = create_sem(0, "unix accept");
|
||||
if (fAcceptSemaphore < 0)
|
||||
RETURN_ERROR(ENOBUFS);
|
||||
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
fCredentials.pid = getpid();
|
||||
fCredentials.uid = geteuid();
|
||||
fCredentials.gid = getegid();
|
||||
|
||||
fState = UNIX_ENDPOINT_LISTENING;
|
||||
}
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Connect(const struct sockaddr *_address)
|
||||
{
|
||||
if (_address->sa_family != AF_UNIX)
|
||||
RETURN_ERROR(EAFNOSUPPORT);
|
||||
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Connect(\"%s\")\n",
|
||||
find_thread(NULL), this,
|
||||
ConstSocketAddress(&gAddressModule, _address).AsString().Data());
|
||||
|
||||
const sockaddr_un* address = (const sockaddr_un*)_address;
|
||||
|
||||
UnixEndpointLocker endpointLocker(this);
|
||||
|
||||
if (fState == UNIX_ENDPOINT_CONNECTED)
|
||||
RETURN_ERROR(EISCONN);
|
||||
|
||||
if (fState != UNIX_ENDPOINT_NOT_CONNECTED)
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
// TODO: If listening, we could set the backlog to 0 and connect.
|
||||
|
||||
// check the address first
|
||||
UnixAddress unixAddress;
|
||||
|
||||
if (address->sun_path[0] == '\0') {
|
||||
// internal address space (or empty address)
|
||||
int32 internalID;
|
||||
if (UnixAddress::IsEmptyAddress(*address))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
internalID = UnixAddress::InternalID(*address);
|
||||
if (internalID < 0)
|
||||
RETURN_ERROR(internalID);
|
||||
|
||||
unixAddress.SetTo(internalID);
|
||||
} else {
|
||||
// FS address space
|
||||
size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path));
|
||||
if (pathLen == 0 || pathLen == sizeof(address->sun_path))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
struct stat st;
|
||||
status_t error = vfs_read_stat(-1, address->sun_path, true, &st,
|
||||
!gStackModule->is_syscall());
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
if (!S_ISSOCK(st.st_mode))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
unixAddress.SetTo(st.st_dev, st.st_ino, NULL);
|
||||
}
|
||||
|
||||
// get the peer endpoint
|
||||
UnixAddressManagerLocker addressLocker(gAddressManager);
|
||||
UnixEndpoint* listeningEndpoint = gAddressManager.Lookup(unixAddress);
|
||||
if (listeningEndpoint == NULL)
|
||||
RETURN_ERROR(ECONNREFUSED);
|
||||
BReference<UnixEndpoint> peerReference(listeningEndpoint);
|
||||
addressLocker.Unlock();
|
||||
|
||||
UnixEndpointLocker peerLocker(listeningEndpoint);
|
||||
|
||||
if (!listeningEndpoint->IsBound()
|
||||
|| listeningEndpoint->fState != UNIX_ENDPOINT_LISTENING
|
||||
|| listeningEndpoint->fAddress != unixAddress) {
|
||||
RETURN_ERROR(ECONNREFUSED);
|
||||
}
|
||||
|
||||
// Allocate FIFOs for us and the socket we're going to spawn. We do that
|
||||
// now, so that the mess we need to cleanup, if allocating them fails, is
|
||||
// harmless.
|
||||
UnixFifo* fifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT);
|
||||
UnixFifo* peerFifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT);
|
||||
ObjectDeleter<UnixFifo> fifoDeleter(fifo);
|
||||
ObjectDeleter<UnixFifo> peerFifoDeleter(peerFifo);
|
||||
|
||||
status_t error;
|
||||
if ((error = fifo->Init()) != B_OK || (error = peerFifo->Init()) != B_OK)
|
||||
return error;
|
||||
|
||||
// spawn new endpoint for accept()
|
||||
net_socket* newSocket;
|
||||
error = gSocketModule->spawn_pending_socket(listeningEndpoint->socket,
|
||||
&newSocket);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// init connected peer endpoint
|
||||
UnixEndpoint* connectedEndpoint = (UnixEndpoint*)newSocket->first_protocol;
|
||||
|
||||
UnixEndpointLocker connectedLocker(connectedEndpoint);
|
||||
|
||||
connectedEndpoint->_Spawn(this, listeningEndpoint, peerFifo);
|
||||
|
||||
// update our attributes
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
fPeerEndpoint = connectedEndpoint;
|
||||
PeerAddress().SetTo(&connectedEndpoint->socket->address);
|
||||
fPeerEndpoint->AcquireReference();
|
||||
fReceiveFifo = fifo;
|
||||
|
||||
fCredentials.pid = getpid();
|
||||
fCredentials.uid = geteuid();
|
||||
fCredentials.gid = getegid();
|
||||
|
||||
fifoDeleter.Detach();
|
||||
peerFifoDeleter.Detach();
|
||||
|
||||
fState = UNIX_ENDPOINT_CONNECTED;
|
||||
fWasConnected = true;
|
||||
|
||||
gSocketModule->set_connected(newSocket);
|
||||
|
||||
release_sem(listeningEndpoint->fAcceptSemaphore);
|
||||
|
||||
connectedLocker.Unlock();
|
||||
peerLocker.Unlock();
|
||||
endpointLocker.Unlock();
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Accept(net_socket **_acceptedSocket)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Accept()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
|
||||
status_t error;
|
||||
do {
|
||||
locker.Unlock();
|
||||
|
||||
error = acquire_sem_etc(fAcceptSemaphore, 1,
|
||||
B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, timeout);
|
||||
if (error < B_OK)
|
||||
break;
|
||||
|
||||
locker.Lock();
|
||||
error = gSocketModule->dequeue_connected(socket, _acceptedSocket);
|
||||
} while (error != B_OK);
|
||||
|
||||
if (error == B_TIMED_OUT && timeout == 0) {
|
||||
// translate non-blocking timeouts to the correct error code
|
||||
error = B_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixEndpoint::Send(const iovec *vecs, size_t vecCount,
|
||||
ancillary_data_container *ancillaryData)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Send(%p, %ld, %p)\n",
|
||||
find_thread(NULL), this, vecs, vecCount, ancillaryData);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->send.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
|
||||
BReference<UnixEndpoint> peerReference;
|
||||
UnixEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
UnixEndpoint* peerEndpoint = fPeerEndpoint;
|
||||
peerReference.SetTo(peerEndpoint);
|
||||
|
||||
// lock the peer's FIFO
|
||||
UnixFifo* peerFifo = peerEndpoint->fReceiveFifo;
|
||||
BReference<UnixFifo> _(peerFifo);
|
||||
UnixFifoLocker fifoLocker(peerFifo);
|
||||
|
||||
// unlock endpoints
|
||||
locker.Unlock();
|
||||
peerLocker.Unlock();
|
||||
|
||||
ssize_t result = peerFifo->Write(vecs, vecCount, ancillaryData, timeout);
|
||||
|
||||
// Notify select()ing readers, if we successfully wrote anything.
|
||||
size_t readable = peerFifo->Readable();
|
||||
bool notifyRead = (error == B_OK && readable > 0
|
||||
&& !peerFifo->IsReadShutdown());
|
||||
|
||||
// Notify select()ing writers, if we failed to write anything and there's
|
||||
// still room to write.
|
||||
size_t writable = peerFifo->Writable();
|
||||
bool notifyWrite = (error != B_OK && writable > 0
|
||||
&& !peerFifo->IsWriteShutdown());
|
||||
|
||||
// re-lock our endpoint (unlock FIFO to respect locking order)
|
||||
fifoLocker.Unlock();
|
||||
locker.Lock();
|
||||
|
||||
bool peerLocked = (fPeerEndpoint == peerEndpoint
|
||||
&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
|
||||
|
||||
// send notifications
|
||||
if (peerLocked && notifyRead)
|
||||
gSocketModule->notify(peerEndpoint->socket, B_SELECT_READ, readable);
|
||||
if (notifyWrite)
|
||||
gSocketModule->notify(socket, B_SELECT_WRITE, writable);
|
||||
|
||||
switch (result) {
|
||||
case UNIX_FIFO_SHUTDOWN:
|
||||
if (fPeerEndpoint == peerEndpoint
|
||||
&& fState == UNIX_ENDPOINT_CONNECTED) {
|
||||
// Orderly write shutdown on our side.
|
||||
// Note: Linux and Solaris also send a SIGPIPE, but according
|
||||
// the send() specification that shouldn't be done.
|
||||
result = EPIPE;
|
||||
} else {
|
||||
// The FD has been closed.
|
||||
result = EBADF;
|
||||
}
|
||||
break;
|
||||
case EPIPE:
|
||||
// The peer closed connection or shutdown its read side. Reward
|
||||
// the caller with a SIGPIPE.
|
||||
if (gStackModule->is_syscall())
|
||||
send_signal(find_thread(NULL), SIGPIPE);
|
||||
break;
|
||||
case B_TIMED_OUT:
|
||||
// Translate non-blocking timeouts to the correct error code.
|
||||
if (timeout == 0)
|
||||
result = B_WOULD_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
RETURN_ERROR(result);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixEndpoint::Receive(const iovec *vecs, size_t vecCount,
|
||||
ancillary_data_container **_ancillaryData, struct sockaddr *_address,
|
||||
socklen_t *_addressLength)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Receive(%p, %ld)\n",
|
||||
find_thread(NULL), this, vecs, vecCount);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
|
||||
// We can read as long as we have a FIFO. I.e. we are still connected, or
|
||||
// disconnected and not yet reconnected/listening/closed.
|
||||
if (fReceiveFifo == NULL)
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
|
||||
UnixEndpoint* peerEndpoint = fPeerEndpoint;
|
||||
BReference<UnixEndpoint> peerReference(peerEndpoint);
|
||||
|
||||
// Copy the peer address upfront. This way, if we read something, we don't
|
||||
// get into a potential race with Close().
|
||||
if (_address != NULL) {
|
||||
socklen_t addrLen = min_c(*_addressLength, socket->peer.ss_len);
|
||||
memcpy(_address, &socket->peer, addrLen);
|
||||
*_addressLength = addrLen;
|
||||
}
|
||||
|
||||
// lock our FIFO
|
||||
UnixFifo* fifo = fReceiveFifo;
|
||||
BReference<UnixFifo> _(fifo);
|
||||
UnixFifoLocker fifoLocker(fifo);
|
||||
|
||||
// unlock endpoint
|
||||
locker.Unlock();
|
||||
|
||||
ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, timeout);
|
||||
|
||||
// Notify select()ing writers, if we successfully read anything.
|
||||
size_t writable = fifo->Writable();
|
||||
bool notifyWrite = (result >= 0 && writable > 0
|
||||
&& !fifo->IsWriteShutdown());
|
||||
|
||||
// Notify select()ing readers, if we failed to read anything and there's
|
||||
// still something left to read.
|
||||
size_t readable = fifo->Readable();
|
||||
bool notifyRead = (result < 0 && readable > 0
|
||||
&& !fifo->IsReadShutdown());
|
||||
|
||||
// re-lock our endpoint (unlock FIFO to respect locking order)
|
||||
fifoLocker.Unlock();
|
||||
locker.Lock();
|
||||
|
||||
UnixEndpointLocker peerLocker;
|
||||
bool peerLocked = (peerEndpoint != NULL && fPeerEndpoint == peerEndpoint
|
||||
&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
|
||||
|
||||
// send notifications
|
||||
if (notifyRead)
|
||||
gSocketModule->notify(socket, B_SELECT_READ, readable);
|
||||
if (peerLocked && notifyWrite)
|
||||
gSocketModule->notify(peerEndpoint->socket, B_SELECT_WRITE, writable);
|
||||
|
||||
switch (result) {
|
||||
case UNIX_FIFO_SHUTDOWN:
|
||||
// Either our socket was closed or read shutdown.
|
||||
if (fState == UNIX_ENDPOINT_CLOSED) {
|
||||
// The FD has been closed.
|
||||
result = EBADF;
|
||||
} else {
|
||||
// if (fReceiveFifo == fifo) {
|
||||
// Orderly shutdown or the peer closed the connection.
|
||||
// } else {
|
||||
// Weird case: Peer closed connection and we are already
|
||||
// reconnected (or listening).
|
||||
// }
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
case B_TIMED_OUT:
|
||||
// translate non-blocking timeouts to the correct error code
|
||||
if (timeout == 0)
|
||||
result = B_WOULD_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
RETURN_ERROR(result);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixEndpoint::Sendable()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Sendable()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
UnixEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// lock the peer's FIFO
|
||||
UnixFifo* peerFifo = fPeerEndpoint->fReceiveFifo;
|
||||
UnixFifoLocker fifoLocker(peerFifo);
|
||||
|
||||
RETURN_ERROR(peerFifo->Writable());
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixEndpoint::Receivable()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Receivable()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
|
||||
if (fState == UNIX_ENDPOINT_LISTENING)
|
||||
return gSocketModule->count_connected(socket);
|
||||
|
||||
if (fState != UNIX_ENDPOINT_CONNECTED)
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
|
||||
UnixFifoLocker fifoLocker(fReceiveFifo);
|
||||
ssize_t readable = fReceiveFifo->Readable();
|
||||
if (readable == 0 && (fReceiveFifo->IsWriteShutdown()
|
||||
|| fReceiveFifo->IsReadShutdown())) {
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
}
|
||||
RETURN_ERROR(readable);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::SetReceiveBufferSize(size_t size)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::SetReceiveBufferSize(%lu)\n",
|
||||
find_thread(NULL), this, size);
|
||||
|
||||
UnixEndpointLocker locker(this);
|
||||
|
||||
if (fReceiveFifo == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
UnixFifoLocker fifoLocker(fReceiveFifo);
|
||||
return fReceiveFifo->SetBufferCapacity(size);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::GetPeerCredentials(ucred* credentials)
|
||||
{
|
||||
UnixEndpointLocker locker(this);
|
||||
UnixEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
*credentials = fPeerEndpoint->fCredentials;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::Shutdown(int direction)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixEndpoint::Shutdown(%d)\n",
|
||||
find_thread(NULL), this, direction);
|
||||
|
||||
uint32 shutdown;
|
||||
uint32 peerShutdown;
|
||||
|
||||
// translate the direction into shutdown flags for our and the peer fifo
|
||||
switch (direction) {
|
||||
case SHUT_RD:
|
||||
shutdown = UNIX_FIFO_SHUTDOWN_READ;
|
||||
peerShutdown = 0;
|
||||
break;
|
||||
case SHUT_WR:
|
||||
shutdown = 0;
|
||||
peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
shutdown = UNIX_FIFO_SHUTDOWN_READ;
|
||||
peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
|
||||
break;
|
||||
default:
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// lock endpoints
|
||||
UnixEndpointLocker locker(this);
|
||||
UnixEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// shutdown our FIFO
|
||||
fReceiveFifo->Lock();
|
||||
fReceiveFifo->Shutdown(shutdown);
|
||||
fReceiveFifo->Unlock();
|
||||
|
||||
// shutdown peer FIFO
|
||||
fPeerEndpoint->fReceiveFifo->Lock();
|
||||
fPeerEndpoint->fReceiveFifo->Shutdown(peerShutdown);
|
||||
fPeerEndpoint->fReceiveFifo->Unlock();
|
||||
|
||||
// send select notifications
|
||||
if (direction == SHUT_RD || direction == SHUT_RDWR) {
|
||||
gSocketModule->notify(socket, B_SELECT_READ, EPIPE);
|
||||
gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_WRITE, EPIPE);
|
||||
}
|
||||
if (direction == SHUT_WR || direction == SHUT_RDWR) {
|
||||
gSocketModule->notify(socket, B_SELECT_WRITE, EPIPE);
|
||||
gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_READ, EPIPE);
|
||||
}
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixEndpoint::_Spawn(UnixEndpoint* connectingEndpoint,
|
||||
UnixEndpoint* listeningEndpoint, UnixFifo* fifo)
|
||||
{
|
||||
ProtocolSocket::Open();
|
||||
|
||||
fIsChild = true;
|
||||
fPeerEndpoint = connectingEndpoint;
|
||||
fPeerEndpoint->AcquireReference();
|
||||
|
||||
fReceiveFifo = fifo;
|
||||
|
||||
PeerAddress().SetTo(&connectingEndpoint->socket->address);
|
||||
|
||||
fCredentials = listeningEndpoint->fCredentials;
|
||||
|
||||
fState = UNIX_ENDPOINT_CONNECTED;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixEndpoint::_Disconnect()
|
||||
{
|
||||
// Both endpoints must be locked.
|
||||
|
||||
// Write shutdown the receive FIFO.
|
||||
fReceiveFifo->Lock();
|
||||
fReceiveFifo->Shutdown(UNIX_FIFO_SHUTDOWN_WRITE);
|
||||
fReceiveFifo->Unlock();
|
||||
|
||||
// select() notification.
|
||||
gSocketModule->notify(socket, B_SELECT_READ, ECONNRESET);
|
||||
gSocketModule->notify(socket, B_SELECT_WRITE, ECONNRESET);
|
||||
|
||||
// Unset the peer endpoint.
|
||||
fPeerEndpoint->ReleaseReference();
|
||||
fPeerEndpoint = NULL;
|
||||
|
||||
// We're officially disconnected.
|
||||
// TODO: Deal with non accept()ed connections correctly!
|
||||
fIsChild = false;
|
||||
fState = UNIX_ENDPOINT_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::_LockConnectedEndpoints(UnixEndpointLocker& locker,
|
||||
UnixEndpointLocker& peerLocker)
|
||||
{
|
||||
if (fState != UNIX_ENDPOINT_CONNECTED)
|
||||
RETURN_ERROR(fWasConnected ? EPIPE : ENOTCONN);
|
||||
|
||||
// We need to lock the peer, too. Get a reference -- we might need to
|
||||
// unlock ourselves to get the locking order right.
|
||||
BReference<UnixEndpoint> peerReference(fPeerEndpoint);
|
||||
UnixEndpoint* peerEndpoint = fPeerEndpoint;
|
||||
|
||||
if (fIsChild) {
|
||||
// We're the child, but locking order is the other way around.
|
||||
locker.Unlock();
|
||||
peerLocker.SetTo(peerEndpoint, false);
|
||||
|
||||
locker.Lock();
|
||||
|
||||
// recheck our state, also whether the peer is still the same
|
||||
if (fState != UNIX_ENDPOINT_CONNECTED || peerEndpoint != fPeerEndpoint)
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
} else
|
||||
peerLocker.SetTo(peerEndpoint, false);
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixEndpoint::_Bind(struct vnode* vnode)
|
||||
{
|
||||
@@ -852,38 +151,11 @@ UnixEndpoint::_Bind(int32 internalID)
|
||||
status_t
|
||||
UnixEndpoint::_Unbind()
|
||||
{
|
||||
if (fState == UNIX_ENDPOINT_CONNECTED || fState == UNIX_ENDPOINT_LISTENING)
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
if (IsBound()) {
|
||||
UnixAddressManagerLocker addressLocker(gAddressManager);
|
||||
gAddressManager.Remove(this);
|
||||
if (struct vnode* vnode = fAddress.Vnode())
|
||||
vfs_put_vnode(vnode);
|
||||
|
||||
fAddress.Unset();
|
||||
}
|
||||
UnixAddressManagerLocker addressLocker(gAddressManager);
|
||||
gAddressManager.Remove(this);
|
||||
if (struct vnode* vnode = fAddress.Vnode())
|
||||
vfs_put_vnode(vnode);
|
||||
|
||||
fAddress.Unset();
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixEndpoint::_UnsetReceiveFifo()
|
||||
{
|
||||
if (fReceiveFifo) {
|
||||
fReceiveFifo->ReleaseReference();
|
||||
fReceiveFifo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixEndpoint::_StopListening()
|
||||
{
|
||||
if (fState == UNIX_ENDPOINT_LISTENING) {
|
||||
delete_sem(fAcceptSemaphore);
|
||||
fAcceptSemaphore = -1;
|
||||
fState = UNIX_ENDPOINT_NOT_CONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,24 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Copyright 2023, Trung Nguyen, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef UNIX_ENDPOINT_H
|
||||
#define UNIX_ENDPOINT_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <Referenceable.h>
|
||||
|
||||
#include <lock.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
#include <vfs.h>
|
||||
|
||||
#include <net_protocol.h>
|
||||
#include <net_socket.h>
|
||||
#include <ProtocolUtilities.h>
|
||||
|
||||
#include "unix.h"
|
||||
#include <lock.h>
|
||||
#include <vfs.h>
|
||||
|
||||
#include "UnixAddress.h"
|
||||
|
||||
|
||||
class UnixEndpoint;
|
||||
class UnixFifo;
|
||||
|
||||
|
||||
enum unix_endpoint_state {
|
||||
UNIX_ENDPOINT_NOT_CONNECTED,
|
||||
UNIX_ENDPOINT_LISTENING,
|
||||
UNIX_ENDPOINT_CONNECTED,
|
||||
UNIX_ENDPOINT_CLOSED
|
||||
};
|
||||
|
||||
|
||||
typedef AutoLocker<UnixEndpoint> UnixEndpointLocker;
|
||||
|
||||
|
||||
class UnixEndpoint : public net_protocol, public ProtocolSocket,
|
||||
public BReferenceable {
|
||||
class UnixEndpoint : public net_protocol, public ProtocolSocket {
|
||||
public:
|
||||
UnixEndpoint(net_socket* socket);
|
||||
virtual ~UnixEndpoint();
|
||||
|
||||
status_t Init();
|
||||
void Uninit();
|
||||
|
||||
status_t Open();
|
||||
status_t Close();
|
||||
status_t Free();
|
||||
virtual ~UnixEndpoint();
|
||||
|
||||
bool Lock()
|
||||
{
|
||||
@@ -60,31 +30,6 @@ public:
|
||||
mutex_unlock(&fLock);
|
||||
}
|
||||
|
||||
status_t Bind(const struct sockaddr *_address);
|
||||
status_t Unbind();
|
||||
status_t Listen(int backlog);
|
||||
status_t Connect(const struct sockaddr *address);
|
||||
status_t Accept(net_socket **_acceptedSocket);
|
||||
|
||||
ssize_t Send(const iovec *vecs, size_t vecCount,
|
||||
ancillary_data_container *ancillaryData);
|
||||
ssize_t Receive(const iovec *vecs, size_t vecCount,
|
||||
ancillary_data_container **_ancillaryData, struct sockaddr *_address,
|
||||
socklen_t *_addressLength);
|
||||
|
||||
ssize_t Sendable();
|
||||
ssize_t Receivable();
|
||||
|
||||
status_t SetReceiveBufferSize(size_t size);
|
||||
status_t GetPeerCredentials(ucred* credentials);
|
||||
|
||||
status_t Shutdown(int direction);
|
||||
|
||||
bool IsBound() const
|
||||
{
|
||||
return !fIsChild && fAddress.IsValid();
|
||||
}
|
||||
|
||||
const UnixAddress& Address() const
|
||||
{
|
||||
return fAddress;
|
||||
@@ -95,31 +40,66 @@ public:
|
||||
return fAddressHashLink;
|
||||
}
|
||||
|
||||
private:
|
||||
void _Spawn(UnixEndpoint* connectingEndpoint,
|
||||
UnixEndpoint* listeningEndpoint, UnixFifo* fifo);
|
||||
void _Disconnect();
|
||||
status_t _LockConnectedEndpoints(UnixEndpointLocker& locker,
|
||||
UnixEndpointLocker& peerLocker);
|
||||
virtual status_t Init() = 0;
|
||||
virtual void Uninit() = 0;
|
||||
|
||||
status_t _Bind(struct vnode* vnode);
|
||||
status_t _Bind(int32 internalID);
|
||||
status_t _Unbind();
|
||||
virtual status_t Open() = 0;
|
||||
virtual status_t Close() = 0;
|
||||
virtual status_t Free() = 0;
|
||||
|
||||
void _UnsetReceiveFifo();
|
||||
void _StopListening();
|
||||
virtual status_t Bind(const struct sockaddr* _address) = 0;
|
||||
virtual status_t Unbind() = 0;
|
||||
virtual status_t Listen(int backlog) = 0;
|
||||
virtual status_t Connect(const struct sockaddr* address) = 0;
|
||||
virtual status_t Accept(net_socket** _acceptedSocket) = 0;
|
||||
|
||||
virtual ssize_t Send(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container* ancillaryData,
|
||||
const struct sockaddr* address,
|
||||
socklen_t addressLength) = 0;
|
||||
virtual ssize_t Receive(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container** _ancillaryData,
|
||||
struct sockaddr* _address, socklen_t* _addressLength) = 0;
|
||||
|
||||
virtual ssize_t Sendable() = 0;
|
||||
virtual ssize_t Receivable() = 0;
|
||||
|
||||
virtual status_t SetReceiveBufferSize(size_t size) = 0;
|
||||
virtual status_t GetPeerCredentials(ucred* credentials) = 0;
|
||||
|
||||
virtual status_t Shutdown(int direction) = 0;
|
||||
|
||||
static status_t Create(net_socket* socket, UnixEndpoint** _endpoint);
|
||||
|
||||
protected:
|
||||
UnixEndpoint(net_socket* socket);
|
||||
|
||||
// These functions perform no locking or checking on the endpoint.
|
||||
status_t _Bind(const struct sockaddr_un* address);
|
||||
status_t _Unbind();
|
||||
|
||||
private:
|
||||
mutex fLock;
|
||||
UnixAddress fAddress;
|
||||
UnixEndpoint* fAddressHashLink;
|
||||
UnixEndpoint* fPeerEndpoint;
|
||||
UnixFifo* fReceiveFifo;
|
||||
unix_endpoint_state fState;
|
||||
sem_id fAcceptSemaphore;
|
||||
ucred fCredentials;
|
||||
bool fIsChild;
|
||||
bool fWasConnected;
|
||||
status_t _Bind(struct vnode* vnode);
|
||||
status_t _Bind(int32 internalID);
|
||||
|
||||
protected:
|
||||
UnixAddress fAddress;
|
||||
|
||||
private:
|
||||
mutex fLock;
|
||||
UnixEndpoint* fAddressHashLink;
|
||||
};
|
||||
|
||||
|
||||
static inline bigtime_t
|
||||
absolute_timeout(bigtime_t timeout)
|
||||
{
|
||||
if (timeout == 0 || timeout == B_INFINITE_TIMEOUT)
|
||||
return timeout;
|
||||
|
||||
// TODO: Make overflow safe!
|
||||
return timeout + system_time();
|
||||
}
|
||||
|
||||
|
||||
#endif // UNIX_ENDPOINT_H
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
|
||||
|
||||
UnixRequest::UnixRequest(const iovec* vecs, size_t count,
|
||||
ancillary_data_container* ancillaryData)
|
||||
ancillary_data_container* ancillaryData,
|
||||
struct sockaddr_storage* address)
|
||||
:
|
||||
fVecs(vecs),
|
||||
fVecCount(count),
|
||||
@@ -32,7 +33,8 @@ UnixRequest::UnixRequest(const iovec* vecs, size_t count,
|
||||
fTotalSize(0),
|
||||
fBytesTransferred(0),
|
||||
fVecIndex(0),
|
||||
fVecOffset(0)
|
||||
fVecOffset(0),
|
||||
fAddress(address)
|
||||
{
|
||||
for (size_t i = 0; i < fVecCount; i++)
|
||||
fTotalSize += fVecs[i].iov_len;
|
||||
@@ -95,10 +97,11 @@ UnixRequest::AddAncillaryData(ancillary_data_container* data)
|
||||
// #pragma mark - UnixBufferQueue
|
||||
|
||||
|
||||
UnixBufferQueue::UnixBufferQueue(size_t capacity)
|
||||
UnixBufferQueue::UnixBufferQueue(size_t capacity, UnixFifoType type)
|
||||
:
|
||||
fBuffer(NULL),
|
||||
fCapacity(capacity)
|
||||
fCapacity(capacity),
|
||||
fType(type)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -147,6 +150,19 @@ UnixBufferQueue::Read(UnixRequest& request)
|
||||
void* data;
|
||||
size_t size;
|
||||
|
||||
DatagramEntry* datagramEntry = NULL;
|
||||
if (fType == UnixFifoType::Datagram) {
|
||||
datagramEntry = fDatagrams.Head();
|
||||
if (datagramEntry == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
if (datagramEntry->size > readable)
|
||||
TRACE("UnixBufferQueue::Read(): expected to read a datagram of size %lu, "
|
||||
"but only %lu bytes are readable\n", datagramEntry->size, readable);
|
||||
else
|
||||
readable = datagramEntry->size;
|
||||
}
|
||||
|
||||
while (readable > 0 && request.GetCurrentChunk(data, size)) {
|
||||
if (size > readable)
|
||||
size = readable;
|
||||
@@ -184,6 +200,30 @@ UnixBufferQueue::Read(UnixRequest& request)
|
||||
readable -= bytesRead;
|
||||
}
|
||||
|
||||
if (fType == UnixFifoType::Datagram) {
|
||||
fDatagrams.RemoveHead();
|
||||
|
||||
memcpy(request.Address(), &datagramEntry->address, sizeof(datagramEntry->address));
|
||||
delete datagramEntry;
|
||||
|
||||
if (readable > 0) {
|
||||
ring_buffer_flush(fBuffer, readable);
|
||||
if (AncillaryDataEntry* entry = fAncillaryData.Head()) {
|
||||
size_t offsetDelta = readable;
|
||||
while (entry != NULL && offsetDelta > entry->offset) {
|
||||
fAncillaryData.RemoveHead();
|
||||
offsetDelta -= entry->offset;
|
||||
delete entry;
|
||||
|
||||
entry = fAncillaryData.Head();
|
||||
}
|
||||
|
||||
if (entry != NULL)
|
||||
entry->offset -= offsetDelta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -197,6 +237,26 @@ UnixBufferQueue::Write(UnixRequest& request)
|
||||
void* data;
|
||||
size_t size;
|
||||
|
||||
DatagramEntry* datagramEntry = NULL;
|
||||
ObjectDeleter<DatagramEntry> datagramEntryDeleter;
|
||||
if (fType == UnixFifoType::Datagram) {
|
||||
datagramEntry = new(std::nothrow) DatagramEntry;
|
||||
if (datagramEntry == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
datagramEntryDeleter.SetTo(datagramEntry);
|
||||
memcpy(&datagramEntry->address, request.Address(),
|
||||
sizeof(datagramEntry->address));
|
||||
datagramEntry->size = request.TotalSize();
|
||||
|
||||
// This should have been handled in UnixFifo
|
||||
if (writable < datagramEntry->size) {
|
||||
TRACE("UnixBufferQueue::Write(): not enough space for"
|
||||
"datagram of size %lu (%lu bytes left)\n", datagramEntry->size, writable);
|
||||
return B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// If the request has ancillary data create an entry first.
|
||||
AncillaryDataEntry* ancillaryEntry = NULL;
|
||||
ObjectDeleter<AncillaryDataEntry> ancillaryEntryDeleter;
|
||||
@@ -244,6 +304,11 @@ UnixBufferQueue::Write(UnixRequest& request)
|
||||
writable -= bytesWritten;
|
||||
}
|
||||
|
||||
if (fType == UnixFifoType::Datagram) {
|
||||
fDatagrams.Add(datagramEntry);
|
||||
datagramEntryDeleter.Detach();
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -259,9 +324,9 @@ return B_ERROR;
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
UnixFifo::UnixFifo(size_t capacity)
|
||||
UnixFifo::UnixFifo(size_t capacity, UnixFifoType type)
|
||||
:
|
||||
fBuffer(capacity),
|
||||
fBuffer(capacity, type),
|
||||
fReaders(),
|
||||
fWriters(),
|
||||
fReadRequested(0),
|
||||
@@ -306,7 +371,8 @@ UnixFifo::Shutdown(uint32 shutdown)
|
||||
|
||||
ssize_t
|
||||
UnixFifo::Read(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container** _ancillaryData, bigtime_t timeout)
|
||||
ancillary_data_container** _ancillaryData,
|
||||
struct sockaddr_storage* address, bigtime_t timeout)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixFifo::Read(%p, %ld, %" B_PRIdBIGTIME ")\n",
|
||||
find_thread(NULL), this, vecs, vecCount, timeout);
|
||||
@@ -314,7 +380,7 @@ UnixFifo::Read(const iovec* vecs, size_t vecCount,
|
||||
if (IsReadShutdown() && fBuffer.Readable() == 0)
|
||||
RETURN_ERROR(UNIX_FIFO_SHUTDOWN);
|
||||
|
||||
UnixRequest request(vecs, vecCount, NULL);
|
||||
UnixRequest request(vecs, vecCount, NULL, address);
|
||||
fReaders.Add(&request);
|
||||
fReadRequested += request.TotalSize();
|
||||
|
||||
@@ -351,7 +417,8 @@ UnixFifo::Read(const iovec* vecs, size_t vecCount,
|
||||
|
||||
ssize_t
|
||||
UnixFifo::Write(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container* ancillaryData, bigtime_t timeout)
|
||||
ancillary_data_container* ancillaryData,
|
||||
const struct sockaddr_storage* address, bigtime_t timeout)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixFifo::Write(%p, %ld, %p, %" B_PRIdBIGTIME
|
||||
")\n", find_thread(NULL), this, vecs, vecCount, ancillaryData,
|
||||
@@ -363,7 +430,8 @@ UnixFifo::Write(const iovec* vecs, size_t vecCount,
|
||||
if (IsReadShutdown())
|
||||
RETURN_ERROR(EPIPE);
|
||||
|
||||
UnixRequest request(vecs, vecCount, ancillaryData);
|
||||
UnixRequest request(vecs, vecCount, ancillaryData,
|
||||
(struct sockaddr_storage*)address);
|
||||
fWriters.Add(&request);
|
||||
fWriteRequested += request.TotalSize();
|
||||
|
||||
@@ -530,8 +598,8 @@ UnixFifo::_Write(UnixRequest& request, bigtime_t timeout)
|
||||
|
||||
while (error == B_OK && request.BytesRemaining() > 0) {
|
||||
// wait for any space to become available
|
||||
while (error == B_OK && fBuffer.Writable() == 0 && !IsWriteShutdown()
|
||||
&& !IsReadShutdown()) {
|
||||
while (error == B_OK && fBuffer.Writable() < _MinimumWritableSize(request)
|
||||
&& !IsWriteShutdown() && !IsReadShutdown()) {
|
||||
ConditionVariableEntry entry;
|
||||
fWriteCondition.Add(&entry);
|
||||
|
||||
@@ -567,7 +635,7 @@ UnixFifo::_WriteNonBlocking(UnixRequest& request)
|
||||
{
|
||||
// We need to be first in queue and space should be available right now,
|
||||
// otherwise we need to fail.
|
||||
if (fWriters.Head() != &request || fBuffer.Writable() == 0)
|
||||
if (fWriters.Head() != &request || fBuffer.Writable() < _MinimumWritableSize(request))
|
||||
RETURN_ERROR(B_WOULD_BLOCK);
|
||||
|
||||
if (request.TotalSize() == 0)
|
||||
@@ -577,3 +645,15 @@ UnixFifo::_WriteNonBlocking(UnixRequest& request)
|
||||
RETURN_ERROR(fBuffer.Write(request));
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
UnixFifo::_MinimumWritableSize(const UnixRequest& request) const
|
||||
{
|
||||
switch (fType) {
|
||||
case UnixFifoType::Datagram:
|
||||
return request.TotalSize();
|
||||
case UnixFifoType::Stream:
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,19 @@
|
||||
#define UNIX_FIFO_MAXIMAL_CAPACITY (128 * 1024)
|
||||
|
||||
|
||||
enum class UnixFifoType {
|
||||
Stream,
|
||||
Datagram
|
||||
};
|
||||
|
||||
|
||||
struct ring_buffer;
|
||||
|
||||
class UnixRequest : public DoublyLinkedListLinkImpl<UnixRequest> {
|
||||
public:
|
||||
UnixRequest(const iovec* vecs, size_t count,
|
||||
ancillary_data_container* ancillaryData);
|
||||
ancillary_data_container* ancillaryData,
|
||||
struct sockaddr_storage* address);
|
||||
|
||||
off_t TotalSize() const { return fTotalSize; }
|
||||
off_t BytesTransferred() const { return fBytesTransferred; }
|
||||
@@ -43,20 +50,23 @@ public:
|
||||
void SetAncillaryData(ancillary_data_container* data);
|
||||
void AddAncillaryData(ancillary_data_container* data);
|
||||
|
||||
struct sockaddr_storage* Address() const { return fAddress; }
|
||||
|
||||
private:
|
||||
const iovec* fVecs;
|
||||
size_t fVecCount;
|
||||
ancillary_data_container* fAncillaryData;
|
||||
off_t fTotalSize;
|
||||
off_t fBytesTransferred;
|
||||
size_t fVecIndex;
|
||||
size_t fVecOffset;
|
||||
const iovec* fVecs;
|
||||
size_t fVecCount;
|
||||
ancillary_data_container* fAncillaryData;
|
||||
off_t fTotalSize;
|
||||
off_t fBytesTransferred;
|
||||
size_t fVecIndex;
|
||||
size_t fVecOffset;
|
||||
struct sockaddr_storage* fAddress;
|
||||
};
|
||||
|
||||
|
||||
class UnixBufferQueue {
|
||||
public:
|
||||
UnixBufferQueue(size_t capacity);
|
||||
UnixBufferQueue(size_t capacity, UnixFifoType type);
|
||||
~UnixBufferQueue();
|
||||
|
||||
status_t Init();
|
||||
@@ -78,15 +88,24 @@ private:
|
||||
|
||||
typedef DoublyLinkedList<AncillaryDataEntry> AncillaryDataList;
|
||||
|
||||
struct DatagramEntry : DoublyLinkedListLinkImpl<DatagramEntry> {
|
||||
struct sockaddr_storage address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
typedef DoublyLinkedList<DatagramEntry> DatagramList;
|
||||
|
||||
ring_buffer* fBuffer;
|
||||
size_t fCapacity;
|
||||
AncillaryDataList fAncillaryData;
|
||||
DatagramList fDatagrams;
|
||||
UnixFifoType fType;
|
||||
};
|
||||
|
||||
|
||||
class UnixFifo : public BReferenceable {
|
||||
public:
|
||||
UnixFifo(size_t capacity);
|
||||
UnixFifo(size_t capacity, UnixFifoType type);
|
||||
~UnixFifo();
|
||||
|
||||
status_t Init();
|
||||
@@ -114,9 +133,11 @@ public:
|
||||
}
|
||||
|
||||
ssize_t Read(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container** _ancillaryData, bigtime_t timeout);
|
||||
ancillary_data_container** _ancillaryData,
|
||||
struct sockaddr_storage* address, bigtime_t timeout);
|
||||
ssize_t Write(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container* ancillaryData, bigtime_t timeout);
|
||||
ancillary_data_container* ancillaryData,
|
||||
const struct sockaddr_storage* address, bigtime_t timeout);
|
||||
|
||||
size_t Readable() const;
|
||||
size_t Writable() const;
|
||||
@@ -130,6 +151,7 @@ private:
|
||||
status_t _Read(UnixRequest& request, bigtime_t timeout);
|
||||
status_t _Write(UnixRequest& request, bigtime_t timeout);
|
||||
status_t _WriteNonBlocking(UnixRequest& request);
|
||||
size_t _MinimumWritableSize(const UnixRequest& request) const;
|
||||
|
||||
private:
|
||||
mutex fLock;
|
||||
@@ -141,6 +163,7 @@ private:
|
||||
ConditionVariable fReadCondition;
|
||||
ConditionVariable fWriteCondition;
|
||||
uint32 fShutdown;
|
||||
UnixFifoType fType;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,801 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "UnixStreamEndpoint.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include <vfs.h>
|
||||
|
||||
#include "UnixAddressManager.h"
|
||||
#include "UnixFifo.h"
|
||||
|
||||
|
||||
#define UNIX_STREAM_ENDPOINT_DEBUG_LEVEL 0
|
||||
#define UNIX_DEBUG_LEVEL UNIX_STREAM_ENDPOINT_DEBUG_LEVEL
|
||||
#include "UnixDebug.h"
|
||||
|
||||
|
||||
// Note on locking order (outermost -> innermost):
|
||||
// UnixStreamEndpoint: connecting -> listening -> child
|
||||
// -> UnixFifo (never lock more than one at a time)
|
||||
// -> UnixAddressManager
|
||||
|
||||
|
||||
UnixStreamEndpoint::UnixStreamEndpoint(net_socket* socket)
|
||||
:
|
||||
UnixEndpoint(socket),
|
||||
fPeerEndpoint(NULL),
|
||||
fReceiveFifo(NULL),
|
||||
fState(unix_stream_endpoint_state::Closed),
|
||||
fAcceptSemaphore(-1),
|
||||
fIsChild(false),
|
||||
fWasConnected(false)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::UnixStreamEndpoint()\n",
|
||||
find_thread(NULL), this);
|
||||
}
|
||||
|
||||
|
||||
UnixStreamEndpoint::~UnixStreamEndpoint()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::~UnixStreamEndpoint()\n",
|
||||
find_thread(NULL), this);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Init()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Init()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixStreamEndpoint::Uninit()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Uninit()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
// check whether we're closed
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
bool closed = (fState == unix_stream_endpoint_state::Closed);
|
||||
locker.Unlock();
|
||||
|
||||
if (!closed) {
|
||||
// That probably means, we're a child endpoint of a listener and
|
||||
// have been fully connected, but not yet accepted. Our Close()
|
||||
// hook isn't called in this case. Do it manually.
|
||||
Close();
|
||||
}
|
||||
|
||||
ReleaseReference();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Open()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Open()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
status_t error = ProtocolSocket::Open();
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
fState = unix_stream_endpoint_state::NotConnected;
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Close()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Close()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
|
||||
if (fState == unix_stream_endpoint_state::Connected) {
|
||||
UnixStreamEndpointLocker peerLocker;
|
||||
if (_LockConnectedEndpoints(locker, peerLocker) == B_OK) {
|
||||
// We're still connected. Disconnect both endpoints!
|
||||
fPeerEndpoint->_Disconnect();
|
||||
_Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
if (fState == unix_stream_endpoint_state::Listening)
|
||||
_StopListening();
|
||||
|
||||
_Unbind();
|
||||
|
||||
fState = unix_stream_endpoint_state::Closed;
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Free()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Free()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Bind(const struct sockaddr* _address)
|
||||
{
|
||||
if (_address->sa_family != AF_UNIX)
|
||||
RETURN_ERROR(EAFNOSUPPORT);
|
||||
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Bind(\"%s\")\n",
|
||||
find_thread(NULL), this,
|
||||
ConstSocketAddress(&gAddressModule, _address).AsString().Data());
|
||||
|
||||
const sockaddr_un* address = (const sockaddr_un*)_address;
|
||||
|
||||
UnixStreamEndpointLocker endpointLocker(this);
|
||||
|
||||
if (fState != unix_stream_endpoint_state::NotConnected || IsBound())
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
RETURN_ERROR(_Bind(address));
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Unbind()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Unbind()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixStreamEndpointLocker endpointLocker(this);
|
||||
|
||||
RETURN_ERROR(_Unbind());
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Listen(int backlog)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Listen(%d)\n", find_thread(NULL),
|
||||
this, backlog);
|
||||
|
||||
UnixStreamEndpointLocker endpointLocker(this);
|
||||
|
||||
if (!IsBound())
|
||||
RETURN_ERROR(EDESTADDRREQ);
|
||||
if (fState != unix_stream_endpoint_state::NotConnected
|
||||
&& fState != unix_stream_endpoint_state::Listening)
|
||||
RETURN_ERROR(EINVAL);
|
||||
|
||||
gSocketModule->set_max_backlog(socket, backlog);
|
||||
|
||||
if (fState == unix_stream_endpoint_state::NotConnected) {
|
||||
fAcceptSemaphore = create_sem(0, "unix accept");
|
||||
if (fAcceptSemaphore < 0)
|
||||
RETURN_ERROR(ENOBUFS);
|
||||
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
fCredentials.pid = getpid();
|
||||
fCredentials.uid = geteuid();
|
||||
fCredentials.gid = getegid();
|
||||
|
||||
fState = unix_stream_endpoint_state::Listening;
|
||||
}
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Connect(const struct sockaddr* _address)
|
||||
{
|
||||
if (_address->sa_family != AF_UNIX)
|
||||
RETURN_ERROR(EAFNOSUPPORT);
|
||||
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Connect(\"%s\")\n",
|
||||
find_thread(NULL), this,
|
||||
ConstSocketAddress(&gAddressModule, _address).AsString().Data());
|
||||
|
||||
const sockaddr_un* address = (const sockaddr_un*)_address;
|
||||
|
||||
UnixStreamEndpointLocker endpointLocker(this);
|
||||
|
||||
if (fState == unix_stream_endpoint_state::Connected)
|
||||
RETURN_ERROR(EISCONN);
|
||||
|
||||
if (fState != unix_stream_endpoint_state::NotConnected)
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
// TODO: If listening, we could set the backlog to 0 and connect.
|
||||
|
||||
// check the address first
|
||||
UnixAddress unixAddress;
|
||||
|
||||
if (address->sun_path[0] == '\0') {
|
||||
// internal address space (or empty address)
|
||||
int32 internalID;
|
||||
if (UnixAddress::IsEmptyAddress(*address))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
internalID = UnixAddress::InternalID(*address);
|
||||
if (internalID < 0)
|
||||
RETURN_ERROR(internalID);
|
||||
|
||||
unixAddress.SetTo(internalID);
|
||||
} else {
|
||||
// FS address space
|
||||
size_t pathLen = strnlen(address->sun_path, sizeof(address->sun_path));
|
||||
if (pathLen == 0 || pathLen == sizeof(address->sun_path))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
struct stat st;
|
||||
status_t error = vfs_read_stat(-1, address->sun_path, true, &st,
|
||||
!gStackModule->is_syscall());
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
if (!S_ISSOCK(st.st_mode))
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
unixAddress.SetTo(st.st_dev, st.st_ino, NULL);
|
||||
}
|
||||
|
||||
// get the peer endpoint
|
||||
UnixAddressManagerLocker addressLocker(gAddressManager);
|
||||
UnixEndpoint* listeningUnixEndpoint = gAddressManager.Lookup(unixAddress);
|
||||
if (listeningUnixEndpoint == NULL)
|
||||
RETURN_ERROR(ECONNREFUSED);
|
||||
UnixStreamEndpoint* listeningEndpoint
|
||||
= dynamic_cast<UnixStreamEndpoint*>(listeningUnixEndpoint);
|
||||
if (listeningEndpoint == NULL)
|
||||
RETURN_ERROR(EPROTOTYPE);
|
||||
BReference<UnixStreamEndpoint> peerReference(listeningEndpoint);
|
||||
addressLocker.Unlock();
|
||||
|
||||
UnixStreamEndpointLocker peerLocker(listeningEndpoint);
|
||||
|
||||
if (!listeningEndpoint->IsBound()
|
||||
|| listeningEndpoint->fState != unix_stream_endpoint_state::Listening
|
||||
|| listeningEndpoint->fAddress != unixAddress) {
|
||||
RETURN_ERROR(ECONNREFUSED);
|
||||
}
|
||||
|
||||
// Allocate FIFOs for us and the socket we're going to spawn. We do that
|
||||
// now, so that the mess we need to cleanup, if allocating them fails, is
|
||||
// harmless.
|
||||
UnixFifo* fifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT, UnixFifoType::Stream);
|
||||
UnixFifo* peerFifo = new(nothrow) UnixFifo(UNIX_MAX_TRANSFER_UNIT, UnixFifoType::Stream);
|
||||
ObjectDeleter<UnixFifo> fifoDeleter(fifo);
|
||||
ObjectDeleter<UnixFifo> peerFifoDeleter(peerFifo);
|
||||
|
||||
status_t error;
|
||||
if ((error = fifo->Init()) != B_OK || (error = peerFifo->Init()) != B_OK)
|
||||
return error;
|
||||
|
||||
// spawn new endpoint for accept()
|
||||
net_socket* newSocket;
|
||||
error = gSocketModule->spawn_pending_socket(listeningEndpoint->socket,
|
||||
&newSocket);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// init connected peer endpoint
|
||||
UnixStreamEndpoint* connectedEndpoint = (UnixStreamEndpoint*)newSocket->first_protocol;
|
||||
|
||||
UnixStreamEndpointLocker connectedLocker(connectedEndpoint);
|
||||
|
||||
connectedEndpoint->_Spawn(this, listeningEndpoint, peerFifo);
|
||||
|
||||
// update our attributes
|
||||
_UnsetReceiveFifo();
|
||||
|
||||
fPeerEndpoint = connectedEndpoint;
|
||||
PeerAddress().SetTo(&connectedEndpoint->socket->address);
|
||||
fPeerEndpoint->AcquireReference();
|
||||
fReceiveFifo = fifo;
|
||||
|
||||
fCredentials.pid = getpid();
|
||||
fCredentials.uid = geteuid();
|
||||
fCredentials.gid = getegid();
|
||||
|
||||
fifoDeleter.Detach();
|
||||
peerFifoDeleter.Detach();
|
||||
|
||||
fState = unix_stream_endpoint_state::Connected;
|
||||
fWasConnected = true;
|
||||
|
||||
gSocketModule->set_connected(newSocket);
|
||||
|
||||
release_sem(listeningEndpoint->fAcceptSemaphore);
|
||||
|
||||
connectedLocker.Unlock();
|
||||
peerLocker.Unlock();
|
||||
endpointLocker.Unlock();
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Accept(net_socket** _acceptedSocket)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Accept()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
|
||||
status_t error;
|
||||
do {
|
||||
locker.Unlock();
|
||||
|
||||
error = acquire_sem_etc(fAcceptSemaphore, 1,
|
||||
B_ABSOLUTE_TIMEOUT | B_CAN_INTERRUPT, timeout);
|
||||
if (error < B_OK)
|
||||
break;
|
||||
|
||||
locker.Lock();
|
||||
error = gSocketModule->dequeue_connected(socket, _acceptedSocket);
|
||||
} while (error != B_OK);
|
||||
|
||||
if (error == B_TIMED_OUT && timeout == 0) {
|
||||
// translate non-blocking timeouts to the correct error code
|
||||
error = B_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
RETURN_ERROR(error);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixStreamEndpoint::Send(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container* ancillaryData,
|
||||
const struct sockaddr* address, socklen_t addressLength)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Send(%p, %ld, %p)\n",
|
||||
find_thread(NULL), this, vecs, vecCount, ancillaryData);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->send.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
|
||||
BReference<UnixStreamEndpoint> peerReference;
|
||||
UnixStreamEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
UnixStreamEndpoint* peerEndpoint = fPeerEndpoint;
|
||||
peerReference.SetTo(peerEndpoint);
|
||||
|
||||
// lock the peer's FIFO
|
||||
UnixFifo* peerFifo = peerEndpoint->fReceiveFifo;
|
||||
BReference<UnixFifo> _(peerFifo);
|
||||
UnixFifoLocker fifoLocker(peerFifo);
|
||||
|
||||
// unlock endpoints
|
||||
locker.Unlock();
|
||||
peerLocker.Unlock();
|
||||
|
||||
ssize_t result = peerFifo->Write(vecs, vecCount, ancillaryData, NULL, timeout);
|
||||
|
||||
// Notify select()ing readers, if we successfully wrote anything.
|
||||
size_t readable = peerFifo->Readable();
|
||||
bool notifyRead = (error == B_OK && readable > 0
|
||||
&& !peerFifo->IsReadShutdown());
|
||||
|
||||
// Notify select()ing writers, if we failed to write anything and there's
|
||||
// still room to write.
|
||||
size_t writable = peerFifo->Writable();
|
||||
bool notifyWrite = (error != B_OK && writable > 0
|
||||
&& !peerFifo->IsWriteShutdown());
|
||||
|
||||
// re-lock our endpoint (unlock FIFO to respect locking order)
|
||||
fifoLocker.Unlock();
|
||||
locker.Lock();
|
||||
|
||||
bool peerLocked = (fPeerEndpoint == peerEndpoint
|
||||
&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
|
||||
|
||||
// send notifications
|
||||
if (peerLocked && notifyRead)
|
||||
gSocketModule->notify(peerEndpoint->socket, B_SELECT_READ, readable);
|
||||
if (notifyWrite)
|
||||
gSocketModule->notify(socket, B_SELECT_WRITE, writable);
|
||||
|
||||
switch (result) {
|
||||
case UNIX_FIFO_SHUTDOWN:
|
||||
if (fPeerEndpoint == peerEndpoint
|
||||
&& fState == unix_stream_endpoint_state::Connected) {
|
||||
// Orderly write shutdown on our side.
|
||||
// Note: Linux and Solaris also send a SIGPIPE, but according
|
||||
// the send() specification that shouldn't be done.
|
||||
result = EPIPE;
|
||||
} else {
|
||||
// The FD has been closed.
|
||||
result = EBADF;
|
||||
}
|
||||
break;
|
||||
case EPIPE:
|
||||
// The peer closed connection or shutdown its read side. Reward
|
||||
// the caller with a SIGPIPE.
|
||||
if (gStackModule->is_syscall())
|
||||
send_signal(find_thread(NULL), SIGPIPE);
|
||||
break;
|
||||
case B_TIMED_OUT:
|
||||
// Translate non-blocking timeouts to the correct error code.
|
||||
if (timeout == 0)
|
||||
result = B_WOULD_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
RETURN_ERROR(result);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixStreamEndpoint::Receive(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container** _ancillaryData, struct sockaddr* _address,
|
||||
socklen_t* _addressLength)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Receive(%p, %ld)\n",
|
||||
find_thread(NULL), this, vecs, vecCount);
|
||||
|
||||
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
|
||||
if (gStackModule->is_restarted_syscall())
|
||||
timeout = gStackModule->restore_syscall_restart_timeout();
|
||||
else
|
||||
gStackModule->store_syscall_restart_timeout(timeout);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
|
||||
// We can read as long as we have a FIFO. I.e. we are still connected, or
|
||||
// disconnected and not yet reconnected/listening/closed.
|
||||
if (fReceiveFifo == NULL)
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
|
||||
UnixStreamEndpoint* peerEndpoint = fPeerEndpoint;
|
||||
BReference<UnixStreamEndpoint> peerReference(peerEndpoint);
|
||||
|
||||
// Copy the peer address upfront. This way, if we read something, we don't
|
||||
// get into a potential race with Close().
|
||||
if (_address != NULL) {
|
||||
socklen_t addrLen = min_c(*_addressLength, socket->peer.ss_len);
|
||||
memcpy(_address, &socket->peer, addrLen);
|
||||
*_addressLength = addrLen;
|
||||
}
|
||||
|
||||
// lock our FIFO
|
||||
UnixFifo* fifo = fReceiveFifo;
|
||||
BReference<UnixFifo> _(fifo);
|
||||
UnixFifoLocker fifoLocker(fifo);
|
||||
|
||||
// unlock endpoint
|
||||
locker.Unlock();
|
||||
|
||||
ssize_t result = fifo->Read(vecs, vecCount, _ancillaryData, NULL, timeout);
|
||||
|
||||
// Notify select()ing writers, if we successfully read anything.
|
||||
size_t writable = fifo->Writable();
|
||||
bool notifyWrite = (result >= 0 && writable > 0
|
||||
&& !fifo->IsWriteShutdown());
|
||||
|
||||
// Notify select()ing readers, if we failed to read anything and there's
|
||||
// still something left to read.
|
||||
size_t readable = fifo->Readable();
|
||||
bool notifyRead = (result < 0 && readable > 0
|
||||
&& !fifo->IsReadShutdown());
|
||||
|
||||
// re-lock our endpoint (unlock FIFO to respect locking order)
|
||||
fifoLocker.Unlock();
|
||||
locker.Lock();
|
||||
|
||||
UnixStreamEndpointLocker peerLocker;
|
||||
bool peerLocked = (peerEndpoint != NULL && fPeerEndpoint == peerEndpoint
|
||||
&& _LockConnectedEndpoints(locker, peerLocker) == B_OK);
|
||||
|
||||
// send notifications
|
||||
if (notifyRead)
|
||||
gSocketModule->notify(socket, B_SELECT_READ, readable);
|
||||
if (peerLocked && notifyWrite)
|
||||
gSocketModule->notify(peerEndpoint->socket, B_SELECT_WRITE, writable);
|
||||
|
||||
switch (result) {
|
||||
case UNIX_FIFO_SHUTDOWN:
|
||||
// Either our socket was closed or read shutdown.
|
||||
if (fState == unix_stream_endpoint_state::Closed) {
|
||||
// The FD has been closed.
|
||||
result = EBADF;
|
||||
} else {
|
||||
// if (fReceiveFifo == fifo) {
|
||||
// Orderly shutdown or the peer closed the connection.
|
||||
// } else {
|
||||
// Weird case: Peer closed connection and we are already
|
||||
// reconnected (or listening).
|
||||
// }
|
||||
result = 0;
|
||||
}
|
||||
break;
|
||||
case B_TIMED_OUT:
|
||||
// translate non-blocking timeouts to the correct error code
|
||||
if (timeout == 0)
|
||||
result = B_WOULD_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
RETURN_ERROR(result);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixStreamEndpoint::Sendable()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Sendable()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
UnixStreamEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// lock the peer's FIFO
|
||||
UnixFifo* peerFifo = fPeerEndpoint->fReceiveFifo;
|
||||
UnixFifoLocker fifoLocker(peerFifo);
|
||||
|
||||
RETURN_ERROR(peerFifo->Writable());
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
UnixStreamEndpoint::Receivable()
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Receivable()\n", find_thread(NULL),
|
||||
this);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
|
||||
if (fState == unix_stream_endpoint_state::Listening)
|
||||
return gSocketModule->count_connected(socket);
|
||||
|
||||
if (fState != unix_stream_endpoint_state::Connected)
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
|
||||
UnixFifoLocker fifoLocker(fReceiveFifo);
|
||||
ssize_t readable = fReceiveFifo->Readable();
|
||||
if (readable == 0 && (fReceiveFifo->IsWriteShutdown()
|
||||
|| fReceiveFifo->IsReadShutdown())) {
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
}
|
||||
RETURN_ERROR(readable);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::SetReceiveBufferSize(size_t size)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::SetReceiveBufferSize(%lu)\n",
|
||||
find_thread(NULL), this, size);
|
||||
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
|
||||
if (fReceiveFifo == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
UnixFifoLocker fifoLocker(fReceiveFifo);
|
||||
return fReceiveFifo->SetBufferCapacity(size);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::GetPeerCredentials(ucred* credentials)
|
||||
{
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
UnixStreamEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
*credentials = fPeerEndpoint->fCredentials;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::Shutdown(int direction)
|
||||
{
|
||||
TRACE("[%" B_PRId32 "] %p->UnixStreamEndpoint::Shutdown(%d)\n",
|
||||
find_thread(NULL), this, direction);
|
||||
|
||||
uint32 shutdown;
|
||||
uint32 peerShutdown;
|
||||
|
||||
// translate the direction into shutdown flags for our and the peer fifo
|
||||
switch (direction) {
|
||||
case SHUT_RD:
|
||||
shutdown = UNIX_FIFO_SHUTDOWN_READ;
|
||||
peerShutdown = 0;
|
||||
break;
|
||||
case SHUT_WR:
|
||||
shutdown = 0;
|
||||
peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
shutdown = UNIX_FIFO_SHUTDOWN_READ;
|
||||
peerShutdown = UNIX_FIFO_SHUTDOWN_WRITE;
|
||||
break;
|
||||
default:
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
}
|
||||
|
||||
// lock endpoints
|
||||
UnixStreamEndpointLocker locker(this);
|
||||
UnixStreamEndpointLocker peerLocker;
|
||||
|
||||
status_t error = _LockConnectedEndpoints(locker, peerLocker);
|
||||
if (error != B_OK)
|
||||
RETURN_ERROR(error);
|
||||
|
||||
// shutdown our FIFO
|
||||
fReceiveFifo->Lock();
|
||||
fReceiveFifo->Shutdown(shutdown);
|
||||
fReceiveFifo->Unlock();
|
||||
|
||||
// shutdown peer FIFO
|
||||
fPeerEndpoint->fReceiveFifo->Lock();
|
||||
fPeerEndpoint->fReceiveFifo->Shutdown(peerShutdown);
|
||||
fPeerEndpoint->fReceiveFifo->Unlock();
|
||||
|
||||
// send select notifications
|
||||
if (direction == SHUT_RD || direction == SHUT_RDWR) {
|
||||
gSocketModule->notify(socket, B_SELECT_READ, EPIPE);
|
||||
gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_WRITE, EPIPE);
|
||||
}
|
||||
if (direction == SHUT_WR || direction == SHUT_RDWR) {
|
||||
gSocketModule->notify(socket, B_SELECT_WRITE, EPIPE);
|
||||
gSocketModule->notify(fPeerEndpoint->socket, B_SELECT_READ, EPIPE);
|
||||
}
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixStreamEndpoint::_Spawn(UnixStreamEndpoint* connectingEndpoint,
|
||||
UnixStreamEndpoint* listeningEndpoint, UnixFifo* fifo)
|
||||
{
|
||||
ProtocolSocket::Open();
|
||||
|
||||
fIsChild = true;
|
||||
fPeerEndpoint = connectingEndpoint;
|
||||
fPeerEndpoint->AcquireReference();
|
||||
|
||||
fReceiveFifo = fifo;
|
||||
|
||||
PeerAddress().SetTo(&connectingEndpoint->socket->address);
|
||||
|
||||
fCredentials = listeningEndpoint->fCredentials;
|
||||
|
||||
fState = unix_stream_endpoint_state::Connected;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixStreamEndpoint::_Disconnect()
|
||||
{
|
||||
// Both endpoints must be locked.
|
||||
|
||||
// Write shutdown the receive FIFO.
|
||||
fReceiveFifo->Lock();
|
||||
fReceiveFifo->Shutdown(UNIX_FIFO_SHUTDOWN_WRITE);
|
||||
fReceiveFifo->Unlock();
|
||||
|
||||
// select() notification.
|
||||
gSocketModule->notify(socket, B_SELECT_READ, ECONNRESET);
|
||||
gSocketModule->notify(socket, B_SELECT_WRITE, ECONNRESET);
|
||||
|
||||
// Unset the peer endpoint.
|
||||
fPeerEndpoint->ReleaseReference();
|
||||
fPeerEndpoint = NULL;
|
||||
|
||||
// We're officially disconnected.
|
||||
// TODO: Deal with non accept()ed connections correctly!
|
||||
fIsChild = false;
|
||||
fState = unix_stream_endpoint_state::NotConnected;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::_LockConnectedEndpoints(UnixStreamEndpointLocker& locker,
|
||||
UnixStreamEndpointLocker& peerLocker)
|
||||
{
|
||||
if (fState != unix_stream_endpoint_state::Connected)
|
||||
RETURN_ERROR(fWasConnected ? EPIPE : ENOTCONN);
|
||||
|
||||
// We need to lock the peer, too. Get a reference -- we might need to
|
||||
// unlock ourselves to get the locking order right.
|
||||
BReference<UnixStreamEndpoint> peerReference(fPeerEndpoint);
|
||||
UnixStreamEndpoint* peerEndpoint = fPeerEndpoint;
|
||||
|
||||
if (fIsChild) {
|
||||
// We're the child, but locking order is the other way around.
|
||||
locker.Unlock();
|
||||
peerLocker.SetTo(peerEndpoint, false);
|
||||
|
||||
locker.Lock();
|
||||
|
||||
// recheck our state, also whether the peer is still the same
|
||||
if (fState != unix_stream_endpoint_state::Connected || peerEndpoint != fPeerEndpoint)
|
||||
RETURN_ERROR(ENOTCONN);
|
||||
} else
|
||||
peerLocker.SetTo(peerEndpoint, false);
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
UnixStreamEndpoint::_Unbind()
|
||||
{
|
||||
if (fState == unix_stream_endpoint_state::Connected
|
||||
|| fState == unix_stream_endpoint_state::Listening)
|
||||
RETURN_ERROR(B_BAD_VALUE);
|
||||
|
||||
if (IsBound())
|
||||
RETURN_ERROR(UnixEndpoint::_Unbind());
|
||||
|
||||
RETURN_ERROR(B_OK);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixStreamEndpoint::_UnsetReceiveFifo()
|
||||
{
|
||||
if (fReceiveFifo) {
|
||||
fReceiveFifo->ReleaseReference();
|
||||
fReceiveFifo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UnixStreamEndpoint::_StopListening()
|
||||
{
|
||||
if (fState == unix_stream_endpoint_state::Listening) {
|
||||
delete_sem(fAcceptSemaphore);
|
||||
fAcceptSemaphore = -1;
|
||||
fState = unix_stream_endpoint_state::NotConnected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef UNIX_STREAM_ENDPOINT_H
|
||||
#define UNIX_STREAM_ENDPOINT_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <Referenceable.h>
|
||||
|
||||
#include <util/DoublyLinkedList.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
#include "unix.h"
|
||||
#include "UnixEndpoint.h"
|
||||
|
||||
class UnixStreamEndpoint;
|
||||
class UnixFifo;
|
||||
|
||||
|
||||
enum class unix_stream_endpoint_state {
|
||||
NotConnected,
|
||||
Listening,
|
||||
Connected,
|
||||
Closed
|
||||
};
|
||||
|
||||
|
||||
typedef AutoLocker<UnixStreamEndpoint> UnixStreamEndpointLocker;
|
||||
|
||||
|
||||
class UnixStreamEndpoint : public UnixEndpoint, public BReferenceable {
|
||||
public:
|
||||
UnixStreamEndpoint(net_socket* socket);
|
||||
virtual ~UnixStreamEndpoint() override;
|
||||
|
||||
status_t Init() override;
|
||||
void Uninit() override;
|
||||
|
||||
status_t Open() override;
|
||||
status_t Close() override;
|
||||
status_t Free() override;
|
||||
|
||||
status_t Bind(const struct sockaddr* _address) override;
|
||||
status_t Unbind() override;
|
||||
status_t Listen(int backlog) override;
|
||||
status_t Connect(const struct sockaddr* address) override;
|
||||
status_t Accept(net_socket** _acceptedSocket) override;
|
||||
|
||||
ssize_t Send(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container* ancillaryData,
|
||||
const struct sockaddr* address,
|
||||
socklen_t addressLength) override;
|
||||
ssize_t Receive(const iovec* vecs, size_t vecCount,
|
||||
ancillary_data_container** _ancillaryData,
|
||||
struct sockaddr* _address,
|
||||
socklen_t* _addressLength) override;
|
||||
|
||||
ssize_t Sendable() override;
|
||||
ssize_t Receivable() override;
|
||||
|
||||
status_t SetReceiveBufferSize(size_t size) override;
|
||||
status_t GetPeerCredentials(ucred* credentials) override;
|
||||
|
||||
status_t Shutdown(int direction) override;
|
||||
|
||||
bool IsBound() const
|
||||
{
|
||||
return !fIsChild && fAddress.IsValid();
|
||||
}
|
||||
|
||||
private:
|
||||
void _Spawn(UnixStreamEndpoint* connectingEndpoint,
|
||||
UnixStreamEndpoint* listeningEndpoint, UnixFifo* fifo);
|
||||
void _Disconnect();
|
||||
status_t _LockConnectedEndpoints(UnixStreamEndpointLocker& locker,
|
||||
UnixStreamEndpointLocker& peerLocker);
|
||||
|
||||
status_t _Unbind();
|
||||
|
||||
void _UnsetReceiveFifo();
|
||||
void _StopListening();
|
||||
|
||||
private:
|
||||
UnixStreamEndpoint* fPeerEndpoint;
|
||||
UnixFifo* fReceiveFifo;
|
||||
unix_stream_endpoint_state fState;
|
||||
sem_id fAcceptSemaphore;
|
||||
ucred fCredentials;
|
||||
bool fIsChild;
|
||||
bool fWasConnected;
|
||||
};
|
||||
|
||||
#endif // UNIX_STREAM_ENDPOINT_H
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <net_socket.h>
|
||||
#include <net_stack.h>
|
||||
|
||||
#include "unix.h"
|
||||
#include "UnixAddressManager.h"
|
||||
#include "UnixEndpoint.h"
|
||||
|
||||
@@ -67,11 +68,12 @@ unix_init_protocol(net_socket *socket)
|
||||
TRACE("[%" B_PRId32 "] unix_init_protocol(%p)\n", find_thread(NULL),
|
||||
socket);
|
||||
|
||||
UnixEndpoint* endpoint = new(std::nothrow) UnixEndpoint(socket);
|
||||
if (endpoint == NULL)
|
||||
UnixEndpoint* endpoint;
|
||||
status_t error = UnixEndpoint::Create(socket, &endpoint);
|
||||
if (error != B_OK)
|
||||
return NULL;
|
||||
|
||||
status_t error = endpoint->Init();
|
||||
error = endpoint->Init();
|
||||
if (error != B_OK) {
|
||||
delete endpoint;
|
||||
return NULL;
|
||||
@@ -408,7 +410,8 @@ unix_send_data_no_buffer(net_protocol *_protocol, const iovec *vecs,
|
||||
size_t vecCount, ancillary_data_container *ancillaryData,
|
||||
const struct sockaddr *address, socklen_t addressLength)
|
||||
{
|
||||
return ((UnixEndpoint*)_protocol)->Send(vecs, vecCount, ancillaryData);
|
||||
return ((UnixEndpoint*)_protocol)->Send(vecs, vecCount, ancillaryData,
|
||||
address, addressLength);
|
||||
}
|
||||
|
||||
|
||||
@@ -435,6 +438,11 @@ init_unix()
|
||||
|
||||
error = gStackModule->register_domain_protocols(AF_UNIX, SOCK_STREAM, 0,
|
||||
"network/protocols/unix/v1", NULL);
|
||||
if (error == B_OK) {
|
||||
error = gStackModule->register_domain_protocols(AF_UNIX, SOCK_DGRAM, 0,
|
||||
"network/protocols/unix/v1", NULL);
|
||||
}
|
||||
|
||||
if (error != B_OK) {
|
||||
gAddressManager.~UnixAddressManager();
|
||||
return error;
|
||||
|
||||
@@ -17,6 +17,8 @@ SimpleTest getpeername : getpeername.cpp : $(TARGET_NETWORK_LIBS) ;
|
||||
|
||||
SimpleTest if_nameindex : if_nameindex.c : $(TARGET_NETWORK_LIBS) ;
|
||||
|
||||
SimpleTest unix_dgram_test : unix_dgram_test.cpp : $(TARGET_NETWORK_LIBS) ;
|
||||
|
||||
SimpleTest tcp_connection_test : tcp_connection_test.cpp
|
||||
: $(TARGET_NETWORK_LIBS) ;
|
||||
|
||||
|
||||
@@ -0,0 +1,620 @@
|
||||
/*
|
||||
* Copyright 2023, Trung Nguyen, trungnt282910@gmail.com.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define REPORT_ERROR(msg, ...) \
|
||||
fprintf(stderr, "%s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
|
||||
int
|
||||
connect_test()
|
||||
{
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
unlink("test2.sock");
|
||||
|
||||
int status;
|
||||
|
||||
int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, "test.sock");
|
||||
status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock1 == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr1;
|
||||
addr1.sun_family = AF_UNIX;
|
||||
strcpy(addr1.sun_path, "test1.sock");
|
||||
status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set non-blocking on both sockets
|
||||
int flags1 = fcntl(sock, F_GETFL, 0);
|
||||
if (flags1 == -1) {
|
||||
REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
status = fcntl(sock, F_SETFL, flags1 | O_NONBLOCK);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
status = fcntl(sock1, F_SETFL, flags1 | O_NONBLOCK);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("connect() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect in the opposite way
|
||||
status = connect(sock1, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("connect() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Reconnect a connected DGRAM socket
|
||||
status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("connect() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sock2 = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock2 == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr2;
|
||||
addr2.sun_family = AF_UNIX;
|
||||
strcpy(addr2.sun_path, "test2.sock");
|
||||
status = bind(sock2, (struct sockaddr*)&addr2, sizeof(addr2));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to a socket that are already connected
|
||||
status = connect(sock2, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status != -1) {
|
||||
REPORT_ERROR("connect() succeeded unexpectedly\n");
|
||||
return 1;
|
||||
}
|
||||
if (errno != EPERM) {
|
||||
REPORT_ERROR("connect() failed with unexpected error: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = close(sock2);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("close() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Connect to a closed socket
|
||||
status = connect(sock, (struct sockaddr*)&addr2, sizeof(addr2));
|
||||
if (status != -1) {
|
||||
REPORT_ERROR("connect() succeeded unexpectedly\n");
|
||||
return 1;
|
||||
}
|
||||
if (errno != ECONNREFUSED) {
|
||||
REPORT_ERROR("connect() failed with unexpected error: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
close(sock1);
|
||||
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
unlink("test2.sock");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
send_test()
|
||||
{
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
unlink("test2.sock");
|
||||
|
||||
int status;
|
||||
|
||||
int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, "test.sock");
|
||||
status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = send(sock, "test", 4, 0);
|
||||
if (status != -1) {
|
||||
REPORT_ERROR("send() succeeded unexpectedly\n");
|
||||
return 1;
|
||||
}
|
||||
// if (errno != ENOTCONN) {
|
||||
// REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock1 == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr1;
|
||||
addr1.sun_family = AF_UNIX;
|
||||
strcpy(addr1.sun_path, "test1.sock");
|
||||
status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Set non-blocking on both sockets
|
||||
status = fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
status = fcntl(sock1, F_SETFL, O_NONBLOCK);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("fcntl() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = sendto(sock, "test1", 5, 0, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("sendto() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("connect() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
status = connect(sock1, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("connect() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = send(sock, "test2", 5, 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("send() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sock2 = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock2 == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr2;
|
||||
addr2.sun_family = AF_UNIX;
|
||||
strcpy(addr2.sun_path, "test2.sock");
|
||||
status = bind(sock2, (struct sockaddr*)&addr2, sizeof(addr2));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = sendto(sock2, "test3", 5, 0, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status != -1) {
|
||||
REPORT_ERROR("sendto() succeeded unexpectedly\n");
|
||||
return 1;
|
||||
}
|
||||
if (errno != EPERM) {
|
||||
REPORT_ERROR("sendto() failed with unexpected error: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buf[16];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
status = recv(sock1, buf, sizeof(buf), 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("recv() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(buf, "test1") != 0) {
|
||||
REPORT_ERROR("recv() received unexpected data: %s\n", buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
struct sockaddr_un addr3;
|
||||
memset(&addr3, 0, sizeof(addr3));
|
||||
socklen_t addrlen = sizeof(addr3);
|
||||
status = recvfrom(sock1, buf, sizeof(buf), 0, (struct sockaddr*)&addr3, &addrlen);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("recv() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(buf, "test2") != 0) {
|
||||
REPORT_ERROR("recv() received unexpected data: %s\n", buf);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(addr.sun_path, addr3.sun_path) != 0) {
|
||||
REPORT_ERROR("recv() received unexpected address: %s\n", addr3.sun_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = send(sock, "test4", 4, 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("send() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = send(sock, "test5", 5, 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("send() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
status = recv(sock1, buf, 4, 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("recv() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(buf, "test") != 0) {
|
||||
REPORT_ERROR("recv() received unexpected data: %s\n", buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The last byte of the previous datagram should be discarded.
|
||||
memset(buf, 0, sizeof(buf));
|
||||
status = recv(sock1, buf, sizeof(buf), 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("recv() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(buf, "test5") != 0) {
|
||||
REPORT_ERROR("recv() received unexpected data: %s\n", buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(sock1);
|
||||
status = send(sock, "test6", 5, 0);
|
||||
if (status != -1) {
|
||||
REPORT_ERROR("send() succeeded unexpectedly\n");
|
||||
return 1;
|
||||
}
|
||||
if (errno != ECONNREFUSED) {
|
||||
REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
close(sock2);
|
||||
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
unlink("test2.sock");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
shutdown_test()
|
||||
{
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
|
||||
int status;
|
||||
|
||||
int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, "test.sock");
|
||||
status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock1 == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr1;
|
||||
addr1.sun_family = AF_UNIX;
|
||||
strcpy(addr1.sun_path, "test1.sock");
|
||||
status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = shutdown(sock, SHUT_WR);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("shutdown() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = sendto(sock, "test", 4, 0, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status != -1) {
|
||||
REPORT_ERROR("send() succeeded unexpectedly\n");
|
||||
return 1;
|
||||
}
|
||||
if (errno != EPIPE) {
|
||||
REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = sendto(sock1, "test", 4, 0, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("send() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = shutdown(sock, SHUT_RD);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("shutdown() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = sendto(sock1, "test", 4, 0, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status != -1) {
|
||||
REPORT_ERROR("send() succeeded unexpectedly\n");
|
||||
return 1;
|
||||
}
|
||||
if (errno != EPIPE) {
|
||||
REPORT_ERROR("send() failed with unexpected error: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buf[16];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
status = recv(sock, buf, sizeof(buf), 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("recv() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (status != 0) {
|
||||
REPORT_ERROR("recv() received unexpected data\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
close(sock1);
|
||||
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
send_fd_test()
|
||||
{
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
|
||||
int status;
|
||||
|
||||
int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, "test.sock");
|
||||
status = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sock1 = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
if (sock1 == -1) {
|
||||
REPORT_ERROR("socket() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr1;
|
||||
addr1.sun_family = AF_UNIX;
|
||||
strcpy(addr1.sun_path, "test1.sock");
|
||||
status = bind(sock1, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("bind() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = connect(sock, (struct sockaddr*)&addr1, sizeof(addr1));
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("connect() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd = shm_open("test_shm", O_CREAT | O_RDWR, 0666);
|
||||
if (fd == -1) {
|
||||
REPORT_ERROR("shm_open() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
shm_unlink("test_shm");
|
||||
|
||||
// Send FD
|
||||
char iobuf[] = "test";
|
||||
struct iovec iov {
|
||||
.iov_base = iobuf,
|
||||
.iov_len = sizeof(iobuf),
|
||||
};
|
||||
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
struct cmsghdr *cmsg;
|
||||
char buf[CMSG_SPACE(sizeof(fd))];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
msg.msg_control = buf;
|
||||
msg.msg_controllen = sizeof(buf);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
|
||||
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
|
||||
msg.msg_controllen = cmsg->cmsg_len;
|
||||
|
||||
status = sendmsg(sock, &msg, 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("sendmsg() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Receive FD
|
||||
memset(buf, 0, sizeof(buf));
|
||||
msg.msg_control = buf;
|
||||
msg.msg_controllen = sizeof(buf);
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
status = recvmsg(sock1, &msg, 0);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("recvmsg() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg == NULL) {
|
||||
REPORT_ERROR("recvmsg() failed: no control message\n");
|
||||
return 1;
|
||||
}
|
||||
if (cmsg->cmsg_level != SOL_SOCKET) {
|
||||
REPORT_ERROR("recvmsg() failed: unexpected level %d\n", cmsg->cmsg_level);
|
||||
return 1;
|
||||
}
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
REPORT_ERROR("recvmsg() failed: unexpected type %d\n", cmsg->cmsg_type);
|
||||
return 1;
|
||||
}
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) {
|
||||
REPORT_ERROR("recvmsg() failed: unexpected length %ld\n", cmsg->cmsg_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int fd1;
|
||||
memcpy(&fd1, CMSG_DATA(cmsg), sizeof(fd1));
|
||||
if (fd1 == -1) {
|
||||
REPORT_ERROR("recvmsg() failed: unexpected fd %d\n", fd1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check that the FD refers to the same file
|
||||
struct stat statbuf;
|
||||
status = fstat(fd, &statbuf);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("fstat() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct stat statbuf1;
|
||||
status = fstat(fd1, &statbuf1);
|
||||
if (status == -1) {
|
||||
REPORT_ERROR("fstat() failed: %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (statbuf.st_dev != statbuf1.st_dev) {
|
||||
REPORT_ERROR("recvmsg() failed: unexpected device %ld\n", (long)statbuf1.st_dev);
|
||||
return 1;
|
||||
}
|
||||
if (statbuf.st_ino != statbuf1.st_ino) {
|
||||
REPORT_ERROR("recvmsg() failed: unexpected inode %ld\n", (long)statbuf1.st_ino);
|
||||
return 1;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
close(sock1);
|
||||
close(fd);
|
||||
close(fd1);
|
||||
|
||||
unlink("test.sock");
|
||||
unlink("test1.sock");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
if (connect_test() != 0)
|
||||
return 1;
|
||||
|
||||
if (send_test() != 0)
|
||||
return 1;
|
||||
|
||||
if (shutdown_test() != 0)
|
||||
return 1;
|
||||
|
||||
if (send_fd_test() != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user