Hi all,

I have a daemon where I am trying to retrieve the common name from the
certificate during an HTTPS connection. What i am observing is that ,
SSL_Connect() function is taking more time to connect on some of the
websites.

I am trying on Mac OS X 10.8.3. Below is the code I have been using and I
am using non-blocking sockets


#include "SSLUtil.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <openssl/crypto.h>
#include <errno.h>
#include <fcntl.h>
#include <string>
#include "Logging.h"




bool SSLUtil::GetSSLServerNameFromIP(std::string ipAddress, std::string
&serverName)
{
    bool bRet = false;
    int sock=-1;
    MCLOG("SSLUtil::GetSSLServerNameFromIP Call for IP",ipAddress.c_str());
    if(ConnectToServerAsync(ipAddress,443,sock))
    {
        if(RetrieveNameUsingSSL(sock,serverName))
        {
            bRet = true;
            MCLOG("SSLUtil::GetSSLServerNameFromIP Call
return",ipAddress.c_str(),serverName.c_str());
        }
        else
        {
            MCLOG("SSLUtil::GetSSLServerNameFromIP RetrieveNameUsingSSL
call failed",ipAddress.c_str());
        }

        close(sock);
    }else
    {
        MCLOG("SSLUtil::GetSSLServerNameFromIP connect asycn
failed",ipAddress.c_str());
    }
    return bRet;

}

#define TIMEOUT_SERVER 3 //seconds

bool SSLUtil::ConnectToServerAsync(std::string ipaddress, int port , int
&sock)
{

    //create the socket
    sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(sock<0)
    {
        //creating socket failed
        MCLOG("SSLUtil::ConnectToServerAsync soceket creation failed ");
        return false;
    }

    //change socket options
    int opts = fcntl(sock,F_GETFL);
    if (opts < 0) {
        //pFailed F_GETFL
        MCLOG("SSLUtil::ConnectToServerAsync F_GETFL failed ");
        return false;
    }
    opts = (opts | O_NONBLOCK);
    if (fcntl(sock,F_SETFL,opts) < 0) {
        //Failed F_SETFL
        MCLOG("SSLUtil::ConnectToServerAsync F_SETFL failed");
        return false;
    }

    //make sure there is no error pending
    int err = errno;
    if (err < 0)
    {
        MCLOG("SSLUtil::ConnectToServerAsync can't create non blocking
socket ");
        //printf("MakeConnectAttempt = can't set non-blocking socket");
        if(sock)
        {
            close(sock);
        }
        return false;
    }

    struct sockaddr_in server_addr;
    memset (&server_addr, '\0', sizeof(server_addr));
    server_addr.sin_family      = AF_INET;
    server_addr.sin_port        = htons(port);       /* Server Port number
*/
    server_addr.sin_addr.s_addr = inet_addr(ipaddress.c_str()); /* Server
IP     */
    int status = connect(sock, (struct sockaddr*) &server_addr,
sizeof(server_addr));


    //wait for connection to succeed
    if(status<0)
    {
        status = errno;
        if(status == EINPROGRESS)
        {
            MCLOG("SSLUtil::ConnectToServerAsync in progress ",
ipaddress.c_str());
            if(!WaitOnSocket(sock,TIMEOUT_SERVER))
            {
                //printf("connect to %s failed\n",argv[1]);
                MCLOG("SSLUtil::ConnectToServerAsync wait failed ",
ipaddress.c_str());
                return false;
            }

        }else
        {
            //printf("connect to %s failed\n",argv[1]);
            MCLOG("SSLUtil::ConnectToServerAsync connect failed ", status);
            return false;
        }
    }
    MCLOG("SSLUtil::ConnectToServerAsync connect success ", status);
    return true;

}


bool SSLUtil::WaitOnSocket(int &sockfd,int timeOutInSeconds)
{
    fd_set         readFDs;
    fd_set         writeFDs;
    int            selectResult;
    // bool        timeOut = false;

    FD_ZERO(&readFDs);
    FD_ZERO(&writeFDs);
    FD_SET(sockfd, &readFDs);
    FD_SET(sockfd, &writeFDs);
    bool continueSelect ;
    struct timeval waitd;

    time_t seconds;
    time_t future;
    time_t now;

    seconds = time(NULL);
    future = seconds + timeOutInSeconds;
    // waitd.tv_sec = timeOutInSeconds;     // Make select wait up to 10
seconds for data
    // waitd.tv_usec = 0;     // and 0 milliseconds.

    int err = 0;
    continueSelect = true;
    do
    {
        waitd.tv_sec = timeOutInSeconds;     // Make select wait up to 5
seconds for data
        waitd.tv_usec = 0;     // and 0 milliseconds.

        selectResult = select(sockfd+1, &readFDs, &writeFDs, NULL, &waitd);
        //printf("after select %d\n", selectResult);
        err = errno;
        if (selectResult > 0)
        {
            for(int i=0;i<sockfd+1;i++)
            {
                if (FD_ISSET(i,&readFDs))
                {
                    continueSelect = false;
                    break;
                }
                if (FD_ISSET(i,&writeFDs))
                {
                    continueSelect = false;
                    break;
                }
            }
        }
        else
        {
            now = time(NULL);
            if (now > future )
            {
                continueSelect = false;
                MCLOG("timed out");
                break;
            }
            else
            {
                MCLOG("time left",(future - now));
                continueSelect = false;
            }

            //err = errno;
            if (err == EINTR)
                continueSelect = true;

        }
    }
    while (continueSelect);

    FD_ZERO(&readFDs);
    FD_ZERO(&writeFDs);

    if (selectResult > 0 )
    {
        return true;
    }
    MCLOG("**** failed connection and got file descriptor %d\n", sockfd);
    return false;
}


bool SSLUtil::RetrieveNameUsingSSL(int &sock , std::string &serverName)
{
    serverName="";
    SSL_library_init();
    SSL_METHOD *meth=SSLv3_method();
    SSL_CTX *sslctx=SSL_CTX_new(meth);
    if(!sslctx)
    {
        //printf("SSL_CTX_new failed");
        MCLOG("SSLUtil::RetrieveNameUsingSSL SSL_CTX_new failed ");
        //close(sock);
        return false;
    }
    SSL_CTX_set_verify(sslctx,SSL_VERIFY_NONE,NULL);
    SSL *ssl =SSL_new(sslctx);
    if(!ssl)
    {
        //printf("SSL_new failed\n"); close(sock);
        MCLOG("SSLUtil::RetrieveNameUsingSSL SSL_new failed ");
        //exit(4);
        return false;
    }
    int status=SSL_set_fd(ssl,sock);
    if(!status)
    {
        //printf("SSL_set_fd failed\n"); close(sock);
        //exit(5);
        MCLOG("SSLUtil::RetrieveNameUsingSSL SSL_set_fd failed ");
        return false;
    }

    status = SSL_connect(ssl);
    int error=SSL_get_error(ssl,status);
    //printf("Error %d\n",error);
    switch(error)
    {
        case SSL_ERROR_NONE:
            //printf("connect successful\n");
            break;
        case SSL_ERROR_ZERO_RETURN:
            //printf("peer close ssl connection \n");
            break;
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
        {
            time_t seconds;
            time_t future;
            time_t now;

            seconds = time(NULL);
            future = seconds + 2;

            MCLOG("SSLUtil::RetrieveNameUsingSSL Before SSL_ERROR_WANT_READ
& SSL_ERROR_WANT_WRITE");
            while(error == SSL_ERROR_WANT_READ || error ==
SSL_ERROR_WANT_WRITE)
            {
                status = SSL_connect(ssl);
                if(!WaitOnSocket(sock,TIMEOUT_SERVER))
                {
                    MCLOG("WaitOnSocket func failed");
                    break;
                }
                now = time(NULL);
                if(now > future)
                {
                    MCLOG("*** break");
                    break;
                }
                error=SSL_get_error(ssl,status);
                if(error == SSL_ERROR_NONE)
                {
                    MCLOG("SSL_ERROR_NONE");
                    break;
                }
                }
            }
            break;

        default:
            MCLOG("SSLUtil::RetrieveNameUsingSSL failed ",error);
            //printf("connect error is %d\n",error);
            break;
    }

    X509*  server_cert = SSL_get_peer_certificate (ssl);
    if (server_cert != NULL)
    {
        //MessageLog.Write("Server certificate");
        //str = X509_NAME_oneline(X509_get_subject_name(server_cert),0,0);
        X509_NAME * name = X509_get_subject_name(server_cert);
        char    str[512] = {} ;
        X509_NAME_get_text_by_NID(name, NID_commonName, str, 512);

        if(str != NULL)
        {
            serverName = str;
            //MessageLog.Write("Domain name :", str);
            //MessageLog.Write("Successfully fetched the certificate");
        }
        else
        {
            MCLOG("SSLUtil::RetrieveNameUsingSSL server name empty ");
        }

        X509_free (server_cert);
    }
    else
    {
        MCLOG("SSLUtil::RetrieveNameUsingSSL certificate empty ");
    }

    if(ssl)
    {
        error = SSL_shutdown(ssl);
        if(error == -1)
        {
            //MessageLog.Write("Failed to do SSLShutdown");
            MCLOG("SSLUtil::RetrieveNameUsingSSL SSLShutdown failed ");
        }
        // Free the SSL structure
        //MessageLog.Write("free SSL structure");
        SSL_free(ssl);
    }

    // Free the SSL_CTX structure
    if(sslctx)
    {
        SSL_CTX_free(sslctx);
    }

    return (!serverName.empty());

}

Reply via email to