Xavier Bouchoux made the SSL connection non-blocking for the multi interface

(when using OpenSSL).
This commit is contained in:
Daniel Stenberg
2006-03-21 21:54:44 +00:00
parent 15f2647d71
commit 83367f67de
10 changed files with 345 additions and 132 deletions

View File

@@ -7,6 +7,9 @@
Changelog Changelog
Daniel (21 March 2006) Daniel (21 March 2006)
- Xavier Bouchoux made the SSL connection non-blocking for the multi interface
(when using OpenSSL).
- Tor Arntsen fixed the AIX Toolbox RPM spec - Tor Arntsen fixed the AIX Toolbox RPM spec
Daniel (20 March 2006) Daniel (20 March 2006)

View File

@@ -11,7 +11,7 @@ Curl and libcurl 7.15.4
This release includes the following changes: This release includes the following changes:
o o less blocking for the multi interface during SSL connect negotiation
This release includes the following bugfixes: This release includes the following bugfixes:
@@ -27,6 +27,6 @@ Other curl-related news since the previous public release:
This release would not have looked like this without help, code, reports and This release would not have looked like this without help, code, reports and
advice from friends like these: advice from friends like these:
Dan Fandrich, Ilja van Sprundel, David McCreedy, Tor Arntsen Dan Fandrich, Ilja van Sprundel, David McCreedy, Tor Arntsen, Xavier Bouchoux
Thanks! (and sorry if I forgot to mention someone) Thanks! (and sorry if I forgot to mention someone)

View File

@@ -1371,13 +1371,6 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
return result; return result;
} }
if(conn->protocol & PROT_HTTPS) {
/* perform SSL initialization for this socket */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result)
return result;
}
if(!data->state.this_is_a_follow) { if(!data->state.this_is_a_follow) {
/* this is not a followed location, get the original host name */ /* this is not a followed location, get the original host name */
if (data->state.first_host) if (data->state.first_host)
@@ -1387,11 +1380,68 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
data->state.first_host = strdup(conn->host.name); data->state.first_host = strdup(conn->host.name);
} }
if(conn->protocol & PROT_HTTPS) {
/* perform SSL initialization */
if(data->state.used_interface == Curl_if_multi) {
result = Curl_https_connecting(conn, done);
if(result)
return result;
}
else {
/* BLOCKING */
result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result)
return result;
*done = TRUE; *done = TRUE;
}
}
else {
*done = TRUE;
}
return CURLE_OK; return CURLE_OK;
} }
CURLcode Curl_https_connecting(struct connectdata *conn, bool *done)
{
CURLcode result;
curlassert(conn->protocol & PROT_HTTPS);
/* perform SSL initialization for this socket */
result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
if(result)
return result;
return CURLE_OK;
}
#ifdef USE_SSLEAY
CURLcode Curl_https_proto_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
{
if (conn->protocol & PROT_HTTPS) {
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
if (connssl->connecting_state == ssl_connect_2_writing) {
/* write mode */
FD_SET(sockfd, write_fd_set);
if((int)sockfd > *max_fdp)
*max_fdp = (int)sockfd;
}
else if (connssl->connecting_state == ssl_connect_2_reading) {
/* read mode */
FD_SET(sockfd, read_fd_set);
if((int)sockfd > *max_fdp)
*max_fdp = (int)sockfd;
}
}
return CURLE_OK;
}
#endif
/* /*
* Curl_http_done() gets called from Curl_done() after a single HTTP request * Curl_http_done() gets called from Curl_done() after a single HTTP request
* has been performed. * has been performed.

View File

@@ -37,6 +37,11 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
CURLcode Curl_http(struct connectdata *conn, bool *done); CURLcode Curl_http(struct connectdata *conn, bool *done);
CURLcode Curl_http_done(struct connectdata *, CURLcode); CURLcode Curl_http_done(struct connectdata *, CURLcode);
CURLcode Curl_http_connect(struct connectdata *conn, bool *done); CURLcode Curl_http_connect(struct connectdata *conn, bool *done);
CURLcode Curl_https_connecting(struct connectdata *conn, bool *done);
CURLcode Curl_https_proto_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp);
/* The following functions are defined in http_chunks.c */ /* The following functions are defined in http_chunks.c */
void Curl_httpchunk_init(struct connectdata *conn); void Curl_httpchunk_init(struct connectdata *conn);

