
/*---------------------------------------------------------------------------*/
/* udsocket.cpp                                                             */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <signal.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#ifdef _IOS_
#include <netdb.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <sys/ioctl.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <sys/types.h>
#else
#ifndef SOCK_NONBLOCK
#include <asm/ioctls.h>
#endif
#endif

#include <platform.h>
#include "sdk/common/os/iomux.h"
#include "sdk/common/interface/socket.h"
#include "udsocket.h"

static const int __UD_SOCKET_IO_EXEC_SEND = 1;
static const int __UD_SOCKET_IO_EXEC_RECV = 2;
static const int __UD_SOCKET_IO_EXEC_BIND = 3;
static const int __UD_SOCKET_IO_EXEC_SHTD = 4;
static const int __UD_SOCKET_IO_EXEC_ACPT = 5;
static const int __UD_SOCKET_IO_EXEC_RRES = 6;

/*---------------------------------------------------------------------------*/
/* class UDSocketContext	                                                 */
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
/* class UDSocketProvider	                                                 */
/*---------------------------------------------------------------------------*/

class UDSocket * UDSocketProvider::NewSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log)
{
    return new UDSocket(iomux, user, log, this);
}

ISocketContext * UDSocketProvider::CreateSocketContext(class IInstanceLog * const log)
{
    return new class UDSocketContext();
}

ISocket * UDSocketProvider::CreateSocket(IIoMux * const iomux, USocket * const user, class IInstanceLog * const log, bool useIPv6 /* ignored */, class ISocketContext * socketContext /* ignored */)
{
    return NewSocket(iomux, user, log);
}

ISocketProvider * CreateLocalSocketProvider()
{
    return new UDSocketProvider;
}

/*---------------------------------------------------------------------------*/
/* struct SendBuffer     	                                                 */
/*---------------------------------------------------------------------------*/

struct UDSSendBuffer
{
    unsigned char * buffer;
    int len;
    int startAt;

    UDSSendBuffer() : buffer(0), len(0), startAt(0) {}

    ~UDSSendBuffer()
    {
        if (buffer)
            free(buffer);
    }
};

/*---------------------------------------------------------------------------*/
/* class UDSocket                                                            */
/*---------------------------------------------------------------------------*/

UDSocket::UDSocket(IIoMux * iomux, USocket * const user, class IInstanceLog * const log, UDSocketProvider * provider)
    : iomux(iomux),
      log(log),
      provider(provider)
{
    this->user = user;
    this->socketStatus = SST_CLOSED;
    this->recvBuf = NULL;
    this->recvBufLen = 0;
    this->recvBufAlreadyRead = 0;
    this->execCnt = 0;
    this->shutDown = false;
    this->shutdownReason = SOCKET_SHUTDOWN_NORMAL;
    this->isServer = false;
    this->remoteFD = -1;
}


UDSocket::~UDSocket()
{
    ASSERT(socketStatus == SST_CLOSED, "UDSocket::~UDSocket deleted without waiting for SocketShutdown");
    if (sendQueue.size() > 0) {
        while (sendQueue.size() > 0) {
            UDSSendBuffer * nextBuffer = sendQueue.front();
            sendQueue.pop();
            delete nextBuffer;
        }
    }
}


void UDSocket::OpenSocket()
{
    ASSERT(socketStatus == SST_CLOSED, "UDSocket::OpenSocket Socket already opened");
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::OpenSocket", this, user);

    FDIIoMux = socket(AF_UNIX, SOCK_STREAM
#ifdef SOCK_NONBLOCK
        | SOCK_NONBLOCK
#endif
        , 0);
    if (FDIIoMux < 0) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::OpenSocket socket() failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }

    socketStatus = SST_OPENED;

#ifndef SOCK_NONBLOCK
    unsigned int nonBlocking = 1;
    ioctl(FDIIoMux, FIONBIO, &nonBlocking);
    // listening sockets must be asynchronous, since otherwise indicated accept(2)s may disappear
    // between poll and accept and then our accept would block
    long flags = fcntl(FDIIoMux, F_GETFL);
    if (flags == -1) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::OpenSocket fcntl(F_GETFL) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
    // do not block if there is no incoming connection
    flags |= O_NONBLOCK;
    if (fcntl(FDIIoMux, F_SETFL, flags) == -1) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::OpenSocket fcntl(F_SETFL) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
