简体   繁体   English

在单线程使用Threadsafe OpenSSL吗?

[英]Threadsafe OpenSSL in a single-threaded use?

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. 我有一个带有单个winsock2套接字的多线程客户机/服务器应用程序,它用于“回拨”客户机的唯一信息。 All of the socket communication is encrypted with OpenSSL before it is transmitted using SSL_write and then decrypted with SSL_read . 它是使用传输之前所有的套接字通信的与OpenSSL的加密SSL_write ,然后用解密SSL_read We are using blocking sockets, and the berkely-style C calls, inside of a Socket wrapper class to make it object-oriented. 我们在Socket包装器类内部使用阻塞套接字和berkely风格的C调用,以使其成为面向对象的。 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. 在单个线程中仅实例化了此Socket类的一个实例,并且对其的所有调用都在该单个线程中串行完成。 The server, who the client is 'phoning home' to, runs on a Server 2008 R2 box. 客户端要“打电话回家”的服务器在Server 2008 R2机器上运行。 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. 在从2000到7的所有版本的Windows中,客户端都可以正常运行:它可以握手,获取所需的信息并干净地关闭连接。

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. 在Windows 8中,客户端在没有说明的情况下使“电话回家”失败,并使服务器处于尝试不断从套接字读取更多信息的状态。

One thing that was suggested to me by a coworker was to make sure my OpenSSL was setup to be threadsafe. 一位同事向我建议的一件事是,确保将我的OpenSSL设置为线程安全的。

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. 原因1:所有套接字I / O都在单个线程中发生。
Reason 2: all of the socket calls occur in serial order. 原因2:所有套接字调用都以串行顺序发生。
Reason 3: it works on all previous versions of Windows prior to 8. 原因3:它适用于Windows 8之前的所有早期版本。

Would it be important to set OpenSSL to be threadsafe? 将OpenSSL设置为线程安全是否重要?

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. 设置OpenSSL回调以使其具有线程安全性很容易,所以为什么不这样做并查看问题是否消失。

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 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM