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

#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#ifdef _IOS_
#include <netdb.h>
#include <netinet/tcp.h> // for TCP_NODELAY
#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

#ifdef __linux__
#else
#define TCP_KEEPIDLE TCP_KEEPALIVE
#endif

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

static const int __TCP_SOCKET_IO_EXEC_SEND = 1;
static const int __TCP_SOCKET_IO_EXEC_RECV = 2;
static const int __TCP_SOCKET_IO_EXEC_BIND = 3;
static const int __TCP_SOCKET_IO_EXEC_SHTD = 4;
static const int __TCP_SOCKET_IO_EXEC_ACPT = 5;
static const int __TCP_SOCKET_IO_EXEC_RRES = 6;


#define TOS_DSCP_VOICE			0xb8    // RFC 3246 - Expedited Forwarding
#define TOS_DSCP_VIDEO			0x88    // RFC 4594 - 
#define TOS_DSCP_SIGNALING		0x68	// RFC 2597 - Assured Forwarding 31

/*---------------------------------------------------------------------------*/
/* class TCPSocketContext	                                                 */
/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/
/* class TCPSocketProvider	                                                 */
/*---------------------------------------------------------------------------*/

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

ISocketContext * TCPSocketProvider::CreateSocketContext(class IInstanceLog * const log)
{
    return new class TCPSocketContext();
}

ISocket * TCPSocketProvider::CreateSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log, bool useIPv6, class ISocketContext * socketContext /* currently ignored */)
{
    TCPSocket * tmp = NewSocket(iomux, user, log);
    tmp->OpenSocket(useIPv6);
    return tmp;
}

ISocketProvider * CreateTCPSocketProvider()
{
    return new TCPSocketProvider;
}

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

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

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

    ~SendBuffer()
    {
        free(buffer);
    }
};

/*---------------------------------------------------------------------------*/
/* class TCPSocket                                                          */
/*---------------------------------------------------------------------------*/

TCPSocket::TCPSocket(IIoMux * iomux, USocket * const user, IInstanceLog * const log, TCPSocketProvider * provider)
    : iomux(iomux),
      log(log),
      provider(provider)
{
    this->useIPv6 = false;
    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->createdByServer = false;
    this->remoteFD = -1;
    this->remoteAddress = NULL;
    this->serverName = NULL;
}


TCPSocket::~TCPSocket()
{
    ASSERT(socketStatus == SST_CLOSED, "TCPSocket::~TCPSocket deleted without waiting for SocketShutdown");
    if (sendQueue.size() > 0) {
        while (sendQueue.size() > 0) {
            SendBuffer * nextBuffer = sendQueue.front();
            sendQueue.pop();
            delete nextBuffer;
        }
    }
    free(this->remoteAddress);
    free(this->serverName);
}


void TCPSocket::SetUser(class USocket * const user, bool * createdByServer, const char ** remoteAddress, const char ** serverName)
{
    this->user = user;
    if (createdByServer) *createdByServer = this->createdByServer;
    if (remoteAddress) *remoteAddress = this->remoteAddress;
    if (serverName) *serverName = this->serverName;
}

