C++ for Simultaneous TLS or Plain Socket Read/Write from Different Threads (Windows)


#if defined(WIN32)

static int _numSockThreadsRunning = 0;
static bool _bTlsConnection = false;

static DWORD WINAPI SockThreadA(LPVOID lpvThreadParm)
    {
    CkSocket *pSock0 = (CkSocket *)lpvThreadParm;

    CkSocket *pSock = pSock0->CloneSocket();

    // Read the HTTP response header.
    while (true)
    {
    CkString strLine;
    bool success = pSock->ReceiveToCRLF(strLine);
    if (!success) 
        {
        printf("%s\n",pSock->lastErrorText());
        break;
        }
    printf("Received: %s",strLine.getString());
    if (strLine.equals("\r\n")) break;    // We have the full header..
    }
    delete pSock;
    _numSockThreadsRunning--;
    return 0;
    }

static DWORD WINAPI SockThreadB(LPVOID lpvThreadParm)
    {
    CkSocket *pSock0 = (CkSocket *)lpvThreadParm;
    CkSocket *pSock = pSock0->CloneSocket();

    while (_numSockThreadsRunning > 1)
    {
    DWORD startTick = GetTickCount();
    bool bConnected = pSock->get_IsConnected();
    DWORD endTick = GetTickCount();

    if ((endTick - startTick) > 500)
        {
        printf("ERROR -- IsConnected blocked.\n");
        break;
        }

    printf("bConnected: %d\n",bConnected);

    Sleep(200);
    }

    delete pSock;
    _numSockThreadsRunning--;
    return 0;
    }

static DWORD WINAPI SockThreadC(LPVOID lpvThreadParm)
    {
    CkSocket *pSock0 = (CkSocket *)lpvThreadParm;
    CkSocket *pSock = pSock0->CloneSocket();

    // Wait 3 seconds, and then send the GET request..
    // The socket read in thread A should not be blocking the IsConnected calls.
    Sleep(3000);

    CkString strGet;
    if (_bTlsConnection)
    {
    pSock->BuildHttpGetRequest("https://www.chilkatsoft.com/test.asp",strGet);
    }
    else
    {
    pSock->BuildHttpGetRequest("http://www.chilkatsoft.com/test.asp",strGet);
    }

    bool success = pSock->SendString(strGet.getString());
    if (!success)
    {
    printf("%s\n",pSock->lastErrorText());
    }

    delete pSock;
    _numSockThreadsRunning--;
    return 0;
    }

static bool startSockThread(char which, CkSocket *pSock)
    {
    bool success = false;
    DWORD threadID;
    HANDLE hThread = NULL;
    if (which == 'A') hThread = CreateThread(0,0,SockThreadA,(void *)pSock,0,&threadID);
    if (which == 'B') hThread = CreateThread(0,0,SockThreadB,(void *)pSock,0,&threadID);
    if (which == 'C') hThread = CreateThread(0,0,SockThreadC,(void *)pSock,0,&threadID);
    if (hThread != NULL)
    {
    CloseHandle(hThread);
    success = true;
    _numSockThreadsRunning++;
    }
    return success;
    }


bool SocketTesting::qa_simultaneousMtReadWrite(bool bTls)
    {
    CkSocket sock;

    _bTlsConnection = bTls;

    sock.put_VerboseLogging(true);
    //sock.put_DebugLogFilePath("c:/aaworkarea/debugLogPath.txt");

    bool success = false;
    if (bTls)
    {
    success = sock.Connect("www.chilkatsoft.com",443,true,5000);
    }
    else
    {
    success = sock.Connect("www.chilkatsoft.com",80,false,5000);
    }
    if (!success)
    {
    printf("%s\n",sock.lastErrorText());
    printf("qa_simultaneousMtReadWrite failed\n");
    return false;
    }

    success = startSockThread('A',&sock);
    if (success) success = startSockThread('B',&sock);
    if (success) success = startSockThread('C',&sock);
    if (!success)
    {
    printf("Failed to start threads.\n");
    printf("qa_simultaneousMtReadWrite failed\n");
    return false;
    }

    int loopCount = 0;
    while (_numSockThreadsRunning)
    {
    Sleep(100);
    loopCount++;
    if (loopCount > 200) 
        {
        // It shouldn't take this long...
        printf("qa_simultaneousMtReadWrite failed! TIMED OUT.\n");
        return false;
        }
    }

    printf("loopCount = %d\n",loopCount);

    printf("qa_simultaneousMtReadWrite success\n");
    return true;
    }

