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

#include <string>

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

#include "sdk/common/service/openssl_shared/ssl.h"
#include "tlssocket.h"

#define TLS_BUFFER_OVERHEAD     128
#define RECV_BUFFER_SIZE        4096
#define SEND_BUFFER_BLOCK_SIZE  512

#define TLS_SOCKET_OFFLINE      0
#define TLS_SOCKET_HS_START     1
#define TLS_SOCKET_CONNECTED    2
#define TLS_SOCKET_BIND_START   3
#define TLS_SOCKET_BIND_DONE    4
#define TLS_SOCKET_LISTEN       5
#define TLS_SOCKET_NEED_ACCAEPT 6
#define TLS_SOCKET_HS_FAILED    7
#define TLS_SOCKET_HS_DONE      8
#define TLS_SOCKET_ALPN_ERR     9
#define TLS_SOCKET_SHUTDOWN     10
#define TLS_SOCKET_DO_SHUTDOWN  11

static const int __TLS_SOCKET_IOEXEC_READ    = 1;
static const int __TLS_SOCKET_IOEXEC_SHTD    = 2;
static const int __TLS_SOCKET_IOEXEC_RECVSIM = 3;
static const int __TLS_SOCKET_IOEXEC_UPGRADE = 4;

static inline size_t get_size_in_blocks(size_t s, size_t block)
{
    size_t tmp = s / block * block;
    if (tmp < s)
        return tmp + block;
    else
        return tmp;
}


/*---------------------------------------------------------------------------*/
/* class TLSSocketProvider	                                                 */
/*---------------------------------------------------------------------------*/

TLSSocketProvider::TLSSocketProvider(ISocketProvider * tcpSocketProvider)
    : tcpSocketProvider(tcpSocketProvider)
{
}


TLSSocketProvider::~TLSSocketProvider()
{
}

class TLSSocket * TLSSocketProvider::NewSocket(IIoMux * const iomux, USocket * const user, class ISocketContext * socketContext, IInstanceLog * const log, ISocket * tcpSocketToUpgrade)
{
    return new TLSSocket(iomux, user, socketContext, log, this, tcpSocketToUpgrade);
};

ISocketContext * TLSSocketProvider::CreateSocketContext(class IInstanceLog * const log)
{
    return ISSLProvider::SharedInstance()->CreateSSLSocketContext(log);
}

ISocket * TLSSocketProvider::CreateSocket(IIoMux * const iomux, USocket * const user, IInstanceLog * const log, bool useIPv6, class ISocketContext * socketContext)
{
    TLSSocket * result = NewSocket(iomux, user, socketContext, log, nullptr);
    result->tcpSocket = tcpSocketProvider->CreateSocket(iomux, result, log, useIPv6, socketContext);
    return result;
}

ISocket * TLSSocketProvider::UpgradeToTLSSocket(class ISocket * fromTCPSocket, IIoMux * const iomux, USocket * const user, IInstanceLog * const log, class ISocketContext * socketContext)
{
    // Well, dynamic cast always is slow, but there is now other chance here to make sure, that the given socket is not 
    // a TLSSocket instance...
    TLSSocket * test = dynamic_cast<TLSSocket *>(fromTCPSocket);
    ASSERT(test == nullptr, "TLSSocketProvider::UpgradeToTLSSocket() must not be called with a TLS socket instance passed to fromTCPSocket!");

    TLSSocket * result = NewSocket(iomux, user, socketContext, log, fromTCPSocket);
    const char * remoteAddress;
    const char * serverName;
    fromTCPSocket->SetUser(result, &result->createdByServer, &remoteAddress, &serverName);
    result->remoteAddress = _strdup(remoteAddress);
    result->serverName = _strdup(serverName);
    iomux->SetExec(result, (void *)__TLS_SOCKET_IOEXEC_UPGRADE); // Defer to deliver eventually outstanding SocketSendResult on the TCP socket first
    (result->execCnt)++;
    return result;
}

ISocketProvider * CreateTLSSocketProvider(ISocketProvider * tcpSocketProvider)
{
    return new TLSSocketProvider(tcpSocketProvider);
}


