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()); }