Newer
Older
case CURLOPT_SSH_AUTH_TYPES:
data->set.ssh_auth_types = va_arg(param, long);
break;
case CURLOPT_SSH_PUBLIC_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa.pub file
*/
result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
Daniel Stenberg
committed
va_arg(param, char *));
break;
case CURLOPT_SSH_PRIVATE_KEYFILE:
/*
* Use this file instead of the $HOME/.ssh/id_dsa file
*/
result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
Daniel Stenberg
committed
va_arg(param, char *));
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
/*
* Option to allow for the MD5 of the host public key to be checked
* for validation purposes.
*/
result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
va_arg(param, char *));
break;
Daniel Stenberg
committed
case CURLOPT_HTTP_TRANSFER_DECODING:
/*
* disable libcurl transfer encoding is used
*/
data->set.http_te_skip = (bool)(0 == va_arg(param, long));
break;
case CURLOPT_HTTP_CONTENT_DECODING:
/*
* raw data passed to the application when content encoding is used
*/
data->set.http_ce_skip = (bool)(0 == va_arg(param, long));
break;
case CURLOPT_NEW_FILE_PERMS:
/*
* Uses these permissions instead of 0644
*/
data->set.new_file_perms = va_arg(param, long);
break;
case CURLOPT_NEW_DIRECTORY_PERMS:
/*
* Uses these permissions instead of 0755
*/
data->set.new_directory_perms = va_arg(param, long);
break;
Daniel Stenberg
committed
case CURLOPT_PROXY_TRANSFER_MODE:
/*
* set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy
*/
switch (va_arg(param, long)) {
case 0:
data->set.proxy_transfer_mode = FALSE;
break;
case 1:
data->set.proxy_transfer_mode = TRUE;
break;
default:
/* reserve other values for future use */
result = CURLE_FAILED_INIT;
break;
}
break;
default:
/* unknown tag and its companion, just ignore: */
Daniel Stenberg
committed
result = CURLE_FAILED_INIT; /* correct this */
break;
Daniel Stenberg
committed
return result;
static void conn_free(struct connectdata *conn)
{
Daniel Stenberg
committed
if(!conn)
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
return;
/* close possibly still open sockets */
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
sclose(conn->sock[SECONDARYSOCKET]);
if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET])
sclose(conn->sock[FIRSTSOCKET]);
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
Curl_safefree(conn->proxyuser);
Curl_safefree(conn->proxypasswd);
Curl_safefree(conn->allocptr.proxyuserpwd);
Curl_safefree(conn->allocptr.uagent);
Curl_safefree(conn->allocptr.userpwd);
Curl_safefree(conn->allocptr.accept_encoding);
Curl_safefree(conn->allocptr.rangeline);
Curl_safefree(conn->allocptr.ref);
Curl_safefree(conn->allocptr.host);
Curl_safefree(conn->allocptr.cookiehost);
Curl_safefree(conn->ip_addr_str);
Curl_safefree(conn->trailer);
Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
Daniel Stenberg
committed
Curl_safefree(conn->master_buffer);
Curl_llist_destroy(conn->send_pipe, NULL);
Curl_llist_destroy(conn->recv_pipe, NULL);
/* possible left-overs from the async name resolvers */
#if defined(USE_ARES)
Curl_safefree(conn->async.hostname);
Curl_safefree(conn->async.os_specific);
#elif defined(CURLRES_THREADED)
Curl_destroy_thread_data(&conn->async);
#endif
Daniel Stenberg
committed
Curl_ssl_close(conn, FIRSTSOCKET);
Curl_ssl_close(conn, SECONDARYSOCKET);
Daniel Stenberg
committed
Curl_free_ssl_config(&conn->ssl_config);
free(conn); /* free all the connection oriented data */
}
CURLcode Curl_disconnect(struct connectdata *conn)
struct SessionHandle *data;
if(!conn)
return CURLE_OK; /* this is closed and fine already */
Daniel Stenberg
committed
if(!data) {
DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n"));
return CURLE_OK;
}
Daniel Stenberg
committed
#if defined(CURLDEBUG) && defined(AGGRESIVE_TEST)
/* scan for DNS cache entries still marked as in use */
Curl_hash_apply(data->hostcache,
NULL, Curl_scan_cache_used);
#endif
Daniel Stenberg
committed
Curl_expire(data, 0); /* shut off timers */
Daniel Stenberg
committed
Curl_hostcache_prune(data); /* kill old DNS cache entries */
if((conn->ntlm.state != NTLMSTATE_NONE) ||
(conn->proxyntlm.state != NTLMSTATE_NONE)) {
/* Authentication data is a mix of connection-related and sessionhandle-
related stuff. NTLM is connection-related so when we close the shop
we shall forget. */
data->state.authhost.done = FALSE;
data->state.authhost.want;
data->state.authproxy.done = FALSE;
data->state.authproxy.want;
data->state.authproblem = FALSE;
Curl_ntlm_cleanup(conn);
Daniel Stenberg
committed
if(conn->handler->disconnect)
Daniel Stenberg
committed
/* This is set if protocol-specific cleanups should be made */
Patrick Monnerat
committed
conn->handler->disconnect(conn);
Daniel Stenberg
committed
Daniel Stenberg
committed
/* unlink ourselves! */
infof(data, "Closing connection #%ld\n", conn->connectindex);
Daniel Stenberg
committed
if(data->state.connc)
/* only clear the table entry if we still know in which cache we
used to be in */
data->state.connc->connects[conn->connectindex] = NULL;
Daniel Stenberg
committed
if(conn->host.encalloc)
Daniel Stenberg
committed
idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
with idn_free() since this was allocated
by libidn */
if(conn->proxy.encalloc)
Daniel Stenberg
committed
idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
freed with idn_free() since this was
allocated by libidn */
Daniel Stenberg
committed
Curl_ssl_close(conn, FIRSTSOCKET);
/* Indicate to all handles on the pipe that we're dead */
Daniel Stenberg
committed
if(IsPipeliningEnabled(data)) {
Daniel Stenberg
committed
signalPipeClose(conn->send_pipe);
signalPipeClose(conn->recv_pipe);
}
Daniel Stenberg
committed
conn_free(conn);
Daniel Stenberg
committed
data->state.current_conn = NULL;
/*
* 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.
*/
static bool SocketIsDead(curl_socket_t sock)
{
int sval;
bool ret_val = TRUE;
sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
Daniel Stenberg
committed
if(sval == 0)
ret_val = FALSE;
static bool IsPipeliningPossible(const struct SessionHandle *handle)
{
Daniel Stenberg
committed
if(handle->multi && Curl_multi_canPipeline(handle->multi) &&
(handle->set.httpreq == HTTPREQ_GET ||
handle->set.httpreq == HTTPREQ_HEAD) &&
handle->set.httpversion != CURL_HTTP_VERSION_1_0)
return TRUE;
return FALSE;
}
static bool IsPipeliningEnabled(const struct SessionHandle *handle)
Daniel Stenberg
committed
{
Daniel Stenberg
committed
if(handle->multi && Curl_multi_canPipeline(handle->multi))
Daniel Stenberg
committed
return TRUE;
return FALSE;
}
CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
struct curl_llist *pipeline)
{
Daniel Stenberg
committed
#ifdef CURLDEBUG
if(!IsPipeliningPossible(data)) {
/* when not pipelined, there MUST be no handle in the list already */
if(pipeline->head)
Daniel Stenberg
committed
infof(data, "PIPE when no PIPE supposed!\n");
}
#endif
Daniel Stenberg
committed
if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
Daniel Stenberg
committed
int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
struct curl_llist *pipeline)
{
struct curl_llist_element *curr;
curr = pipeline->head;
Daniel Stenberg
committed
while(curr) {
if(curr->ptr == handle) {
Curl_llist_remove(pipeline, curr, NULL);
Daniel Stenberg
committed
return 1; /* we removed a handle */
}
curr = curr->next;
}
Daniel Stenberg
committed
Daniel Stenberg
committed
return 0;
}
Daniel Stenberg
committed
#if 0 /* this code is saved here as it is useful for debugging purposes */
static void Curl_printPipeline(struct curl_llist *pipeline)
{
struct curl_llist_element *curr;
curr = pipeline->head;
Daniel Stenberg
committed
while(curr) {
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
infof(data, "Handle in pipeline: %s\n", data->state.path);
curr = curr->next;
}
}
#endif
bool Curl_isHandleAtHead(struct SessionHandle *handle,
struct curl_llist *pipeline)
{
struct curl_llist_element *curr = pipeline->head;
Daniel Stenberg
committed
if(curr) {
}
return FALSE;
}
static struct SessionHandle* gethandleathead(struct curl_llist *pipeline)
{
struct curl_llist_element *curr = pipeline->head;
Daniel Stenberg
committed
if(curr) {
return (struct SessionHandle *) curr->ptr;
}
return NULL;
}
static void signalPipeClose(struct curl_llist *pipeline)
{
struct curl_llist_element *curr;
Daniel Stenberg
committed
if(!pipeline)
curr = pipeline->head;
Daniel Stenberg
committed
while(curr) {
struct curl_llist_element *next = curr->next;
struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
Daniel Stenberg
committed
#ifdef CURLDEBUG /* debug-only code */
if(data->magic != CURLEASY_MAGIC_NUMBER) {
/* MAJOR BADNESS */
infof(data, "signalPipeClose() found BAAD easy handle\n");
Daniel Stenberg
committed
}
#endif
Daniel Stenberg
committed
data->state.pipe_broke = TRUE;
Curl_multi_handlePipeBreak(data);
Curl_llist_remove(pipeline, curr, NULL);
curr = next;
}
}
* Given one filled in connection struct (named needle), this function should
* detect if there already is one that has all the significant details
* exactly the same and thus should be used instead.
*
* If there is a match, this function returns TRUE - and has marked the
* connection as 'in-use'. It must later be called with ConnectionDone() to
* return back to 'idle' (unused) state.
Daniel Stenberg
committed
ConnectionExists(struct SessionHandle *data,
struct connectdata *needle,
struct connectdata **usethis)
struct connectdata *check;
bool canPipeline = IsPipeliningPossible(data);
for(i=0; i< data->state.connc->num; i++) {
bool match = FALSE;
/*
* Note that if we use a HTTP proxy, we check connections to that
* proxy and not to the actual remote server.
*/
check = data->state.connc->connects[i];
if(!check)
/* NULL pointer means not filled-in entry */
continue;
pipeLen = check->send_pipe->size + check->recv_pipe->size;
Daniel Stenberg
committed
if(check->connectindex == -1) {
Daniel Stenberg
committed
check->connectindex = i; /* Set this appropriately since it might have
been set to -1 when the easy was removed
from the multi */
}
if(pipeLen > 0 && !canPipeline) {
Daniel Stenberg
committed
/* can only happen within multi handles, and means that another easy
handle is using this connection */
Daniel Stenberg
committed
continue;
}
Daniel Stenberg
committed
#ifdef CURLRES_ASYNCH
Daniel Stenberg
committed
/* ip_addr_str is NULL only if the resolving of the name hasn't completed
yet and until then we don't re-use this connection */
Daniel Stenberg
committed
if(!check->ip_addr_str) {
Daniel Stenberg
committed
infof(data,
"Connection #%ld hasn't finished name resolve, can't reuse\n",
Daniel Stenberg
committed
check->connectindex);
continue;
Daniel Stenberg
committed
}
#endif
Daniel Stenberg
committed
if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
/* Don't pick a connection that hasn't connected yet or that is going to
get closed. */
infof(data, "Connection #%ld isn't open enough, can't reuse\n",
check->connectindex);
#ifdef CURLDEBUG
Daniel Stenberg
committed
if(check->recv_pipe->size > 0) {
infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
check->connectindex);
}
#endif
continue;
}
Daniel Stenberg
committed
if(pipeLen >= MAX_PIPELINE_LENGTH) {
Daniel Stenberg
committed
infof(data, "Connection #%ld has its pipeline full, can't reuse\n",
check->connectindex);
continue;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(canPipeline) {
/* Make sure the pipe has only GET requests */
struct SessionHandle* sh = gethandleathead(check->send_pipe);
struct SessionHandle* rh = gethandleathead(check->recv_pipe);
Daniel Stenberg
committed
if(sh) {
if(!IsPipeliningPossible(sh))
continue;
}
Daniel Stenberg
committed
else if(rh) {
if(!IsPipeliningPossible(rh))
continue;
}
}
if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
/* don't do mixed SSL and non-SSL connections */
continue;
Daniel Stenberg
committed
if(needle->bits.proxy != check->bits.proxy)
/* don't do mixed proxy and non-proxy connections */
continue;
Daniel Stenberg
committed
if(!needle->bits.httpproxy || needle->protocol&PROT_SSL ||
(needle->bits.httpproxy && check->bits.httpproxy &&
needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
strequal(needle->proxy.name, check->proxy.name) &&
(needle->port == check->port))) {
/* The requested connection does not use a HTTP proxy or it uses SSL or
it is a non-SSL protocol tunneled over the same http proxy name and
port number */
if(strequal(needle->protostr, check->protostr) &&
strequal(needle->host.name, check->host.name) &&
(needle->remote_port == check->remote_port) ) {
Daniel Stenberg
committed
if(needle->protocol & PROT_SSL) {
/* This is SSL, verify that we're using the same
ssl options as well */
if(!Curl_ssl_config_matches(&needle->ssl_config,
&check->ssl_config)) {
Daniel Stenberg
committed
infof(data,
"Connection #%ld has different SSL parameters, "
"can't reuse\n",
check->connectindex );
Daniel Stenberg
committed
continue;
}
}
if((needle->protocol & PROT_FTP) ||
((needle->protocol & PROT_HTTP) &&
(data->state.authhost.want==CURLAUTH_NTLM))) {
/* This is FTP or HTTP+NTLM, verify that we're using the same name
and password as well */
if(!strequal(needle->user, check->user) ||
!strequal(needle->passwd, check->passwd)) {
/* one of them was different */
continue;
}
}
match = TRUE;
else { /* The requested needle connection is using a proxy,
Daniel Stenberg
committed
is the checked one using the same host, port and type? */
if(check->bits.proxy &&
(needle->proxytype == check->proxytype) &&
strequal(needle->proxy.name, check->proxy.name) &&
needle->port == check->port) {
/* This is the same proxy connection, use it! */
match = TRUE;
}
}
if(match) {
Daniel Stenberg
committed
if(!IsPipeliningEnabled(data)) {
Daniel Stenberg
committed
/* The check for a dead socket makes sense only in the
non-pipelining case */
bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
if(dead) {
Daniel Stenberg
committed
check->data = data;
Daniel Stenberg
committed
infof(data, "Connection #%d seems to be dead!\n", i);
Daniel Stenberg
committed
Curl_disconnect(check); /* disconnect resources */
data->state.connc->connects[i]=NULL; /* nothing here */
Daniel Stenberg
committed
return FALSE;
}
check->inuse = TRUE; /* mark this as being in use so that no other
handle in a multi stack may nick it */
Daniel Stenberg
committed
if(canPipeline) {
Daniel Stenberg
committed
/* Mark the connection as being in a pipeline */
check->is_in_pipeline = TRUE;
}
*usethis = check;
return TRUE; /* yes, we found one to use! */
Daniel Stenberg
committed
return FALSE; /* no matching connecting exists */
}
Daniel Stenberg
committed
/*
* 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.
*/
Daniel Stenberg
committed
ConnectionKillOne(struct SessionHandle *data)
long highscore=-1;
long connindex=-1;
long score;
struct timeval now;
now = Curl_tvnow();
for(i=0; data->state.connc && (i< data->state.connc->num); i++) {
conn = data->state.connc->connects[i];
Daniel Stenberg
committed
if(!conn || conn->inuse)
/* Set higher score for the age passed since the connection was used */
score = Curl_tvdiff(now, conn->now);
if(score > highscore) {
highscore = score;
connindex = i;
}
}
if(connindex >= 0) {
/* Set the connection's owner correctly */
conn = data->state.connc->connects[connindex];
conn->data = data;
/* the winner gets the honour of being disconnected */
(void)Curl_disconnect(conn);
/* clean the array entry */
data->state.connc->connects[connindex] = NULL;
}
return connindex; /* return the available index or -1 */
}
/* this connection can now be marked 'idle' */
static void
ConnectionDone(struct connectdata *conn)
{
conn->inuse = FALSE;
Daniel Stenberg
committed
if(!conn->send_pipe && !conn->recv_pipe)
Daniel Stenberg
committed
conn->is_in_pipeline = FALSE;
}
/*
* 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.
*/
Daniel Stenberg
committed
ConnectionStore(struct SessionHandle *data,
struct connectdata *conn)
{
for(i=0; i< data->state.connc->num; i++) {
if(!data->state.connc->connects[i])
if(i == data->state.connc->num) {
/* there was no room available, kill one */
i = ConnectionKillOne(data);
Daniel Stenberg
committed
if(-1 != i)
Daniel Stenberg
committed
infof(data, "Connection (#%d) was killed to make room (holds %d)\n",
i, data->state.connc->num);
Daniel Stenberg
committed
else
infof(data, "This connection did not fit in the connection cache\n");
conn->connectindex = i; /* Make the child know where the pointer to this
particular data is stored. But note that this -1
if this is not within the cache and this is
probably not checked for everywhere (yet). */
conn->inuse = TRUE;
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.
TODO: make sure we really can work with more handles than positions in
the cache, or possibly we should (allow to automatically) resize the
connection cache when we add more easy handles to a multi handle!
*/
data->state.connc->connects[i] = conn; /* fill in this */
conn->data = data;
static CURLcode ConnectPlease(struct SessionHandle *data,
struct connectdata *conn,
struct Curl_dns_entry *hostaddr,
bool *connected)
Daniel Stenberg
committed
Curl_addrinfo *addr;
Daniel Stenberg
committed
char *hostname = conn->bits.proxy?conn->proxy.name:conn->host.name;
Daniel Stenberg
committed
Daniel Stenberg
committed
infof(data, "About to connect() to %s%s port %d (#%d)\n",
Daniel Stenberg
committed
conn->bits.proxy?"proxy ":"",
Daniel Stenberg
committed
hostname, conn->port, conn->connectindex);
/*************************************************************
*************************************************************/
hostaddr,
Daniel Stenberg
committed
&conn->sock[FIRSTSOCKET],
&addr,
connected);
Daniel Stenberg
committed
/* All is cool, then we store the current information */
conn->dns_entry = hostaddr;
conn->ip_addr = addr;
Daniel Stenberg
committed
result = Curl_store_ip_addr(conn);
if(CURLE_OK == result) {
switch(data->set.proxytype) {
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
conn->host.name, conn->remote_port,
FIRSTSOCKET, conn);
/* do nothing here. handled later. */
break;
case CURLPROXY_SOCKS4:
Daniel Stenberg
committed
result = Curl_SOCKS4(conn->proxyuser, conn->host.name,
conn->remote_port, FIRSTSOCKET, conn, FALSE);
Daniel Stenberg
committed
break;
case CURLPROXY_SOCKS4A:
result = Curl_SOCKS4(conn->proxyuser, conn->host.name,
conn->remote_port, FIRSTSOCKET, conn, TRUE);
failf(data, "unknown proxytype option given");
result = CURLE_COULDNT_CONNECT;
break;
Daniel Stenberg
committed
}
}
Daniel Stenberg
committed
if(result)
*connected = FALSE; /* mark it as not connected */
* verboseconnect() displays verbose information after a connect
static void verboseconnect(struct connectdata *conn)
Daniel Stenberg
committed
{
Daniel Stenberg
committed
infof(conn->data, "Connected to %s (%s) port %d (#%d)\n",
Daniel Stenberg
committed
conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname,
Daniel Stenberg
committed
conn->ip_addr_str, conn->port, conn->connectindex);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
Daniel Stenberg
committed
int Curl_protocol_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
Patrick Monnerat
committed
if(conn->handler->proto_getsock)
return conn->handler->proto_getsock(conn, socks, numsocks);
Daniel Stenberg
committed
return GETSOCK_BLANK;
}
Daniel Stenberg
committed
int Curl_doing_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
Daniel Stenberg
committed
if(conn && conn->handler->doing_getsock)
Patrick Monnerat
committed
return conn->handler->doing_getsock(conn, socks, numsocks);
Daniel Stenberg
committed
return GETSOCK_BLANK;
}
/*
* We are doing protocol-specific connecting and this is being called over and
* over from the multi interface until the connection phase is done on
* protocol layer.
*/
CURLcode Curl_protocol_connecting(struct connectdata *conn,
bool *done)
{
CURLcode result=CURLE_OK;
Daniel Stenberg
committed
if(conn && conn->handler->connecting) {
*done = FALSE;
Patrick Monnerat
committed
result = conn->handler->connecting(conn, done);
}
else
*done = TRUE;
return result;
}
/*
* We are DOING this is being called over and over from the multi interface
* until the DOING phase is done on protocol layer.
*/
CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done)
{
CURLcode result=CURLE_OK;
Daniel Stenberg
committed
if(conn && conn->handler->doing) {
*done = FALSE;
Patrick Monnerat
committed
result = conn->handler->doing(conn, done);
}
else
*done = TRUE;
return result;
}
/*
* We have discovered that the TCP connection has been successful, we can now
* proceed with some action.
*
*/
CURLcode Curl_protocol_connect(struct connectdata *conn,
bool *protocol_done)
{
CURLcode result=CURLE_OK;
struct SessionHandle *data = conn->data;
*protocol_done = FALSE;
if(conn->bits.tcpconnect && conn->bits.protoconnstart) {
/* We already are connected, get back. This may happen when the connect
worked fine in the first call, like when we connect to a local server
or proxy. Note that we don't know if the protocol is actually done.
Unless this protocol doesn't have any protocol-connect callback, as
then we know we're done. */
Patrick Monnerat
committed
if(!conn->handler->connecting)
*protocol_done = TRUE;
return CURLE_OK;
}
if(!conn->bits.tcpconnect) {
Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
if(data->set.verbose)
verboseconnect(conn);
}
if(!conn->bits.protoconnstart) {
Patrick Monnerat
committed
if(conn->handler->connect_it) {
/* is there a protocol-specific connect() procedure? */
/* Set start time here for timeout purposes in the connect procedure, it
is later set again for the progress meter purpose */
conn->now = Curl_tvnow();
/* Call the protocol-specific connect function */
Patrick Monnerat
committed
result = conn->handler->connect_it(conn, protocol_done);
}
else
*protocol_done = TRUE;
/* it has started, possibly even completed but that knowledge isn't stored
in this bit! */
Daniel Stenberg
committed
if(!result)
Daniel Stenberg
committed
conn->bits.protoconnstart = TRUE;
}
return result; /* pass back status */
}
/*
* Helpers for IDNA convertions.
*/
#ifdef USE_LIBIDN
static bool is_ASCII_name(const char *hostname)
{
const unsigned char *ch = (const unsigned char*)hostname;
Daniel Stenberg
committed
while(*ch) {
if(*ch++ & 0x80)
return FALSE;
}
return TRUE;
}
/*
* Check if characters in hostname is allowed in Top Level Domain.
*/
static bool tld_check_name(struct SessionHandle *data,
const char *ace_hostname)
char *tld_errmsg = (char *)"<no msg>";
/* Convert (and downcase) ACE-name back into locale's character set */
rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0);
Daniel Stenberg
committed
if(rc != IDNA_SUCCESS)
#ifdef HAVE_TLD_STRERROR
Daniel Stenberg
committed
if(rc != TLD_SUCCESS)
#endif
Daniel Stenberg
committed
if(rc == TLD_INVALID)
infof(data, "WARNING: %s; pos %u = `%c'/0x%02X\n",
tld_errmsg, err_pos, uc_name[err_pos],
uc_name[err_pos] & 255);
Daniel Stenberg
committed
else if(rc != TLD_SUCCESS)
infof(data, "WARNING: TLD check for %s failed; %s\n",
uc_name, tld_errmsg);
#endif /* CURL_DISABLE_VERBOSE_STRINGS */
Daniel Stenberg
committed
if(uc_name)
#endif
static void fix_hostname(struct SessionHandle *data,
struct connectdata *conn, struct hostname *host)
{
#ifndef USE_LIBIDN
(void)data;
(void)conn;
#elif defined(CURL_DISABLE_VERBOSE_STRINGS)
(void)conn;
#endif
/* set the name we use to display the host name */
#ifdef USE_LIBIDN
/*************************************************************
* Check name for non-ASCII and convert hostname to ACE form.
*************************************************************/
Daniel Stenberg
committed
if(!is_ASCII_name(host->name) &&
Daniel Stenberg
committed
stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
char *ace_hostname = NULL;
int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0);
infof (data, "Input domain encoded as `%s'\n",
stringprep_locale_charset ());
Daniel Stenberg
committed
if(rc != IDNA_SUCCESS)
infof(data, "Failed to convert %s to ACE; %s\n",
host->name, Curl_idn_strerror(conn,rc));
else {
/* tld_check_name() displays a warning if the host name contains
"illegal" characters for this TLD */
(void)tld_check_name(data, ace_hostname);
host->encalloc = ace_hostname;
/* change the name pointer to point to the encoded hostname */
host->name = host->encalloc;
}
}
}
/*
* Parse URL and fill in the relevant members of the connection struct.
Daniel Stenberg
committed
*/
static CURLcode ParseURLAndFillConnection(struct SessionHandle *data,
struct connectdata *conn)
Daniel Stenberg
committed
char *at;
char *tmp;
Daniel Stenberg
committed
char *path = data->state.path;
/*************************************************************
* 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 ...
************************************************************/
if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]",
path)) && strequal(conn->protostr, "file")) {
if(path[0] == '/' && path[1] == '/') {
/* Allow omitted hostname (e.g. file:/<path>). This is not strictly
* speaking a valid file: URL by RFC 1738, but treating file:/<path> as
* file://localhost/<path> is similar to how other schemes treat missing
* hostnames. See RFC 1808. */
/* This cannot be done with strcpy() in a portable manner, since the
memory areas overlap! */
memmove(path, path + 2, strlen(path + 2)+1);
}
/*
* 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
*/
if(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(path, '/');
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 preceding 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 */
Daniel Stenberg
committed
/* This cannot be made with strcpy, as the memory chunks overlap! */
memmove(path, ptr, strlen(ptr)+1);
strcpy(conn->protostr, "file"); /* store protocol string lowercase */
/* clear path */
path[0]=0;
Daniel Stenberg
committed
if(2 > sscanf(data->change.url,
Daniel Stenberg
committed
"%15[^\n:]://%[^\n/]%[^\n]",
Daniel Stenberg
committed
conn->protostr,
conn->host.name, 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, "%[^\n/]%[^\n]",
conn->host.name, path)) ) {
/*
* 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.
*/