void TCPSocket::OpenSocket(bool useIPv6)
{
    ASSERT(socketStatus == SST_CLOSED, "TCPSocket::OpenSocket socket already opened");
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket", this, user);

    this->useIPv6 = useIPv6;

    FDIIoMux = socket((useIPv6 ? AF_INET6 : AF_INET), SOCK_STREAM
#ifdef SOCK_NONBLOCK
        | SOCK_NONBLOCK
#endif
        , IPPROTO_TCP);
    if (FDIIoMux < 0) {
        log->Log(LOG_TCP, "TCPSocket(%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_TCP, "TCPSocket(%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_TCP, "TCPSocket(%08x,%08x)::OpenSocket fcntl(F_SETFL) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
#endif
    int keepAlive = 1;
    if (setsockopt(FDIIoMux, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(SO_KEEPALIVE) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
    int keepCnt = 10;
    if (setsockopt(FDIIoMux, IPPROTO_TCP, TCP_KEEPCNT, &keepCnt, sizeof(keepCnt)) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(TCP_KEEPCNT) failed (%u : %s)", this, user, errno, strerror(errno));
    }
    int keepIdle = 30;
    if (setsockopt(FDIIoMux, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(keepIdle)) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(TCP_KEEPIDLE) failed (%u : %s)", this, user, errno, strerror(errno));
    }
    int keepIntvl = 1;
    if (setsockopt(FDIIoMux, IPPROTO_TCP, TCP_KEEPINTVL, &keepIntvl, sizeof(keepIntvl)) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(TCP_KEEPINTVL) failed (%u : %s)", this, user, errno, strerror(errno));
    }
    int noDelay = 1;
    if (setsockopt(FDIIoMux, IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay)) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(TCP_NODELAY) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
#ifdef _IOS_
    int noSigPipe = 1;
    if (setsockopt(FDIIoMux, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, sizeof(noSigPipe)) < 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(SO_NOSIGPIPE) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
#endif
#ifdef __ANDROID__
//    int maxseg = 500; // #380169 too big maximum segment size with LTE. We don't need full TCP network performance on Android.
    int maxseg = 1460; // #409665 server not reachable via reverse proxy with MSS 500. Working with MSS 1460.
    socklen_t maxseg_len = sizeof(maxseg);
    if (setsockopt(FDIIoMux, IPPROTO_TCP, TCP_MAXSEG, &maxseg, maxseg_len)) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(TCP_MAXSEG) failed (%u : %s)", this, user, errno, strerror(errno));
    }
    else {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket setsockopt(TCP_MAXSEG) %u", this, user, maxseg);
    }
#endif
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGPIPE);
    if (sigprocmask(SIG_BLOCK, &set, 0) < 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket sigprocmask(SIG_BLOCK) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_OPEN_FAILED;
        this->Shutdown();
        return;
    }
}


void TCPSocket::SetQoS(qostraffictype_t type)
{
    if ((type == QOS_VOICE) || (type == QOS_AUDIO_VIDEO)) {
        int tos = (type == QOS_VOICE) ? TOS_DSCP_VOICE : ((type == QOS_AUDIO_VIDEO) ? TOS_DSCP_VIDEO : TOS_DSCP_SIGNALING);
        if(useIPv6) {
#ifdef IPV6_TCLASS
            if(setsockopt(FDIIoMux, IPPROTO_IPV6, IPV6_TCLASS, &tos, (socklen_t)(sizeof(tos))) < 0) {
                log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SetQoS setsockopt(IPV6_TCLASS) failed (%u : %s)", this, user, errno, strerror(errno));
            }
#else
            if(setsockopt(FDIIoMux, IPPROTO_IPV6, IP_TOS, &tos, (socklen_t)(sizeof(tos))) < 0) {
                log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SetQoS setsockopt(IPV6_TOS) failed (%u : %s)", this, user, errno, strerror(errno));
            }
#endif
        }
        else {
            if(setsockopt(FDIIoMux, IPPROTO_IP, IP_TOS, &tos, (socklen_t)(sizeof(tos))) < 0) {
                log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SetQoS setsockopt(IP_TOS) failed (%u : %s)", this, user, errno, strerror(errno));
            }
        }
    }
}


bool TCPSocket::ConnectIPv4(const char * address)
{
    int len = strlen(address);
    int port = 0;
    char ipaddress[128];
    char *c = strpbrk((char *)address, ":");

    if (c != 0) {
        len -= strlen(c);
        port = atoi(c + 1);
    }

    memcpy(&ipaddress, address, len);
    ipaddress[len] = 0;

    memset(&connectedAddr, 0x0, sizeof(struct sockaddr_storage));
    int result = inet_pton(AF_INET, ipaddress, &(((sockaddr_in*)&connectedAddr)->sin_addr));
    if ((result == 0) || (port == 0)) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::ConnectIPv4 \"%s\" is not a valid IPv4 address", this, user, ipaddress);
        shutdownReason = SOCKET_ADDRESS_INVALID;
        return false;
    }

    connectedAddr.ss_family = AF_INET;
    ((sockaddr_in*)&connectedAddr)->sin_port = htons(port);

    int res = connect(FDIIoMux, (struct sockaddr *)(&connectedAddr), sizeof(sockaddr_in));
    if (res < 0 && errno != EINPROGRESS) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::ConnectIPv4 address:%s connect() failed (%u : %s)", this, user, address, errno, strerror(errno));
        shutdownReason = SOCKET_CONNECT_FAILED;
        return false;
    }

    return true;
}


bool TCPSocket::ConnectIPv6(const char *address)
{
    int port = 0;
    char ipaddress[128];
    char *bo = strpbrk((char *)address, "[");
    char *bc = strpbrk((char *)address, "]");

    if (bc != 0) {
        // Format of IPv6 with Port = [xxxx]:port, optional
        char *c = strpbrk(bc + 1, ":");
        if (c != 0)
            port = atoi(c + 1);
    }

    const char *x = (bo == 0 ? address : bo + 1);
    int len = strlen(x);
    if (bc != 0) len -= strlen(bc);

    memcpy(&ipaddress, x, len);
    ipaddress[len] = 0;

    memset(&connectedAddr, 0x0, sizeof(struct sockaddr_storage));
    int result = inet_pton(AF_INET6, ipaddress, &(((sockaddr_in6*)&connectedAddr)->sin6_addr));
    if ((result == 0) || (port == 0)) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::ConnectIPv6 \"%s\" is not a valid IPv6 address", this, user, address);
        shutdownReason = SOCKET_ADDRESS_INVALID;
        return false;
    }

    connectedAddr.ss_family = AF_INET6;
    ((sockaddr_in6*)&connectedAddr)->sin6_port = htons(port);

    int res = connect(FDIIoMux, (struct sockaddr *)(&connectedAddr), sizeof(sockaddr_in6));
    if (res < 0 && errno != EINPROGRESS) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::ConnectIPv6 address:%s connect() failed (%u : %s)", this, user, address, errno, strerror(errno));
        shutdownReason = SOCKET_CONNECT_FAILED;
        return false;
    }

    return true;
}


