调用SSL_shutdown()挂起,客户端仍处于FIN_WAIT2状态

时间:2014-07-31 03:35:17

标签: ssl

在我的ActiveUpdate模块(从服务器下载文件)中,如果服务器丢弃FIN包,则关闭OpenSSL连接将挂起(等待超过2小时)。

SSL连接的伪代码关闭

def  ssl_close():
if (!SSL_shutdown(m_ssl)):
    …
    shutdown(SSL_get_fd(m_ssl), SHUT_WR);
    SSL_shutdown(m_ssl)
    ...

挂起连接的netstat输出

tcp        0      0 192.168.133.135:52453   10.203.136.169:4122     FIN_WAIT2   off (0.00/0/0)
  

https://www.openssl.org/docs/ssl/SSL_shutdown.html

     

为了完成双向关闭握手,必须再次调用SSL_shutdown()。   如果底层BIO阻塞,则SSL_shutdown()仅在握手步骤完成或发生错误后才会返回。

如果调用SSL_shutdown()一次,它就不会挂起,是否对内存和连接资源有任何影响?

是否有超时机制来控制OpenSSL中的SSL_shutdown()或更好的方法?

2 个答案:

答案 0 :(得分:2)

第一个SSL_shutdown将导致向对等方发送关闭警报,以通知对等方不再有SSL数据 - 类似于在普通套接字上发送FIN或关闭SHUT_WR。第二个SSL_shutdown将一直等到收到来自对等方的关闭警报。

要确保套接字完全处于活动状态以便对等方发送SSL关闭警报并且您将收到它,我建议只在第二次SSL_shutdown完成后关闭底层套接字。即首先将套接字从SSL完全降级为plain,然后关闭普通套接字。

答案 1 :(得分:1)

  

...如果服务器丢弃FIN包。

我不相信你看到的问题是由于丢失了FIN。我怀疑(作为Steffen)您正在等待客户端的close_notify表单,但客户端没有发送它。


  

if(!SSL_shutdown(m_ssl))...

SSL_shutdown返回-1,0和1;不是0和1。


  

如果调用SSL_shutdown()一次,它就不会挂起,是否对内存和连接资源有任何影响?

我不相信,但在Valgrind或其他检漏仪工具下验证可能会更好。


  

是否有超时机制来控制OpenSSL中的SSL_shutdown()或更好的方法?

我不相信有超时。

您可以使用两种策略:

  1. 您提出请求并且不关心客户close_notify
  2. 您希望遵循该协议,并希望客户做同样的事情
  3. 调用SSL_shutdown一次是策略1.只需关闭套接字即可完成。

    两次调用SSL_shutdown是策略2.问题是,并非所有客户端都发送它。很多时候,他们只是在阅读了他们要求的所有内容之后才关闭套接字。

    您可以通过捕获SIGPIPE来检测第二种情况。如果连接上有SIGPIPE,则客户端关闭套接字。只需拨打SSL_shutdown一次。

    Eric Rescorla在An Introduction to OpenSSL Programming (Part I)更详细地介绍了它。这是他提供的服务器端代码:

    51     r=SSL_shutdown(ssl);
    52     if(!r){
    53         /* If we called SSL_shutdown() first then
    54            we always get return value of ’0’. In
    55            this case, try again, but first send a
    56            TCP FIN to trigger the other side’s
    57            close_notify */
    58         shutdown(s,1);
    59         r=SSL_shutdown(ssl);
    60     }
    61
    62     switch(r){
    63     case 1:
    64         break; /* Success */
    65     case 0:
    66     case -1:
    67     default:
    68         berr_exit("Shutdown failed");
    69     }