문제

I've completely re-written this question because hopefully I can get some more useful answers this way.

I've got a multithreaded client/server application with a single winsock2 socket that it uses to 'phone home' for client-unique information. All of the socket communication is encrypted with OpenSSL before it is transmitted using SSL_write and then decrypted with SSL_read. We are using blocking sockets, and the berkely-style C calls, inside of a Socket wrapper class to make it object-oriented. There is only 1 instance of this Socket class instantiated in a single thread, and all of the calls to it are done serially within that single thread. The server, who the client is 'phoning home' to, runs on a Server 2008 R2 box. On all versions of windows from 2000 up through 7, the client functions perfectly: it handshakes, gets the information it needs, and closes the connection cleanly.

In Windows 8, the client fails the 'phone home' without explanation and leaves the server in a state of trying to continually read more information from the socket.

One thing that was suggested to me by a coworker was to make sure my OpenSSL was setup to be threadsafe.

Due to several reasons, I believe this is unnecessary, but I need a more informed answer.
Reason 1: all of the socket I/O occurs in a single thread.
Reason 2: all of the socket calls occur in serial order.
Reason 3: it works on all previous versions of Windows prior to 8.

Would it be important to set OpenSSL to be threadsafe?

도움이 되었습니까?

해결책

It's easy enough to set up the OpenSSL callbacks to make it thread safe so why not just do it and see if the problem goes away.

Whilst you've reasoned that there's no need to do so the fact that there's a potential problem shouldn't blind you to a potentially easy fix to the issue even if "there's no way it should be that"...

Here's the code that I use for this,

struct CRYPTO_dynlock_value 
{ 
   CRYPTO_dynlock_value()
   {
      ::InitializeCriticalSection(&crit);
   }

   ~CRYPTO_dynlock_value()
   {
      ::DeleteCriticalSection(&crit);
   }

    CRITICAL_SECTION crit;
}; 

static CRITICAL_SECTION *InitStaticCrit()
{
   CRITICAL_SECTION *pCrit = new CRITICAL_SECTION();

   ::InitializeCriticalSection(pCrit);

   return pCrit;
}

static CRITICAL_SECTION *s_pCriticalSection = InitStaticCrit();

static CRITICAL_SECTION *s_pLocks = 0;

static struct CRYPTO_dynlock_value *dyn_create_function(
   const char *file,
   int line) 
{
   (void)file;
   (void)line;

   CRYPTO_dynlock_value *pValue = new CRYPTO_dynlock_value();

   return pValue;
}

static void dyn_lock_function(
   int mode,
   struct CRYPTO_dynlock_value *pLock,
   const char *file,
   int line) 
{
   (void)file;
   (void)line;

    if (mode & CRYPTO_LOCK)
    {
       ::EnterCriticalSection(&pLock->crit);
    }
    else
    {
       ::LeaveCriticalSection(&pLock->crit);
    } 
} 

static void dyn_destroy_function(
   struct CRYPTO_dynlock_value *pLock,
   const char *file,
   int line) 
{
   (void)file;
   (void)line;

   delete pLock;
}

static bool ThreadingSetup(
   const DWORD spinCount)
{
   ::EnterCriticalSection(&s_criticalSection);

   bool ok = false;

   if (!s_pLocks)
   {
      s_pLocks = (CRITICAL_SECTION *)malloc(CRYPTO_num_locks() * sizeof(CRITICAL_SECTION));

      for (int i = 0; i < CRYPTO_num_locks(); i++)
      {
         if (spinCount != 0)
         {
#if(_WIN32_WINNT >= 0x0403)
            (void)::InitializeCriticalSectionAndSpinCount(&s_pLocks[i], spinCount);
#else
#pragma warning(suppress: 6011)  // Dereferencing null pointer. No, we're not.
            ::InitializeCriticalSection(&s_pLocks[i]);

            OutputDebugString(_T("CUsesOpenSSL::ThreadingSetup() - spin count specified but _WIN32_WINNT < 0x0403, spin count not used\n"));
#endif
         }
         else
         {
#pragma warning(suppress: 6011)  // Dereferencing null pointer. No, we're not.
            ::InitializeCriticalSection(&s_pLocks[i]);
         }
      }

      CRYPTO_set_locking_callback(LockingCallback);

      //CRYPTO_set_id_callback(id_function); 

      // dynamic locks callbacks

      CRYPTO_set_dynlock_create_callback(dyn_create_function); 
      CRYPTO_set_dynlock_lock_callback(dyn_lock_function); 
      CRYPTO_set_dynlock_destroy_callback(dyn_destroy_function); 

      ok = true;
   }

   ::LeaveCriticalSection(&s_criticalSection);

   return ok;
}

static void ThreadingCleanup()
{
   if (s_pLocks)
   {
      CRYPTO_set_locking_callback(0);

      CRYPTO_set_dynlock_create_callback(0);
      CRYPTO_set_dynlock_lock_callback(0);
      CRYPTO_set_dynlock_destroy_callback(0);

      for (int i = 0; i < CRYPTO_num_locks(); i++)
      {
         ::DeleteCriticalSection(&s_pLocks[i]);
      }

      free(s_pLocks);

      s_pLocks = 0;
   }
}

static void LockingCallback(
   int mode,
   int type,
   const char * /*file*/,
   int /*line*/)
{
   if (mode & CRYPTO_LOCK)
   {
      ::EnterCriticalSection(&s_pLocks[type]);
   }
   else
   {
      ::LeaveCriticalSection(&s_pLocks[type]);
   }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top