void TCPSocket::GetLocalAddress(char * localAddr, unsigned size, word * localPort)
{
    char ipaddr[INET6_ADDRSTRLEN];
    memset(ipaddr, 0, INET6_ADDRSTRLEN);

    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::GetLocalAddress useIPv6=%d size=%d", this, user, useIPv6, size);

    if(useIPv6) {
        sockaddr_in6 tmp;
        socklen_t l = sizeof(sockaddr_in6);
        getsockname(FDIIoMux, (sockaddr *)&tmp, &l);
        if(!inet_ntop(AF_INET6, &tmp.sin6_addr, ipaddr, INET6_ADDRSTRLEN)) {
            *localPort = 0;
            return;
        }
        *localPort = ntohs(tmp.sin6_port);
    }
    else {
        sockaddr_in tmp;
        socklen_t l = sizeof(sockaddr_in);
        getsockname(FDIIoMux, (sockaddr *)&tmp, &l);
        if(!inet_ntop(AF_INET, &tmp.sin_addr, ipaddr, INET6_ADDRSTRLEN)) {
            *localPort = 0;
            return;
        }
        *localPort = ntohs(tmp.sin_port);
    }

    if(size <= strlen(ipaddr)) {
        *localPort = 0;
        return;
    }
 
    strcpy(localAddr, ipaddr);
}

