OpenSSL and signals

OpenSSL and signals

本文关键字:signals and OpenSSL      更新时间:2023-10-16

sendreceive等Socket功能可被信号中断。因此,需要一些额外的代码,例如检查errno == EINTR。这在相应的手册页中有描述。

我想知道当使用OpenSSL函数时如何工作,例如SSL_write, SSL_read。它们的手册页没有提到任何关于信号的内容。我也试着在谷歌上搜索这个,但没有运气。你知道OpenSSL是内部处理信号,还是需要一些额外的代码?如果是,如何检查函数调用是否被信号中断?

更新:

看起来OpenSSL不处理重试。它只在BIO对象上设置"应该重试"标志。所以我需要使用这样的东西来检测调用是否被中断,是否需要重试:

int result = SSL_write(ssl, buff, length);
if ((result < 0) && BIO_should_retry(SSL_get_wbio(ssl)))
    // need to retry
int result = SSL_read(ssl, buff, length);
if ((result < 0) && BIO_should_retry(SSL_get_rbio(ssl)))
    // need to retry

作为一个库,OpenSSL不能对使用它的特定程序如何处理信号做出任何假设。因此,它需要处理接收到信号时系统调用被中断的情况。

检查重试失败的read()是否有意义在OpenSSL中实现。

如果BIO_read()(或BIO_write())失败,则使用BIO_should_retry()

示例如下:http://cvs.openssl.org/rlog?f=openssl/demos/bio/sconnect.c

实现的相关代码如下:

static int sock_read(BIO *b, char *out, int outl)
    {
    int ret=0;
    if (out != NULL)
            {
            clear_socket_error();
            ret=readsocket(b->num,out,outl);
            BIO_clear_retry_flags(b);
            if (ret <= 0)
                    {
                    if (BIO_sock_should_retry(ret))
                            BIO_set_retry_read(b);
                    }
            }
    return(ret);
    }

相关的引用函数在这里:

(get_last_socket_error()在大多数IXish平台上返回errno)

int BIO_sock_should_retry(int i)
    {
    int err;
    if ((i == 0) || (i == -1))
            {
            err=get_last_socket_error();
#if defined(OPENSSL_SYS_WINDOWS) && 0 /* more microsoft stupidity? perhaps not? Ben 4/1/99 */
            if ((i == -1) && (err == 0))
                    return(1);
#endif
            return(BIO_sock_non_fatal_error(err));
            }
    return(0);
    }

int BIO_sock_non_fatal_error(int err)
        {
        switch (err)
                {
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_NETWARE)
# if defined(WSAEWOULDBLOCK)
        case WSAEWOULDBLOCK:
# endif
# if 0 /* This appears to always be an error */
#  if defined(WSAENOTCONN)
        case WSAENOTCONN:
#  endif
# endif
#endif
#ifdef EWOULDBLOCK
# ifdef WSAEWOULDBLOCK
#  if WSAEWOULDBLOCK != EWOULDBLOCK
        case EWOULDBLOCK:
#  endif
# else
        case EWOULDBLOCK:
# endif
#endif
#if defined(ENOTCONN)
        case ENOTCONN:
#endif
#ifdef EINTR
        case EINTR:
#endif
#ifdef EAGAIN
# if EWOULDBLOCK != EAGAIN
        case EAGAIN:
# endif
#endif
#ifdef EPROTO
        case EPROTO:
#endif
#ifdef EINPROGRESS
        case EINPROGRESS:
#endif
#ifdef EALREADY
        case EALREADY:
#endif
                return(1);
                /* break; */
        default:
                break;
                }
        return(0);
        }

详情见sock_read()待检:http://cvs.openssl.org/rlog?f=openssl/crypto/bio/bss_sock.c

(引用的来源以及使用的缩进来自1.0.1e版)

如果一个信号中断了导致它返回错误的系统调用,那么调用SSL函数将返回一个错误指示(例如-1),并且SSL_get_error可以根据SSL的状态返回任意数量的错误。

最有可能的是,它将返回SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITESSL_ERROR_SYSCALL(希望errno中有EINTR)。其他错误,如SSL_ERROR_WANT_CONNECTSSL_ERROR_WANT_ACCEPTSSL_ERROR_WANT_X509_LOOKUPSSL_ERROR_SSL,都是可能的,具体取决于您在做什么。

对于OpenSSL v1.0.2j, SSL_get_error返回的错误通常取决于SSL结构的读写状态及其底层BIOs。通常SSL_readSSL_ERROR_WANT_READ, SSL_writeSSL_ERROR_WANT_WRITE。然而,在初始握手期间,可能在重新协商期间(这可能发生在对SSL_readSSL_write的正常调用中),当低级调用被信号中断时,SSL_get_error看起来可以返回SSL_ERROR_SYSCALL, EINTR应该在errno中。此外,在重新协商过程中,SSL_read产生SSL_ERROR_WANT_WRITE, SSL_write产生SSL_ERROR_WANT_READ的可能性始终存在。

我不建议您尝试像在更新中那样直接咨询底层BIOs。相反,请查看SSL_get_error,如果这是一个非致命错误,则重试或采取其他适当的操作。