Newer
Older
#ifndef CURL_DISABLE_FTP
int port = PORT_FTP;
Daniel Stenberg
committed
#ifdef USE_SSL
conn->protocol |= PROT_FTPS|PROT_SSL;
Daniel Stenberg
committed
conn->ssl[SECONDARYSOCKET].use = TRUE; /* send data securely */
port = PORT_FTPS;
failf(data, LIBCURL_NAME
" was built with SSL disabled, ftps: not supported!");
Daniel Stenberg
committed
#endif /* !USE_SSL */
Daniel Stenberg
committed
conn->port = port;
conn->remote_port = port;
Daniel Stenberg
committed
if(data->change.proxy &&
*data->change.proxy &&
Daniel Stenberg
committed
!data->set.tunnel_thru_httpproxy) {
/* Unless we have asked to tunnel ftp operations through the proxy, we
switch and use HTTP operations only */
#ifndef CURL_DISABLE_HTTP
conn->curl_do = Curl_http;
conn->curl_done = Curl_http_done;
Daniel Stenberg
committed
conn->protocol = PROT_HTTP; /* switch to HTTP */
#else
failf(data, "FTP over http proxy requires HTTP support built-in!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
conn->curl_do = Curl_ftp;
conn->curl_do_more = Curl_ftp_nextconnect;
conn->curl_done = Curl_ftp_done;
conn->curl_connect = Curl_ftp_connect;
conn->curl_connecting = Curl_ftp_multi_statemach;
conn->curl_doing = Curl_ftp_doing;
conn->curl_proto_fdset = Curl_ftp_fdset;
conn->curl_doing_fdset = Curl_ftp_fdset;
Daniel Stenberg
committed
conn->path++; /* don't include the initial slash */
/* FTP URLs support an extension like ";type=<typecode>" that
Daniel Stenberg
committed
type=strstr(conn->path, ";type=");
type=strstr(conn->host.rawalloc, ";type=");
*type=0; /* it was in the middle of the hostname */
Daniel Stenberg
committed
command = toupper((int)type[6]);
data->set.ftp_ascii = 1;
break;
data->set.ftp_list_only = 1;
break;
/* switch off ASCII */
data->set.ftp_ascii = 0;
break;
#else /* CURL_DISABLE_FTP */
failf(data, LIBCURL_NAME
" was built with FTP disabled, ftp/ftps: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
else if(strequal(conn->protostr, "TELNET")) {
#ifndef CURL_DISABLE_TELNET
conn->protocol |= PROT_TELNET;
Daniel Stenberg
committed
conn->port = PORT_TELNET;
conn->remote_port = PORT_TELNET;
conn->curl_do = Curl_telnet;
conn->curl_done = Curl_telnet_done;
#else
failf(data, LIBCURL_NAME
" was built with TELNET disabled!");
#endif
else if (strequal(conn->protostr, "DICT")) {
#ifndef CURL_DISABLE_DICT
Daniel Stenberg
committed
conn->port = PORT_DICT;
conn->remote_port = PORT_DICT;
conn->curl_do = Curl_dict;
conn->curl_done = NULL; /* no DICT-specific done */
#else
failf(data, LIBCURL_NAME
" was built with DICT disabled!");
#endif
else if (strequal(conn->protostr, "LDAP")) {
#ifndef CURL_DISABLE_LDAP
Daniel Stenberg
committed
conn->port = PORT_LDAP;
conn->remote_port = PORT_LDAP;
conn->curl_do = Curl_ldap;
conn->curl_done = NULL; /* no LDAP-specific done */
#else
failf(data, LIBCURL_NAME
" was built with LDAP disabled!");
#endif
else if (strequal(conn->protostr, "FILE")) {
#ifndef CURL_DISABLE_FILE
conn->curl_do = Curl_file;
conn->curl_done = Curl_file_done;
/* 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) {
conn->bits.tcpconnect = TRUE; /* we are "connected */
result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
-1, NULL); /* no upload */
}
return result;
#else
failf(data, LIBCURL_NAME
" was built with FILE disabled!");
#endif
}
else if (strequal(conn->protostr, "TFTP")) {
#ifndef CURL_DISABLE_TFTP
char *type;
Daniel Stenberg
committed
conn->socktype = SOCK_DGRAM; /* UDP datagram based */
conn->protocol |= PROT_TFTP;
conn->port = PORT_TFTP;
conn->remote_port = PORT_TFTP;
conn->curl_connect = Curl_tftp_connect;
conn->curl_do = Curl_tftp;
Daniel Stenberg
committed
conn->curl_done = Curl_tftp_done;
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
/* TFTP URLs support an extension like ";mode=<typecode>" that
* we'll try to get now! */
type=strstr(conn->path, ";mode=");
if(!type) {
type=strstr(conn->host.rawalloc, ";mode=");
}
if(type) {
char command;
*type=0; /* it was in the middle of the hostname */
command = toupper((int)type[6]);
switch(command) {
case 'A': /* ASCII mode */
case 'N': /* NETASCII mode */
data->set.ftp_ascii = 1;
break;
case 'O': /* octet mode */
case 'I': /* binary mode */
default:
/* switch off ASCII */
data->set.ftp_ascii = 0;
break;
}
}
#else
failf(data, LIBCURL_NAME
" was built with TFTP disabled!");
#endif
/* We fell through all checks and thus we don't support the specified
protocol */
failf(data, "Unsupported protocol: %s", conn->protostr);
if(data->change.proxy && *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);
/* We use 'proxyptr' to point to the proxy name from now on... */
char *proxyptr=proxydup;
char *portptr;
Daniel Stenberg
committed
char *atsign;
if(NULL == proxydup) {
failf(data, "memory shortage");
return CURLE_OUT_OF_MEMORY;
}
/* 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;
Daniel Stenberg
committed
/* 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];
if(2 == sscanf(proxyptr,
Daniel Stenberg
committed
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
"%" 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_unescape(proxyuser,0);
if(!conn->proxyuser)
res = CURLE_OUT_OF_MEMORY;
else {
Curl_safefree(conn->proxypasswd);
conn->proxypasswd = curl_unescape(proxypasswd,0);
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(proxydup); /* free the former proxy string */
proxydup = proxyptr = atsign; /* now use this instead */
}
else
res = CURLE_OUT_OF_MEMORY;
}
if(res) {
free(proxydup); /* free the allocated proxy string */
return res;
}
Daniel Stenberg
committed
}
}
/* 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((int)*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);
}
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->proxy.rawalloc = strdup(proxyptr);
conn->proxy.name = conn->proxy.rawalloc;
free(proxydup); /* free the duplicate pointer and not the modified */
if(!conn->proxy.rawalloc)
return CURLE_OUT_OF_MEMORY;
/*************************************************************
* If the protcol 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
*
* We need somewhere to put the embedded details, so do that first.
*/
user[0] =0; /* to make everything well-defined */
passwd[0]=0;
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, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
user, passwd);
}
else
/* no name given, get the password only */
sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", 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);
}
}
}
}
Daniel Stenberg
committed
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
/*************************************************************
* 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[0-9a-fA-F:.]%c", &endbracket)) &&
(']' == endbracket)) {
/* this is a RFC2732-style specified IP-address */
conn->bits.ipv6_ip = TRUE;
conn->host.name++; /* pass the starting bracket */
tmp = strchr(conn->host.name, ']');
*tmp = 0; /* zero terminate */
tmp++; /* pass the ending bracket */
if(':' != *tmp)
tmp = NULL; /* no port number available */
}
else
tmp = strrchr(conn->host.name, ':');
Daniel Stenberg
committed
if(data->set.use_port && data->state.allow_port) {
/* if set, we use this and ignore the port possibly given in the URL */
Daniel Stenberg
committed
conn->remote_port = (unsigned short)data->set.use_port;
Daniel Stenberg
committed
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
if(tmp)
*tmp = '\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 */
char *url;
url = aprintf("http://%s:%d%s", conn->host.name, conn->remote_port,
conn->path);
if(!url)
return CURLE_OUT_OF_MEMORY;
if(data->change.url_alloc)
free(data->change.url);
data->change.url = url;
data->change.url_alloc = TRUE;
}
}
else if (tmp) {
/* no CURLOPT_PORT given, extract the one from the URL */
Daniel Stenberg
committed
char *rest;
unsigned long port;
port=strtoul(tmp+1, &rest, 10); /* Port number must be decimal */
if (rest != (tmp+1) && *rest == '\0') {
/* The colon really did have only digits after it,
* so it is either a port number or a mistake */
if (port > 0xffff) { /* Single unix standard says port numbers are
* 16 bits long */
failf(data, "Port number too large: %lu", port);
return CURLE_URL_MALFORMAT;
}
*tmp = '\0'; /* cut off the name there */
conn->remote_port = (unsigned short)port;
}
}
/* 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
*/
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]",
Daniel Stenberg
committed
conn->bits.netrc = FALSE;
if (data->set.use_netrc != CURL_NETRC_IGNORED) {
if(Curl_parsenetrc(conn->host.name,
user, passwd,
data->set.netrc_file)) {
Daniel Stenberg
committed
infof(data, "Couldn't find host %s in the " DOT_CHAR
"netrc file, using defaults\n",
conn->host.name);
Daniel Stenberg
committed
else {
/* set bits.netrc TRUE to remember that we got the name from a .netrc
file, so that it is safe to use even if we followed a Location: to a
different host or similar. */
conn->bits.netrc = TRUE;
conn->bits.user_passwd = 1; /* enable user+password */
Daniel Stenberg
committed
}
}
/* 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;
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.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 persistent connection 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 */
prev_alarm = alarm((unsigned int) (data->set.connecttimeout?
data->set.connecttimeout:
data->set.timeout));
Daniel Stenberg
committed
/* 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() is called after the name resolve initiated in
Daniel Stenberg
committed
* CreateConnection() is all done.
*
* NOTE: the argument 'hostaddr' is NULL when this function is called for a
* re-used connection.
Daniel Stenberg
committed
*/
Daniel Stenberg
committed
static CURLcode SetupConnection(struct connectdata *conn,
struct Curl_dns_entry *hostaddr,
bool *protocol_done)
Daniel Stenberg
committed
{
struct SessionHandle *data = conn->data;
CURLcode result=CURLE_OK;
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
if(conn->protocol & PROT_FILE) {
Daniel Stenberg
committed
/* There's nothing in this function to setup if we're only doing
a file:// transfer */
*protocol_done = TRUE;
Daniel Stenberg
committed
return result;
}
*protocol_done = FALSE; /* default to not done */
/*************************************************************
* 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 = FALSE;
result = ConnectPlease(conn, hostaddr, &connected);
if(connected) {
result = Curl_protocol_connect(conn, protocol_done);
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;
*protocol_done = 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,
bool *protocol_done)
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 this is a re-used connection 'dns'
will be NULL here. Continue connecting from here */
code = SetupConnection(*in_connect, dns, protocol_done);
Daniel Stenberg
committed
/* 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.
Note: this function disconnects and frees the conn data in case of
resolve failure */
CURLcode Curl_async_resolved(struct connectdata *conn,
bool *protocol_done)
Daniel Stenberg
committed
{
#if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
defined(USE_THREADING_GETADDRINFO)
CURLcode code = SetupConnection(conn, conn->async.dns, protocol_done);
Daniel Stenberg
committed
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;
Daniel Stenberg
committed
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;
}
if(conn->dns_entry) {
Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
conn->dns_entry = NULL;
}
/* Cleanup possible redirect junk */
if(conn->newurl) {
free(conn->newurl);
conn->newurl = NULL;
}