void TCPSocket::GetRemoteAddress(char * remoteAddr, unsigned size, word * remotePort)
{
    if (this->remoteAddress) {
        char * port = strchr(this->remoteAddress, ':');
        if (port) {
            _snprintf(remoteAddr, size, "%.*s", (int)(port - this->remoteAddress), this->remoteAddress);
            *remotePort = (word)strtoul(port + 1, nullptr, 10);
        }
        else {
            _snprintf(remoteAddr, size, "%s", this->remoteAddress);
            *remotePort = 0;
        }
    }
    else {
        if(size > 0) *remoteAddr = '\0';
        *remotePort = 0;
    }
}

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

    if ((this->socketStatus == SST_CLOSED) && !shutDown) this->OpenSocket(useIPv6);
    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, "TCPSocket::Connect called, but socketState != SST_OPENED");

    free(remoteAddress);
    remoteAddress = _strdup(address);
    free(this->serverName);
    this->serverName = _strdup(serverName);
    bool result;
    if (useIPv6)
        result = this->ConnectIPv6(address);
    else
        result = this->ConnectIPv4(address);

    if (!result) {
        this->Shutdown();
        return;
    }

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


bool TCPSocket::BindIPv4(const char * address, word port)
{
    memset(&localAddr, 0, sizeof(sockaddr_in));

    localAddr.ss_family = AF_INET;

    if (address == NULL) {
        (((sockaddr_in*)&localAddr)->sin_addr).s_addr = htonl(INADDR_ANY);
    }
    else {
        int len = strlen(address);
        char ipaddress[128];
        char *c = strpbrk((char *)address, ":");

        if (c != 0) {
            len -= strlen(c);
            port = atoi(c + 1);
        }

        memcpy(&ipaddress, address, len);
        ipaddress[len] = 0;

        int result = inet_pton(AF_INET, ipaddress, &(((sockaddr_in*)&localAddr)->sin_addr));
        if (result == 0) {
            log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::BindIPv4 \"%s\" is not a valid IPv6 address", this, user, address);
            errno = 0;
            shutdownReason = SOCKET_ADDRESS_INVALID;
            return false;
        }
    }

    ((sockaddr_in*)&localAddr)->sin_port = htons(port);

    int res = bind(FDIIoMux, (struct sockaddr *)(&localAddr), sizeof(sockaddr_in));
    if (res != 0) {
        if (errno == EADDRINUSE) {
            shutdownReason = SOCKET_ADDR_IN_USE;
        }
        else {
            shutdownReason = SOCKET_BIND_FAILED;
        }
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::BindIPv4 bind() failed (%u : %s)", this, user, errno, strerror(errno));
        return false;
    }
    return true;
}


bool TCPSocket::BindIPv6(const char * address, word port)
{
    int yes = 1;
    if (setsockopt(FDIIoMux, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Bind setsockopt(IPPROTO_IPV6) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_BIND_FAILED;
        return false;
    }

    memset(&localAddr, 0, sizeof(sockaddr_storage));

    localAddr.ss_family = AF_INET6;

    if (address == NULL) {
        memcpy(&(((sockaddr_in6*)&localAddr)->sin6_addr), &in6addr_any, sizeof(struct in6_addr));
    }
    else {
        char ipaddress[128];
        char *bo = strpbrk((char *)address, "[");
        char *bc = strpbrk((char *)address, "]");

        if (bc != 0) {
            // Format of IPv6 with Port = [xxxx]:port, optional
            char *c = strpbrk(bc + 1, ":");
            if (c != 0)
                port = atoi(c + 1);
        }

        const char *x = (bo == 0 ? address : bo + 1);
        int len = strlen(x);
        if (bc != 0) len -= strlen(bc);

        memcpy(&ipaddress, x, len);
        ipaddress[len] = 0;

        int result = inet_pton(AF_INET6, ipaddress, &(((sockaddr_in6*)&localAddr)->sin6_addr));
        if (result == 0) {
            log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::BindIPv6 \"%s\" is not a valid IPv6 address", this, user, address);
            errno = 0;
            shutdownReason = SOCKET_ADDRESS_INVALID;
            return false;
        }
    }

    ((sockaddr_in6*)&localAddr)->sin6_port = htons(port);

    int res = bind(FDIIoMux, (struct sockaddr *)(&localAddr), sizeof(sockaddr_in6));
    if (res != 0) {
        if (errno == EADDRINUSE) {
            shutdownReason = SOCKET_ADDR_IN_USE;
        }
        else {
            shutdownReason = SOCKET_BIND_FAILED;
        }
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::BindIPv6 bind() failed (%u : %s)", this, user, errno, strerror(errno));
        return false;
    }
    return true;
}


