非阻塞握手SSL_ERROR_SYSCALL出错

时间:2017-06-01 13:27:38

标签: c linux ssl openssl

我写了一个支持SSL的嵌入式web服务器,可以在linux上运行。它运作得很好。除了一件事。

SSL_accept()上,它返回-1(对于OpenSSL 1.1.0)或0(对于OpenSSL 1.0.2),SSL_get_error()返回SSL_ERROR_SYSCALL

在我孤立的测试环境中,即使我发送了300万个请求,我也没有任何问题。但是当在AWS(相同的CentOS 7)或连接到互联网的另一台VM上运行时,我经常看到这些问题。当发生这种情况时,应用程序也会挂起几秒钟。

这是我的听力线程的相关代码部分:

int myPort = 443;
struct sockaddr_in serverSockAddr;
memset(&serverSockAddr,0,sizeof(serverSockAddr));

serverSockAddr.sin_family = AF_INET;
serverSockAddr.sin_port = htons(myPort);
serverSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

int tcpListenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (tcpListenSocket != INVALID_SOCKET)
{
    int val = 1;
    setsockopt(tcpListenSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val));
    if (bind(tcpListenSocket,(struct sockaddr *)&serverSockAddr,sizeof(serverSockAddr)) == 0)
    {
        if (listen(tcpListenSocket,128) != 0) // Allow 128 backlog connections
        {
            printf("Failed to listen on port: %d\n",myPort);
            tcpListenSocket = INVALID_SOCKET;
        }
    }
    else
    {
        printf("Not able to bind port: %d (%d-%s)\n",myPort,errno,strerror(errno));
        tcpListenSocket = INVALID_SOCKET;
    }
}

if (tcpListenSocket != INVALID_SOCKET)
{
    printf("TCP Listen Thread: Socket for port %d bound\n",myPort);
    printf("TCP Listen Thread: Waiting for incoming data\n");

    // Hostname or IP Adress of client who sent the request. Allocate here to avoid reallocation on stack on each accept.
    char clientHostName[NI_MAXHOST];
    clientHostName[0] = '\0';

    while (_tcpListenShutdown == 0)
    {
        // wait for incoming data
        int pendingConnection = 0;
        do
        {
            struct timeval tv;
            fd_set descriptor;

            tv.tv_sec = 0;
            tv.tv_usec = 75000; // 75ms
            FD_ZERO(&descriptor);
            FD_SET(tcpListenSocket,&descriptor);

            pendingConnection = select(tcpListenSocket+1,&descriptor,NULL,NULL,&tv);                        
        }while((_tcpListenShutdown == 0) && (pendingConnection == 0));

        if (pendingConnection > 0)
        {
            struct sockaddr_in clientSockAddr;
            ovsocklen_t clientAddrLen = sizeof(clientSockAddr);
            memset(&clientSockAddr,0,sizeof(clientSockAddr));

            int clientSocket = accept(tcpListenSocket,(struct sockaddr *)&clientSockAddr,&clientAddrLen);
            if (clientSocket != INVALID_SOCKET)
            {
                // find hostname who originates connection
                getnameinfo((struct sockaddr *)&clientSockAddr,sizeof(clientSockAddr),clientHostName,sizeof(clientHostName),NULL,0,0);

                // If resolving failed use IP address
                if (clientHostName[0] == '\0')
                    strlcpy(clientHostName,inet_ntoa(clientSockAddr.sin_addr),sizeof(clientHostName));

                int clientPort = ntohs(clientSockAddr.sin_port);
                printf("TCP Listen Thread: Received connection from host '%s:%d' on clientsocket:'%d'\n",clientHostName,clientPort,clientSocket);

                SSL *sslSession = NULL;
                // create SSL Session
                int error = 1;
                sslSession = SSL_new(_sslContext);
                if (sslSession)
                {
                    // hook to our own client socket
                    SSL_set_fd(sslSession,clientSocket);

                    printf("TCP Listen Thread('%s:%d'): Accepting on SSL session\n",clientHostName,clientPort);
                    // SSL_Accept() might need to be repeated if socket is non blocking
                    while (error)
                    {
                        int acceptResult = SSL_accept(sslSession);
                        if (acceptResult == 1)
                            error = 0;
                        else
                        {
                            int sslError = SSL_get_error(sslSession,acceptResult);
                            if ((sslError == SSL_ERROR_WANT_READ) || (sslError == SSL_ERROR_WANT_WRITE))
                                continue;
                            else
                            {
                                printf("TCP Listen Thread('%s:%d'): Could not accept SSL session(%d/%d) (%d-%s)",clientHostName,clientPort,acceptResult,sslError,errno,strerror(errno));
                                break;
                            }
                        }
                    }
                }
                else
                    printf("TCP Listen Thread('%s:%d'): Could not generate SSL session\n",clientHostName,clientPort);

                // create new thread for handling new connection
                if (error == 0)
                {
                    /// SSL Handshake complete => Handle request
                }
                else
                {
                    printf("TCP Listen Thread('%s:%d'): Dropping connection due to errors.\n",clientHostName,clientPort);
                    if (sslSession)
                        SSL_free(sslSession);
                    shutdown(clientSocket,SHUT_RDWR);
                }
            }
        }
        else
        {
            if (_tcpListenShutdown == 0)
                printf("TCP Listen Thread: Lost connection => exiting\n");
        }
    }

示例错误消息:

  

TCP听   螺纹( 'ec2-54-248-220-38.ap-northeast-1.compute.amazonaws.com:43507'):   无法接受SSL会话(-1/5)(0成功)

-1是SSL_accept()的结果 5是SSL_get_error()的结果,表示SSL_ERROR_SYSCALL 其余的是errno及其字符串表示。

错误消息通常出现在3到15条相同类型但来自不同主机的消息中。看起来这个线程会阻塞一段时间,当它继续时,出现的消息代表了无法及时完成的​​连接尝试。

有任何想法可以隔离并修复此问题吗? 谢谢你的帮助!

0 个答案:

没有答案