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
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)

View File

@ -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)

View File

@ -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);
}
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.

View File

@ -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);

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
* 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
/*

View File

@ -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) */

View File

@ -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,11 +1295,22 @@ Curl_ossl_connect(struct connectdata *conn,
return CURLE_SSL_CONNECT_ERROR;
}
while(1) {
int writefd;
int readfd;
long timeout_ms;
connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
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
@ -1312,29 +1321,26 @@ Curl_ossl_connect(struct connectdata *conn,
/* 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;
*timeout_ms = data->set.timeout*1000;
else
timeout_ms = data->set.connecttimeout*1000;
*timeout_ms = data->set.connecttimeout*1000;
}
else
/* 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 */
has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
/* 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 */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEOUTED;
}
readfd = CURL_SOCKET_BAD;
writefd = CURL_SOCKET_BAD;
err = SSL_connect(connssl->handle);
/* 1 is fine
@ -1343,10 +1349,14 @@ Curl_ossl_connect(struct connectdata *conn,
if(1 != err) {
int detail = SSL_get_error(connssl->handle, err);
if(SSL_ERROR_WANT_READ == detail)
readfd = sockfd;
else if(SSL_ERROR_WANT_WRITE == detail)
writefd = sockfd;
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;
@ -1355,6 +1365,10 @@ Curl_ossl_connect(struct connectdata *conn,
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. */
@ -1398,33 +1412,33 @@ Curl_ossl_connect(struct connectdata *conn,
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 {
/* 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() */
/* 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));
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
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,

View File

@ -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) */

View File

@ -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

View File

@ -135,6 +135,16 @@ enum protection_level {
};
#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;