View File

@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@@ -215,6 +215,23 @@ Curl_ssl_connect(struct connectdata *conn, int sockindex)
#endif /* USE_SSL */ #endif /* USE_SSL */
} }
CURLcode
Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
bool *done)
{
#if defined(USE_SSL) && defined(USE_SSLEAY)
/* mark this is being ssl enabled from here on. */
conn->ssl[sockindex].use = TRUE;
return Curl_ossl_connect_nonblocking(conn, sockindex, done);
#else
/* not implemented!
fallback to BLOCKING call. */
*done = TRUE;
return Curl_ssl_connect(conn, sockindex);
#endif
}
#ifdef USE_SSL #ifdef USE_SSL
/* /*

View File

@@ -32,6 +32,9 @@ void Curl_free_ssl_config(struct ssl_config_data* sslc);
int Curl_ssl_init(void); int Curl_ssl_init(void);
void Curl_ssl_cleanup(void); void Curl_ssl_cleanup(void);
CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex); CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex);
CURLcode Curl_ssl_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done);
void Curl_ssl_close(struct connectdata *conn); void Curl_ssl_close(struct connectdata *conn);
/* tell the SSL stuff to close down all open information regarding /* tell the SSL stuff to close down all open information regarding
connections (and thus session ID caching etc) */ connections (and thus session ID caching etc) */

View File

