Newer
Older
/*
* This function should return TRUE if the socket is to be assumed to
* be dead. Most commonly this happens when the server has closed the
* connection due to inactivity.
*/
Daniel Stenberg
committed
static bool SocketIsDead(int sock)
{
int sval;
bool ret_val = TRUE;
fd_set check_set;
struct timeval to;
FD_ZERO(&check_set);
FD_SET(sock,&check_set);
to.tv_sec = 0;
to.tv_usec = 1;
sval = select(sock + 1, &check_set, 0, 0, &to);
Daniel Stenberg
committed
if(sval == 0)
ret_val = FALSE;
* Given one filled in connection struct (named needle), this function should
* detect if there already is one that have all the significant details
* exactly the same and thus should be used instead.
Daniel Stenberg
committed
ConnectionExists(struct SessionHandle *data,
struct connectdata *needle,
struct connectdata **usethis)
struct connectdata *check;
Daniel Stenberg
committed
for(i=0; i< data->state.numconnects; i++) {
/*
* Note that if we use a HTTP proxy, we check connections to that
* proxy and not to the actual remote server.
*/
Daniel Stenberg
committed
check = data->state.connects[i];
if(!check)
/* NULL pointer means not filled-in entry */
continue;
if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) {
/* The requested connection does not use a HTTP proxy or it
uses SSL. */
if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy)
/* we don't do SSL but the cached connection has a proxy,
then don't match this */
continue;
if(strequal(needle->protostr, check->protostr) &&
strequal(needle->name, check->name) &&
(needle->remote_port == check->remote_port) ) {
if(strequal(needle->protostr, "FTP")) {
/* This is FTP, verify that we're using the same name and
password as well */
Daniel Stenberg
committed
if(!strequal(needle->data->state.user, check->proto.ftp->user) ||
!strequal(needle->data->state.passwd, check->proto.ftp->passwd)) {
/* one of them was different */
continue;
}
}
Daniel Stenberg
committed
dead = SocketIsDead(check->firstsocket);
/*
* Even though the connection seems to have passed away, we could
* still make an effort to get the name information, as we intend to
* connect to the same host again.
*
* This is now subject to discussion. What do you think?
*/
infof(data, "Connection %d seems to be dead!\n", i);
Curl_disconnect(check); /* disconnect resources */
Daniel Stenberg
committed
data->state.connects[i]=NULL; /* nothing here */
/* There's no need to continue search, because we only store
one connection for each unique set of identifiers */
return FALSE;
Daniel Stenberg
committed
*usethis = check;
return TRUE; /* yes, we found one to use! */
else { /* The requested needle connection is using a proxy,
is the checked one using the same? */
if(check->bits.httpproxy &&
strequal(needle->proxyhost, check->proxyhost) &&
needle->port == check->port) {
/* This is the same proxy connection, use it! */
*usethis = check;
return TRUE;
}
}
}
return FALSE; /* no matching connecting exists */
}
/*
* This function frees/closes a connection in the connection cache. This
* should take the previously set policy into account when deciding which
* of the connections to kill.
*/
static int
Daniel Stenberg
committed
ConnectionKillOne(struct SessionHandle *data)
int highscore=-1;
int connindex=-1;
int score;
CURLcode result;
struct timeval now;
now = Curl_tvnow();
Daniel Stenberg
committed
for(i=0; i< data->state.numconnects; i++) {
conn = data->state.connects[i];
if(!conn)
continue;
/*
* By using the set policy, we score each connection.
*/
Daniel Stenberg
committed
switch(data->set.closepolicy) {
case CURLCLOSEPOLICY_LEAST_RECENTLY_USED:
/*
* Set higher score for the age passed since the connection
* was used.
*/
score = Curl_tvdiff(now, conn->now);
break;
case CURLCLOSEPOLICY_OLDEST:
/*
* Set higher score for the age passed since the connection
* was created.
*/
score = Curl_tvdiff(now, conn->created);
break;
}
if(score > highscore) {
highscore = score;
connindex = i;
}
}
if(connindex >= 0) {
/* the winner gets the honour of being disconnected */
Daniel Stenberg
committed
result = Curl_disconnect(data->state.connects[connindex]);
/* clean the array entry */
Daniel Stenberg
committed
data->state.connects[connindex] = NULL;
}
return connindex; /* return the available index or -1 */
}
/*
* The given input connection struct pointer is to be stored. If the "cache"
* is already full, we must clean out the most suitable using the previously
* set policy.
*
* The given connection should be unique. That must've been checked prior to
* this call.
*/
static unsigned int
Daniel Stenberg
committed
ConnectionStore(struct SessionHandle *data,
struct connectdata *conn)
{
Daniel Stenberg
committed
for(i=0; i< data->state.numconnects; i++) {
if(!data->state.connects[i])
Daniel Stenberg
committed
if(i == data->state.numconnects) {
/* there was no room available, kill one */
i = ConnectionKillOne(data);
infof(data, "Connection (#%d) was killed to make room\n", i);
}
if(-1 != i) {
/* only do this if a true index was returned, if -1 was returned there
is no room in the cache for an unknown reason and we cannot store
this there. */
data->state.connects[i] = conn; /* fill in this */
conn->connectindex = i; /* make the child know where the pointer to this
particular data is stored */
}
static CURLcode ConnectPlease(struct connectdata *conn)
/*************************************************************
*************************************************************/
result= Curl_connecthost(conn,
conn->hostaddr,
conn->port,
&conn->firstsocket,
&addr);
if(CURLE_OK == result) {
/* All is cool, then we store the current information from the hostaddr
struct to the serv_addr, as it might be needed later. The address
returned from the function above is crucial here. */
#ifdef ENABLE_IPV6
conn->serv_addr = addr;
#else
memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
memcpy((char *)&(conn->serv_addr.sin_addr),
(struct in_addr *)addr, sizeof(struct in_addr));
conn->serv_addr.sin_family = conn->hostaddr->h_addrtype;
conn->serv_addr.sin_port = htons(conn->port);
#endif
}
static CURLcode CreateConnection(struct SessionHandle *data,
struct connectdata **in_connect)
{
char *tmp;
char *buf;
CURLcode result=CURLE_OK;
char resumerange[40]="";
struct connectdata *conn;
struct connectdata *conn_temp;
char endbracket;
int urllen;
#ifdef HAVE_INET_NTOA_R
char ntoa_buf[64];
#endif
Daniel Stenberg
committed
#ifdef HAVE_ALARM
unsigned int prev_alarm;
#endif
#ifdef HAVE_SIGACTION
struct sigaction keep_sigact; /* store the old struct here */
bool keep_copysig; /* did copy it? */
#else
#ifdef HAVE_SIGNAL
void *keep_sigact; /* store the old handler here */
#endif
Daniel Stenberg
committed
#endif
/*************************************************************
* Check input data
*************************************************************/
Daniel Stenberg
committed
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 *)malloc(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;
/* we have to init the struct */
memset(conn, 0, sizeof(struct connectdata));
/* and we setup a few fields in case we end up actually using this struct */
conn->data = data; /* remember our daddy */
conn->upload_bufsize = UPLOAD_BUFSIZE; /* default upload buffer size */
conn->firstsocket = -1; /* no file descriptor */
conn->secondarysocket = -1; /* no file descriptor */
conn->connectindex = -1; /* no index */
Daniel Stenberg
committed
conn->bits.httpproxy = data->change.proxy?TRUE:FALSE; /* proxy-or-not */
conn->bits.use_range = data->set.set_range?TRUE:FALSE; /* range status */
conn->range = data->set.set_range; /* clone the range setting */
conn->resume_from = data->set.set_resume_from; /* inherite resume_from */
/* Default protocol-independent behavior doesn't support persistant
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;
Daniel Stenberg
committed
/* inherite initial knowledge from the data struct */
Daniel Stenberg
committed
conn->bits.user_passwd = data->set.userpwd?1:0;
conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0;
/* maxdownload must be -1 on init, as 0 is a valid value! */
conn->maxdownload = -1; /* might have been used previously! */
/* Store creation time to help future close decision making */
conn->created = Curl_tvnow();
Daniel Stenberg
committed
/***********************************************************
* 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
***********************************************************/
Daniel Stenberg
committed
urllen=strlen(data->change.url);
if(urllen < LEAST_PATH_ALLOC)
urllen=LEAST_PATH_ALLOC;
conn->path=(char *)malloc(urllen);
if(NULL == conn->path)
return CURLE_OUT_OF_MEMORY; /* really bad error */
/*************************************************************
* Parse the URL.
*
* We need to parse the url even when using the proxy, because we will need
* the hostname and port in case we are trying to SSL connect through the
* proxy -- and we don't know if we will need to use SSL until we parse the
* url ...
************************************************************/
Daniel Stenberg
committed
if((2 == sscanf(data->change.url, "%64[^:]://%[^\n]",
conn->protostr,
conn->path)) && strequal(conn->protostr, "file")) {
/*
* we deal with file://<host>/<path> differently since it supports no
* hostname other than "localhost" and "127.0.0.1", which is unique among
* the URL protocols specified in RFC 1738
*/
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
if(conn->path[0] != '/') {
/* the URL included a host name, we ignore host names in file:// URLs
as the standards don't define what to do with them */
char *ptr=strchr(conn->path, '/');
if(ptr) {
/* there was a slash present
RFC1738 (section 3.1, page 5) says:
The rest of the locator consists of data specific to the scheme,
and is known as the "url-path". It supplies the details of how the
specified resource can be accessed. Note that the "/" between the
host (or port) and the url-path is NOT part of the url-path.
As most agents use file://localhost/foo to get '/foo' although the
slash preceeding foo is a separator and not a slash for the path,
a URL as file://localhost//foo must be valid as well, to refer to
the same file with an absolute path.
*/
if(ptr[1] && ('/' == ptr[1]))
/* if there was two slashes, we skip the first one as that is then
used truly as a separator */
ptr++;
strcpy(conn->path, ptr);
}
}
strcpy(conn->protostr, "file"); /* store protocol string lowercase */
else {
/* Set default host and default path */
Daniel Stenberg
committed
if (2 > sscanf(data->change.url,
Daniel Stenberg
committed
"%64[^\n:]://%512[^\n/]%[^\n]",
conn->protostr, conn->gname, conn->path)) {
/*
* The URL was badly formatted, let's try the browser-style _without_
* protocol specified like 'http://'.
*/
Daniel Stenberg
committed
if((1 > sscanf(data->change.url, "%512[^\n/]%[^\n]",
/*
* We couldn't even get this format.
*/
failf(data, "<url> malformed");
return CURLE_URL_MALFORMAT;
}
/*
* Since there was no protocol part specified, we guess what protocol it
* is based on the first letters of the server name.
*/
if(strnequal(conn->gname, "FTP", 3)) {
strcpy(conn->protostr, "ftp");
}
else if(strnequal(conn->gname, "GOPHER", 6))
strcpy(conn->protostr, "gopher");
else if(strnequal(conn->gname, "HTTPS", 5))
strcpy(conn->protostr, "https");
else if(strnequal(conn->gname, "FTPS", 4))
strcpy(conn->protostr, "ftps");
else if(strnequal(conn->gname, "TELNET", 6))
strcpy(conn->protostr, "telnet");
else if (strnequal(conn->gname, "DICT", sizeof("DICT")-1))
strcpy(conn->protostr, "DICT");
else if (strnequal(conn->gname, "LDAP", sizeof("LDAP")-1))
strcpy(conn->protostr, "LDAP");
strcpy(conn->protostr, "http");
}
conn->protocol |= PROT_MISSING; /* not given in URL */
}
Daniel Stenberg
committed
buf = data->state.buffer; /* this is our buffer */
/*************************************************************
* Take care of user and password authentication stuff
*************************************************************/
Daniel Stenberg
committed
if(conn->bits.user_passwd && !data->set.use_netrc) {
data->state.user[0] =0;
data->state.passwd[0]=0;
Daniel Stenberg
committed
if(*data->set.userpwd != ':') {
/* the name is given, get user+password */
Daniel Stenberg
committed
sscanf(data->set.userpwd, "%127[^:]:%127[^\n]",
data->state.user, data->state.passwd);
else
/* no name given, get the password only */
Daniel Stenberg
committed
sscanf(data->set.userpwd+1, "%127[^\n]", data->state.passwd);
/* check for password, if no ask for one */
Daniel Stenberg
committed
if( !data->state.passwd[0] ) {
if(!data->set.fpasswd ||
data->set.fpasswd(data->set.passwd_client,
"password:", data->state.passwd,
sizeof(data->state.passwd)))
return CURLE_BAD_PASSWORD_ENTERED;
/*************************************************************
* Take care of proxy authentication stuff
*************************************************************/
Daniel Stenberg
committed
if(conn->bits.proxy_user_passwd) {
Daniel Stenberg
committed
data->state.proxyuser[0] =0;
data->state.proxypasswd[0]=0;
Daniel Stenberg
committed
if(*data->set.proxyuserpwd != ':') {
/* the name is given, get user+password */
Daniel Stenberg
committed
sscanf(data->set.proxyuserpwd, "%127[^:]:%127[^\n]",
data->state.proxyuser, data->state.proxypasswd);
else
/* no name given, get the password only */
Daniel Stenberg
committed
sscanf(data->set.proxyuserpwd+1, "%127[^\n]", data->state.proxypasswd);
/* check for password, if no ask for one */
Daniel Stenberg
committed
if( !data->state.proxypasswd[0] ) {
if(!data->set.fpasswd ||
data->set.fpasswd( data->set.passwd_client,
Daniel Stenberg
committed
data->state.proxypasswd,
sizeof(data->state.proxypasswd)))
return CURLE_BAD_PASSWORD_ENTERED;
/*************************************************************
* Set a few convenience pointers
*************************************************************/
conn->name = conn->gname;
conn->ppath = conn->path;
conn->hostname = conn->name;
/*************************************************************
* Detect what (if any) proxy to use
*************************************************************/
Daniel Stenberg
committed
if(!data->change.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/
* gopher_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;
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;
if(strlen(nope) <= strlen(conn->name)) {
conn->name + strlen(conn->name) - strlen(nope);
if(strnequal(nope, checkn, strlen(nope))) {
/* 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;
/* Now, build <protocol>_proxy and check for such a one to use */
while(*protop)
*envp++ = tolower(*protop++);
/* append _proxy */
strcpy(envp, "_proxy");
prox=curl_getenv(proxy_env);
Daniel Stenberg
committed
/*
* 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 = toupper(*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 */
proxy=curl_getenv("ALL_PROXY");
if(proxy && *proxy) {
/* we have a proxy here to set */
Daniel Stenberg
committed
data->change.proxy = proxy;
data->change.proxy_alloc=TRUE; /* this needs to be freed later */
conn->bits.httpproxy = TRUE;
} /* if (!nope) - it wasn't specified non-proxy */
if(no_proxy)
free(no_proxy);
/*************************************************************
Daniel Stenberg
committed
* No protocol part in URL was used, add it!
*************************************************************/
Daniel Stenberg
committed
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. */
Daniel Stenberg
committed
reurl = aprintf("%s://%s", conn->protostr, data->change.url);
Daniel Stenberg
committed
data->change.url = reurl;
data->change.url_alloc = TRUE; /* free this later */
conn->protocol &= ~PROT_MISSING; /* switch that one off again */
/************************************************************
* RESUME on a HTTP page is a tricky business. First, let's just check that
* 'range' isn't used, then set the range parameter and leave the resume as
* it is to inform about this situation for later use. We will then
* "attempt" to resume, and if we're talking to a HTTP/1.1 (or later)
* server, we will get the document resumed. If we talk to a HTTP/1.0
* server, we just fail since we can't rewind the file writing from within
* this function.
***********************************************************/
if(conn->resume_from) {
if(!conn->bits.use_range) {
snprintf(resumerange, sizeof(resumerange), "%d-", conn->resume_from);
conn->range=strdup(resumerange); /* tell ourselves to fetch this range */
conn->bits.rangestringalloc = TRUE; /* mark as allocated */
conn->bits.use_range = 1; /* switch on range usage */
/*************************************************************
* Setup internals depending on protocol
*************************************************************/
if (strequal(conn->protostr, "HTTP")) {
conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port:PORT_HTTP;
conn->remote_port = PORT_HTTP;
conn->curl_do = Curl_http;
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
else if (strequal(conn->protostr, "HTTPS")) {
conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port:PORT_HTTPS;
conn->remote_port = PORT_HTTPS;
conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
conn->curl_do = Curl_http;
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
failf(data, LIBCURL_NAME
" was built with SSL disabled, https: not supported!");
else if (strequal(conn->protostr, "GOPHER")) {
conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port:PORT_GOPHER;
conn->remote_port = PORT_GOPHER;
if (isdigit((int)conn->path[1])) {
conn->ppath = strchr(&conn->path[1], '/');
if (conn->ppath == NULL)
conn->ppath = conn->path;
conn->curl_do = Curl_http;
conn->curl_done = Curl_http_done;
else if(strequal(conn->protostr, "FTP") ||
strequal(conn->protostr, "FTPS")) {
if(strequal(conn->protostr, "FTPS")) {
#ifdef USE_SSLEAY
conn->protocol |= PROT_FTPS|PROT_SSL;
failf(data, LIBCURL_NAME
" was built with SSL disabled, ftps: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif /* !USE_SSLEAY */
}
conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port:PORT_FTP;
conn->remote_port = PORT_FTP;
Daniel Stenberg
committed
if(data->change.proxy &&
!data->set.tunnel_thru_httpproxy) {
/* Unless we have asked to tunnel ftp operations through the proxy, we
switch and use HTTP operations only */
if(conn->protocol & PROT_FTPS) {
/* FTPS is a hacked protocol and does not work through your
ordinary http proxy! */
failf(data, "ftps does not work through http proxy!");
return CURLE_UNSUPPORTED_PROTOCOL;
}
conn->curl_do = Curl_http;
conn->curl_done = Curl_http_done;
conn->curl_do = Curl_ftp;
conn->curl_done = Curl_ftp_done;
conn->curl_connect = Curl_ftp_connect;
}
conn->ppath++; /* don't include the initial slash */
/* FTP URLs support an extension like ";type=<typecode>" that
type=strstr(conn->ppath, ";type=");
type=strstr(conn->gname, ";type=");
}
if(type) {
char command;
*type=0;
command = toupper(type[6]);
switch(command) {
case 'A': /* ASCII mode */
Daniel Stenberg
committed
data->set.ftp_ascii = 1;
Daniel Stenberg
committed
data->set.ftp_list_only = 1;
break;
case 'I': /* binary mode */
default:
/* switch off ASCII */
Daniel Stenberg
committed
data->set.ftp_ascii = 0;
else if(strequal(conn->protostr, "TELNET")) {
conn->protocol |= PROT_TELNET;
conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port: PORT_TELNET;
conn->remote_port = PORT_TELNET;
conn->curl_do = Curl_telnet;
conn->curl_done = Curl_telnet_done;
else if (strequal(conn->protostr, "DICT")) {
conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port:PORT_DICT;
conn->remote_port = PORT_DICT;
conn->curl_do = Curl_dict;
conn->curl_done = NULL; /* no DICT-specific done */
else if (strequal(conn->protostr, "LDAP")) {
conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port:PORT_LDAP;
conn->remote_port = PORT_LDAP;
conn->curl_do = Curl_ldap;
conn->curl_done = NULL; /* no LDAP-specific done */
else if (strequal(conn->protostr, "FILE")) {
conn->curl_do = Curl_file;
/* anyway, this is supposed to be the connect function so we better
at least check that the file is present here! */
result = Curl_file_connect(conn);
/* Setup a "faked" transfer that'll do nothing */
if(CURLE_OK == result) {
result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
-1, NULL); /* no upload */
}
return result;
/* We fell through all checks and thus we don't support the specified
protocol */
failf(data, "Unsupported protocol: %s", conn->protostr);
/*************************************************************
* .netrc scanning coming up
*************************************************************/
Daniel Stenberg
committed
if(data->set.use_netrc) {
if(Curl_parsenetrc(conn->hostname,
data->state.user,
data->state.passwd)) {
infof(data, "Couldn't find host %s in the .netrc file, using defaults",
Daniel Stenberg
committed
else
Daniel Stenberg
committed
conn->bits.user_passwd = 1; /* enable user+password */
Daniel Stenberg
committed
/* weather we failed or not, we don't know which fields that were filled
in anyway */
Daniel Stenberg
committed
if(!data->state.user[0])
strcpy(data->state.user, CURL_DEFAULT_USER);
if(!data->state.passwd[0])
strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
Daniel Stenberg
committed
else if(!(conn->bits.user_passwd) &&
(conn->protocol & (PROT_FTP|PROT_HTTP)) ) {
/* This is a FTP or HTTP URL, and we haven't got the user+password in
* the extra parameter, we will now try to extract the possible
* user+password pair in a string like:
* ftp://user:password@ftp.my.site:8021/README */
char *ptr=NULL; /* assign to remove possible warnings */
if((ptr=strchr(conn->name, '@'))) {
/* there's a user+password given here, to the left of the @ */
Daniel Stenberg
committed
data->state.user[0] =0;
data->state.passwd[0]=0;
if(*conn->name != ':') {
/* the name is given, get user+password */
sscanf(conn->name, "%127[^:@]:%127[^@]",
Daniel Stenberg
committed
data->state.user, data->state.passwd);
}
else
/* no name given, get the password only */
Daniel Stenberg
committed
sscanf(conn->name+1, "%127[^@]", data->state.passwd);
Daniel Stenberg
committed
if(data->state.user[0]) {
char *newname=curl_unescape(data->state.user, 0);
if(strlen(newname) < sizeof(data->state.user)) {
strcpy(data->state.user, newname);
}
/* if the new name is longer than accepted, then just use
the unconverted name, it'll be wrong but what the heck */
free(newname);
}
/* check for password, if no ask for one */
Daniel Stenberg
committed
if( !data->state.passwd[0] ) {
if(!data->set.fpasswd ||
data->set.fpasswd(data->set.passwd_client,
"password:", data->state.passwd,
sizeof(data->state.passwd)))
return CURLE_BAD_PASSWORD_ENTERED;
else {
/* we have a password found in the URL, decode it! */
Daniel Stenberg
committed
char *newpasswd=curl_unescape(data->state.passwd, 0);
if(strlen(newpasswd) < sizeof(data->state.passwd)) {
strcpy(data->state.passwd, newpasswd);
}
free(newpasswd);
}
Daniel Stenberg
committed
conn->bits.user_passwd=TRUE; /* enable user+password */
Daniel Stenberg
committed
strcpy(data->state.user, CURL_DEFAULT_USER);
strcpy(data->state.passwd, CURL_DEFAULT_PASSWORD);
/*************************************************************
* 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)
*************************************************************/
if((1 == sscanf(conn->name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) &&
Daniel Stenberg
committed
(']' == endbracket)) {
/* This is a (IPv6-style) specified IP-address. We support _any_
IP within brackets to be really generic. */
conn->name++; /* pass the starting bracket */
Daniel Stenberg
committed
tmp = strchr(conn->name, ']');
*tmp = 0; /* zero terminate */
Daniel Stenberg
committed
tmp++; /* pass the ending bracket */
if(':' != *tmp)
tmp = NULL; /* no port number available */
}
else {
/* traditional IPv4-style port-extracting */
tmp = strchr(conn->name, ':');
}
if (tmp) {
*tmp++ = '\0'; /* cut off the name there */
conn->remote_port = atoi(tmp);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(data->change.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 need to make a duplicate of the proxy so that we can modify the
string safely. */
Daniel Stenberg
committed
char *proxydup=strdup(data->change.proxy);
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
/* We use 'proxyptr' to point to the proxy name from now on... */
char *proxyptr=proxydup;
if(NULL == proxydup) {
failf(data, "memory shortage");
return CURLE_OUT_OF_MEMORY;
}
/* Daniel Dec 10, 1998:
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. */
/* 1. skip the protocol part if present */
endofprot=strstr(proxyptr, "://");
if(endofprot) {
proxyptr = endofprot+3;
}
/* allow user to specify proxy.server.com:1080 if desired */
prox_portno = strchr (proxyptr, ':');
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);
}
Daniel Stenberg
committed
else if(data->set.proxyport) {
/* None given in the proxy string, then get the default one if it is
given */
Daniel Stenberg
committed
conn->port = data->set.proxyport;
}
/* now, clone the cleaned proxy host name */
conn->proxyhost = strdup(proxyptr);
free(proxydup); /* free the duplicate pointer and not the modified */
}
/*************************************************************
* 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.
*************************************************************/
/* reuse_fresh is set TRUE if we are told to use a fresh connection
by force */
Daniel Stenberg
committed
if(!data->set.reuse_fresh &&
ConnectionExists(data, conn, &conn_temp)) {
/*
* 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.
*/
Daniel Stenberg
committed
struct connectdata *old_conn = conn;
char *path = old_conn->path; /* setup the current path pointer properly */
char *ppath = old_conn->ppath; /* this is the modified path pointer */
Daniel Stenberg
committed
if(old_conn->proxyhost)
free(old_conn->proxyhost);
conn = conn_temp; /* use this connection from now on */
Daniel Stenberg
committed
/* we need these pointers if we speak over a proxy */
conn->hostname = old_conn->gname;
Daniel Stenberg
committed
free(conn->path); /* free the previously allocated path pointer */
/* 'path' points to the allocated data, 'ppath' may have been advanced
to point somewhere within the 'path' area. */
conn->path = path;
conn->ppath = ppath;
/* re-use init */
conn->bits.reuse = TRUE; /* yes, we're re-using here */
conn->bits.chunk = FALSE; /* always assume not chunked unless told
otherwise */
conn->maxdownload = -1; /* might have been used previously! */
Daniel Stenberg
committed
free(old_conn); /* we don't need this anymore */
/*
* If we're doing a resumed transfer, we need to setup our stuff
* properly.