非阻塞I/O与OpenSSL BIO

3
我正在使用Linux上的OpenSSL 1.0.0-fips。我遇到的问题是,SSL_connect()返回-1,SSL_get_error()返回SSL_ERROR_WANT_READ。然后我将文件描述符放入一个带有设置为10秒的timeval结构的select()中,但select()超时了。
我启动了Wireshark,我看到“Client Hello”发送出去,我看到ServerHello返回给客户端,但它从未在select()中“唤醒”,它只是超时了。
我的问题是:
  1. 我是否需要使用BIO_new_socket()创建BIO对象,然后使用SSL_set_bio()将BIO对象分配给我的SSL对象?SSL_set_fd()的手册说它将自动创建一个BIO对象,因此似乎意味着您永远不必调用SSL_set_bio()这种似乎是无用的函数。

  2. 假设我们使用SSL_set_fd()并分配一个连接的TCP文件描述符,该文件描述符是阻塞的。假设我们稍后使用fcntl()将该文件描述符更改为非阻塞。这会以任何方式破坏SSL对象(或基础BIO对象)吗?


你应该展示一些代码,包括如何设置上下文。异步 I/O 和非阻塞套接字会给人们带来很多麻烦,因为库不够灵活。查看 apps/ocsp.c 的源代码,看看 OpenSSL 是如何处理的。我记得正常的上下文是以阻塞模式设置的,然后使用底层套接字切换到非阻塞模式。搜索调用 BIO_get_fdselect 的代码。据我所知,它是自文档化代码中唯一的非阻塞 I/O 示例 :) - jww
1
我没有发布代码,因为我实现的C++ SSL Socket类的整个连接函数非常大。但是,我找到了我的问题所在。我忘记将1添加到select()的maxfd参数中。 - T Lytle
1个回答

3

1) 我是否需要使用BIO_new_socket()创建BIO对象,并使用SSL_set_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例程将不会预期该行为,并且很可能会出现错误,并且不能正常工作。


1
所以,我在我的C++ SSL Socket类中编写的connect函数使用SSL_set_fd()将文件描述符分配给SSL对象,然后使用fnctl()将文件描述符设置为非阻塞,并且它可以正常工作。我甚至可以切换回阻塞模式,SSL层的行为也符合我的预期。因此,那份文档似乎是误导性的,或者这只是纯粹的运气让它按照我想要的方式工作。 - T Lytle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接