#endif
#ifdef _IOS_
    int noSigPipe = 1;
    if (setsockopt(FDIIoMux, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)) < 0) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::OpenSocket setsockopt(SO_NOSIGPIPE) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
#endif

    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGPIPE);
    if (sigprocmask(SIG_BLOCK, &set, 0) < 0) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::OpenSocket sigprocmask(SIG_BLOCK) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
}


void UDSocket::Connect(const char * address, const char * serverName)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Connect address:%s serverName:%s", this, user, address, serverName);

    if ((this->socketStatus == SST_CLOSED) && !shutDown) this->OpenSocket();
    if (shutDown) {
        if (execCnt == 0) { // if an OpenSocket failed inside CreateSocket, we must answer async to a bind/connect call to avoid the ASSERT below
            this->socketStatus = SST_DO_SHUTDOWN;
            this->Shutdown();
        }
        return;
    }
    ASSERT(socketStatus == SST_OPENED, "UDSocket::Connect called, but socketState != SST_OPENED");

    size_t len = strlen(address);
    ASSERT(len != 0, "UDSocket::Bind called with empty address");

    struct sockaddr_un udsaddr;

    if (len > sizeof(udsaddr.sun_path) - 1) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Connect given address too long (length = %u, but max = %u)", this, user, len, sizeof(udsaddr.sun_path) - 1);
        this->shutdownReason = SOCKET_ADDRESS_INVALID;
        this->Shutdown();
        return;
    }

    udsaddr.sun_family = AF_UNIX;
    strcpy(udsaddr.sun_path, address);
    int res = connect(FDIIoMux, (struct sockaddr *)&udsaddr, sizeof(udsaddr));
    if (res < 0 && errno != EINPROGRESS) {
        if (errno == ECONNREFUSED) {
            // No one is listening to that path - maybe the application need to try again later...
            shutdownReason = SOCKET_CONNECT_REJECTED;
        }
        else {
            log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Connect connect() failed(%u : %s)", this, user, errno, strerror(errno));
            shutdownReason = SOCKET_CONNECT_FAILED;
        }
        this->Shutdown();
        return;
    }

    iomux->FDAdd(FDIIoMux, this, true);
    FDNotify(false, false);
    socketStatus = SST_CONNECTION_INITIATED;
}


void UDSocket::Bind(const char * localAddr, word localPort /* ignored */)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Bind localAddr:%s localPort:%i", this, user, localAddr, localPort);

    if ((this->socketStatus == SST_CLOSED) && !shutDown) this->OpenSocket();
    if (shutDown) {
        if (execCnt == 0) { // if an OpenSocket failed inside CreateSocket, we must answer async to a bind/connect call to avoid the ASSERT below
            this->socketStatus = SST_DO_SHUTDOWN;
            this->Shutdown();
        }
        return;
    }
    ASSERT(socketStatus == SST_OPENED, "UDSocket::Bind called but socketState != SST_OPENED");

    size_t len = strlen(localAddr);
    ASSERT(len != 0, "UDSocket::Bind called with empty address");

    struct sockaddr_un udsaddr;

    if (len > sizeof(udsaddr.sun_path) - 1) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Bind given address too long (length = %u, but max = %u)", this, user, len, sizeof(udsaddr.sun_path) - 1);
        this->shutdownReason = SOCKET_ADDRESS_INVALID;
        this->Shutdown();
        return;
    }

    // If the path already exists, give a warnin g to the log and make sure that
    // we can bind successfully to that file.
    struct stat buf;
    if (stat((char *)localAddr, &buf) == 0) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Bind WARNING: unix domain socket %s already exists", this, user, localAddr);
        if (unlink(localAddr) != 0) {
            log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Bind ERROR: unlink() failed (%u : %s)", this, user, errno, strerror(errno));
            shutdownReason = SOCKET_BIND_FAILED;
            this->Shutdown();
        }
    }

    udsaddr.sun_family = AF_UNIX;
    strcpy(udsaddr.sun_path, localAddr);

    //mode_t oldMode = umask(777);
    int res = bind(FDIIoMux, (sockaddr *)&udsaddr, sizeof(sockaddr_un));
    //umask(oldMode);
    if (res != 0) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Bind ERROR: bind() failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_BIND_FAILED;
        this->Shutdown();
        return;
    }
    if (chmod(localAddr, 0766) != 0) {
        log->Log(LOG_LDS, "UDSocket(%08x, %08x)::Bind ERROR: chmod() failed(%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_BIND_FAILED;
        this->Shutdown();
        return;
    }

    isServer = true;
    socketStatus = SST_BIND_START;
    // Bind is not async, but we need it...
    iomux->SetExec(this, (void *)__UD_SOCKET_IO_EXEC_BIND);
    execCnt++;
}