@@ -1116,23 +1116,21 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
/* ====================================================== */ /* ====================================================== */
CURLcode
Curl_ossl_connect(struct connectdata *conn, static CURLcode
Curl_ossl_connect_step1(struct connectdata *conn,
int sockindex) int sockindex)
{ {
CURLcode retcode = CURLE_OK; CURLcode retcode = CURLE_OK;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
int err;
long lerr;
int what;
char * str;
SSL_METHOD_QUAL SSL_METHOD *req_method=NULL; SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
void *ssl_sessionid=NULL; void *ssl_sessionid=NULL;
ASN1_TIME *certdate;
curl_socket_t sockfd = conn->sock[sockindex]; curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curlassert(ssl_connect_1 == connssl->connecting_state);
if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
/* Make funny stuff to get random input */ /* Make funny stuff to get random input */
random_the_seed(data); random_the_seed(data);
@@ -1297,11 +1295,22 @@ Curl_ossl_connect(struct connectdata *conn,
return CURLE_SSL_CONNECT_ERROR; return CURLE_SSL_CONNECT_ERROR;
} }
while(1) { connssl->connecting_state = ssl_connect_2;
int writefd; return CURLE_OK;
int readfd; }
long timeout_ms;
static CURLcode
Curl_ossl_connect_step2(struct connectdata *conn,
int sockindex, long* timeout_ms)
{
struct SessionHandle *data = conn->data;
int err;
long has_passed; long has_passed;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curlassert(ssl_connect_2 == connssl->connecting_state
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
/* Find out if any timeout is set. If not, use 300 seconds. /* Find out if any timeout is set. If not, use 300 seconds.
Otherwise, figure out the most strict timeout of the two possible one Otherwise, figure out the most strict timeout of the two possible one
@@ -1312,29 +1321,26 @@ Curl_ossl_connect(struct connectdata *conn,
/* get the most strict timeout of the ones converted to milliseconds */ /* get the most strict timeout of the ones converted to milliseconds */
if(data->set.timeout && if(data->set.timeout &&
(data->set.timeout>data->set.connecttimeout)) (data->set.timeout>data->set.connecttimeout))
timeout_ms = data->set.timeout*1000; *timeout_ms = data->set.timeout*1000;
else else
timeout_ms = data->set.connecttimeout*1000; *timeout_ms = data->set.connecttimeout*1000;
} }
else else
/* no particular time-out has been set */ /* no particular time-out has been set */
timeout_ms= DEFAULT_CONNECT_TIMEOUT; *timeout_ms= DEFAULT_CONNECT_TIMEOUT;
/* Evaluate in milliseconds how much time that has passed */ /* Evaluate in milliseconds how much time that has passed */
has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
/* subtract the passed time */ /* subtract the passed time */
timeout_ms -= has_passed; *timeout_ms -= has_passed;
if(timeout_ms < 0) { if(*timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */ /* a precaution, no need to continue if time already is up */
failf(data, "SSL connection timeout"); failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEOUTED; return CURLE_OPERATION_TIMEOUTED;
} }
readfd = CURL_SOCKET_BAD;
writefd = CURL_SOCKET_BAD;
err = SSL_connect(connssl->handle); err = SSL_connect(connssl->handle);
/* 1 is fine /* 1 is fine
@@ -1343,10 +1349,14 @@ Curl_ossl_connect(struct connectdata *conn,
if(1 != err) { if(1 != err) {
int detail = SSL_get_error(connssl->handle, err); int detail = SSL_get_error(connssl->handle, err);
if(SSL_ERROR_WANT_READ == detail) if(SSL_ERROR_WANT_READ == detail) {
readfd = sockfd; connssl->connecting_state = ssl_connect_2_reading;
else if(SSL_ERROR_WANT_WRITE == detail) return CURLE_OK;
writefd = sockfd; }
else if(SSL_ERROR_WANT_WRITE == detail) {
connssl->connecting_state = ssl_connect_2_writing;
return CURLE_OK;
}
else { else {
/* untreated error */ /* untreated error */
unsigned long errdetail; unsigned long errdetail;
@@ -1355,6 +1365,10 @@ Curl_ossl_connect(struct connectdata *conn,
CURLcode rc; CURLcode rc;
const char *cert_problem = NULL; const char *cert_problem = NULL;
connssl->connecting_state = ssl_connect_2; /* the connection failed,
we're not waiting for
anything else. */
errdetail = ERR_get_error(); /* Gets the earliest error code from the errdetail = ERR_get_error(); /* Gets the earliest error code from the
thread's error queue and removes the thread's error queue and removes the
entry. */ entry. */
@@ -1398,33 +1412,33 @@ Curl_ossl_connect(struct connectdata *conn,
return rc; return rc;
} }
} }
else
/* we have been connected fine, get out of the connect loop */
break;
while(1) {
what = Curl_select(readfd, writefd, (int)timeout_ms);
if(what > 0)
/* reabable or writable, go loop in the outer loop */
break;
else if(0 == what) {
/* timeout */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
else { else {
/* anything that gets here is fatally bad */ /* we have been connected fine, we're not waiting for anything else. */
failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); connssl->connecting_state = ssl_connect_3;
return CURLE_SSL_CONNECT_ERROR;
}
} /* while()-loop for the select() */
} /* while()-loop for the SSL_connect() */
/* Informational message */ /* Informational message */
infof (data, "SSL connection using %s\n", infof (data, "SSL connection using %s\n",
SSL_get_cipher(connssl->handle)); SSL_get_cipher(connssl->handle));
if(!ssl_sessionid) { return CURLE_OK;
}
}
static CURLcode
Curl_ossl_connect_step3(struct connectdata *conn,
int sockindex)
{
CURLcode retcode = CURLE_OK;
char * str;
long lerr;
ASN1_TIME *certdate;
void *ssl_sessionid=NULL;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curlassert(ssl_connect_3 == connssl->connecting_state);
if(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
/* Since this is not a cached session ID, then we want to stach this one /* Since this is not a cached session ID, then we want to stach this one
in the cache! */ in the cache! */
SSL_SESSION *ssl_sessionid; SSL_SESSION *ssl_sessionid;
@@ -1529,9 +1543,114 @@ Curl_ossl_connect(struct connectdata *conn,
X509_free(connssl->server_cert); X509_free(connssl->server_cert);
connssl->server_cert = NULL; connssl->server_cert = NULL;
connssl->connecting_state = ssl_connect_done;
return retcode; return retcode;
} }
static CURLcode
Curl_ossl_connect_common(struct connectdata *conn,
int sockindex,
bool nonblocking,
bool *done)
{
CURLcode retcode;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
long timeout_ms;
if (ssl_connect_1==connssl->connecting_state) {
retcode = Curl_ossl_connect_step1(conn, sockindex);
if (retcode)
return retcode;
}
timeout_ms = 0;
while (ssl_connect_2 == connssl->connecting_state ||
ssl_connect_2_reading == connssl->connecting_state ||
ssl_connect_2_writing == connssl->connecting_state) {
/* if ssl is expecting something, check if it's available. */
if (connssl->connecting_state == ssl_connect_2_reading
|| connssl->connecting_state == ssl_connect_2_writing) {
int writefd = ssl_connect_2_writing==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
int readfd = ssl_connect_2_reading==
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
while(1) {
int what = Curl_select(readfd, writefd, nonblocking?0:(int)timeout_ms);
if(what > 0)
/* reabable or writable, go loop in the outer loop */
break;
else if(0 == what) {
if (nonblocking) {
*done = FALSE;
return CURLE_OK;
}
else {
/* timeout */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
}
else {
/* anything that gets here is fatally bad */
failf(data, "select on SSL socket, errno: %d", Curl_ourerrno());
return CURLE_SSL_CONNECT_ERROR;
}
} /* while()-loop for the select() */
}
/* get the timeout from step2 to avoid computing it twice. */
retcode = Curl_ossl_connect_step2(conn, sockindex, &timeout_ms);
if (retcode)
return retcode;
} /* repeat step2 until all transactions are done. */
if (ssl_connect_3==connssl->connecting_state) {
retcode = Curl_ossl_connect_step3(conn, sockindex);
if (retcode)
return retcode;
}
if (ssl_connect_done==connssl->connecting_state) {
*done = TRUE;
}
else {
*done = FALSE;
}
return CURLE_OK;
}
CURLcode
Curl_ossl_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done)
{
return Curl_ossl_connect_common(conn, sockindex, TRUE, done);
}
CURLcode
Curl_ossl_connect(struct connectdata *conn,
int sockindex)
{
CURLcode retcode;
bool done = FALSE;
retcode = Curl_ossl_connect_common(conn, sockindex, FALSE, &done);
if (retcode)
return retcode;
curlassert(done);
return CURLE_OK;
}
/* return number of sent (non-SSL) bytes */ /* return number of sent (non-SSL) bytes */
int Curl_ossl_send(struct connectdata *conn, int Curl_ossl_send(struct connectdata *conn,
int sockindex, int sockindex,

View File

@@ -29,6 +29,9 @@
#include "urldata.h" #include "urldata.h"
CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex); CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex);
CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done);
void Curl_ossl_close(struct connectdata *conn); /* close a SSL connection */ void Curl_ossl_close(struct connectdata *conn); /* close a SSL connection */
/* tell OpenSSL to close down all open information regarding connections (and /* tell OpenSSL to close down all open information regarding connections (and
thus session ID caching etc) */ thus session ID caching etc) */