#endif

v9.5.0.34 Micro Update: SSL/TLS Perfect Forward Secrecy, Minor HTTP and ASN.1 Fixes

The internal Chilkat SSL/TLS implementation now supports the TLS_DHE_RSA_WITH_AES_256_CBC_SHA and TLS_DHE_RSA_WITH_AES_128_CBC_SHA cipher suites. These allow for perfect forward secrecy. Note: This is implemented on the client-side for all protocols using SSL/TLS. The SSL/TLS client provides the server with a list of algorithms it supports, and it is the server that decides which is to be used. These new algorithms are now included in the list, and will be used if the server chooses.

Also, a minor problem was fixed in Chilkat HTTP. If a server responds with no Content-Length header, then there was a chance that Chilkat would not return the full response. This does not apply to “chunked” responses — only to non-chunked responses that are lacking the Content-Length header — which is a rare occurrence (and it is poor practice for an HTTP response to omit the Content-Length).

Finally, a minor and rarely encountered ASN.1 problem was fixed. (ASN.1 code is internal to Chilkat and has to do with implementations for PKCS, PFX, etc.

SSL/TLS Error – SEC_E_INTERNAL_ERROR

Problem:
An SSL/TLS connection failed and the LastErrorText contains “SEC_E_INTERNAL_ERROR”, such as in the error text below:

(See cause solution below)

ChilkatLog:
  Connect:
    DllDate: Dec  4 2009
    UnlockPrefix: ****
    Username: ****
    Component: .NET 2.0
    objectId: 1
    hostname: *.*.*.*
    port: 443
    ssl: 1
    maxWaitMs: 20000
    windowsAccount: ****
    ClientCertDN: ****
    protocol: default
    An existing connection was forcibly closed by the remote host.
    Failed to receive on the TCP socket
    connectionClosed: 0
    timedOut: 0
    Error reading data from server in handshake loop
    Setting scRet = SEC_E_INTERNAL_ERROR
    Error performing handshake
    Failed.

Cause #1:
This is caused when the server-side expect an SSL 3.0 initial packet and cannot handle an SSL 2.0 initial packet. Normally, when a client and server connect and begin the SSL/TLS handshake, the SSL 2.0 packet is the first one sent. It allows the client and server to negotiate and agree upon protocols starting from SSL 2.0 and up (SSL 2.0, SSL 3.0, TLS 1.0, etc.) Normally, the most secure protocol is used and SSL 2.0 is never actually used (unless there is some very very old legacy server that only supports SSL 2.0).

In some cases, the server does not expect the 1st packet to be SSL 2.0 formatted. One such case is the following OpenSSL command:

OpenSSL> s_server -accept 443 -Verify 5 -ssl3 -cert c:\certs\server.pem


Solution #1:

Set the SslProtocol property equal to the string “SSL 3.0”. For example:

socket.SslProtocol = "SSL 3.0";

This applies to all components capable of using SSL/TLS connections: FTP2, HTTP, Socket, IMAP, MailMan, etc.

Cause #2
The server requires the client to provide a certificate for authentication, but none was provided.

Solution #2
Load a Chilkat certificate object with the appropriate client-side certificate (there are many ways of doing this..) and then set the clients-side certificate by calling the SetSslClientCert method. This method exists on all Chilkat objects that use SSL/TLS connections (FTP2, HTTP, Socket, IMAP, MailMan, etc.)

Important: It’s entirely possible that your application might need to apply both solutions.