void UDSocket::Listen()
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Listen", this, user);
    if (shutDown) return;
    ASSERT(socketStatus == SST_BIND_START || socketStatus == SST_BIND_DONE, "UDSocket::Listen socketStatus need to be SST_BIND_DONE!");

    if (listen(FDIIoMux, SOMAXCONN) != 0) {
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Listen ERROR: listen() failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_LISTEN_FAILED;
        this->Shutdown();
        return;
    }

    iomux->FDAdd(FDIIoMux, this, true);
    FDNotify(false, false);
    socketStatus = SST_LISTEN;
}


void UDSocket::Accept(USocket * const remoteUser)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Accept remoteUser:%08x", this, user, remoteUser);
    ASSERT(isServer == true, "UDSocket::Accept called, but socket is not initialized as server");
    ASSERT(socketStatus == SST_NEED_ACCEPT, "UDSocket::Accept called, but socketStatus != SST_NEED_ACCEPT");

    socketStatus = SST_LISTEN;

    if (remoteUser == NULL) { // Accept denied...
        close(remoteFD);
        remoteFD = -1;
        return;
    }

    UDSocket * remoteSocket = provider->NewSocket(iomux, remoteUser, this->log);
    remoteSocket->socketStatus = SST_CONNECTED;
    iomux->FDUnblock(remoteFD);
    iomux->FDAdd(remoteFD, remoteSocket, true);
    remoteSocket->FDNotify(false, false);
    remoteFD = -1;
    UserSocketAcceptComplete(remoteUser, remoteSocket);
}


bool UDSocket::DoAccept()
{
    if (socketStatus != SST_LISTEN)
        return true;

    remoteFD = accept(FDIIoMux, NULL, NULL);
    FDNotify(true, false);

    if (remoteFD == -1) {
        if (errno == ECONNABORTED)
            return true;

        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::DoAccept ERROR: accept() failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_ACCEPT_FAILED;
        return false;
    }

    socketStatus = SST_NEED_ACCEPT;
    UserSocketListenResult(NULL, 0);

    // Call an other accept, to get all incoming connections...
    iomux->SetExec(this, (void *)__UD_SOCKET_IO_EXEC_ACPT);
    execCnt++;
    return true;
}


void UDSocket::Shutdown()
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Shutdown", this, user);
    // So accept one shutdown call...
    shutDown = true;
    if ((socketStatus != SST_CLOSED) && (execCnt == 0)) {
        iomux->SetExec(this, (void *)__UD_SOCKET_IO_EXEC_SHTD);
        execCnt++;
    }
}


void UDSocket::DoShutdown()
{
    if (execCnt != 0) {
        socketStatus = SST_DO_SHUTDOWN;
        return;
    }
    socketStatus = SST_CLOSED;
    if (isServer) {
        sockaddr_un tmp;
        socklen_t l = sizeof(sockaddr_un);
        getsockname(FDIIoMux, (sockaddr *)&tmp, &l);

        shutdown(FDIIoMux, SHUT_RDWR);
        iomux->FDClose(this);

        if (unlink(tmp.sun_path) != 0)
            log->Log(LOG_LDS, "UDSocket(%08x,%08x)::DoShutdown ERROR: unlink(\"%s\") failed %u : %s)", this, user, tmp.sun_path, errno, strerror(errno));
    }
    else {
        shutdown(FDIIoMux, SHUT_RDWR);
        iomux->FDClose(this);
    }
    FDIIoMux = -1;
    if (recvBuf) {
        UserSocketRecvCanceled(recvBuf);
        recvBuf = NULL;
    }
    isServer = false;
    shutDown = false;
    shutdownreason_t r = shutdownReason;
    shutdownReason = SOCKET_SHUTDOWN_NORMAL;
    UserSocketShutdown(r);
}


