muk*_*nda 6 c++ multithreading boost openssl boost-asio
我是否创建了一个所有 SSL套接字共享的链,或者每个SSL上下文一个链(由任何相关的套接字共享)?
Boost.Asio SSL文档声明了这一点,但它没有提到上下文.我认为这意味着我必须只使用一个链,但我认为这是在OpenSSL支持多线程之前编写的.
SSL和线程
SSL流对象不执行自己的锁定.因此,必须在隐式或显式链中执行所有异步SSL操作.请注意,这意味着在单线程程序中不需要同步(因此不会产生锁定开销).
我很可能只有一个SSL上下文,但我想知道这个链是由SSL上下文还是全局网络服务所拥有的更合适.
我确实提供了一个处理程序CRYPTO_set_locking_callback,以防万一.
小智 5
我认为这个线程存在一些混乱,因为有一些事情需要澄清.让我们先断言::asio::ssl::context == SSL_CTX.这两个是一个.
其次,在使用时boost::asio::ssl,除非你在绕过内部init对象时做了一些疯狂的事情,否则你无需手动设置加密锁定回调.这是为您完成的,您可以在此处的来源中看到.
实际上,这样做可能会导致问题,因为init对象的析构函数在假设它们已在内部完成此工作的情况下运行.最后一点用盐,因为我没有深入审查这一点.
第三,我相信你将SSL流与SSL上下文混淆.为简单起见,将SSL流视为套接字,将SSL上下文视为套接字可用于各种SSL功能的单独对象,例如使用特定协商密钥进行握手,或作为服务器,提供有关服务器证书的信息到连接的客户端,以便您可以与客户端握手.
提到线索可以防止针对一个特定流(套接字)的可能同时IO,而不是上下文.显然,尝试读取缓冲区并在同一个套接字上同时从同一缓冲区写入将是一个问题.因此,当您提供由各种::asio::async_X方法包裹的完成处理程序时,您将执行特定的排序以防止上述情况.你可以在这个答案中阅读更多,这个答案是由一个比我更了解这个的人给出的.
现在,就上下文而言,David Schwartz在评论中指出了他写的另一个我需要挖掘的答案,上下文本身的全部目的是提供有助于跨多个SSL流的SSL功能的信息.他似乎暗示,鉴于其预期目的,他们基本上必须是线程安全的.我相信也许他是在上下文中发言::asio::ssl::context,只是因为::asio::ssl正确使用线程安全回调的方式,或者他只是在多线程程序中正确使用openSSL的上下文中发言.
无论如何,除了关于SO的这些评论和答案,以及我自己的实践经验之外,在文档中找到具体的证据,或者在线程安全和非线程安全之间明确定义的界限是非常困难的.boost::asio::ssl:context正如大卫所指出的那样,只是一个非常薄的包装纸SSL_CTX.我还要补充一点,它的意思是为了处理底层结构提供更多"c ++ ish"的感觉.它可能也设计了一些解耦::asio::ssl和底层实现库的意图,但它没有实现这一点,两者紧密绑定.大卫再次正确地提到这个薄的包装器文档很少,必须看一下实现以获得洞察力.
如果你开始深入研究实现,那么有一种相当简单的方法可以找出在上下文中什么是线程安全的,哪些不是线程安全的.您可以CRYPTO_LOCK_SSL_CTX在ssl_lib.c等源代码中搜索.
int SSL_CTX_set_generate_session_id(SSL_CTX *ctx, GEN_SESSION_CB cb)
{
CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
ctx->generate_session_id = cb;
CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
return 1;
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的那样,CRYPTO_w_lock使用了,这将我们带回到关于openSSL和线程的官方页面,这里指出:
只要设置了至少两个回调函数,即lock_function和 threadid_func,OpenSSL就可以安全地用在多线程应用程序中.
现在我们来asio/ssl/detail/impl/openssl_init.ipp看看本答案第一段中链接源代码的完整循环,我们看到:
do_init()
{
::SSL_library_init();
::SSL_load_error_strings();
::OpenSSL_add_all_algorithms();
mutexes_.resize(::CRYPTO_num_locks());
for (size_t i = 0; i < mutexes_.size(); ++i)
mutexes_[i].reset(new boost::asio::detail::mutex);
::CRYPTO_set_locking_callback(&do_init::openssl_locking_func);
::CRYPTO_set_id_callback(&do_init::openssl_id_func);
#if !defined(SSL_OP_NO_COMPRESSION) \
&& (OPENSSL_VERSION_NUMBER >= 0x00908000L)
null_compression_methods_ = sk_SSL_COMP_new_null();
#endif // !defined(SSL_OP_NO_COMPRESSION)
// && (OPENSSL_VERSION_NUMBER >= 0x00908000L)
}
Run Code Online (Sandbox Code Playgroud)
注意当然:
CRYPTO_set_ locking_callback
CRYPTO_set_ id_callback
因此,至少就::asio::ssl::context线程安全而言,线程安全与线程无关,而与openSSL相关的一切与openSSL相关,只有在多线程程序中正确使用时才能正常工作.
回到原来的问题,现在解释了所有这些,David在评论中也非常简单地给出了答案:
最常见的方法是每个SSL连接使用一个链.
举一个提供内容的HTTPS服务器的例子example.com.服务器具有单个上下文,配置有诸如证书之类的信息example.com.客户端连接,此上下文用于所有连接的客户端以执行握手等.将连接的客户端包装在新session对象中,您可以在该对象中处理该客户端.在此会话中,您将拥有单个链(隐式或显式)来保护套接字,而不是上下文.
虽然我不是任何方式的专家,我欢迎对此答案进行更正,但我已将所有关于这些主题的知识应用于开源透明过滤HTTPS代理中.对于超过17K行的代码比率,它有超过50%的注释,所以我知道的所有内容都写在那里(无论是对还是错;)).如果你想看到这个东西的实例,你可以查看TlsCapableHttpBridge.hpp源,它作为客户端和每个主机上的服务器,每个连接.
服务器上下文和证书被欺骗/生成一次,并在跨多个线程的所有客户端之间共享.唯一的手动锁定是在存储和检索上下文期间.桥的每一侧有一条链,一条用于真正的下游客户端套接字,另一条用于上游服务器连接,尽管它们在技术上甚至不是必需的,因为操作顺序无论如何都会产生隐式链.
请注意,该项目正在开发中,因为我正在重写许多内容,(dep构建指令尚未出现),但是所有内容都在MITM SSL代码方面起作用,因此您正在查看功能齐全的类和相关组件.
更新
大卫·施瓦茨(David Schwarz)对这个答案的要点提出了质疑,我非常尊重他在这一领域的权威。
有理由期望 ssl 上下文可以在线程之间共享 - 至少对于某些操作来说,如果只是为了促进 SSL 会话恢复的话。
我认为 David 在 OpenSSL 使用 SSL 上下文方面有经验。Boost ASIO 依次使用它(至少在我知道的所有平台上)。因此,要么 David 写一个答案来分享他的知识,要么你/我必须花一些时间研究 OpenSSL 文档和 Boost Asio 源代码,以找出适用于 Boost Asiossl::context使用的有效限制。
以下是当前记录的限制。
[旧答案文本如下]
线程安全
一般来说,并发使用不同的对象是安全的,但并发使用单个对象是不安全的。然而,诸如 io_service 之类的类型提供了更有力的保证,即并发使用单个对象是安全的。
从逻辑上讲,因为文档没有ssl_context特别提到该类的线程安全性,所以您必须得出结论:它不是。
如果您使用某些特定的挂钩(如您提到的),您知道底层 SSL 库支持这一点并不重要。这只是告诉您,实现 ssl_context线程感知可能并不难。
但在您(与库开发人员合作)提供此补丁之前,它不可用。
长话短说,您可以ssl_context从单链访问每一个。
| 归档时间: |
|
| 查看次数: |
1824 次 |
| 最近记录: |