View File

@@ -2990,6 +2990,8 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_do_more = NULL; conn->curl_do_more = NULL;
conn->curl_done = Curl_http_done; conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect; conn->curl_connect = Curl_http_connect;
conn->curl_connecting = Curl_https_connecting;
conn->curl_proto_fdset = Curl_https_proto_fdset;
#else /* USE_SS */ #else /* USE_SS */
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME

View File

@@ -135,6 +135,16 @@ enum protection_level {
}; };
#endif #endif
/* enum for the nonblocking SSL connection state machine */
typedef enum {
ssl_connect_1,
ssl_connect_2,
ssl_connect_2_reading,
ssl_connect_2_writing,
ssl_connect_3,
ssl_connect_done
} ssl_connect_state;
/* struct for data related to each SSL connection */ /* struct for data related to each SSL connection */
struct ssl_connect_data { struct ssl_connect_data {
bool use; /* use ssl encrypted communications TRUE/FALSE */ bool use; /* use ssl encrypted communications TRUE/FALSE */
@@ -143,6 +153,7 @@ struct ssl_connect_data {
SSL_CTX* ctx; SSL_CTX* ctx;
SSL* handle; SSL* handle;
X509* server_cert; X509* server_cert;
ssl_connect_state connecting_state;
#endif /* USE_SSLEAY */ #endif /* USE_SSLEAY */
#ifdef USE_GNUTLS #ifdef USE_GNUTLS
gnutls_session session; gnutls_session session;