void UDSocket::AddNewSendBuffer(const void * buf, size_t len)
{
    UDSSendBuffer *buffer = new UDSSendBuffer;
    buffer->buffer = (byte *)malloc(len);
    memcpy(buffer->buffer, buf, len);
    buffer->len = len;

    sendQueue.push(buffer);
}


bool UDSocket::SendQueuedData()
{
    while (sendQueue.size() > 0) {
        UDSSendBuffer * nextBuffer = sendQueue.front();
        int sendLen = send(FDIIoMux, &nextBuffer->buffer[nextBuffer->startAt], nextBuffer->len, 0);
        FDNotify(false, true);

        if (sendLen == -1) {
            if (errno != EAGAIN) {
                if (errno == ECONNRESET)
                    shutdownReason = SOCKET_SHUTDOWN_BY_PEER;
                else
                    shutdownReason = SOCKET_LOST;

                log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SendQueuedData ERROR: send() failed (%u : %s)", this, user, errno, strerror(errno));
                return false;
            }
            return true;
        }

        if (sendLen < nextBuffer->len) {
            // socket buffer full - continue next time...
            nextBuffer->startAt += sendLen;
            nextBuffer->len -= sendLen;
            return true;
        }

        sendQueue.pop();
        delete nextBuffer;

        // All data send - raise callback. No need to use IIoMux::SetExec(), because when this function
        // is called, we are already in an asynchrone state (because this function is called when the
        // can send data again).
        UserSocketSendResult();
    }
    return true;
}


void UDSocket::Send(const void * buf, size_t len)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Send len:%i", this, user, len);
    log->HexDump(LOG_LDS, buf, len);
    if (this->shutDown || (socketStatus == SST_CLOSED)) return;

    ASSERT(isServer == false, "UDSocket::Send called for socket that is initialized as server");

    if (sendQueue.size() > 0 || socketStatus == SST_CONNECTION_INITIATED) {
        this->AddNewSendBuffer(buf, len);
        return;
    }

    int sendLen = send(FDIIoMux, buf, len, 0);
    FDNotify(false, true);
    if (sendLen < 0) {
        // Einzige Buffering Ausnahme: wenn erneut gesendet werden muss. Zur Sicherheit
        // wird eine Kopie angelegt.
        if (errno == EAGAIN) {
            log->Log(LOG_LDS, "**** UDSocket::Send(): send() returned EAGAIN - going to cache data to send it later");
            this->AddNewSendBuffer(buf, len);
            return;
        }
        if (errno == ECONNRESET)
            shutdownReason = SOCKET_SHUTDOWN_BY_PEER;
        else
            shutdownReason = SOCKET_LOST;
        log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Send ERROR: send() failed (%u : %s), fd = %u", this, user, errno, strerror(errno), FDIIoMux);
        this->Shutdown();
    }
    else if ((size_t)sendLen < len) {
        log->Log(LOG_LDS, "**** UDSocket::Send(): Could not send all data (sended: %u, needed to send: %u, diff: %u) - going to cache data to send it later", sendLen, len, len - sendLen);
        int toAdd = (sendLen == -1 ? 0 : sendLen);
        // Socket buffer full - continue later...
        const char *tmp = (const char *)buf + toAdd;
        this->AddNewSendBuffer(tmp, len - toAdd);
    }
    else {
        // Start an Exec-Callback...
        iomux->SetExec(this, (void *)__UD_SOCKET_IO_EXEC_SEND);
        execCnt++;
    }
}