void TCPSocket::Bind(const char * localAddr, word localPort, bool reuseAddr)
{
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Bind localAddr:%s localPort:%u reuseAddr=%u", this, user, localAddr, localPort, reuseAddr);

    if ((this->socketStatus == SST_CLOSED) && !shutDown) this->OpenSocket(useIPv6);
    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, "TCPSocket::Bind called but socketState != SST_OPENED");

    // It will be good to allow the socket to reuse the address.
    int r = reuseAddr ? 1 : 0;
    if (setsockopt(FDIIoMux, SOL_SOCKET, SO_REUSEADDR, &r, sizeof(r)) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Bind setsockopt(SO_REUSEADDR) failed (%u : %s)", this, user, errno, strerror(errno));
        shutdownReason = SOCKET_BIND_FAILED;
        this->Shutdown();
        return;
    }
#ifdef SO_REUSEPORT
    r = reuseAddr ? 1 : 0;
    setsockopt(FDIIoMux, SOL_SOCKET, SO_REUSEPORT, &r, sizeof(r));
#endif

    bool result;
    if (useIPv6)
        result = this->BindIPv6(localAddr, localPort);
    else
        result = this->BindIPv4(localAddr, localPort);

    if (!result) {
        this->Shutdown();
        return;
    }

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


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

    if (listen(FDIIoMux, SOMAXCONN) != 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Listen 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 TCPSocket::Accept(USocket * const remoteUser)
{
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Accept remoteUser:%08x", this, user, remoteUser);
    ASSERT(isServer == true, "TCPSocket::Accept called, but socket is not initialized as server");
    ASSERT(socketStatus == SST_NEED_ACCEPT, "TCPSocket::Accept called, but socketStatus != SST_NEED_ACCEPT");

    socketStatus = SST_LISTEN;

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

    char ipaddr[INET6_ADDRSTRLEN];
    int port;

    if (useIPv6) {
        sockaddr_in6 tmp;
        socklen_t l = sizeof(sockaddr_in6);
        getsockname(remoteFD, (sockaddr *)&tmp, &l);
        inet_ntop(AF_INET6, &tmp.sin6_addr, ipaddr, INET6_ADDRSTRLEN);
        port = ntohs(tmp.sin6_port);
    }
    else {
        sockaddr_in tmp;
        socklen_t l = sizeof(sockaddr_in);

        getsockname(remoteFD, (sockaddr *)&tmp, &l);
        inet_ntop(AF_INET, &tmp.sin_addr, ipaddr, INET6_ADDRSTRLEN);
        port = ntohs(tmp.sin_port);
    }

    TCPSocket * remoteSocket = provider->NewSocket(iomux, remoteUser, this->log);
    remoteSocket->createdByServer = true;
    remoteSocket->useIPv6 = useIPv6;
    remoteSocket->SetTCPKeepAlive();
    remoteSocket->socketStatus = SST_CONNECTED;
    remoteSocket->remoteAddress = _strdup(this->remoteAddress);
    iomux->FDUnblock(remoteFD);
    iomux->FDAdd(remoteFD, remoteSocket, true);
    remoteSocket->FDNotify(false, false);
    remoteFD = -1;
    UserSocketAcceptComplete(remoteUser, remoteSocket, ipaddr, port);
}


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

    sockaddr * addr;
    sockaddr_in addr4;
    sockaddr_in6 addr6;
    socklen_t size;

    if (useIPv6) {
        addr = (sockaddr *)&addr6;
        size = sizeof(sockaddr_in6);
    }
    else {
        addr = (sockaddr *)&addr4;
        size = sizeof(sockaddr_in);
    }
    memset(addr, 0, size);

    remoteFD = accept(FDIIoMux, addr, &size);
    FDNotify(true, false);
    if (remoteFD == -1) {
        // ignore EAGAIN, as DoAccept is called everytime after a previous accept() and might then fail because there is no other socket to accept
        if (errno == ECONNABORTED || errno == EAGAIN)
            return true;

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

    char ipaddr[INET6_ADDRSTRLEN + 1];
    if (useIPv6)
        inet_ntop(AF_INET6, &addr6.sin6_addr, ipaddr, INET6_ADDRSTRLEN);
    else
        inet_ntop(AF_INET, &addr4.sin_addr, ipaddr, INET6_ADDRSTRLEN);
    word port = ntohs((useIPv6 ? addr6.sin6_port : addr4.sin_port));
    socketStatus = SST_NEED_ACCEPT;
    free(this->remoteAddress);
    this->remoteAddress = (char *)(malloc(strlen(ipaddr) + 9));
    if (strchr(ipaddr, ':')) {
        sprintf(this->remoteAddress, "[%s]:%u", ipaddr, port);
    }
    else {
        sprintf(this->remoteAddress, "%s:%u", ipaddr, port);
    }
    UserSocketListenResult(ipaddr, port);

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


// TODO: Maybe better to activate this optionally with config
void TCPSocket::SetTCPKeepAlive()
{
    return;
/*
    int sockOpt = 1;
    if (setsockopt(FDIIoMux, SOL_SOCKET, SO_KEEPALIVE, &sockOpt, sizeof(sockOpt)) < 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SetTCPKeepAlive WARNING setsockopt(SO_KEEPALIVE) failed (%u : %s)", this, user, errno, strerror(errno));
        return;
    }

    sockOpt = 20; //  TCP_KEEPINTVL: Interval[s] between keepalives 
    if (setsockopt(FDIIoMux, SOL_TCP, TCP_KEEPINTVL, &sockOpt, sizeof(sockOpt)) < 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SetTCPKeepAlive WARNING setsockopt(TCP_KEEPINTVL) failed (%u : %s)", this, user, errno, strerror(errno));
        return;
    }

    sockOpt = 20; // TCP_KEEPIDLE: Start keeplives after this period[s]
    if (setsockopt(FDIIoMux, SOL_TCP, TCP_KEEPIDLE, &sockOpt, sizeof(sockOpt)) < 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SetTCPKeepAlive WARNING setsockopt(TCP_KEEPIDLE) failed (%u : %s)", this, user, errno, strerror(errno));
        return;
    }

    sockOpt = 2;  // TCP_KEEPCNT: Number of keepalives before death
    if (setsockopt(FDIIoMux, SOL_TCP, TCP_KEEPCNT, &sockOpt, sizeof(sockOpt)) < 0) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SetTCPKeepAlive WARNING setsockopt(TCP_KEEPCNT) failed (%u : %s)", this, user, errno, strerror(errno));
        return;
    }
*/
}


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


void TCPSocket::DoShutdown()
{
    if (execCnt != 0) {
        socketStatus = SST_DO_SHUTDOWN;
        return;
    }
    socketStatus = SST_CLOSED;
    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 TCPSocket::AddNewSendBuffer(const void * buf, size_t len)
{
    SendBuffer *buffer = new SendBuffer;
    buffer->buffer = (byte *)malloc(len);
    memcpy(buffer->buffer, buf, len);
    buffer->len = len;

    sendQueue.push(buffer);
}


bool TCPSocket::SendQueuedData()
{
    while (sendQueue.size() > 0) {
        SendBuffer * 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_TCP, "TCPSocket(%08x,%08x)::SendQueuedData send() failed (%u : %s)", this, user, errno, strerror(errno));
                return false;
            }
            return true;
        }

        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SendQueuedData() queued data sent (%i of %i bytes)", this, user, sendLen, nextBuffer->len);
        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 TCPSocket::Send(const void * buf, size_t len)
{
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Send len:%i", this, user, len);
    log->HexDump(LOG_TCP, buf, len);
    if (this->shutDown || (socketStatus == SST_CLOSED))
        return;

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

    if (sendQueue.size() > 0) {
        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_TCP, "TCPSocket(%08x,%08x)::Send() send() returned EAGAIN - going to cache data to send it later", this, user);
            this->AddNewSendBuffer(buf, len);
            return;
        }
        if (errno == ECONNRESET)
            shutdownReason = SOCKET_SHUTDOWN_BY_PEER;
        else
            shutdownReason = SOCKET_LOST;
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Send send() failed (%u : %s), fd:%u", this, user, errno, strerror(errno), FDIIoMux);
        this->Shutdown();
    }
    else if ((size_t)sendLen < len) {
        log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Send() could not send all data(sended : % u, needed to send : % u, diff : % u) - going to cache data to send it later", this, user, 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 *)__TCP_SOCKET_IO_EXEC_SEND);
        execCnt++;
    }
}