/*---------------------------------------------------------------------------*/
/* class TCPSocket                                                          */
/*---------------------------------------------------------------------------*/
TLSSocket::TLSSocket(IIoMux * const iomux, USocket * const user, class ISocketContext * socketContext, IInstanceLog * const log, TLSSocketProvider * provider, ISocket * tcpSocketToUpgrade)
    : iomux(iomux),
      log(log),
      provider(provider)
{
    this->user = user;
    this->tcpSocket = tcpSocketToUpgrade;
    this->startTLSMode = tcpSocketToUpgrade != nullptr;
    this->socketContext = socketContext;
    this->ssl = NULL;
    this->state = TLS_SOCKET_OFFLINE;
    this->tcpSending = 0;
    this->execCnt = 0;
    this->shutDown = false;
    this->shutdownReason = SOCKET_SHUTDOWN_NORMAL;
    this->isServer = false;
    this->createdByServer = false;
    this->remoteAddress = NULL;
    this->serverName = NULL;
    this->sendBuffer = NULL;
    this->sendBufferSize = SEND_BUFFER_BLOCK_SIZE;
    this->recvBuffer = NULL;
    this->recvBufferSize = RECV_BUFFER_SIZE;
    this->userRecvBuffer = NULL;
    this->userRecvBufferSize = 0;
    this->userDataReceived = 0;
    this->userReadPartial = false;
    this->recvPending = false;
    this->recvBuffer = (byte *)malloc(recvBufferSize);
    this->sendBuffer = (byte *)malloc(sendBufferSize);
}


TLSSocket::~TLSSocket()
{
    delete ssl;
    delete tcpSocket;
    free(remoteAddress);
    free(serverName);
    free(sendBuffer);
    free(recvBuffer);

    while (sendQueue.size() > 0) {
        byte * cur = sendQueue.front();
        sendQueue.pop();
        free(cur);
    }
}

void TLSSocket::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 TLSSocket::GetLocalAddress(char * localAddr, unsigned size, word * localPort)
{
    tcpSocket->GetLocalAddress(localAddr, size, localPort);
}

void TLSSocket::GetRemoteAddress(char * remoteAddr, unsigned size, word * remotePort)
{
    tcpSocket->GetRemoteAddress(remoteAddr, size, remotePort);
}

void TLSSocket::Connect(const char * address, const char * serverName)
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Connect address:%s serverName:%s", (USocket *)this, user, address, serverName);
    ASSERT(state == TLS_SOCKET_OFFLINE, "TLSSocket::Connect called, but socketState != TLS_SOCKET_OFFLINE");
    free(remoteAddress);
    remoteAddress = _strdup(address);
    free(this->serverName);
    this->serverName = _strdup(serverName);
    tcpSocket->Connect(address);
}

void TLSSocket::Bind(const char * localAddr, word localPort)
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Bind localAddr:%s localPort:%i", (USocket *)this, user, localAddr, localPort);
    ASSERT(state == TLS_SOCKET_OFFLINE, "TLSSocket::Bind called but socketState != TLS_SOCKET_OFFLINE");
    isServer = true;
    tcpSocket->Bind(localAddr, localPort);
}

void TLSSocket::Bind(const char * localAddr, word localPort, bool reuseAddr)
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Bind localAddr:%s localPort:%i reuseAddr:%i", (USocket *)this, user, localAddr, localPort, reuseAddr);
    ASSERT(state == TLS_SOCKET_OFFLINE, "TLSSocket::Bind called but socketState != TLS_SOCKET_OFFLINE");
    isServer = true;
    tcpSocket->Bind(localAddr, localPort, reuseAddr);
}

void TLSSocket::Listen()
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Listen", (USocket *)this, user);
    ASSERT(state == TLS_SOCKET_OFFLINE || state == TLS_SOCKET_BIND_DONE, "TLSSocket::Listen socketStatus need to be TLS_SOCKET_BIND_DONE!");
    state = TLS_SOCKET_LISTEN;
    tcpSocket->Listen();
}


void TLSSocket::Accept(class USocket * remoteUser)
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Accept remoteUser:%08x", (USocket *)this, user, remoteUser);
    ASSERT(isServer == true, "TLSSocket::Accept called, but socket is not initialized as server");
    ASSERT(state == TLS_SOCKET_NEED_ACCAEPT, "TLSSocket::Accept called, but socketStatus != TLS_SOCKET_NEED_ACCAEPT");

    TLSSocket * remoteTLSSocket = NULL;
    if (remoteUser) {
        remoteTLSSocket = provider->NewSocket(iomux, remoteUser, this->socketContext, this->log);
        remoteTLSSocket->createdByServer = true;
    }
    tcpSocket->Accept(remoteTLSSocket);
}


