是否有可能强制OpenSSL在没有全局信号处理程序更改的情况下不生成SIGPIPE?

时间:2014-08-05 17:09:05

标签: c++ c sockets openssl

我编写的静态库引用了静态OpenSSL库。 OpenSSL有时会生成SIGPIPE并崩溃程序。我知道可以使用signal函数全局禁用SIGPIPE。但是,对于静态库,这样的要求并不优雅(与可以在初始化函数中调用signal的共享库不同)。此外,不需要在程序中进行全局更改的方法要好得多,因为谁知道可能是任何其他库需要SIGPIPE,并且会与需要忽略此信号的库冲突。我认为良好的做法是仅改变BIO而不改变其他任何东西。 OpenSSL使用套接字,套接字send函数包含良好的解决方案(MSG_NOSIGNAL标志)。 OpenSSL有没有类似的解决方案?有没有办法以这样的方式设置OpenSSL BIO,它不会生成SIGPIPE而不会导致整个程序崩溃?

3 个答案:

答案 0 :(得分:2)

OpenSSL本身不会生成SIGPIPE。但是,OpenSSL使用套接字,如果您写入另一端关闭的套接字,则会生成SIGPIPE。如果您不想这样做,则必须处理SIGPIPE(例如将其设置为SIG_IGN)。这将导致写入返回EPIPE错误。

因为此行为不是特定于OpenSSL,而是所有套接字都通用,并且因为这是一个全局设置,所以不应在库中更改它。您可以在文档中提及它,但只有在您希望用户不熟悉套接字的标准行为时才应该这样做。

答案 1 :(得分:1)

  

OpenSSL有时会生成SIGPIPE并崩溃程序

我不相信它的OpenSSL;相反,它是操作系统响应对等体以“脏”的方式关闭SSL套接字。

Eric Rescorla触及它An Introduction to OpenSSL Programming (Part I)

  

当我们发送close_notify时,另一方可能会发送TCP RST   段,在这种情况下,程序将捕获SIGPIPE。我们安装   SIGPIPE中的虚拟initialize_ctx()处理程序,以防范。{1}}   这个问题。

我不同意“除了记录它之外什么都不做”。如果RTFM会起作用,那么它现在就会发生。它的确定崩溃应用程序并声明答案在手册的某个地方。您应该开箱即用,并记录如何更改行为。

如果程序没有自行安装,可能应该安装SIGPIPE处理程序。如果程序安装了一个,那么你可能应该提供一个代码路径回到你的库中,以便在它发生时通知你(如果你需要的话)。

您可以使用以下内容测试现有的SIGPIPE处理程序(如果不存在,则安装一个)。我在Debug版本中使用类似的方法来安装SIGTRAP处理程序,因此我的断言不会使我正在调试的程序崩溃。 (完全覆盖断言创建自调试代码。我很少花时间在调试器下,因为代码告诉我问题在哪里)。

struct SigPipeHandler
{
  SigPipeHandler()
  {
    // http://pubs.opengroup.org/onlinepubs/007908799/xsh/sigaction.html
    struct sigaction old_handler, new_handler={ };

    do
      {
        int ret = 0;

        ret = sigaction (SIGPIPE, NULL, &old_handler);
        if (ret != 0) break; // Failed

        // Don't step on another's handler
        if (old_handler.sa_handler != NULL) break;

        // Set up the structure to specify the null action.
        new_handler.sa_handler = &SigPipeHandler::NullSigPipeHandler;
        new_handler.sa_flags = 0;

        ret = sigemptyset (&new_handler.sa_mask);
        if (ret != 0) break; // Failed

        // Install it
        ret = sigaction (SIGPIPE, &new_handler, NULL);
        if (ret != 0) break; // Failed

      } while(0);
  }

  static void NullSigPipeHandler(int /*unused*/) { }
}

答案 2 :(得分:1)

这取决于您如何在套接字上设置SSL。如果您使用SSL_set_fd,则可以阻止使用setsocketoptSO_NOSIGPIPE生成SIG_PIPE。