Newer
Older
if (conn->protocol & (PROT_FTP|PROT_HTTP)) {
/* This is a FTP or HTTP URL, 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=strchr(conn->host.name, '@');
char *userpass = conn->host.name;
if(ptr != NULL) {
/* there's a user+password given here, to the left of the @ */
conn->host.name = ++ptr;
/* So the hostname is sane. Only bother interpreting the
* results if we could care. It could still be wasted
* work because it might be overtaken by the programmatically
* set user/passwd, but doing that first adds more cases here :-(
*/
if (data->set.use_netrc != CURL_NETRC_REQUIRED) {
/* We could use the one in the URL */
conn->bits.user_passwd = 1; /* enable user+password */
if(*userpass != ':') {
/* the name is given, get user+password */
sscanf(userpass, "%127[^:@]:%127[^@]",
user, passwd);
}
else
/* no name given, get the password only */
sscanf(userpass, ":%127[^@]", passwd);
if(user[0]) {
char *newname=curl_unescape(user, 0);
if(!newname)
return CURLE_OUT_OF_MEMORY;
if(strlen(newname) < sizeof(user))
strcpy(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);
}
if (passwd[0]) {
/* we have a password found in the URL, decode it! */
char *newpasswd=curl_unescape(passwd, 0);
if(!newpasswd)
return CURLE_OUT_OF_MEMORY;
if(strlen(newpasswd) < sizeof(passwd))
strcpy(passwd, newpasswd);
free(newpasswd);
}
}
}
}
/* 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 "inherite initial knowledge' above,
* so it doesn't have to be set in this block
*/
if (data->set.userpwd != NULL) {
/* the name is given, get user+password */
Daniel Stenberg
committed
sscanf(data->set.userpwd,
"%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
if (data->set.use_netrc != CURL_NETRC_IGNORED) {
if(Curl_parsenetrc(conn->host.name,
user, passwd,
data->set.netrc_file)) {
infof(data, "Couldn't find host %s in the .netrc file, using defaults\n",
conn->host.name);
conn->bits.user_passwd = 1; /* enable user+password */
}
/* If our protocol needs a password and we have none, use the defaults */
Daniel Stenberg
committed
if ( (conn->protocol & PROT_FTP) &&
!conn->bits.user_passwd) {
Daniel Stenberg
committed
conn->user = strdup(CURL_DEFAULT_USER);
conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
/* This is the default password, so DON'T set conn->bits.user_passwd */
}
Daniel Stenberg
committed
else {
/* store user + password, zero-length if not set */
conn->user = strdup(user);
conn->passwd = strdup(passwd);
Daniel Stenberg
committed
}
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.
*************************************************************/
/* get a cloned copy of the SSL config situation stored in the
Daniel Stenberg
committed
connection struct */
if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
/* 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)
Daniel Stenberg
committed
reuse = FALSE;
Daniel Stenberg
committed
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.
*/
Daniel Stenberg
committed
struct connectdata *old_conn = conn;
Daniel Stenberg
committed
if(old_conn->proxy.rawalloc)
free(old_conn->proxy.rawalloc);
Daniel Stenberg
committed
/* 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 */
Daniel Stenberg
committed
/* 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;
conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
/* host can change, when doing keepalive with a proxy ! */
if (conn->bits.httpproxy) {
free(conn->host.rawalloc);
conn->host=old_conn->host;
}
Daniel Stenberg
committed
/* get the newly set value, not the old one */
conn->bits.no_body = old_conn->bits.no_body;
if (!conn->bits.httpproxy)
free(old_conn->host.rawalloc); /* free the newly allocated name buffer */
Daniel Stenberg
committed
free(conn->pathbuffer); /* free the newly allocated path pointer */
conn->pathbuffer = old_conn->pathbuffer; /* use the old one */
conn->path = old_conn->path;
/* 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
Curl_safefree(old_conn->user);
Curl_safefree(old_conn->passwd);
Curl_safefree(old_conn->proxyuser);
Curl_safefree(old_conn->proxypasswd);
if(old_conn->bits.rangestringalloc)
free(old_conn->range);
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.
*/
Daniel Stenberg
committed
conn->resume_from = data->set.set_resume_from;
if (conn->resume_from) {
if (conn->bits.rangestringalloc == TRUE)
free(conn->range);
Daniel Stenberg
committed
conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from);
if(!conn->range)
return CURLE_OUT_OF_MEMORY;
/* tell ourselves to fetch this range */
conn->bits.use_range = TRUE; /* enable range download */
conn->bits.rangestringalloc = TRUE; /* mark range string allocated */
Daniel Stenberg
committed
else if (data->set.set_range) {
/* There is a range, but is not a resume, useful for random ftp access */
Daniel Stenberg
committed
conn->range = strdup(data->set.set_range);
Daniel Stenberg
committed
if(!conn->range)
return CURLE_OUT_OF_MEMORY;
conn->bits.rangestringalloc = TRUE; /* mark range string allocated */
conn->bits.use_range = TRUE; /* enable range download */
}
Daniel Stenberg
committed
else
conn->bits.use_range = FALSE; /* disable range download */
*in_connect = conn; /* return this instead! */
infof(data, "Re-using existing connection! (#%ld) with host %s\n",
conn->connectindex,
conn->bits.httpproxy?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);
}
/* Continue connectdata initialization here.
* Inherit the proper values from the urldata struct AFTER we have arranged
* the persistant conncetion stuff */
conn->fread = data->set.fread;
conn->fread_in = data->set.in;
conn->bits.upload_chunky =
((conn->protocol&PROT_HTTP) &&
data->set.upload &&
(data->set.infilesize == -1) &&
(data->set.httpversion != CURL_HTTP_VERSION_1_0))?
/* HTTP, upload, unknown file size and not HTTP 1.0 */
TRUE:
/* else, no chunky upload */
FALSE;
Daniel Stenberg
committed
#ifndef USE_ARES
/*************************************************************
Daniel Stenberg
committed
* Set timeout if that is being used, and we're not using an asynchronous
* name resolve.
*************************************************************/
if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
Daniel Stenberg
committed
/*************************************************************
* Set signal handler to catch SIGALRM
* Store the old value to be able to set it back later!
*************************************************************/
Daniel Stenberg
committed
#ifdef HAVE_SIGACTION
struct sigaction sigact;
sigaction(SIGALRM, NULL, &sigact);
keep_copysig = TRUE; /* yes, we have a copy */
Daniel Stenberg
committed
sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
/* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
sigact.sa_flags &= ~SA_RESTART;
#endif
/* now set the new struct */
sigaction(SIGALRM, &sigact, NULL);
#else /* HAVE_SIGACTION */
Daniel Stenberg
committed
/* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
keep_sigact = signal(SIGALRM, alarmfunc);
Daniel Stenberg
committed
#endif
#endif /* HAVE_SIGACTION */
Daniel Stenberg
committed
/* We set the timeout on the name resolving phase first, separately from
* the download/upload part to allow a maximum time on everything. This is
* a signal-based timeout, why it won't work and shouldn't be used in
* multi-threaded environments. */
Daniel Stenberg
committed
#ifdef HAVE_ALARM
/* alarm() makes a signal get sent when the timeout fires off, and that
will abort system calls */
Daniel Stenberg
committed
prev_alarm = alarm(data->set.connecttimeout?
data->set.connecttimeout:
data->set.timeout);
/* We can expect the conn->created time to be "now", as that was just
recently set in the beginning of this function and nothing slow
has been done since then until now. */
#endif
#endif /* SIGALRM */
}
#endif /* USE_ARES */
/*************************************************************
* Resolve the name of the server or proxy
*************************************************************/
if(conn->bits.reuse) {
/* re-used connection, no resolving is necessary */
hostaddr = NULL;
Daniel Stenberg
committed
/* we'll need to clear conn->dns_entry later in Curl_disconnect() */
if (conn->bits.httpproxy)
fix_hostname(conn, &conn->host);
else {
/* this is a fresh connect */
/* set a pointer to the hostname we display */
fix_hostname(conn, &conn->host);
if(!data->change.proxy || !*data->change.proxy) {
/* If not connecting via a proxy, extract the port from the URL, if it is
* there, thus overriding any defaults that might have been set above. */
conn->port = conn->remote_port; /* it is the same port */
/* Resolve target host right on */
rc = Curl_resolv(conn, conn->host.name, (int)conn->port, &hostaddr);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if(!hostaddr) {
failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
result = CURLE_COULDNT_RESOLVE_HOST;
/* don't return yet, we need to clean up the timeout first */
}
else {
/* This is a proxy that hasn't been resolved yet. */
fix_hostname(conn, &conn->proxy);
Daniel Stenberg
committed
/* resolve proxy */
rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if(!hostaddr) {
failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
result = CURLE_COULDNT_RESOLVE_PROXY;
/* don't return yet, we need to clean up the timeout first */
}
Daniel Stenberg
committed
*addr = hostaddr;
#if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
Daniel Stenberg
committed
#ifdef HAVE_SIGACTION
if(keep_copysig) {
/* we got a struct as it looked before, now put that one back nice
and clean */
sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
}
#else
#ifdef HAVE_SIGNAL
/* restore the previous SIGALRM handler */
signal(SIGALRM, keep_sigact);
#endif
#endif /* HAVE_SIGACTION */
Daniel Stenberg
committed
/* switch back the alarm() to either zero or to what it was before minus
the time we spent until now! */
if(prev_alarm) {
/* there was an alarm() set before us, now put it back */
Daniel Stenberg
committed
unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
unsigned long alarm_set;
Daniel Stenberg
committed
/* the alarm period is counted in even number of seconds */
alarm_set = prev_alarm - elapsed_ms/1000;
Daniel Stenberg
committed
if(!alarm_set ||
((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
/* if the alarm time-left reached zero or turned "negative" (counted
with unsigned values), we should fire off a SIGALRM here, but we
Daniel Stenberg
committed
won't, and zero would be to switch it off so we never set it to
less than 1! */
alarm(1);
result = CURLE_OPERATION_TIMEOUTED;
failf(data, "Previous alarm fired off!");
}
else
alarm((unsigned int)alarm_set);
Daniel Stenberg
committed
}
else
alarm(0); /* just shut it off */
}
#endif
Daniel Stenberg
committed
return result;
}
/* SetupConnection() should be called after the name resolve initiated in
* CreateConnection() is all done.
*/
Daniel Stenberg
committed
static CURLcode SetupConnection(struct connectdata *conn,
struct Curl_dns_entry *hostaddr)
{
struct SessionHandle *data = conn->data;
CURLcode result=CURLE_OK;
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
if(conn->protocol & PROT_FILE)
/* There's nothing in this function to setup if we're only doing
a file:// transfer */
Daniel Stenberg
committed
return result;
/*************************************************************
* Send user-agent to HTTP proxies even if the target protocol
* isn't HTTP.
*************************************************************/
if((conn->protocol&PROT_HTTP) ||
(data->change.proxy && *data->change.proxy)) {
Daniel Stenberg
committed
if(data->set.useragent) {
Daniel Stenberg
committed
Curl_safefree(conn->allocptr.uagent);
conn->allocptr.uagent =
Daniel Stenberg
committed
aprintf("User-Agent: %s\015\012", data->set.useragent);
if(!conn->allocptr.uagent)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
conn->bytecount = 0;
conn->headerbytecount = 0;
Daniel Stenberg
committed
if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
bool connected;
result = ConnectPlease(conn, hostaddr, &connected);
if(connected) {
result = Curl_protocol_connect(conn);
if(CURLE_OK == result)
conn->bits.tcpconnect = TRUE;
}
else
conn->bits.tcpconnect = FALSE;
Daniel Stenberg
committed
else {
Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
conn->bits.tcpconnect = TRUE;
Daniel Stenberg
committed
if(data->set.verbose)
verboseconnect(conn);
Daniel Stenberg
committed
conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
set this here perhaps a second time */
#ifdef __EMX__
/* 20000330 mgs
* the check is quite a hack...
* we're calling _fsetmode to fix the problem with fwrite converting newline
* characters (you get mangled text files, and corrupted binary files when
* you download to stdout and redirect it to a file). */
Daniel Stenberg
committed
if ((data->set.out)->_handle == NULL) {
_fsetmode(stdout, "b");
}
#endif
Daniel Stenberg
committed
CURLcode Curl_connect(struct SessionHandle *data,
Daniel Stenberg
committed
struct connectdata **in_connect,
bool *asyncp)
Daniel Stenberg
committed
struct Curl_dns_entry *dns;
Daniel Stenberg
committed
*asyncp = FALSE; /* assume synchronous resolves by default */
/* call the stuff that needs to be called */
Daniel Stenberg
committed
code = CreateConnection(data, in_connect, &dns, asyncp);
if(CURLE_OK == code) {
/* no error */
if(dns || !*asyncp)
/* If an address is available it means that we already have the name
resolved, OR it isn't async.
If so => continue connecting from here */
code = SetupConnection(*in_connect, dns);
/* else
response will be received and treated async wise */
}
if(CURLE_OK != code) {
/* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */
Daniel Stenberg
committed
if(*in_connect) {
Curl_disconnect(*in_connect); /* close the connection */
*in_connect = NULL; /* return a NULL */
Daniel Stenberg
committed
}
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Call this function after Curl_connect() has returned async=TRUE and
then a successful name resolve has been received */
CURLcode Curl_async_resolved(struct connectdata *conn)
{
#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
defined(USE_THREADING_GETADDRINFO)
Daniel Stenberg
committed
CURLcode code = SetupConnection(conn, conn->async.dns);
if(code)
/* We're not allowed to return failure with memory left allocated
in the connectdata struct, free those here */
Curl_disconnect(conn); /* close the connection */
return code;
#else
(void)conn;
return CURLE_OK;
#endif
}
Daniel Stenberg
committed
CURLcode Curl_done(struct connectdata **connp,
CURLcode status) /* an error if this is called after an
error was detected */
Daniel Stenberg
committed
struct connectdata *conn = *connp;
struct SessionHandle *data=conn->data;
/* cleanups done even if the connection is re-used */
if(conn->bits.rangestringalloc) {
free(conn->range);
conn->bits.rangestringalloc = FALSE;
}
/* Cleanup possible redirect junk */
if(conn->newurl) {
free(conn->newurl);
conn->newurl = NULL;
}
/* this calls the protocol-specific function pointer previously set */
if(conn->curl_done)
result = conn->curl_done(conn, status);
Curl_pgrsDone(conn); /* done with the operation */
Daniel Stenberg
committed
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this no matter what we think.
if conn->bits.close is TRUE, it means that the connection should be
closed in spite of all our efforts to be nice, due to protocol
restrictions in our or the server's end */
Daniel Stenberg
committed
if(data->set.reuse_forbid || conn->bits.close) {
CURLcode res2;
res2 = Curl_disconnect(conn); /* close the connection */
Daniel Stenberg
committed
*connp = NULL; /* to make the caller of this function better detect that
this was actually killed here */
Daniel Stenberg
committed
/* If we had an error already, make sure we return that one. But
if we got a new error, return that. */
if(!result && res2)
result = res2;
}
else
infof(data, "Connection #%ld to host %s left intact\n",
conn->connectindex,
conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
CURLcode Curl_do(struct connectdata **connp)
CURLcode result=CURLE_OK;
struct connectdata *conn = *connp;
struct SessionHandle *data=conn->data;
conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */
/* generic protocol-specific function pointer set in curl_connect() */
result = conn->curl_do(conn);
/* This was formerly done in transfer.c, but we better do it here */
if((CURLE_SEND_ERROR == result) && conn->bits.reuse) {
/* This was a re-use of a connection and we got a write error in the
* DO-phase. Then we DISCONNECT this connection and have another attempt
* to CONNECT and then DO again! The retry cannot possibly find another
* connection to re-use, since we only keep one possible connection for
* each. */
infof(data, "Re-used connection seems dead, get a new one\n");
conn->bits.close = TRUE; /* enforce close of this connection */
Daniel Stenberg
committed
result = Curl_done(&conn, result); /* we are so done with this */
Daniel Stenberg
committed
/* conn may no longer be a good pointer */
if(CURLE_OK == result) {
Daniel Stenberg
committed
bool async;
/* Now, redo the connect and get a new connection */
Daniel Stenberg
committed
result = Curl_connect(data, connp, &async);
if(CURLE_OK == result) {
/* We have connected or sent away a name resolve query fine */
conn = *connp; /* setup conn to again point to something nice */
Daniel Stenberg
committed
if(async) {
/* Now, if async is TRUE here, we need to wait for the name
to resolve */
result = Curl_wait_for_resolv(conn, NULL);
if(result)
return result;
Daniel Stenberg
committed
/* Resolved, continue with the connection */
Daniel Stenberg
committed
if(result)
return result;
}
/* ... finally back to actually retry the DO phase */
Daniel Stenberg
committed
result = conn->curl_do(conn);
}
CURLcode Curl_do_more(struct connectdata *conn)
{
CURLcode result=CURLE_OK;
if(conn->curl_do_more)
result = conn->curl_do_more(conn);
return result;
}
Daniel Stenberg
committed
static bool safe_strequal(char* str1, char* str2)
{
if(str1 && str2)
/* both pointers point to something then compare them */
return strequal(str1, str2);
else
/* if both pointers are NULL then treat them as equal */
return (!str1 && !str2);
}
bool
Curl_ssl_config_matches(struct ssl_config_data* data,
struct ssl_config_data* needle)
Daniel Stenberg
committed
{
if((data->version == needle->version) &&
(data->verifypeer == needle->verifypeer) &&
(data->verifyhost == needle->verifyhost) &&
safe_strequal(data->CApath, needle->CApath) &&
safe_strequal(data->CAfile, needle->CAfile) &&
safe_strequal(data->random_file, needle->random_file) &&
safe_strequal(data->egdsocket, needle->egdsocket) &&
safe_strequal(data->cipher_list, needle->cipher_list))
Daniel Stenberg
committed
Daniel Stenberg
committed
}
bool
Curl_clone_ssl_config(struct ssl_config_data *source,
struct ssl_config_data *dest)
Daniel Stenberg
committed
{
dest->verifyhost = source->verifyhost;
dest->verifypeer = source->verifypeer;
dest->version = source->version;
Daniel Stenberg
committed
if(source->CAfile) {
dest->CAfile = strdup(source->CAfile);
if(!dest->CAfile)
Daniel Stenberg
committed
}
if(source->CApath) {
dest->CApath = strdup(source->CApath);
if(!dest->CApath)
Daniel Stenberg
committed
}
if(source->cipher_list) {
dest->cipher_list = strdup(source->cipher_list);
if(!dest->cipher_list)
Daniel Stenberg
committed
}
if(source->egdsocket) {
dest->egdsocket = strdup(source->egdsocket);
if(!dest->egdsocket)
Daniel Stenberg
committed
}
if(source->random_file) {
dest->random_file = strdup(source->random_file);
if(!dest->random_file)
Daniel Stenberg
committed
}
return TRUE;
}
void Curl_free_ssl_config(struct ssl_config_data* sslc)
Daniel Stenberg
committed
{
if(sslc->CAfile)
free(sslc->CAfile);
if(sslc->CApath)
free(sslc->CApath);
if(sslc->cipher_list)
free(sslc->cipher_list);
if(sslc->egdsocket)
free(sslc->egdsocket);
if(sslc->random_file)
free(sslc->random_file);
}