bool TCPSocket::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_TCP, "TCPSocket(%08x,%08x)::ReceiveData 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) {
            // if isError/isHangup is set, we have a non partial read but the socket is already gone, so no more data will ever arrive -> close
            if (this->isError) {    
                shutdownReason = SOCKET_LOST;
                return false;
            }
            else if (this->isHangup) {
                shutdownReason = SOCKET_SHUTDOWN_BY_PEER;
                return false;
            }
            return true;
        }
        void * tmp = recvBuf; // Need a copy because the user could call recv() in SocketRecvResult()
        recvBuf = NULL;
        UserSocketRecvResult(tmp, recvBufAlreadyRead);
    }
    return true;
}


void TCPSocket::Recv(void * buf, size_t len, bool recvPartial)
{
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::Recv len:%i recvPartial:%i", this, user, len, recvPartial);
    
    ASSERT(isServer == false, "TCPSocket::Recv called for socket that is initialized as server");
    ASSERT(recvBuf == NULL, "TCPSocket::Recv called with an already set receive buffer");
    ASSERT(buf != NULL, "TCPSocket::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 *)__TCP_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 *)__TCP_SOCKET_IO_EXEC_RECV);
        execCnt++;
    }
    else if (isHangup || isError) {
        socketStatus = SST_DO_SHUTDOWN;
        iomux->SetExec(this, (void *)__TCP_SOCKET_IO_EXEC_SHTD);
        execCnt++;
    }
}


