From 0d3d84e3ffcaa6118cc4f33c5f2d6c9166d60662 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 3 Aug 2007 22:46:59 +0000 Subject: Refactored CreateConnection() somewhat to reduce its length by splitting it into a few new functions. Fixed a few leaks in out of memory conditions, including for test case 231. --- lib/url.c | 949 +++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 510 insertions(+), 439 deletions(-) (limited to 'lib') diff --git a/lib/url.c b/lib/url.c index e184c2b09..ba48da8eb 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2888,345 +2888,13 @@ static CURLcode setup_range(struct SessionHandle *data) return CURLE_OK; } -/** - * CreateConnection() 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 SetupConnection() - * - * *NOTE* this function assigns the conn->data pointer! - */ -static CURLcode CreateConnection(struct SessionHandle *data, - struct connectdata **in_connect, - struct Curl_dns_entry **addr, - bool *async) +/*************************************************************** +* Setup connection internals specific to the requested protocol +***************************************************************/ +static CURLcode setup_connection_internals(struct SessionHandle *data, + struct connectdata *conn) { - - 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; - - /* 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.no_body = data->set.opt_no_body; - 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); - if (!conn->send_pipe || !conn->recv_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->reqdata.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->reqdata.pathbuffer=(char *)malloc(urllen+2); - if(NULL == data->reqdata.pathbuffer) - return CURLE_OUT_OF_MEMORY; /* really bad error */ - data->reqdata.path = data->reqdata.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) { - char proxyuser[MAX_CURL_USER_LENGTH]=""; - char proxypasswd[MAX_CURL_PASSWORD_LENGTH]=""; - - sscanf(data->set.str[STRING_PROXYUSERPWD], - "%" MAX_CURL_USER_LENGTH_TXT "[^:]:" - "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]", - proxyuser, proxypasswd); - - conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); - if(!conn->proxyuser) - return CURLE_OUT_OF_MEMORY; - - conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); - if(!conn->proxypasswd) - return CURLE_OUT_OF_MEMORY; - } - - 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; - } - } - /* proxy must be freed later unless NULL */ - -#ifndef CURL_DISABLE_HTTP - /************************************************************* - * Detect what (if any) proxy to use. Remember that this selects a host - * name and is not limited to HTTP proxies only. - *************************************************************/ - if(!proxy) { - /* If proxy was not specified, we check for default proxy environment - * variables, to enable i.e Lynx compliance: - * - * http_proxy=http://some.server.dom:port/ - * https_proxy=http://some.server.dom:port/ - * ftp_proxy=http://some.server.dom:port/ - * no_proxy=domain1.dom,host.domain2.dom - * (a comma-separated list of hosts which should - * not be proxied, or an asterisk to override - * all proxy variables) - * all_proxy=http://some.server.dom:port/ - * (seems to exist for the CERN www lib. Probably - * the first to check for.) - * - * For compatibility, the all-uppercase versions of these variables are - * checked if the lowercase versions don't exist. - */ - char *no_proxy=NULL; - char *no_proxy_tok_buf; - char proxy_env[128]; - - no_proxy=curl_getenv("no_proxy"); - if(!no_proxy) - no_proxy=curl_getenv("NO_PROXY"); - - if(!no_proxy || !strequal("*", no_proxy)) { - /* NO_PROXY wasn't specified or it wasn't just an asterisk */ - char *nope; - - nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL; - while(nope) { - size_t namelen; - char *endptr = strchr(conn->host.name, ':'); - if(endptr) - namelen=endptr-conn->host.name; - else - namelen=strlen(conn->host.name); - - if(strlen(nope) <= namelen) { - char *checkn= - conn->host.name + namelen - strlen(nope); - if(checkprefix(nope, checkn)) { - /* no proxy for this host! */ - break; - } - } - nope=strtok_r(NULL, ", ", &no_proxy_tok_buf); - } - if(!nope) { - /* It was not listed as without proxy */ - char *protop = conn->protostr; - char *envp = proxy_env; - char *prox; - - /* Now, build _proxy and check for such a one to use */ - while(*protop) - *envp++ = (char)tolower((int)*protop++); - - /* append _proxy */ - strcpy(envp, "_proxy"); - - /* read the protocol proxy: */ - prox=curl_getenv(proxy_env); - - /* - * We don't try the uppercase version of HTTP_PROXY because of - * security reasons: - * - * When curl is used in a webserver application - * environment (cgi or php), this environment variable can - * be controlled by the web server user by setting the - * http header 'Proxy:' to some value. - * - * This can cause 'internal' http/ftp requests to be - * arbitrarily redirected by any external attacker. - */ - if(!prox && !strequal("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - for(envp = proxy_env; *envp; envp++) - *envp = (char)toupper((int)*envp); - prox=curl_getenv(proxy_env); - } - - if(prox && *prox) { /* don't count "" strings */ - proxy = prox; /* use this */ - } - else { - proxy = curl_getenv("all_proxy"); /* default proxy to use */ - if(!proxy) - proxy=curl_getenv("ALL_PROXY"); - } - - if(proxy && *proxy) { - long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING); - - if(conn->proxytype == CURLPROXY_HTTP) { - /* force this connection's protocol to become HTTP */ - conn->protocol = PROT_HTTP | bits; - conn->bits.httpproxy = TRUE; - } - } - } /* if (!nope) - it wasn't specified non-proxy */ - } /* NO_PROXY wasn't specified or '*' */ - if(no_proxy) - free(no_proxy); - } /* if not using proxy */ -#endif /* CURL_DISABLE_HTTP */ - - /************************************************************* - * 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 - *************************************************************/ - conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ if (strequal(conn->protostr, "HTTP")) { @@ -3244,6 +2912,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if (strequal(conn->protostr, "HTTPS")) { #if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) @@ -3264,6 +2933,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif /* !USE_SSL */ } + else if(strequal(conn->protostr, "FTP") || strequal(conn->protostr, "FTPS")) { @@ -3344,6 +3014,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if(strequal(conn->protostr, "TELNET")) { #ifndef CURL_DISABLE_TELNET /* telnet testing factory */ @@ -3359,6 +3030,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if (strequal(conn->protostr, "DICT")) { #ifndef CURL_DISABLE_DICT conn->protocol |= PROT_DICT; @@ -3373,6 +3045,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if (strequal(conn->protostr, "LDAP")) { #ifndef CURL_DISABLE_LDAP conn->protocol |= PROT_LDAP; @@ -3387,6 +3060,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if (strequal(conn->protostr, "FILE")) { #ifndef CURL_DISABLE_FILE conn->protocol |= PROT_FILE; @@ -3399,6 +3073,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if (strequal(conn->protostr, "TFTP")) { #ifndef CURL_DISABLE_TFTP char *type; @@ -3438,6 +3113,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if (strequal(conn->protostr, "SCP")) { #ifdef USE_LIBSSH2 conn->port = PORT_SSH; @@ -3455,6 +3131,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else if (strequal(conn->protostr, "SFTP")) { #ifdef USE_LIBSSH2 conn->port = PORT_SSH; @@ -3472,128 +3149,520 @@ static CURLcode CreateConnection(struct SessionHandle *data, return CURLE_UNSUPPORTED_PROTOCOL; #endif } + else { /* We fell through all checks and thus we don't support the specified protocol */ failf(data, "Unsupported protocol: %s", conn->protostr); return CURLE_UNSUPPORTED_PROTOCOL; } + return CURLE_OK; +} - if(proxy && *proxy) { - /* If this is supposed to use a proxy, we need to figure out the proxy - host name name, so that we can re-use an existing connection - that may exist registered to the same proxy host. */ - - char *prox_portno; - char *endofprot; - - /* We use 'proxyptr' to point to the proxy name from now on... */ - char *proxyptr=proxy; - char *portptr; - char *atsign; - - /* We do the proxy host string parsing here. We want the host name and the - * port name. Accept a protocol:// prefix, even though it should just be - * ignored. - */ - - /* Skip the protocol part if present */ - endofprot=strstr(proxyptr, "://"); - if(endofprot) - proxyptr = endofprot+3; - - /* Is there a username and password given in this proxy url? */ - atsign = strchr(proxyptr, '@'); - if(atsign) { - char proxyuser[MAX_CURL_USER_LENGTH]; - char proxypasswd[MAX_CURL_PASSWORD_LENGTH]; - proxypasswd[0] = 0; - - if(1 <= sscanf(proxyptr, - "%" MAX_CURL_USER_LENGTH_TXT"[^:]:" - "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", - proxyuser, proxypasswd)) { - CURLcode res = CURLE_OK; - - /* found user and password, rip them out. note that we are - unescaping them, as there is otherwise no way to have a - username or password with reserved characters like ':' in - them. */ - Curl_safefree(conn->proxyuser); - conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); - - if(!conn->proxyuser) - res = CURLE_OUT_OF_MEMORY; - else { - Curl_safefree(conn->proxypasswd); - conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); - - if(!conn->proxypasswd) - res = CURLE_OUT_OF_MEMORY; - } - - if(CURLE_OK == res) { - conn->bits.proxy_user_passwd = TRUE; /* enable it */ - atsign = strdup(atsign+1); /* the right side of the @-letter */ +/**************************************************************** +* Detect what (if any) proxy to use. Remember that this selects a host +* name and is not limited to HTTP proxies only. +* The returned pointer must be freed by the caller (unless NULL) +****************************************************************/ +static char *detect_proxy(struct connectdata *conn) +{ + char *proxy = NULL; - if(atsign) { - free(proxy); /* free the former proxy string */ - proxy = proxyptr = atsign; /* now use this instead */ - } - else - res = CURLE_OUT_OF_MEMORY; - } - - if(res) { - free(proxy); /* free the allocated proxy string */ - return res; - } +#ifndef CURL_DISABLE_HTTP + /* If proxy was not specified, we check for default proxy environment + * variables, to enable i.e Lynx compliance: + * + * http_proxy=http://some.server.dom:port/ + * https_proxy=http://some.server.dom:port/ + * ftp_proxy=http://some.server.dom:port/ + * no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + * all_proxy=http://some.server.dom:port/ + * (seems to exist for the CERN www lib. Probably + * the first to check for.) + * + * For compatibility, the all-uppercase versions of these variables are + * checked if the lowercase versions don't exist. + */ + char *no_proxy=NULL; + char *no_proxy_tok_buf; + char proxy_env[128]; + + no_proxy=curl_getenv("no_proxy"); + if(!no_proxy) + no_proxy=curl_getenv("NO_PROXY"); + + if(!no_proxy || !strequal("*", no_proxy)) { + /* NO_PROXY wasn't specified or it wasn't just an asterisk */ + char *nope; + + nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL; + while(nope) { + size_t namelen; + char *endptr = strchr(conn->host.name, ':'); + if(endptr) + namelen=endptr-conn->host.name; + else + namelen=strlen(conn->host.name); + + if(strlen(nope) <= namelen) { + char *checkn= + conn->host.name + namelen - strlen(nope); + if(checkprefix(nope, checkn)) { + /* no proxy for this host! */ + break; + } } + nope=strtok_r(NULL, ", ", &no_proxy_tok_buf); } + if(!nope) { + /* It was not listed as without proxy */ + char *protop = conn->protostr; + char *envp = proxy_env; + char *prox; - /* start scanning for port number at this point */ - portptr = proxyptr; - - /* detect and extract RFC2732-style IPv6-addresses */ - if(*proxyptr == '[') { - char *ptr = ++proxyptr; /* advance beyond the initial bracket */ - while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':'))) - ptr++; - if(*ptr == ']') { - /* yeps, it ended nicely with a bracket as well */ - *ptr = 0; - portptr = ptr+1; - } - /* Note that if this didn't end with a bracket, we still advanced the - * proxyptr first, but I can't see anything wrong with that as no host - * name nor a numeric can legally start with a bracket. + /* Now, build _proxy and check for such a one to use */ + while(*protop) + *envp++ = (char)tolower((int)*protop++); + + /* append _proxy */ + strcpy(envp, "_proxy"); + + /* read the protocol proxy: */ + prox=curl_getenv(proxy_env); + + /* + * We don't try the uppercase version of HTTP_PROXY because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. */ - } + if(!prox && !strequal("http_proxy", proxy_env)) { + /* There was no lowercase variable, try the uppercase version: */ + for(envp = proxy_env; *envp; envp++) + *envp = (char)toupper((int)*envp); + prox=curl_getenv(proxy_env); + } + + if(prox && *prox) { /* don't count "" strings */ + proxy = prox; /* use this */ + } + else { + proxy = curl_getenv("all_proxy"); /* default proxy to use */ + if(!proxy) + proxy=curl_getenv("ALL_PROXY"); + } + + if(proxy && *proxy) { + long bits = conn->protocol & (PROT_HTTPS|PROT_SSL|PROT_MISSING); + + if(conn->proxytype == CURLPROXY_HTTP) { + /* force this connection's protocol to become HTTP */ + conn->protocol = PROT_HTTP | bits; + conn->bits.httpproxy = TRUE; + } + } + } /* if (!nope) - it wasn't specified non-proxy */ + } /* NO_PROXY wasn't specified or '*' */ + if(no_proxy) + free(no_proxy); +#endif /* CURL_DISABLE_HTTP */ + + return 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. + */ +static CURLcode parse_proxy(struct SessionHandle *data, + struct connectdata *conn, char *proxy) +{ + char *prox_portno; + char *endofprot; + + /* We use 'proxyptr' to point to the proxy name from now on... */ + char *proxyptr=proxy; + char *portptr; + char *atsign; + + /* We do the proxy host string parsing here. We want the host name and the + * port name. Accept a protocol:// prefix, even though it should just be + * ignored. + */ + + /* Skip the protocol part if present */ + endofprot=strstr(proxyptr, "://"); + if(endofprot) + proxyptr = endofprot+3; + + /* Is there a username and password given in this proxy url? */ + atsign = strchr(proxyptr, '@'); + if(atsign) { + char proxyuser[MAX_CURL_USER_LENGTH]; + char proxypasswd[MAX_CURL_PASSWORD_LENGTH]; + proxypasswd[0] = 0; + + if(1 <= sscanf(proxyptr, + "%" MAX_CURL_USER_LENGTH_TXT"[^:]:" + "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", + proxyuser, proxypasswd)) { + CURLcode res = CURLE_OK; + + /* found user and password, rip them out. note that we are + unescaping them, as there is otherwise no way to have a + username or password with reserved characters like ':' in + them. */ + Curl_safefree(conn->proxyuser); + conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); + + if(!conn->proxyuser) + res = CURLE_OUT_OF_MEMORY; + else { + Curl_safefree(conn->proxypasswd); + conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); - /* Get port number off proxy.server.com:1080 */ - prox_portno = strchr(portptr, ':'); - if (prox_portno) { - *prox_portno = 0x0; /* cut off number from host name */ - prox_portno ++; - /* now set the local port number */ - conn->port = atoi(prox_portno); + if(!conn->proxypasswd) + res = CURLE_OUT_OF_MEMORY; + } + + if(CURLE_OK == res) { + conn->bits.proxy_user_passwd = TRUE; /* enable it */ + atsign = strdup(atsign+1); /* the right side of the @-letter */ + + if(atsign) { + free(proxy); /* free the former proxy string */ + proxy = proxyptr = atsign; /* now use this instead */ + } + else + res = CURLE_OUT_OF_MEMORY; + } + + if(res) { + free(proxy); /* free the allocated proxy string */ + return res; + } } - else if(data->set.proxyport) { - /* None given in the proxy string, then get the default one if it is - given */ - conn->port = data->set.proxyport; + } + + /* start scanning for port number at this point */ + portptr = proxyptr; + + /* detect and extract RFC2732-style IPv6-addresses */ + if(*proxyptr == '[') { + char *ptr = ++proxyptr; /* advance beyond the initial bracket */ + while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':'))) + ptr++; + if(*ptr == ']') { + /* yeps, it ended nicely with a bracket as well */ + *ptr = 0; + portptr = ptr+1; } + /* Note that if this didn't end with a bracket, we still advanced the + * proxyptr first, but I can't see anything wrong with that as no host + * name nor a numeric can legally start with a bracket. + */ + } + + /* Get port number off proxy.server.com:1080 */ + prox_portno = strchr(portptr, ':'); + if (prox_portno) { + *prox_portno = 0x0; /* cut off number from host name */ + prox_portno ++; + /* now set the local port number */ + conn->port = atoi(prox_portno); + } + else if(data->set.proxyport) { + /* None given in the proxy string, then get the default one if it is + given */ + conn->port = data->set.proxyport; + } + + /* now, clone the cleaned proxy host name */ + conn->proxy.rawalloc = strdup(proxyptr); + conn->proxy.name = conn->proxy.rawalloc; + + free(proxy); + if(!conn->proxy.rawalloc) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +/* Extract the user and password from the authentication string */ +static CURLcode parse_proxy_auth(struct SessionHandle *data, struct connectdata *conn) +{ + char proxyuser[MAX_CURL_USER_LENGTH]=""; + char proxypasswd[MAX_CURL_PASSWORD_LENGTH]=""; + + sscanf(data->set.str[STRING_PROXYUSERPWD], + "%" MAX_CURL_USER_LENGTH_TXT "[^:]:" + "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]", + proxyuser, proxypasswd); + + conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); + if(!conn->proxyuser) + return CURLE_OUT_OF_MEMORY; + + conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); + if(!conn->proxypasswd) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +/** + * CreateConnection() 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 SetupConnection() + * + * *NOTE* this function assigns the conn->data pointer! + */ + +static CURLcode CreateConnection(struct SessionHandle *data, + struct connectdata **in_connect, + struct Curl_dns_entry **addr, + bool *async) +{ + + 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 + *************************************************************/ - /* now, clone the cleaned proxy host name */ - conn->proxy.rawalloc = strdup(proxyptr); - conn->proxy.name = conn->proxy.rawalloc; + 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; - free(proxy); + /* 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.no_body = data->set.opt_no_body; + 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); + if (!conn->send_pipe || !conn->recv_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->reqdata.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->reqdata.pathbuffer=(char *)malloc(urllen+2); + if(NULL == data->reqdata.pathbuffer) + return CURLE_OUT_OF_MEMORY; /* really bad error */ + data->reqdata.path = data->reqdata.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; - if(!conn->proxy.rawalloc) + } + /* 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 ***********************************************************************/ @@ -3611,8 +3680,10 @@ static CURLcode CreateConnection(struct SessionHandle *data, ConnectionStore(data, conn); result = setup_range(data); - if(result) + if(result) { + Curl_file_done(conn, result, FALSE); return result; + } result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */ -- cgit v1.2.3