bool UDSocket::ReceiveData()
{
    int bytesRead;
    byte * buf = NULL;

    if (recvBuf != NULL) {
        buf = (byte *)recvBuf + recvBufAlreadyRead;
        bytesRead = recv(FDIIoMux, buf, recvBufLen - recvBufAlreadyRead, 0);
        FDNotify(true, false);
    }
    else {
        // Make a read call with PEEK to check, if the sockt already is alive...
        byte tmp;
        bytesRead = recv(FDIIoMux, &tmp, 1, MSG_PEEK);
    }

    if (bytesRead == 0) { // recv() returns 0 if the peer has closed the connection
        shutdownReason = SOCKET_SHUTDOWN_BY_PEER;
        return false;
    }
    else if (bytesRead == -1) {
        switch (errno)
        {
        case EAGAIN:
            // Try again later...
            break;

        default: // All other errors lead to shutdown of socket
            log->Log(LOG_LDS, "UDSocket(%08x,%08x)::ReceiveData ERROR: recv() failed (%u : %s)", this, user, errno, strerror(errno));
            shutdownReason = SOCKET_LOST;
            return false;
        }
    }
    else if (buf != NULL) { // read of user data...
        recvBufAlreadyRead += bytesRead;
        if (recvBufAlreadyRead < recvBufLen && !recvBufReadPartial) {
            return true;
        }
        void * tmp = recvBuf; // Need a copy because the user could call recv() in SocketRecvResult()
        recvBuf = NULL;
        UserSocketRecvResult(tmp, recvBufAlreadyRead);
    }
    return true;
}


void UDSocket::Recv(void * buf, size_t len, bool recvPartial)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::Recv len:%i recvPartial:%i", this, user, len, recvPartial);
    
    ASSERT(isServer == false, "UDSocket::Recv called for socket that is initialized as server");
    ASSERT(recvBuf == NULL, "ERROR: UDSocket::Recv called with an already set receive buffer");
    ASSERT(buf != NULL, "ERROR: UDSocket::Recv called with NULL pointer to buffer ");

    recvBuf = buf;
    recvBufLen = len;
    recvBufAlreadyRead = 0;
    recvBufReadPartial = recvPartial;

    // if the socket is closing, cancel the recv request after assigning the buffer (to ensure a SocketRecvCanceled inside DoShutdown) ...
    // check shutdown as a previous Send might have failed and socket is already broken
    if (this->shutDown || (socketStatus == SST_CLOSED)) {
        return;
    }

    // For a length of 0 execute a simulated receive result call....
    if (len == 0) {
        iomux->SetExec(this, (void *)__UD_SOCKET_IO_EXEC_RRES);
        execCnt++;
        return;
    }

    // recv() in general will be called in SysRecv() to allow the TLSSocket
    // to override that functions for TLS communication. The call to recv()
    // here is an exception, because it also is called to check if the
    // socket has data to read.
    char tmp[1];
    if (recv(FDIIoMux, tmp, 1, MSG_PEEK) == 1) {
        iomux->SetExec(this, (void *)__UD_SOCKET_IO_EXEC_RECV);
        execCnt++;
    }
}


void UDSocket::IoContextNotify(bool indRX, bool indTX)
{
    int errNum = 0;
    if (isError) {
        socklen_t errNumLen = sizeof(errNum);
        if (getsockopt(FDIIoMux, SOL_SOCKET, SO_ERROR, &errNum, &errNumLen) != 0) {
            log->Log(LOG_LDS, "UDSocket(%08x,%08x)::IoContextNotify getsockopt() failed (%u : %s)", this, user, errno, strerror(errno));
        }
        else {
            log->Log(LOG_LDS, "UDSocket(%08x,%08x)::IoContextNotify socket error (%u : %s)", this, user, errNum, strerror(errNum));
        }
    }
    if (indTX) {
        if (socketStatus == SST_CONNECTION_INITIATED) {
            socklen_t errNumLen = sizeof(errNum);
            if (!isError && (getsockopt(FDIIoMux, SOL_SOCKET, SO_ERROR, &errNum, &errNumLen) != 0)) {
                log->Log(LOG_LDS, "UDSocket(%08x,%08x)::IoContextNotify getsockopt() failed (%u : %s)", this, user, errno, strerror(errno));
                this->DoShutdown();
                return;
            }
            if (errNum != 0) {
                log->Log(LOG_LDS, "UDSocket(%08x,%08x)::IoContextNotify socket connection failed (%u : %s)", this, user, errNum, strerror(errNum));
                shutdownReason = SOCKET_CONNECT_FAILED;
                this->DoShutdown();
                return;
            }
            else {
                socketStatus = SST_CONNECTED;
                UserSocketConnectComplete();
            }

            // Don't return here, bacause it could be that there are data to send
            // inside the queue
        }
        if (sendQueue.size() > 0) {
            if (!this->SendQueuedData()) {
                this->DoShutdown();
                return;
            }
        }
        else if (shutDown && (execCnt == 0)) {
            this->DoShutdown();
            return;
        }
    }

    if (indRX) {
        if (isServer) {
            if (!this->DoAccept()) {
                this->DoShutdown();
                return;
            }
        }
        else {
            if (!this->ReceiveData()) {
                this->DoShutdown();
                return;
            }
        }
    }

    byte tmp;
    if ((isError || isHangup) && (shutDown || (recv(FDIIoMux, &tmp, 1, MSG_PEEK) != 1))) {
        if (isError) {
            shutdownReason = SOCKET_LOST;
        }
        this->DoShutdown();
    }
}


