使用 OpenSSL BIO 的非阻塞 I/O
Posted
技术标签:
【中文标题】使用 OpenSSL BIO 的非阻塞 I/O【英文标题】:Non-Blocking I/O With OpenSSL BIO 【发布时间】:2017-01-30 20:58:12 【问题描述】:我在 Linux 上使用 OpenSSL 1.0.0-fips。我遇到的问题是SSL_connect()
返回-1,SSL_get_error()
返回SSL_ERROR_WANT_READ
。然后我将文件描述符放入select()
中,timeval
结构设置为 10 秒,select()
只是超时。
我启动了 Wireshark,我看到“Client Hello”消失了,我看到 ServerHello
返回到客户端,但它从未在 select()
中“唤醒”。它只是超时。
我的问题是:
是否必须使用 BIO_new_socket()
创建 BIO 对象,然后使用 SSL_set_bio()
将 BIO 对象分配给我的 SSL 对象? SSL_set_fd()
的手册页说它将自动创建一个 BIO 对象,因此这似乎暗示 SSL_set_bio()
是一种你永远不必真正调用的无用函数。
假设我们使用SSL_set_fd()
并分配一个已连接的阻塞TCP 文件描述符。假设我们稍后使用fcntl()
将该文件描述符更改为非阻塞。这是否会破坏 SSL 对象(或底层 BIO 对象)?
【问题讨论】:
您可能应该展示一些代码,包括您如何设置上下文。异步 I/O 和非阻塞套接字给人们带来了相当多的麻烦,因为库不太适应。查看apps/ocsp.c
的源代码,了解 OpenSSL 是如何做到的。我似乎记得一个正常的上下文是在阻塞模式下设置的,然后使用底层套接字切换到非阻塞模式。搜索调用BIO_get_fd
和select
的代码。 AFAIK,它是自记录代码中非阻塞 I/O 的唯一示例 :)
我没有发布代码,因为我实现的 C++ SSL Socket 类的整个连接函数非常大。但是,我发现了我的问题。我忘记在 select() 的 maxfd 参数中加 1。
【参考方案1】:
1) 我是否必须使用 BIO_new_socket() 创建一个 BIO 对象,然后 使用 SSL_set_bio() 将 BIO 对象分配给我的 SSL 对象?男人 SSL_set_fd() 的页面说它会自动创建一个 BIO 对象 所以这似乎暗示 SSL_set_bio() 有点没用 你永远不需要调用的函数。
如果默认 BIO 对象足以满足您的需求,那么您不必手动创建和安装自己的 BIO 对象。 SSL_set_bio() 调用只是为了以防您想创建/使用与 SSL_set_fd() 代表您创建的默认对象不同的 BIO 对象。
2) 假设我们使用 SSL_set_fd() 并分配一个连接的 TCP 文件 阻塞的描述符。让我们说我们稍后会改变它 使用 fcntl() 将文件描述符变为非阻塞。这会破坏 SSL 对象(或底层 BIO 对象)?
是的,我相信它会坏掉。非阻塞 OpenSSL 的调用模式与阻塞 OpenSSL 的调用模式非常不同,我不相信您可以即时从一种模式来回切换到另一种模式。也就是说,我自己没有尝试过,所以我可能是错的,但我认为为了安全起见(并且保持一致),您应该预先选择是否要使用阻塞或非阻塞 I/ O 并在连接期间坚持使用它。
特别是手册页中的这句话:
BIO 和 SSL 引擎继承 fd 的行为。
...建议 SSL 设置调用将检查您的 fd 的状态,并根据 fd 的阻塞/非阻塞状态在 BIO 和 SSL 对象中设置私有变量。如果您随后“背着 OpenSSL”改变 fd 的行为,OpenSSL 的例程将不会预料到这一点,并且很可能会做错事而无法正常工作。
【讨论】:
因此,我在 C++ SSL Socket 类中编写的连接函数使用 SSL_set_fd() 将文件描述符分配给 SSL 对象,然后使用 fnctl( ) 它工作得很好。我什至可以将其切换回阻塞状态,SSL 层的行为与我期望的一样。因此,该文档似乎具有误导性,或者它按我想要的方式工作可能只是运气。以上是关于使用 OpenSSL BIO 的非阻塞 I/O的主要内容,如果未能解决你的问题,请参考以下文章