void TLSSocket::Shutdown()
{
    if (!this->shutDown) {
        this->shutDown = true;
        log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Shutdown", (USocket *)this, user);
        if (!tcpSocket) {
            iomux->SetExec(this, (void *)__TLS_SOCKET_IOEXEC_SHTD);
            execCnt++;
        }
        else {
            if ((state == TLS_SOCKET_HS_DONE) || ((state == TLS_SOCKET_CONNECTED) && !recvPending)) {
                tcpSocket->Recv(recvBuffer, recvBufferSize, true);
                recvPending = true;
            }
            if (((state == TLS_SOCKET_HS_DONE) || (state == TLS_SOCKET_CONNECTED)) && (execCnt == 0) && ssl) {
                state = TLS_SOCKET_SHUTDOWN;
                ssl->DoShutdown();
            }
            if (ssl) {
                this->SendSSLData();
            }
            if ((execCnt == 0) && (!ssl || (ssl->BytesToSend() == 0))) {
                tcpSocket->Shutdown();
            }
        }
    }
}


void TLSSocket::SSLSend(const void * buf, size_t len)
{
    if (len == 0) {
        tcpSocket->Send(buf, 0);
        tcpSending++;
    }
    else {
        int sent = ssl->WriteDecoded(buf, len);
        if (sent > 0) {
            this->SendSSLData();
        }
        else if (sent < 0) {
            log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SSLSend ssl->WriteEncoded() failed", (USocket *)this, user);
        }
    }
}


void TLSSocket::Send(const void * buf, size_t len)
{
    if (shutDown || !tcpSocket || !ssl) return;

    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Send len:%i", (USocket *)this, user, len);
    log->HexDump(LOG_TLS, buf, len);
    ASSERT(isServer == false, "TLSSocket::Send called for socket that is initialized as server");

    if (state != TLS_SOCKET_CONNECTED) {
        byte * tmp = (byte *)malloc(len + sizeof(size_t));
        *(size_t *)tmp = len;
        memcpy(tmp + sizeof(size_t), buf, len);
        sendQueue.push(tmp);
        return;
    }

    this->SSLSend(buf, len);
}


void TLSSocket::Recv(void * buf, size_t len, bool recvPartial)
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Recv len:%i recvPartial:%i", (USocket *)this, user, len, recvPartial);
    if (userRecvBuffer != nullptr) {
        debug->printf("****** ITLSSocket::Recv(): critical error, printing backtrace!!!");
        this->iomux->PrintBacktrace();
        ASSERT(userRecvBuffer == NULL, "TLSSocket::Recv called with an already set receive buffer");
    }
    ASSERT(isServer == false, "TLSSocket::Recv called for socket that is initialized as server");

    userRecvBuffer = (byte *)buf;
    userRecvBufferSize = len;
    userReadPartial = recvPartial;
    userDataReceived = 0;

    // 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 (shutDown) {
        return;
    }

    if (len == 0) {
        iomux->SetExec(this, (void *)__TLS_SOCKET_IOEXEC_RECVSIM);
        execCnt++;
        return;
    }

    // SSL_pending() returns the number of bytes avaiable for a complete, decrypted SSL
    // frame. A result of 0 doesn't mean, that there is no data available (only that there
    // is no more data of an decrypted TLS frame available).
    // BIO_pending tells us the number of bytes the BIO has to read (because we use it here
    // with a read BIO). A result of 0 means, there os no data to read, but it could be,
    // that there is alread decryoted data inside the SSL object available. Because of
    // this, we have to check both of them, the result of SSL_pending AND of BIO_pending.
    if (tcpSocket && (state == TLS_SOCKET_CONNECTED) && ssl) {
        if (ssl->DataPending()) {
            iomux->SetExec(this, (void *)__TLS_SOCKET_IOEXEC_READ);
            execCnt++;
        }
    }
    else {
        log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::Recv ignored, tcpSocket:%08x state:%u ssl:%08x", (USocket *)this, user, tcpSocket, state, ssl);
    }
}