void UDSocket::IoExec(void * context)
{
    execCnt--;
    if (context == (void *)__UD_SOCKET_IO_EXEC_SEND) {
        UserSocketSendResult();
    }
    else if (context == (void *)__UD_SOCKET_IO_EXEC_RECV) {
        this->ReceiveData();
    }
    else if (context == (void *)__UD_SOCKET_IO_EXEC_BIND) {
        sockaddr_un tmp;
        socklen_t l = sizeof(sockaddr_un);
        getsockname(FDIIoMux, (sockaddr *)&tmp, &l);
        if (socketStatus != SST_LISTEN) socketStatus = SST_BIND_DONE;
        UserSocketBindResult(tmp.sun_path, 0);
    }
    else if (context == (void *)__UD_SOCKET_IO_EXEC_ACPT) {
        this->DoAccept();
    }
    else if (context == (void *)__UD_SOCKET_IO_EXEC_RRES) {
        // Simulated RecvResult call because of a Recv() call with len = 0.
        void * tmp = recvBuf;
        recvBuf = NULL;
        UserSocketRecvResult(tmp, 0);
    }
    else if (context == (void *)__UD_SOCKET_IO_EXEC_SHTD) {
        // Nothing to do here. The exec Counter will deleted later and because of
        // the already set shutDown flag, the end of that function will finally
        // let the socket close...
    }

    if ((shutDown || (socketStatus == SST_DO_SHUTDOWN)) && (execCnt == 0) && ((sendQueue.size() == 0) || (shutdownReason != SOCKET_SHUTDOWN_NORMAL))) {
        this->DoShutdown();
    }
}

void UDSocket::UserSocketConnectComplete()
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketConnectComplete", this, user);
    user->SocketConnectComplete(this);
}

void UDSocket::UserSocketBindResult(const char * localAddr, word localPort)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketBindResult localAddr:%s localPort:%i", this, user, localAddr, localPort);
    user->SocketBindResult(this, localAddr, localPort);
}

void UDSocket::UserSocketListenResult(const char * remoteAddr, word remotePort)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketListenResult remoteAddr:%s remotePort:%i", this, user, remoteAddr, remotePort);
    user->SocketListenResult(this, remoteAddr, remotePort);
}

void UDSocket::UserSocketAcceptComplete(USocket * const remoteUser, UDSocket * remoteSocket)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketAcceptComplete", remoteSocket, remoteUser);
    remoteUser->SocketAcceptComplete(remoteSocket);
}

void UDSocket::UserSocketShutdown(shutdownreason_t reason)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketShutdown reason:%i", this, user, reason);
    user->SocketShutdown(this, reason);
}

void UDSocket::UserSocketSendResult()
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketSendResult", this, user);
    user->SocketSendResult(this);
}

void UDSocket::UserSocketRecvResult(void * buf, size_t len)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketRecvResult len:%i", this, user, len);
    log->HexDump(LOG_LDS, buf, len);
    user->SocketRecvResult(this, buf, len);
}

void UDSocket::UserSocketRecvCanceled(void * buf)
{
    log->Log(LOG_LDS, "UDSocket(%08x,%08x)::SocketRecvCanceled", this, user);
    user->SocketRecvCanceled(this, buf);
}
