From 09664f1ab8eda36174b91dde4c84537e834892a0 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 1 Aug 2008 00:55:43 +0000 Subject: [PATCH] Refactored create_conn by breaking it up into many smaller functions --- lib/url.c | 1027 ++++++++++++++++++++++++++++------------------------- 1 file changed, 553 insertions(+), 474 deletions(-) diff --git a/lib/url.c b/lib/url.c index abad3ba97..b53cc5053 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2887,6 +2887,9 @@ static bool tld_check_name(struct SessionHandle *data, } #endif +/* + * Perform any necessary IDN conversion of hostname + */ static void fix_hostname(struct SessionHandle *data, struct connectdata *conn, struct hostname *host) { @@ -2926,6 +2929,38 @@ static void fix_hostname(struct SessionHandle *data, #endif } +/* + * Allocate and initialize a new connectdata object. + */ +static struct connectdata *allocate_conn(void) +{ + struct connectdata *conn; + + conn = (struct connectdata *)calloc(1, sizeof(struct connectdata)); + if(!conn) + return NULL; + + conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined + already from start to avoid NULL + situations and checks */ + + /* and we setup a few fields in case we end up actually using this struct */ + + conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->connectindex = -1; /* no index */ + + /* Default protocol-independent behavior doesn't support persistent + connections, so we set this to force-close. Protocols that support + this need to set this to FALSE in their "curl_do" functions. */ + conn->bits.close = TRUE; + + /* Store creation time to help future close decision making */ + conn->created = Curl_tvnow(); + + return conn; +} + /* * Parse URL and fill in the relevant members of the connection struct. */ @@ -3128,12 +3163,12 @@ static void llist_dtor(void *user, void *element) /* Do nothing */ } +/* + * If we're doing a resumed transfer, we need to setup our stuff + * properly. + */ static CURLcode setup_range(struct SessionHandle *data) { - /* - * If we're doing a resumed transfer, we need to setup our stuff - * properly. - */ struct UrlState *s = &data->state; s->resume_from = data->set.set_resume_from; if(s->resume_from || data->set.str[STRING_SET_RANGE]) { @@ -3326,7 +3361,8 @@ static char *detect_proxy(struct connectdata *conn) return proxy; } -/* If this is supposed to use a proxy, we need to figure out the proxy +/* + * If this is supposed to use a proxy, we need to figure out the proxy * host name, so that we can re-use an existing connection * that may exist registered to the same proxy host. * proxy will be freed before this function returns. @@ -3453,7 +3489,9 @@ static CURLcode parse_proxy(struct SessionHandle *data, return CURLE_OK; } -/* Extract the user and password from the authentication string */ +/* + * Extract the user and password from the authentication string + */ static CURLcode parse_proxy_auth(struct SessionHandle *data, struct connectdata *conn) { @@ -3476,314 +3514,23 @@ static CURLcode parse_proxy_auth(struct SessionHandle *data, return CURLE_OK; } -/** - * create_conn() sets up a new connectdata struct, or re-uses an already - * existing one, and resolves host name. +/* * - * if this function returns CURLE_OK and *async is set to TRUE, the resolve - * response will be coming asynchronously. If *async is FALSE, the name is - * already resolved. + * Parse a user name and password in the URL and strip it out of the host name * - * @param data The sessionhandle pointer - * @param in_connect is set to the next connection data pointer - * @param addr is set to the new dns entry for this connection. If this - * connection is re-used it will be NULL. - * @param async is set TRUE/FALSE depending on the nature of this lookup - * @return CURLcode - * @see setup_conn() + * Inputs: data->set.use_netrc (CURLOPT_NETRC) + * conn->host.name * - * *NOTE* this function assigns the conn->data pointer! + * Outputs: (almost :- all currently undefined) + * conn->bits.user_passwd - non-zero if non-default passwords exist + * user - non-zero length if defined + * passwd - ditto + * conn->host.name - remove user name and password */ - -static CURLcode create_conn(struct SessionHandle *data, - struct connectdata **in_connect, - struct Curl_dns_entry **addr, - bool *async) +static CURLcode parse_url_userpass(struct SessionHandle *data, + struct connectdata *conn, + char *user, char *passwd) { - - char *tmp; - CURLcode result=CURLE_OK; - struct connectdata *conn; - struct connectdata *conn_temp = NULL; - size_t urllen; - struct Curl_dns_entry *hostaddr; -#if defined(HAVE_ALARM) && !defined(USE_ARES) - unsigned int prev_alarm=0; -#endif - char endbracket; - char user[MAX_CURL_USER_LENGTH]; - char passwd[MAX_CURL_PASSWORD_LENGTH]; - int rc; - bool reuse; - char *proxy = NULL; - -#ifndef USE_ARES -#ifdef SIGALRM -#ifdef HAVE_SIGACTION - struct sigaction keep_sigact; /* store the old struct here */ - bool keep_copysig=FALSE; /* did copy it? */ -#else -#ifdef HAVE_SIGNAL - void (*keep_sigact)(int); /* store the old handler here */ -#endif /* HAVE_SIGNAL */ -#endif /* HAVE_SIGACTION */ -#endif /* SIGALRM */ -#endif /* USE_ARES */ - - *addr = NULL; /* nothing yet */ - *async = FALSE; - - /************************************************************* - * Check input data - *************************************************************/ - - if(!data->change.url) - return CURLE_URL_MALFORMAT; - - /* First, split up the current URL in parts so that we can use the - parts for checking against the already present connections. In order - to not have to modify everything at once, we allocate a temporary - connection data struct and fill in for comparison purposes. */ - - conn = (struct connectdata *)calloc(1, sizeof(struct connectdata)); - if(!conn) { - *in_connect = NULL; /* clear the pointer */ - return CURLE_OUT_OF_MEMORY; - } - /* We must set the return variable as soon as possible, so that our - parent can cleanup any possible allocs we may have done before - any failure */ - *in_connect = conn; - - conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined - already from start to avoid NULL - situations and checks */ - - /* and we setup a few fields in case we end up actually using this struct */ - - conn->data = data; /* Setup the association between this connection - and the SessionHandle */ - - conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ - conn->connectindex = -1; /* no index */ - - conn->proxytype = data->set.proxytype; /* type */ - conn->bits.proxy = (bool)(data->set.str[STRING_PROXY] && - *data->set.str[STRING_PROXY]); - conn->bits.httpproxy = (bool)(conn->bits.proxy - && (conn->proxytype == CURLPROXY_HTTP)); - - - /* Default protocol-independent behavior doesn't support persistent - connections, so we set this to force-close. Protocols that support - this need to set this to FALSE in their "curl_do" functions. */ - conn->bits.close = TRUE; - - conn->readchannel_inuse = FALSE; - conn->writechannel_inuse = FALSE; - - conn->read_pos = 0; - conn->buf_len = 0; - - /* Store creation time to help future close decision making */ - conn->created = Curl_tvnow(); - - conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERPWD]); - conn->bits.proxy_user_passwd = (bool)(NULL != data->set.str[STRING_PROXYUSERPWD]); - conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; - conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; - conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; - - if(data->multi && Curl_multi_canPipeline(data->multi) && - !conn->master_buffer) { - /* Allocate master_buffer to be used for pipelining */ - conn->master_buffer = calloc(BUFSIZE, sizeof (char)); - if(!conn->master_buffer) - return CURLE_OUT_OF_MEMORY; - } - - /* Initialize the pipeline lists */ - conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe) - return CURLE_OUT_OF_MEMORY; - - /* This initing continues below, see the comment "Continue connectdata - * initialization here" */ - - /*********************************************************** - * We need to allocate memory to store the path in. We get the size of the - * full URL to be sure, and we need to make it at least 256 bytes since - * other parts of the code will rely on this fact - ***********************************************************/ -#define LEAST_PATH_ALLOC 256 - urllen=strlen(data->change.url); - if(urllen < LEAST_PATH_ALLOC) - urllen=LEAST_PATH_ALLOC; - - /* Free the old buffer */ - Curl_safefree(data->state.pathbuffer); - - /* - * We malloc() the buffers below urllen+2 to make room for to possibilities: - * 1 - an extra terminating zero - * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used) - */ - - data->state.pathbuffer=(char *)malloc(urllen+2); - if(NULL == data->state.pathbuffer) - return CURLE_OUT_OF_MEMORY; /* really bad error */ - data->state.path = data->state.pathbuffer; - - conn->host.rawalloc=(char *)malloc(urllen+2); - if(NULL == conn->host.rawalloc) - return CURLE_OUT_OF_MEMORY; - - conn->host.name = conn->host.rawalloc; - conn->host.name[0] = 0; - - result = ParseURLAndFillConnection(data, conn); - if(result != CURLE_OK) { - return result; - } - - /************************************************************* - * Take care of proxy authentication stuff - *************************************************************/ - if(conn->bits.proxy_user_passwd) { - result = parse_proxy_auth(data, conn); - if(result != CURLE_OK) - return result; - } - - /************************************************************* - * Detect what (if any) proxy to use - *************************************************************/ - if(data->set.str[STRING_PROXY]) { - proxy = strdup(data->set.str[STRING_PROXY]); - /* if global proxy is set, this is it */ - if(NULL == proxy) { - failf(data, "memory shortage"); - return CURLE_OUT_OF_MEMORY; - } - } - - if(!proxy) - proxy = detect_proxy(conn); - if(proxy && !*proxy) { - free(proxy); /* Don't bother with an empty proxy string */ - proxy = NULL; - } - /* proxy must be freed later unless NULL */ - - /************************************************************* - * No protocol part in URL was used, add it! - *************************************************************/ - if(conn->protocol&PROT_MISSING) { - /* We're guessing prefixes here and if we're told to use a proxy or if - we're gonna follow a Location: later or... then we need the protocol - part added so that we have a valid URL. */ - char *reurl; - - reurl = aprintf("%s://%s", conn->protostr, data->change.url); - - if(!reurl) { - Curl_safefree(proxy); - return CURLE_OUT_OF_MEMORY; - } - - data->change.url = reurl; - data->change.url_alloc = TRUE; /* free this later */ - conn->protocol &= ~PROT_MISSING; /* switch that one off again */ - } - - /************************************************************* - * Setup internals depending on protocol - *************************************************************/ - result = setup_connection_internals(data, conn); - if(result != CURLE_OK) { - Curl_safefree(proxy); - return result; - } - - - /*********************************************************************** - * If this is supposed to use a proxy, we need to figure out the proxy - * host name, so that we can re-use an existing connection - * that may exist registered to the same proxy host. - ***********************************************************************/ - if(proxy) { - result = parse_proxy(data, conn, proxy); - /* parse_proxy has freed the proxy string, so don't try to use it again */ - proxy = NULL; - if(result != CURLE_OK) - return result; - } - - - /*********************************************************************** - * file: is a special case in that it doesn't need a network connection - ***********************************************************************/ -#ifndef CURL_DISABLE_FILE - if(conn->protocol & PROT_FILE) { - bool done; - /* this is supposed to be the connect function so we better at least check - that the file is present here! */ - DEBUGASSERT(conn->handler->connect_it); - result = conn->handler->connect_it(conn, &done); - - /* Setup a "faked" transfer that'll do nothing */ - if(CURLE_OK == result) { - conn->data = data; - conn->bits.tcpconnect = TRUE; /* we are "connected */ - - ConnectionStore(data, conn); - - result = setup_range(data); - if(result) { - DEBUGASSERT(conn->handler->done); - /* we ignore the return code for the protocol-specific DONE */ - (void)conn->handler->done(conn, result, FALSE); - return result; - } - - result = Curl_setup_transfer(conn, -1, -1, FALSE, - NULL, /* no download */ - -1, NULL); /* no upload */ - } - - return result; - } -#endif - - /************************************************************* - * If the protocol is using SSL and HTTP proxy is used, we set - * the tunnel_proxy bit. - *************************************************************/ - if((conn->protocol&PROT_SSL) && conn->bits.httpproxy) - conn->bits.tunnel_proxy = TRUE; - - /************************************************************* - * Take care of user and password authentication stuff - *************************************************************/ - - /* - * Inputs: data->set.userpwd (CURLOPT_USERPWD) - * data->set.fpasswd (CURLOPT_PASSWDFUNCTION) - * data->set.use_netrc (CURLOPT_NETRC) - * conn->host.name - * netrc file - * hard-coded defaults - * - * Outputs: (almost :- all currently undefined) - * conn->bits.user_passwd - non-zero if non-default passwords exist - * conn->user - non-zero length if defined - * conn->passwd - ditto - * conn->host.name - remove user name and password - */ - /* At this point, we're hoping all the other special cases have * been taken care of, so conn->host.name is at most * [user[:password]]@]hostname @@ -3830,7 +3577,7 @@ static CURLcode create_conn(struct SessionHandle *data, char *newname=curl_easy_unescape(data, user, 0, NULL); if(!newname) return CURLE_OUT_OF_MEMORY; - if(strlen(newname) < sizeof(user)) + if(strlen(newname) < MAX_CURL_USER_LENGTH) strcpy(user, newname); /* if the new name is longer than accepted, then just use @@ -3842,7 +3589,7 @@ static CURLcode create_conn(struct SessionHandle *data, char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL); if(!newpasswd) return CURLE_OUT_OF_MEMORY; - if(strlen(newpasswd) < sizeof(passwd)) + if(strlen(newpasswd) < MAX_CURL_PASSWORD_LENGTH) strcpy(passwd, newpasswd); free(newpasswd); @@ -3850,38 +3597,48 @@ static CURLcode create_conn(struct SessionHandle *data, } } } + return CURLE_OK; +} + +/************************************************************* + * Figure out the remote port number and fix it in the URL + * + * No matter if we use a proxy or not, we have to figure out the remote + * port number of various reasons. + * + * To be able to detect port number flawlessly, we must not confuse them + * IPv6-specified addresses in the [0::1] style. (RFC2732) + * + * The conn->host.name is currently [user:passwd@]host[:port] where host + * could be a hostname, IPv4 address or IPv6 address. + * + * The port number embedded in the URL is replaced, if necessary. + *************************************************************/ +static CURLcode parse_remote_port(struct SessionHandle *data, + struct connectdata *conn) +{ + char *portptr; + char endbracket; - /************************************************************* - * Figure out the remote port number - * - * No matter if we use a proxy or not, we have to figure out the remote - * port number of various reasons. - * - * To be able to detect port number flawlessly, we must not confuse them - * IPv6-specified addresses in the [0::1] style. (RFC2732) - * - * The conn->host.name is currently [user:passwd@]host[:port] where host - * could be a hostname, IPv4 address or IPv6 address. - *************************************************************/ if((1 == sscanf(conn->host.name, "[%*39[0123456789abcdefABCDEF:.%]%c", &endbracket)) && (']' == endbracket)) { /* this is a RFC2732-style specified IP-address */ conn->bits.ipv6_ip = TRUE; conn->host.name++; /* skip over the starting bracket */ - tmp = strchr(conn->host.name, ']'); - *tmp++ = 0; /* zero terminate, killing the bracket */ - if(':' != *tmp) - tmp = NULL; /* no port number available */ + portptr = strchr(conn->host.name, ']'); + *portptr++ = 0; /* zero terminate, killing the bracket */ + if(':' != *portptr) + portptr = NULL; /* no port number available */ } else - tmp = strrchr(conn->host.name, ':'); + portptr = strrchr(conn->host.name, ':'); if(data->set.use_port && data->state.allow_port) { /* if set, we use this and ignore the port possibly given in the URL */ conn->remote_port = (unsigned short)data->set.use_port; - if(tmp) - *tmp = '\0'; /* cut off the name there anyway - if there was a port + if(portptr) + *portptr = '\0'; /* cut off the name there anyway - if there was a port number - since the port number is to be ignored! */ if(conn->bits.httpproxy) { /* we need to create new URL with the new port number */ @@ -3908,15 +3665,15 @@ static CURLcode create_conn(struct SessionHandle *data, data->change.url_alloc = TRUE; } } - else if(tmp) { + else if(portptr) { /* no CURLOPT_PORT given, extract the one from the URL */ char *rest; unsigned long port; - port=strtoul(tmp+1, &rest, 10); /* Port number must be decimal */ + port=strtoul(portptr+1, &rest, 10); /* Port number must be decimal */ - if(rest != (tmp+1) && *rest == '\0') { + if(rest != (portptr+1) && *rest == '\0') { /* The colon really did have only digits after it, * so it is either a port number or a mistake */ @@ -3926,20 +3683,21 @@ static CURLcode create_conn(struct SessionHandle *data, return CURLE_URL_MALFORMAT; } - *tmp = '\0'; /* cut off the name there */ + *portptr = '\0'; /* cut off the name there */ conn->remote_port = (unsigned short)port; } } + return CURLE_OK; +} - /* Programmatically set password: - * - always applies, if available - * - takes precedence over the values we just set above - * so scribble it over the top. - * User-supplied passwords are assumed not to need unescaping. - * - * user_password is set in "inherit initial knowledge' above, - * so it doesn't have to be set in this block - */ +/* + * Override a user name and password from the URL with that in the + * CURLOPT_USERPWD option or a .netrc file, if applicable. + */ +static void override_userpass(struct SessionHandle *data, + struct connectdata *conn, + char *user, char *passwd) +{ if(data->set.str[STRING_USERPWD] != NULL) { /* the name is given, get user+password */ sscanf(data->set.str[STRING_USERPWD], @@ -3953,8 +3711,8 @@ static CURLcode create_conn(struct SessionHandle *data, if(Curl_parsenetrc(conn->host.name, user, passwd, data->set.str[STRING_NETRC_FILE])) { - infof(data, "Couldn't find host %s in the " DOT_CHAR - "netrc file, using defaults\n", + infof(data, "Couldn't find host %s in the " + DOT_CHAR "netrc file; using defaults\n", conn->host.name); } else { @@ -3966,7 +3724,14 @@ static CURLcode create_conn(struct SessionHandle *data, conn->bits.user_passwd = 1; /* enable user+password */ } } +} +/* + * Set password so it's available in the connection. + */ +static CURLcode set_userpass(struct connectdata *conn, + const char *user, const char *passwd) +{ /* If our protocol needs a password and we have none, use the defaults */ if( (conn->protocol & PROT_FTP) && !conn->bits.user_passwd) { @@ -3983,135 +3748,34 @@ static CURLcode create_conn(struct SessionHandle *data, if(!conn->user || !conn->passwd) return CURLE_OUT_OF_MEMORY; - /************************************************************* - * Check the current list of connections to see if we can - * re-use an already existing one or if we have to create a - * new one. - *************************************************************/ + return CURLE_OK; +} - /* Get a cloned copy of the SSL config situation stored in the - connection struct. But to get this going nicely, we must first make - sure that the strings in the master copy are pointing to the correct - strings in the session handle strings array! +/************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ +static CURLcode resolve_server(struct SessionHandle *data, + struct connectdata *conn, + struct Curl_dns_entry **addr, + bool *async) +{ + CURLcode result=CURLE_OK; +#ifndef USE_ARES +#ifdef SIGALRM +#ifdef HAVE_SIGACTION + struct sigaction keep_sigact; /* store the old struct here */ + bool keep_copysig=FALSE; /* did copy it? */ +#else +#ifdef HAVE_SIGNAL + void (*keep_sigact)(int); /* store the old handler here */ +#endif /* HAVE_SIGNAL */ +#endif /* HAVE_SIGACTION */ +#endif /* SIGALRM */ +#endif /* USE_ARES */ - Keep in mind that the pointers in the master copy are pointing to strings - that will be freed as part of the SessionHandle struct, but all cloned - copies will be separately allocated. - */ - data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH]; - data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE]; - data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE]; - data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; - data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; - - if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config)) - return CURLE_OUT_OF_MEMORY; - - /* reuse_fresh is TRUE if we are told to use a new connection by force, but - we only acknowledge this option if this is not a re-used connection - already (which happens due to follow-location or during a HTTP - authentication phase). */ - if(data->set.reuse_fresh && !data->state.this_is_a_follow) - reuse = FALSE; - else - reuse = ConnectionExists(data, conn, &conn_temp); - - if(reuse) { - /* - * We already have a connection for this, we got the former connection - * in the conn_temp variable and thus we need to cleanup the one we - * just allocated before we can move along and use the previously - * existing one. - */ - struct connectdata *old_conn = conn; - - if(old_conn->proxy.rawalloc) - free(old_conn->proxy.rawalloc); - - /* free the SSL config struct from this connection struct as this was - allocated in vain and is targeted for destruction */ - Curl_free_ssl_config(&conn->ssl_config); - - conn = conn_temp; /* use this connection from now on */ - - conn->data = old_conn->data; - - /* get the user+password information from the old_conn struct since it may - * be new for this request even when we re-use an existing connection */ - conn->bits.user_passwd = old_conn->bits.user_passwd; - if(conn->bits.user_passwd) { - /* use the new user namd and password though */ - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - conn->user = old_conn->user; - conn->passwd = old_conn->passwd; - old_conn->user = NULL; - old_conn->passwd = NULL; - } - - conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; - if(conn->bits.proxy_user_passwd) { - /* use the new proxy user name and proxy password though */ - Curl_safefree(conn->proxyuser); - Curl_safefree(conn->proxypasswd); - conn->proxyuser = old_conn->proxyuser; - conn->proxypasswd = old_conn->proxypasswd; - old_conn->proxyuser = NULL; - old_conn->proxypasswd = NULL; - } - - /* host can change, when doing keepalive with a proxy ! */ - if(conn->bits.proxy) { - free(conn->host.rawalloc); - conn->host=old_conn->host; - } - else - free(old_conn->host.rawalloc); /* free the newly allocated name buffer */ - - /* re-use init */ - conn->bits.reuse = TRUE; /* yes, we're re-using here */ - - Curl_safefree(old_conn->user); - Curl_safefree(old_conn->passwd); - Curl_safefree(old_conn->proxyuser); - Curl_safefree(old_conn->proxypasswd); - Curl_llist_destroy(old_conn->send_pipe, NULL); - Curl_llist_destroy(old_conn->recv_pipe, NULL); - Curl_llist_destroy(old_conn->pend_pipe, NULL); - Curl_safefree(old_conn->master_buffer); - - free(old_conn); /* we don't need this anymore */ - - *in_connect = conn; /* return this instead! */ - - infof(data, "Re-using existing connection! (#%ld) with host %s\n", - conn->connectindex, - conn->proxy.name?conn->proxy.dispname:conn->host.dispname); - } - else { - /* - * This is a brand new connection, so let's store it in the connection - * cache of ours! - */ - ConnectionStore(data, conn); - } - - result = setup_range(data); - if(result) - return result; - - /* Continue connectdata initialization here. */ - - /* - * - * Inherit the proper values from the urldata struct AFTER we have arranged - * the persistent connection stuff */ - conn->fread_func = data->set.fread_func; - conn->fread_in = data->set.in; - conn->seek_func = data->set.seek_func; - conn->seek_client = data->set.seek_client; +#if defined(HAVE_ALARM) && !defined(USE_ARES) + unsigned int prev_alarm=0; +#endif #ifndef USE_ARES /************************************************************* @@ -4183,7 +3847,7 @@ static CURLcode create_conn(struct SessionHandle *data, *************************************************************/ if(conn->bits.reuse) { /* re-used connection, no resolving is necessary */ - hostaddr = NULL; + *addr = NULL; /* we'll need to clear conn->dns_entry later in Curl_disconnect() */ if(conn->bits.proxy) @@ -4191,6 +3855,8 @@ static CURLcode create_conn(struct SessionHandle *data, } else { /* this is a fresh connect */ + int rc; + struct Curl_dns_entry *hostaddr; /* set a pointer to the hostname we display */ fix_hostname(data, conn, &conn->host); @@ -4229,8 +3895,8 @@ static CURLcode create_conn(struct SessionHandle *data, /* don't return yet, we need to clean up the timeout first */ } } + *addr = hostaddr; } - *addr = hostaddr; #if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES) if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) { @@ -4274,6 +3940,419 @@ static CURLcode create_conn(struct SessionHandle *data, alarm(0); /* just shut it off */ } #endif + return result; +} + +/* + * Cleanup the connection just allocated before we can move along and use the + * previously existing one. All relevant data is copied over and old_conn is + * ready for freeing once this function returns. + */ +static void reuse_conn(struct connectdata *old_conn, + struct connectdata *conn) +{ + if(old_conn->proxy.rawalloc) + free(old_conn->proxy.rawalloc); + + /* free the SSL config struct from this connection struct as this was + allocated in vain and is targeted for destruction */ + Curl_free_ssl_config(&old_conn->ssl_config); + + conn->data = old_conn->data; + + /* get the user+password information from the old_conn struct since it may + * be new for this request even when we re-use an existing connection */ + conn->bits.user_passwd = old_conn->bits.user_passwd; + if(conn->bits.user_passwd) { + /* use the new user name and password though */ + Curl_safefree(conn->user); + Curl_safefree(conn->passwd); + conn->user = old_conn->user; + conn->passwd = old_conn->passwd; + old_conn->user = NULL; + old_conn->passwd = NULL; + } + + conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; + if(conn->bits.proxy_user_passwd) { + /* use the new proxy user name and proxy password though */ + Curl_safefree(conn->proxyuser); + Curl_safefree(conn->proxypasswd); + conn->proxyuser = old_conn->proxyuser; + conn->proxypasswd = old_conn->proxypasswd; + old_conn->proxyuser = NULL; + old_conn->proxypasswd = NULL; + } + + /* host can change, when doing keepalive with a proxy ! */ + if(conn->bits.proxy) { + free(conn->host.rawalloc); + conn->host=old_conn->host; + } + else + free(old_conn->host.rawalloc); /* free the newly allocated name buffer */ + + /* re-use init */ + conn->bits.reuse = TRUE; /* yes, we're re-using here */ + + Curl_safefree(old_conn->user); + Curl_safefree(old_conn->passwd); + Curl_safefree(old_conn->proxyuser); + Curl_safefree(old_conn->proxypasswd); + Curl_llist_destroy(old_conn->send_pipe, NULL); + Curl_llist_destroy(old_conn->recv_pipe, NULL); + Curl_llist_destroy(old_conn->pend_pipe, NULL); + Curl_safefree(old_conn->master_buffer); +} + +/** + * create_conn() sets up a new connectdata struct, or re-uses an already + * existing one, and resolves host name. + * + * if this function returns CURLE_OK and *async is set to TRUE, the resolve + * response will be coming asynchronously. If *async is FALSE, the name is + * already resolved. + * + * @param data The sessionhandle pointer + * @param in_connect is set to the next connection data pointer + * @param addr is set to the new dns entry for this connection. If this + * connection is re-used it will be NULL. + * @param async is set TRUE/FALSE depending on the nature of this lookup + * @return CURLcode + * @see setup_conn() + * + * *NOTE* this function assigns the conn->data pointer! + */ + +static CURLcode create_conn(struct SessionHandle *data, + struct connectdata **in_connect, + struct Curl_dns_entry **addr, + bool *async) +{ + CURLcode result=CURLE_OK; + struct connectdata *conn; + struct connectdata *conn_temp = NULL; + size_t urllen; + char user[MAX_CURL_USER_LENGTH]; + char passwd[MAX_CURL_PASSWORD_LENGTH]; + bool reuse; + char *proxy = NULL; + + *addr = NULL; /* nothing yet */ + *async = FALSE; + + /************************************************************* + * Check input data + *************************************************************/ + + if(!data->change.url) + return CURLE_URL_MALFORMAT; + + /* First, split up the current URL in parts so that we can use the + parts for checking against the already present connections. In order + to not have to modify everything at once, we allocate a temporary + connection data struct and fill in for comparison purposes. */ + + conn = allocate_conn(); + + /* We must set the return variable as soon as possible, so that our + parent can cleanup any possible allocs we may have done before + any failure */ + *in_connect = conn; + + if(!conn) + return CURLE_OUT_OF_MEMORY; + + conn->data = data; /* Setup the association between this connection + and the SessionHandle */ + + conn->proxytype = data->set.proxytype; /* type */ + conn->bits.proxy = (bool)(data->set.str[STRING_PROXY] && + *data->set.str[STRING_PROXY]); + conn->bits.httpproxy = (bool)(conn->bits.proxy + && (conn->proxytype == CURLPROXY_HTTP)); + + + conn->bits.user_passwd = (bool)(NULL != data->set.str[STRING_USERPWD]); + conn->bits.proxy_user_passwd = (bool)(NULL != data->set.str[STRING_PROXYUSERPWD]); + conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; + conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; + conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; + + if(data->multi && Curl_multi_canPipeline(data->multi) && + !conn->master_buffer) { + /* Allocate master_buffer to be used for pipelining */ + conn->master_buffer = calloc(BUFSIZE, sizeof (char)); + if(!conn->master_buffer) + return CURLE_OUT_OF_MEMORY; + } + + /* Initialize the pipeline lists */ + conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); + conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); + conn->pend_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); + if(!conn->send_pipe || !conn->recv_pipe || !conn->pend_pipe) + return CURLE_OUT_OF_MEMORY; + + /* This initing continues below, see the comment "Continue connectdata + * initialization here" */ + + /*********************************************************** + * We need to allocate memory to store the path in. We get the size of the + * full URL to be sure, and we need to make it at least 256 bytes since + * other parts of the code will rely on this fact + ***********************************************************/ +#define LEAST_PATH_ALLOC 256 + urllen=strlen(data->change.url); + if(urllen < LEAST_PATH_ALLOC) + urllen=LEAST_PATH_ALLOC; + + /* + * We malloc() the buffers below urllen+2 to make room for to possibilities: + * 1 - an extra terminating zero + * 2 - an extra slash (in case a syntax like "www.host.com?moo" is used) + */ + + Curl_safefree(data->state.pathbuffer); + data->state.pathbuffer=(char *)malloc(urllen+2); + if(NULL == data->state.pathbuffer) + return CURLE_OUT_OF_MEMORY; /* really bad error */ + data->state.path = data->state.pathbuffer; + + conn->host.rawalloc=(char *)malloc(urllen+2); + if(NULL == conn->host.rawalloc) + return CURLE_OUT_OF_MEMORY; + + conn->host.name = conn->host.rawalloc; + conn->host.name[0] = 0; + + result = ParseURLAndFillConnection(data, conn); + if(result != CURLE_OK) { + return result; + } + + /************************************************************* + * Extract the user and password from the authentication string + *************************************************************/ + if(conn->bits.proxy_user_passwd) { + result = parse_proxy_auth(data, conn); + if(result != CURLE_OK) + return result; + } + + /************************************************************* + * Detect what (if any) proxy to use + *************************************************************/ + if(data->set.str[STRING_PROXY]) { + proxy = strdup(data->set.str[STRING_PROXY]); + /* if global proxy is set, this is it */ + if(NULL == proxy) { + failf(data, "memory shortage"); + return CURLE_OUT_OF_MEMORY; + } + } + + if(!proxy) + proxy = detect_proxy(conn); + if(proxy && !*proxy) { + free(proxy); /* Don't bother with an empty proxy string */ + proxy = NULL; + } + /* proxy must be freed later unless NULL */ + + /************************************************************* + * No protocol part in URL was used, add it! + *************************************************************/ + if(conn->protocol&PROT_MISSING) { + /* We're guessing prefixes here and if we're told to use a proxy or if + we're gonna follow a Location: later or... then we need the protocol + part added so that we have a valid URL. */ + char *reurl; + + reurl = aprintf("%s://%s", conn->protostr, data->change.url); + + if(!reurl) { + Curl_safefree(proxy); + return CURLE_OUT_OF_MEMORY; + } + + data->change.url = reurl; + data->change.url_alloc = TRUE; /* free this later */ + conn->protocol &= ~PROT_MISSING; /* switch that one off again */ + } + + /************************************************************* + * Setup internals depending on protocol + *************************************************************/ + result = setup_connection_internals(data, conn); + if(result != CURLE_OK) { + Curl_safefree(proxy); + return result; + } + + + /*********************************************************************** + * If this is supposed to use a proxy, we need to figure out the proxy + * host name, so that we can re-use an existing connection + * that may exist registered to the same proxy host. + ***********************************************************************/ + if(proxy) { + result = parse_proxy(data, conn, proxy); + /* parse_proxy has freed the proxy string, so don't try to use it again */ + proxy = NULL; + if(result != CURLE_OK) + return result; + } + + /*********************************************************************** + * file: is a special case in that it doesn't need a network connection + ***********************************************************************/ +#ifndef CURL_DISABLE_FILE + if(conn->protocol & PROT_FILE) { + bool done; + /* this is supposed to be the connect function so we better at least check + that the file is present here! */ + DEBUGASSERT(conn->handler->connect_it); + result = conn->handler->connect_it(conn, &done); + + /* Setup a "faked" transfer that'll do nothing */ + if(CURLE_OK == result) { + conn->data = data; + conn->bits.tcpconnect = TRUE; /* we are "connected */ + + ConnectionStore(data, conn); + + /* + * Setup whatever necessary for a resumed transfer + */ + result = setup_range(data); + if(result) { + DEBUGASSERT(conn->handler->done); + /* we ignore the return code for the protocol-specific DONE */ + (void)conn->handler->done(conn, result, FALSE); + return result; + } + + result = Curl_setup_transfer(conn, -1, -1, FALSE, + NULL, /* no download */ + -1, NULL); /* no upload */ + } + + return result; + } +#endif + + /************************************************************* + * If the protocol is using SSL and HTTP proxy is used, we set + * the tunnel_proxy bit. + *************************************************************/ + if((conn->protocol&PROT_SSL) && conn->bits.httpproxy) + conn->bits.tunnel_proxy = TRUE; + + /************************************************************* + * Parse a user name and password in the URL and strip it out + * of the host name + *************************************************************/ + result = parse_url_userpass(data, conn, user, passwd); + if(result != CURLE_OK) + return result; + + /************************************************************* + * Figure out the remote port number and fix it in the URL + *************************************************************/ + result = parse_remote_port(data, conn); + if(result != CURLE_OK) + return result; + + /************************************************************* + * Check for an overridden user name and password, then set it + * for use + *************************************************************/ + override_userpass(data, conn, user, passwd); + result = set_userpass(conn, user, passwd); + if(result != CURLE_OK) + return result; + + /************************************************************* + * Check the current list of connections to see if we can + * re-use an already existing one or if we have to create a + * new one. + *************************************************************/ + + /* Get a cloned copy of the SSL config situation stored in the + connection struct. But to get this going nicely, we must first make + sure that the strings in the master copy are pointing to the correct + strings in the session handle strings array! + + Keep in mind that the pointers in the master copy are pointing to strings + that will be freed as part of the SessionHandle struct, but all cloned + copies will be separately allocated. + */ + data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH]; + data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE]; + data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE]; + data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE]; + data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; + data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; + + if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config)) + return CURLE_OUT_OF_MEMORY; + + /* reuse_fresh is TRUE if we are told to use a new connection by force, but + we only acknowledge this option if this is not a re-used connection + already (which happens due to follow-location or during a HTTP + authentication phase). */ + if(data->set.reuse_fresh && !data->state.this_is_a_follow) + reuse = FALSE; + else + reuse = ConnectionExists(data, conn, &conn_temp); + + if(reuse) { + /* + * We already have a connection for this, we got the former connection + * in the conn_temp variable and thus we need to cleanup the one we + * just allocated before we can move along and use the previously + * existing one. + */ + reuse_conn(conn, conn_temp); + free(conn); /* we don't need this anymore */ + conn = conn_temp; + *in_connect = conn; + infof(data, "Re-using existing connection! (#%ld) with host %s\n", + conn->connectindex, + conn->proxy.name?conn->proxy.dispname:conn->host.dispname); + } + else { + /* + * This is a brand new connection, so let's store it in the connection + * cache of ours! + */ + ConnectionStore(data, conn); + } + + /* + * Setup whatever necessary for a resumed transfer + */ + result = setup_range(data); + if(result) + return result; + + /* Continue connectdata initialization here. */ + + /* + * Inherit the proper values from the urldata struct AFTER we have arranged + * the persistent connection stuff + */ + conn->fread_func = data->set.fread_func; + conn->fread_in = data->set.in; + conn->seek_func = data->set.seek_func; + conn->seek_client = data->set.seek_client; + + /************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ + result = resolve_server(data, conn, addr, async); return result; }