int TLSSocket::SendSSLData()
{
    int res = ssl->BytesToSend();
    if (res > 0) {
        if (res > (int)sendBufferSize) {
            free(sendBuffer);
            sendBufferSize = get_size_in_blocks(res, SEND_BUFFER_BLOCK_SIZE);
            sendBuffer = (byte *)malloc(sendBufferSize);
        }

        res = ssl->ReadEncoded(sendBuffer, sendBufferSize);
        if (res > 0) {
            tcpSocket->Send(sendBuffer, res);
            tcpSending++;
        }
        else if (res == OP_SSL_ERROR) {
            log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SendSSLData ssl->ReadEncoded() failed", (USocket *)this, user);
            state = TLS_SOCKET_HS_FAILED;
            tcpSocket->Shutdown();
        }
        else if (res == OP_SSL_CLOSE) {
            log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SendSSLData ssl->ReadEncoded() close", (USocket *)this, user);
            tcpSocket->Shutdown();
        }
    }
    return res;
}


bool TLSSocket::ReadSSLData()
{
    int res;

    if (shutDown) {
        do {
            res = ssl->ReadDecoded(recvBuffer, recvBufferSize);
        } while ((res > 0) && ssl->DataPending());
    }
    else if (userRecvBuffer) {
        do {
            byte * userBuf = userRecvBuffer + userDataReceived;
            size_t userLen = userRecvBufferSize - userDataReceived;
            res = ssl->ReadDecoded(userBuf, userLen);

            if (res > 0) {
                userDataReceived += res;
                if (userReadPartial || (userDataReceived >= userRecvBufferSize)) {
                    if (!ssl->DataPending() && tcpSocket && (state == TLS_SOCKET_CONNECTED) && !recvPending) {
                        tcpSocket->Recv(recvBuffer, recvBufferSize, true);
                        recvPending = true;
                    }
                    byte * tmp = userRecvBuffer;
                    userRecvBuffer = NULL;
                    UserSocketRecvResult(tmp, userDataReceived);
                    return true;
                }
            }
        }
        while ((res > 0) && ssl->DataPending());
    }
    else {
        res = 0;
    }
    if (!tcpSocket) {
        return false;
    }
    if (res == OP_SSL_ERROR) {
        log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::ReadSSLData ssl->ReadDecoded() failed", (USocket *)this, user);
        state = TLS_SOCKET_HS_FAILED;
        tcpSocket->Shutdown();
    }
    else if (res == OP_SSL_CLOSE) {
        log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::ReadSSLData ssl->ReadDecoded() close", (USocket *)this, user);
        tcpSocket->Shutdown();
    }
    else if (!recvPending) {
        tcpSocket->Recv(recvBuffer, recvBufferSize, true);
        recvPending = true;
    }
    return true;
}


bool TLSSocket::ALPNProtocolAccepted()
{
    const byte * protocolList = nullptr;
    size_t protocolLength = 0;
    this->ssl->GetSelectedALPNProtocol(protocolList, protocolLength);
    std::string protocolStr(reinterpret_cast<const char *>(protocolList), protocolLength);
    return this->user->SocketAcceptSelectedALPNProtocol(this, protocolStr.c_str());
}


void TLSSocket::DoSSLHandshake()
{
    switch (ssl->DoHandshake())
    {
    case OP_SSL_DONE:
        state = TLS_SOCKET_HS_DONE;
        // If the socket was created because of an accept of a server, the other side initiated
        // the SSL handshake. Because of that we will still have data to send, so that the client
        // can finalize the (accepted) handshake...
        this->SendSSLData();

        // Wenn called from a TLSSocket Server, a TCPSocket Sendresult for the TLSSocket
        // will be redirected to the TLSSocket user. To prevent this, finalize the handshake
        // will be done with the help of SetExec().
        if (!tcpSending) {
            ConnectionEstablished();
        }
        break;

    case OP_SSL_NEED_WRITE:
    case OP_SSL_NEED_READ:
        this->SendSSLData();
        if (!recvPending) {
            tcpSocket->Recv(recvBuffer, recvBufferSize, true);
            recvPending = true;
        }
        break;

    default: // error
        state = TLS_SOCKET_HS_FAILED;
        tcpSocket->Shutdown();
    }
    if (shutDown && ((state == TLS_SOCKET_HS_DONE) || (state == TLS_SOCKET_CONNECTED)) && (execCnt == 0)) {
        state = TLS_SOCKET_SHUTDOWN;
        ssl->DoShutdown();
    }
    this->SendSSLData();
    if (shutDown && (execCnt == 0) && (ssl->BytesToSend() == 0)) {
        tcpSocket->Shutdown();
    }
}