void TCPSocket::IoContextNotify(bool indRX, bool indTX)
{
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::IoContextNotify getsockopt() isError:%u isHangup:%u", this, user, isError, isHangup);
    int errNum = 0;
    if (isError) {
        socklen_t errNumLen = sizeof(errNum);
        if (getsockopt(FDIIoMux, SOL_SOCKET, SO_ERROR, &errNum, &errNumLen) != 0) {
            log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::IoContextNotify getsockopt() failed (%u : %s)", this, user, errno, strerror(errno));
        }
        else {
            log->Log(LOG_TCP, "TCPSocket(%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_TCP, "TCPSocket(%08x,%08x)::IoContextNotify getsockopt() failed (%u : %s)", this, user, errno, strerror(errno));
                this->DoShutdown();
                return;
            }
            if (errNum != 0) {
                log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::IoContextNotify socket connection failed (%u : %s)", this, user, errNum, strerror(errNum));
                shutdownReason = SOCKET_CONNECT_FAILED;
                this->DoShutdown();
                return;
            }
            else {
                char ipaddr[INET6_ADDRSTRLEN];
                int port;

                if (useIPv6) {
                    sockaddr_in6 tmp;
                    socklen_t l = sizeof(sockaddr_in6);
                    getsockname(FDIIoMux, (sockaddr *)&tmp, &l);
                    inet_ntop(AF_INET6, &tmp.sin6_addr, ipaddr, INET6_ADDRSTRLEN);
                    port = ntohs(tmp.sin6_port);
                }
                else {
                    sockaddr_in tmp;
                    socklen_t l = sizeof(sockaddr_in);

                    getsockname(FDIIoMux, (sockaddr *)&tmp, &l);
                    inet_ntop(AF_INET, &tmp.sin_addr, ipaddr, INET6_ADDRSTRLEN);
                    port = ntohs(tmp.sin_port);
                }
#ifdef __ANDROID__
                int maxseg;
                socklen_t maxseg_len = sizeof(maxseg);
                if (getsockopt(FDIIoMux, IPPROTO_TCP, TCP_MAXSEG, &maxseg, &maxseg_len)) {
                    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket getsockopt(TCP_MAXSEG) failed (%u : %s)", this, user, errno, strerror(errno));
                }
                else {
                    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::OpenSocket getsockopt(TCP_MAXSEG) %u", this, user, maxseg);
                }
#endif
                socketStatus = SST_CONNECTED;
                UserSocketConnectComplete(ipaddr, port);
            }

            // 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 && (errNum != ECONNABORTED)) || isHangup) {
        if (isError) {
            shutdownReason = SOCKET_LOST;
        }
        else if (isHangup) {
            shutdownReason = SOCKET_SHUTDOWN_BY_PEER;
        }
        if (shutDown || (recv(FDIIoMux, &tmp, 1, MSG_PEEK) != 1)) {
            this->DoShutdown();
        }
    }
}


void TCPSocket::IoExec(void * context)
{
    execCnt--;
    if (context == (void *)__TCP_SOCKET_IO_EXEC_SEND) {
        UserSocketSendResult();
    }
    else if (context == (void *)__TCP_SOCKET_IO_EXEC_RECV) {
        if (!this->ReceiveData()) {
            this->DoShutdown();
            return;
        }
    }
    else if (context == (void *)__TCP_SOCKET_IO_EXEC_BIND) {

        char ipaddr[INET6_ADDRSTRLEN];
        int port;

        if (useIPv6) {
            sockaddr_in6 tmp;
            socklen_t l = sizeof(sockaddr_in6);
            getsockname(FDIIoMux, (sockaddr *)&tmp, &l);
            inet_ntop(AF_INET6, &tmp.sin6_addr, ipaddr, INET6_ADDRSTRLEN);
            port = ntohs(tmp.sin6_port);
        }
        else {
            sockaddr_in tmp;
            socklen_t l = sizeof(sockaddr_in);

            getsockname(FDIIoMux, (sockaddr *)&tmp, &l);
            inet_ntop(AF_INET, &tmp.sin_addr, ipaddr, INET6_ADDRSTRLEN);
            port = ntohs(tmp.sin_port);
        }

        if (socketStatus != SST_LISTEN) socketStatus = SST_BIND_DONE;
        UserSocketBindResult(ipaddr, port);
    }
    else if (context == (void *)__TCP_SOCKET_IO_EXEC_ACPT) {
        if (!this->DoAccept()) {
            this->DoShutdown();
            return;
        }
    }
    else if (context == (void *)__TCP_SOCKET_IO_EXEC_RRES) {
        // Simulated RecvResult call because of a Recv() call with len = 0.
        void * tmp = recvBuf;
        recvBuf = NULL;
        UserSocketRecvResult(tmp, 0);
        if (shutDown && (execCnt == 0) && (socketStatus != SST_CLOSED) && (sendQueue.size() == 0)) {
            this->DoShutdown();
            return;
        }
    }
    else if (context == (void *)__TCP_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) && (socketStatus != SST_CLOSED)) {
        if ((socketStatus == SST_DO_SHUTDOWN) || (shutdownReason != SOCKET_SHUTDOWN_NORMAL)) {
            this->DoShutdown();
        }
        else if (sendQueue.size() == 0) {
            this->DoShutdown();
        }
    }
}

void TCPSocket::UserSocketConnectComplete(const char * localAddr, word localPort)
{
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SocketConnectComplete localAddr:%s localPort:%i", this, user, localAddr, localPort);
    user->SocketConnectComplete(this);
}

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

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

void TCPSocket::UserSocketAcceptComplete(USocket * const remoteUser, TCPSocket * remoteSocket, const char * localAddr, word localPort)
{
    log->Log(LOG_TCP, "TCPSocket(%08x,%08x)::SocketAcceptComplete localAddr:%s localPort:%i", remoteSocket, remoteUser, localAddr, localPort);
    remoteUser->SocketAcceptComplete(remoteSocket);
}

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

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

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

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