aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2006-03-21 21:54:44 +0000
committerDaniel Stenberg <daniel@haxx.se>2006-03-21 21:54:44 +0000
commit83367f67de9584b91570bcb53a153b8aa496d455 (patch)
tree83fe48b1331c3b40653ea85ec56b663093feae12
parent15f2647d713b2de6d76424ef83e5dd00cfd3403d (diff)
Xavier Bouchoux made the SSL connection non-blocking for the multi interface
(when using OpenSSL).
-rw-r--r--CHANGES3
-rw-r--r--RELEASE-NOTES4
-rw-r--r--lib/http.c66
-rw-r--r--lib/http.h5
-rw-r--r--lib/sslgen.c19
-rw-r--r--lib/sslgen.h3
-rw-r--r--lib/ssluse.c357
-rw-r--r--lib/ssluse.h3
-rw-r--r--lib/url.c2
-rw-r--r--lib/urldata.h19
10 files changed, 347 insertions, 134 deletions
diff --git a/CHANGES b/CHANGES
index c139ade72..9fa1ff566 100644
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,9 @@
Changelog
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
Daniel (20 March 2006)
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index 7ef611926..780643ec3 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -11,7 +11,7 @@ Curl and libcurl 7.15.4
This release includes the following changes:
- o
+ o less blocking for the multi interface during SSL connect negotiation
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
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)
diff --git a/lib/http.c b/lib/http.c
index e15054f57..a7db903fd 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -1371,13 +1371,6 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
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) {
/* this is not a followed location, get the original host name */
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);
}
- *done = TRUE;
+ 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;
+ }
+ }
+ else {
+ *done = TRUE;
+ }
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
* has been performed.
diff --git a/lib/http.h b/lib/http.h
index 599d067c0..bc459f5d3 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -37,6 +37,11 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn,
CURLcode Curl_http(struct connectdata *conn, bool *done);
CURLcode Curl_http_done(struct connectdata *, CURLcode);
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 */
void Curl_httpchunk_init(struct connectdata *conn);
diff --git a/lib/sslgen.c b/lib/sslgen.c
index d7d1259f3..a4c941050 100644
--- a/lib/sslgen.c
+++ b/lib/sslgen.c
@@ -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
* 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 */
}
+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
/*
diff --git a/lib/sslgen.h b/lib/sslgen.h
index 3e1dfa0a6..6b63c3688 100644
--- a/lib/sslgen.h
+++ b/lib/sslgen.h
@@ -32,6 +32,9 @@ void Curl_free_ssl_config(struct ssl_config_data* sslc);
int Curl_ssl_init(void);
void Curl_ssl_cleanup(void);
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);
/* tell the SSL stuff to close down all open information regarding
connections (and thus session ID caching etc) */
diff --git a/lib/ssluse.c b/lib/ssluse.c
index 0ec216a3f..7be5a7cad 100644
--- a/lib/ssluse.c
+++ b/lib/ssluse.c
@@ -1116,23 +1116,21 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
#ifdef USE_SSLEAY
/* ====================================================== */
-CURLcode
-Curl_ossl_connect(struct connectdata *conn,
+
+static CURLcode
+Curl_ossl_connect_step1(struct connectdata *conn,
int sockindex)
{
CURLcode retcode = CURLE_OK;
struct SessionHandle *data = conn->data;
- int err;
- long lerr;
- int what;
- char * str;
SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
void *ssl_sessionid=NULL;
- ASN1_TIME *certdate;
curl_socket_t sockfd = conn->sock[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) {
/* Make funny stuff to get random input */
random_the_seed(data);
@@ -1297,134 +1295,150 @@ Curl_ossl_connect(struct connectdata *conn,
return CURLE_SSL_CONNECT_ERROR;
}
- while(1) {
- int writefd;
- int readfd;
- long timeout_ms;
- long has_passed;
-
- /* Find out if any timeout is set. If not, use 300 seconds.
- Otherwise, figure out the most strict timeout of the two possible one
- and then how much time that has elapsed to know how much time we
- allow for the connect call */
- if(data->set.timeout || data->set.connecttimeout) {
-
- /* get the most strict timeout of the ones converted to milliseconds */
- if(data->set.timeout &&
- (data->set.timeout>data->set.connecttimeout))
- timeout_ms = data->set.timeout*1000;
- else
- timeout_ms = data->set.connecttimeout*1000;
- }
- else
- /* no particular time-out has been set */
- timeout_ms= DEFAULT_CONNECT_TIMEOUT;
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+}
- /* Evaluate in milliseconds how much time that has passed */
- has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
+static CURLcode
+Curl_ossl_connect_step2(struct connectdata *conn,
+ int sockindex, long* timeout_ms)
+{
+ struct SessionHandle *data = conn->data;
+ int err;
+ long has_passed;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- /* subtract the passed time */
- timeout_ms -= has_passed;
+ curlassert(ssl_connect_2 == connssl->connecting_state
+ || ssl_connect_2_reading == connssl->connecting_state
+ || ssl_connect_2_writing == connssl->connecting_state);
- if(timeout_ms < 0) {
- /* a precaution, no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEOUTED;
- }
+ /* Find out if any timeout is set. If not, use 300 seconds.
+ Otherwise, figure out the most strict timeout of the two possible one
+ and then how much time that has elapsed to know how much time we
+ allow for the connect call */
+ if(data->set.timeout || data->set.connecttimeout) {
- readfd = CURL_SOCKET_BAD;
- writefd = CURL_SOCKET_BAD;
+ /* get the most strict timeout of the ones converted to milliseconds */
+ if(data->set.timeout &&
+ (data->set.timeout>data->set.connecttimeout))
+ *timeout_ms = data->set.timeout*1000;
+ else
+ *timeout_ms = data->set.connecttimeout*1000;
+ }
+ else
+ /* no particular time-out has been set */
+ *timeout_ms= DEFAULT_CONNECT_TIMEOUT;
- err = SSL_connect(connssl->handle);
+ /* Evaluate in milliseconds how much time that has passed */
+ has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
- /* 1 is fine
- 0 is "not successful but was shut down controlled"
- <0 is "handshake was not successful, because a fatal error occurred" */
- if(1 != err) {
- int detail = SSL_get_error(connssl->handle, err);
+ /* subtract the passed time */
+ *timeout_ms -= has_passed;
- if(SSL_ERROR_WANT_READ == detail)
- readfd = sockfd;
- else if(SSL_ERROR_WANT_WRITE == detail)
- writefd = sockfd;
- else {
- /* untreated error */
- unsigned long errdetail;
- char error_buffer[120]; /* OpenSSL documents that this must be at least
- 120 bytes long. */
- CURLcode rc;
- const char *cert_problem = NULL;
-
- errdetail = ERR_get_error(); /* Gets the earliest error code from the
- thread's error queue and removes the
- entry. */
-
- switch(errdetail) {
- case 0x1407E086:
- /* 1407E086:
- SSL routines:
- SSL2_SET_CERTIFICATE:
- certificate verify failed */
- /* fall-through */
- case 0x14090086:
- /* 14090086:
- SSL routines:
- SSL3_GET_SERVER_CERTIFICATE:
- certificate verify failed */
- cert_problem = "SSL certificate problem, verify that the CA cert is"
- " OK. Details:\n";
- rc = CURLE_SSL_CACERT;
- break;
- default:
- rc = CURLE_SSL_CONNECT_ERROR;
- break;
- }
+ if(*timeout_ms < 0) {
+ /* a precaution, no need to continue if time already is up */
+ failf(data, "SSL connection timeout");
+ return CURLE_OPERATION_TIMEOUTED;
+ }
- /* detail is already set to the SSL error above */
+ err = SSL_connect(connssl->handle);
- /* If we e.g. use SSLv2 request-method and the server doesn't like us
- * (RST connection etc.), OpenSSL gives no explanation whatsoever and
- * the SO_ERROR is also lost.
- */
- if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
- failf(data, "Unknown SSL protocol error in connection to %s:%d ",
- conn->host.name, conn->port);
- return rc;
- }
- /* Could be a CERT problem */
+ /* 1 is fine
+ 0 is "not successful but was shut down controlled"
+ <0 is "handshake was not successful, because a fatal error occurred" */
+ if(1 != err) {
+ int detail = SSL_get_error(connssl->handle, err);
- SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
- failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
- return rc;
- }
+ if(SSL_ERROR_WANT_READ == detail) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
}
- 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 */
+ else if(SSL_ERROR_WANT_WRITE == detail) {
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+ }
+ else {
+ /* untreated error */
+ unsigned long errdetail;
+ char error_buffer[120]; /* OpenSSL documents that this must be at least
+ 120 bytes long. */
+ CURLcode rc;
+ 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
+ thread's error queue and removes the
+ entry. */
+
+ switch(errdetail) {
+ case 0x1407E086:
+ /* 1407E086:
+ SSL routines:
+ SSL2_SET_CERTIFICATE:
+ certificate verify failed */
+ /* fall-through */
+ case 0x14090086:
+ /* 14090086:
+ SSL routines:
+ SSL3_GET_SERVER_CERTIFICATE:
+ certificate verify failed */
+ cert_problem = "SSL certificate problem, verify that the CA cert is"
+ " OK. Details:\n";
+ rc = CURLE_SSL_CACERT;
+ break;
+ default:
+ rc = CURLE_SSL_CONNECT_ERROR;
break;
- else if(0 == what) {
- /* 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;
+
+ /* detail is already set to the SSL error above */
+
+ /* If we e.g. use SSLv2 request-method and the server doesn't like us
+ * (RST connection etc.), OpenSSL gives no explanation whatsoever and
+ * the SO_ERROR is also lost.
+ */
+ if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
+ failf(data, "Unknown SSL protocol error in connection to %s:%d ",
+ conn->host.name, conn->port);
+ return rc;
}
- } /* while()-loop for the select() */
- } /* while()-loop for the SSL_connect() */
+ /* Could be a CERT problem */
+
+ SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
+ failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
+ return rc;
+ }
+ }
+ else {
+ /* we have been connected fine, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_3;
+
+ /* Informational message */
+ infof (data, "SSL connection using %s\n",
+ SSL_get_cipher(connssl->handle));
+
+ 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];
- /* Informational message */
- infof (data, "SSL connection using %s\n",
- SSL_get_cipher(connssl->handle));
+ curlassert(ssl_connect_3 == connssl->connecting_state);
- if(!ssl_sessionid) {
+ if(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
/* Since this is not a cached session ID, then we want to stach this one
in the cache! */
SSL_SESSION *ssl_sessionid;
@@ -1529,9 +1543,114 @@ Curl_ossl_connect(struct connectdata *conn,
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
+ connssl->connecting_state = ssl_connect_done;
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 */
int Curl_ossl_send(struct connectdata *conn,
int sockindex,
diff --git a/lib/ssluse.h b/lib/ssluse.h
index 00345317d..1bbc2cc1c 100644
--- a/lib/ssluse.h
+++ b/lib/ssluse.h
@@ -29,6 +29,9 @@
#include "urldata.h"
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 */
/* tell OpenSSL to close down all open information regarding connections (and
thus session ID caching etc) */
diff --git a/lib/url.c b/lib/url.c
index 9a715c9f0..38a2a27da 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -2990,6 +2990,8 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_do_more = NULL;
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
+ conn->curl_connecting = Curl_https_connecting;
+ conn->curl_proto_fdset = Curl_https_proto_fdset;
#else /* USE_SS */
failf(data, LIBCURL_NAME
diff --git a/lib/urldata.h b/lib/urldata.h
index 6cb3729b9..1418f3e92 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -128,13 +128,23 @@ struct krb4buffer {
int eof_flag;
};
enum protection_level {
- prot_clear,
- prot_safe,
- prot_confidential,
- prot_private
+ prot_clear,
+ prot_safe,
+ prot_confidential,
+ prot_private
};
#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 ssl_connect_data {
bool use; /* use ssl encrypted communications TRUE/FALSE */
@@ -143,6 +153,7 @@ struct ssl_connect_data {
SSL_CTX* ctx;
SSL* handle;
X509* server_cert;
+ ssl_connect_state connecting_state;
#endif /* USE_SSLEAY */
#ifdef USE_GNUTLS
gnutls_session session;