void TLSSocket::SocketConnectComplete(ISocket * const socket)
{
    state = TLS_SOCKET_HS_START;

    ssl = ISSLProvider::SharedInstance()->CreateSSLInstance(log, false, remoteAddress, this->socketContext, this->serverName);
    if (ssl == NULL) {
        tcpSocket->Shutdown();
        return;
    }
    this->DoSSLHandshake();
}


void TLSSocket::SocketBindResult(ISocket * const socket, const char * localAddr, word localPort)
{
    if (state != TLS_SOCKET_LISTEN) state = TLS_SOCKET_BIND_DONE;
    UserSocketBindResult(localAddr, localPort);
}


void TLSSocket::SocketListenResult(ISocket * const socket, const char * remoteAddr, word remotePort)
{
    state = TLS_SOCKET_NEED_ACCAEPT;
    free(this->remoteAddress);
    this->remoteAddress = (char *)(malloc(strlen(remoteAddr) + 9));
    if (strchr(remoteAddr, ':')) {
        sprintf(this->remoteAddress, "[%s]:%u", remoteAddr, remotePort);
    }
    else {
        sprintf(this->remoteAddress, "%s:%u", remoteAddr, remotePort);
    }
    UserSocketListenResult(this->remoteAddress, remotePort);
}


void TLSSocket::SocketAcceptComplete(ISocket * const socket)
{
    tcpSocket = socket;
    state = TLS_SOCKET_HS_START;

    ssl = ISSLProvider::SharedInstance()->CreateSSLInstance(log, true, remoteAddress, this->socketContext, this->serverName);
    if (ssl == NULL) {
        tcpSocket->Shutdown();
        return;
    }
    this->DoSSLHandshake();
}


void TLSSocket::SocketShutdown(ISocket * const socket, shutdownreason_t reason)
{
    if ((state == TLS_SOCKET_HS_START) || (state == TLS_SOCKET_HS_DONE)) {
        log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketShutdown unexpected %d", (USocket *)this, user, (int)reason);
    }

    if (state == TLS_SOCKET_ALPN_ERR) shutdownReason = SOCKET_ALPN_SELECTION_FAILED;
    else if (state == TLS_SOCKET_HS_FAILED) shutdownReason = SOCKET_TLS_HANDSHAKE_FAILED;
    else shutdownReason = reason;

    delete tcpSocket;
    tcpSocket = NULL;

    this->DoShutdown();
}


void TLSSocket::DoShutdown()
{
    if (tcpSocket) {
        tcpSocket->Shutdown();
        state = TLS_SOCKET_DO_SHUTDOWN;
        return;
    }
    if (execCnt != 0) {
        execCnt = 0;
        iomux->CancelSetExec(this);
    }
    if (ssl) {
        ssl->SetShutdown();
    }
    state = TLS_SOCKET_OFFLINE;
    isServer = false;
    shutDown = false;
    if (userRecvBuffer) {
        UserSocketRecvCanceled(userRecvBuffer);
        userRecvBuffer = NULL;
    }
    shutdownreason_t r = shutdownReason;
    shutdownReason = SOCKET_SHUTDOWN_NORMAL;
    UserSocketShutdown(r);
}


void TLSSocket::SocketSendResult(ISocket * const socket)
{
    if (state == TLS_SOCKET_OFFLINE) {
        UserSocketSendResult();
        return;
    }
    tcpSending--;
    if (!shutDown && (state == TLS_SOCKET_CONNECTED)) {
        UserSocketSendResult();
        if (shutDown && (execCnt == 0) && ssl) {
            state = TLS_SOCKET_SHUTDOWN;
            ssl->DoShutdown();
        }
    }
    if (ssl) {
        this->SendSSLData();
    }
    if (shutDown) {
        if ((execCnt == 0) && (!ssl || (ssl->BytesToSend() == 0))) {
            tcpSocket->Shutdown();
        }
    }
    else if ((state == TLS_SOCKET_HS_DONE) && !tcpSending) {
        ConnectionEstablished();
    }
}


