Index: lib/http.c =================================================================== RCS file: /cvsroot/curl/curl/lib/http.c,v retrieving revision 1.278 diff -u -r1.278 http.c --- lib/http.c 11 Feb 2006 22:35:17 -0000 1.278 +++ lib/http.c 14 Mar 2006 17:46:22 -0000 @@ -1370,14 +1370,7 @@ if(CURLE_OK != 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) { /* this is not a followed location, get the original host name */ if (data->state.first_host) @@ -1387,8 +1380,62 @@ 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 { + 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; +} + +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; + } + 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; } Index: lib/http.h =================================================================== RCS file: /cvsroot/curl/curl/lib/http.h,v retrieving revision 1.28 diff -u -r1.28 http.h --- lib/http.h 3 Jul 2005 22:25:15 -0000 1.28 +++ lib/http.h 14 Mar 2006 17:46:22 -0000 @@ -37,6 +37,11 @@ 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); Index: lib/sslgen.c =================================================================== RCS file: /cvsroot/curl/curl/lib/sslgen.c,v retrieving revision 1.6 diff -u -r1.6 sslgen.c --- lib/sslgen.c 11 Aug 2005 21:41:11 -0000 1.6 +++ lib/sslgen.c 14 Mar 2006 17:46:22 -0000 @@ -215,6 +215,22 @@ #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 /* @@ -536,4 +552,3 @@ #endif /* USE_GNUTLS */ #endif /* USE_SSLEAY */ } - Index: lib/sslgen.h =================================================================== RCS file: /cvsroot/curl/curl/lib/sslgen.h,v retrieving revision 1.1 diff -u -r1.1 sslgen.h --- lib/sslgen.h 7 Apr 2005 15:27:14 -0000 1.1 +++ lib/sslgen.h 14 Mar 2006 17:46:22 -0000 @@ -32,6 +32,7 @@ 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) */ Index: lib/ssluse.c =================================================================== RCS file: /cvsroot/curl/curl/lib/ssluse.c,v retrieving revision 1.142 diff -u -r1.142 ssluse.c --- lib/ssluse.c 13 Mar 2006 23:34:25 -0000 1.142 +++ lib/ssluse.c 14 Mar 2006 17:46:22 -0000 @@ -1116,23 +1116,21 @@ #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); @@ -1296,135 +1294,150 @@ ERR_error_string(ERR_get_error(),NULL)); return CURLE_SSL_CONNECT_ERROR; } + + connssl->connecting_state = ssl_connect_2; + return CURLE_OK; +} - 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; - } +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]; + + 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. + 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 - /* no particular time-out has been set */ - timeout_ms= DEFAULT_CONNECT_TIMEOUT; + *timeout_ms = data->set.connecttimeout*1000; + } + else + /* no particular time-out has been set */ + *timeout_ms= DEFAULT_CONNECT_TIMEOUT; - /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); - /* subtract the passed time */ - timeout_ms -= has_passed; + /* subtract the passed time */ + *timeout_ms -= has_passed; - if(timeout_ms < 0) { - /* a precaution, no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEOUTED; - } + if(*timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + 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 + 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); - /* 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); + if(SSL_ERROR_WANT_READ == detail) { + connssl->connecting_state = ssl_connect_2_reading; + return CURLE_OK; + } + 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; - 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; + 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 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; - } - - /* detail is already set to the SSL error above */ + 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 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 */ + /* detail is already set to the SSL error above */ - SSL_strerror(errdetail, error_buffer, sizeof(error_buffer)); - failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); + /* 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 */ + + 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, get out of the connect loop */ - break; + } + 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)); - 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 { - /* 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() */ - } /* while()-loop for the SSL_connect() */ + return CURLE_OK; + } +} - /* Informational message */ - infof (data, "SSL connection using %s\n", - SSL_get_cipher(connssl->handle)); +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(!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 +1542,107 @@ 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]; + + if (ssl_connect_1==connssl->connecting_state) { + retcode = Curl_ossl_connect_step1(conn, sockindex); + if (retcode) + return retcode; + } + + while (ssl_connect_2==connssl->connecting_state + || ssl_connect_2_reading==connssl->connecting_state + || ssl_connect_2_writing==connssl->connecting_state) { + + long timeout_ms; /* avoid computing the timeout twice. */ + retcode = Curl_ossl_connect_step2(conn, sockindex, &timeout_ms); + if (retcode) + return retcode; + + /* is ssl expecting something? */ + 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() */ + } + }; /* 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, Index: lib/ssluse.h =================================================================== RCS file: /cvsroot/curl/curl/lib/ssluse.h,v retrieving revision 1.23 diff -u -r1.23 ssluse.h --- lib/ssluse.h 7 Apr 2005 15:27:14 -0000 1.23 +++ lib/ssluse.h 14 Mar 2006 17:46:22 -0000 @@ -29,6 +29,7 @@ #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) */ Index: lib/url.c =================================================================== RCS file: /cvsroot/curl/curl/lib/url.c,v retrieving revision 1.498 diff -u -r1.498 url.c --- lib/url.c 7 Mar 2006 23:11:42 -0000 1.498 +++ lib/url.c 14 Mar 2006 17:46:23 -0000 @@ -2990,7 +2990,9 @@ 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 " was built with SSL disabled, https: not supported!"); @@ -3876,6 +3878,9 @@ if(connected) { result = Curl_protocol_connect(conn, protocol_done); + while (CURLE_OK == result && !protocol_done) { + result = Curl_protocol_connecting(conn, protocol_done); + } if(CURLE_OK == result) conn->bits.tcpconnect = TRUE; } Index: lib/urldata.h =================================================================== RCS file: /cvsroot/curl/curl/lib/urldata.h,v retrieving revision 1.281 diff -u -r1.281 urldata.h --- lib/urldata.h 7 Mar 2006 23:11:42 -0000 1.281 +++ lib/urldata.h 14 Mar 2006 17:46:23 -0000 @@ -135,6 +135,18 @@ }; #endif +/* enum for the nonblocking SSL connection state machine */ +#ifdef USE_SSLEAY +typedef enum { + ssl_connect_1 = 0, + ssl_connect_2, + ssl_connect_2_reading, + ssl_connect_2_writing, + ssl_connect_3, + ssl_connect_done +} ssl_connect_state; +#endif + /* struct for data related to each SSL connection */ struct ssl_connect_data { bool use; /* use ssl encrypted communications TRUE/FALSE */ @@ -143,6 +155,7 @@ SSL_CTX* ctx; SSL* handle; X509* server_cert; + ssl_connect_state connecting_state; #endif /* USE_SSLEAY */ #ifdef USE_GNUTLS gnutls_session session;