I have googled the world, while not found related fully functioned
code. Here is my solution:
1 set underlying socket fd to none-blocking
2 SSL_connect
3 if return "SSL_ERROR_WANT_WRITE" or "SSL_ERROR_WANT_READ", select
4 if not time out, set socket fd back to blocking and SSL_connect
again
Here are my questions:
1 is there any potential risk in my code?
2 I only meet "SSL_ERROR_WANT_READ" when SSL_connect by none-blocking,
is "SSL_ERROR_WANT_WRITE" and "SSL_ERROR_NONE" possible?
3 It seems internally SSL records the connect states for the twice
SSL_connect, so, when multithreading, can SSL work properly by this
code?
Thank you for your help!
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/** If the socket has a timeout, do a select() on the socket.
* The argument writing indicates the direction.
* Return non-zero if the socket timed out, zero otherwise.
*/
static int wait_select_timeout(int sock_fd, int sock_timeout, int
writing)
{
fd_set fds;
struct timeval tv;
int rc;
/* Nothing to do unless we're in timeout mode (not non-blocking) */
if (sock_timeout <= 0.0)
return 0;
/* Guard against closed socket */
if (sock_fd < 0)
return 0;
/* Construct the arguments to select */
tv.tv_sec = sock_timeout;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock_fd, &fds);
/* See if the socket is ready */
if (writing)
rc = select(sock_fd+1, NULL, &fds, NULL, &tv);
else
rc = select(sock_fd+1, &fds, NULL, NULL, &tv);
/* Return 1 on timeout, 0 otherwise */
return rc == 0;
}
void TmSecureHandler::setTimeOut(unsigned int nSeconds)
{
m_timeout=nSeconds;
}
/**
* only do the SSL handshake.
* if ok, return 1.
* if fail, return 0.
*/
int TmSecureHandler::connect()
{
assert(this != NULL);
if (m_lastError != ERR_SSL_NOERRORS)
return 0;
#ifdef WIN32
/* set to non-blocking mode */
unsigned long ulArgp = 1;
if(SOCKET_ERROR == ioctlsocket(m_fd, FIONBIO, (unsigned
long*)&ulArgp))
{
goto err_return;
}
#else
int fflag; /* file flag for fcntl */
/* get original sock flag */
if ((fflag = fcntl(m_fd, F_GETFL, 0)) < 0)
{
goto err_return;
}
/* set O_NONBLOCK(non-blocking) flag to sock */
if (fcntl(m_fd, F_SETFL, fflag|O_NONBLOCK) < 0)
{
goto err_return;
}
#endif
int ret, err, isTimeOut;
ret = SSL_connect(m_ssl);
err = SSL_get_error(m_ssl, ret);
if (err == SSL_ERROR_WANT_READ)
{
isTimeOut = wait_select_timeout(m_fd, m_timeout, 0);
} else if (err == SSL_ERROR_WANT_WRITE)
{
isTimeOut = wait_select_timeout(m_fd, m_timeout, 1);
} else if (err == SSL_ERROR_NONE)
{
goto ok_return;
} else
{
goto err_return;
}
if (isTimeOut)
{
goto timeout_return;
}
else
{
#ifdef WIN32
ulArgp = 0 ;
if(SOCKET_ERROR == ioctlsocket(m_fd, FIONBIO, (unsigned
long*)&ulArgp))
{
goto err_return;
}
#else
if (fcntl(m_fd, F_SETFL, fflag) < 0)
{
goto err_return;
}
#endif
if (1 != SSL_connect(m_ssl))
{
goto err_return;
}
}
ok_return:
return 1;
timeout_return:
m_lastError = ERR_SSL_TIMEOUT;
return 0;
err_return:
m_lastError = ERR_SSL_CONNECT;
return 0;
}