void TLSSocket::SocketRecvResult(ISocket * const socket, void * buf, size_t len)
{
    recvPending = false;
    if (!ssl) return;

    int res = ssl->WriteEncoded(buf, len);
    if (res < (int)len) { // Because we user memory buffers, BIO_write always should work completely
        log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketRecvResult number of ssl->WriteEncoded() bytes is %u bytes, but should be %u", (USocket *)this, user, res, len);
        log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketRecvResult closing connection because of data inconsistency", (USocket *)this, user);
        tcpSocket->Shutdown();
        return;
    }

    if (state == TLS_SOCKET_HS_START) {
        this->DoSSLHandshake();
    }
    else if ((state == TLS_SOCKET_CONNECTED) || (state == TLS_SOCKET_SHUTDOWN)) {
        this->ReadSSLData();
    }
}


void TLSSocket::SocketRecvCanceled(ISocket * const socket, void * buf)
{
    recvPending = false;
}


void TLSSocket::ConnectionEstablished()
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::IoExec TLS connection established", (USocket *)this, user);
    state = TLS_SOCKET_CONNECTED;

    if (!recvPending) {
        tcpSocket->Recv(recvBuffer, recvBufferSize, true);
        recvPending = true;
    }
    // If we have a serverContext, the TLSSocket was initialized from a TLSSocket server.
    // In that case fake the SocketAcceptComplete call back for the TLSSocket server.
    if (startTLSMode) UserSocketUpgradeToTLSComplete(this);
    else if (createdByServer) UserSocketAcceptComplete();
    else {
        if (this->ALPNProtocolAccepted()) UserSocketConnectComplete();
        else {
            this->state = TLS_SOCKET_ALPN_ERR;
            this->Shutdown();
        }
    }

    if (ssl && tcpSocket) {
        while (sendQueue.size() > 0) {
            byte * cur = sendQueue.front();
            sendQueue.pop();
            size_t len = *(size_t *)cur;
            this->SSLSend(cur + sizeof(size_t), len);
            free(cur);
        }
    }
}


void TLSSocket::IoExec(void * execContext)
{
    execCnt--;
    if (execContext == (void *)__TLS_SOCKET_IOEXEC_READ) {
        if (ssl && ((state == TLS_SOCKET_CONNECTED) || (state == TLS_SOCKET_SHUTDOWN))) {
            if (!this->ReadSSLData()) {
                this->DoShutdown();
                return;
            }
        }
        else {
            log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::IoExec ignored state:%u ssl:%08x", (USocket *)this, user, state, ssl);
        }
    }
    else if (execContext == (void *)__TLS_SOCKET_IOEXEC_RECVSIM) {
        byte * tmp = userRecvBuffer;
        userRecvBuffer = NULL;
        UserSocketRecvResult(tmp, userDataReceived);
        return;
    }
    else if (execContext == (void *)__TLS_SOCKET_IOEXEC_SHTD) {
    }
    else if (execContext == (void *)__TLS_SOCKET_IOEXEC_UPGRADE) {
        if (state == TLS_SOCKET_OFFLINE) {
            if (createdByServer) SocketAcceptComplete(tcpSocket);
            else SocketConnectComplete(tcpSocket);
        }
    }

    if ((execCnt == 0) && (shutDown || ((state != TLS_SOCKET_HS_DONE) && (state != TLS_SOCKET_CONNECTED)) || !ssl || !ssl->DataPending())) {
        if (!tcpSocket) {
            this->DoShutdown();
        }
        else if (shutDown) {
            tcpSocket->Shutdown();
        }
    }
}


void TLSSocket::UserSocketConnectComplete()
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketConnectComplete", (USocket *)this, user);
    user->SocketConnectComplete(this);
}

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

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

void TLSSocket::UserSocketAcceptComplete()
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketAcceptComplete", (USocket *)this, user);
    user->SocketAcceptComplete(this);
}

void TLSSocket::UserSocketShutdown(shutdownreason_t reason)
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketShutdown reason:%i", (USocket *)this, user, reason);
    user->SocketShutdown(this, reason);
}

void TLSSocket::UserSocketSendResult()
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketSendResult", (USocket *)this, user);
    user->SocketSendResult(this);
}

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

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


void TLSSocket::UserSocketUpgradeToTLSComplete(ISocket * const socket)
{
    log->Log(LOG_TLS, "TLSSocket(%08x,%08x)::SocketUpgradeToTLSComplete", (USocket *)this, user);
    user->SocketUpgradeToTLSComplete(this);
}
