Newer
Older
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
dead = Curl_rtsp_connisdead(conn);
else
dead = SocketIsDead(conn->sock[FIRSTSOCKET]);
if(dead) {
conn->data = data;
infof(data, "Connection %ld seems to be dead!\n", conn->connection_id);
/* disconnect resources */
Curl_disconnect(conn, /* dead_connection */TRUE);
return TRUE;
}
}
return FALSE;
}
/*
* Wrapper to use disconnect_if_dead() function in Curl_conncache_foreach()
*
* Returns always 0.
*/
static int call_disconnect_if_dead(struct connectdata *conn,
void *param)
{
struct SessionHandle* data = (struct SessionHandle*)param;
disconnect_if_dead(conn, data);
return 0; /* continue iteration */
}
/*
* This function scans the connection cache for half-open/dead connections,
* closes and removes them.
* The cleanup is done at most once per second.
*/
static void prune_dead_connections(struct SessionHandle *data)
{
struct timeval now = Curl_tvnow();
long elapsed = Curl_tvdiff(now, data->state.conn_cache->last_cleanup);
if(elapsed >= 1000L) {
Curl_conncache_foreach(data->state.conn_cache, data,
call_disconnect_if_dead);
data->state.conn_cache->last_cleanup = now;
}
}
* 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.
*
* The force_reuse flag is set if the connection must be used, even if
* the pipelining strategy wants to open a new connection instead of reusing.
Daniel Stenberg
committed
ConnectionExists(struct SessionHandle *data,
struct connectdata *needle,
struct connectdata **usethis,
bool *force_reuse)
struct connectdata *check;
bool canPipeline = IsPipeliningPossible(data, needle);
bool wantNTLMhttp = ((data->state.authhost.want & CURLAUTH_NTLM) ||
(data->state.authhost.want & CURLAUTH_NTLM_WB)) &&
(needle->handler->protocol & PROTO_FAMILY_HTTP) ? TRUE : FALSE;
struct connectbundle *bundle;
*force_reuse = FALSE;
/* We can't pipe if the site is blacklisted */
if(canPipeline && Curl_pipeline_site_blacklisted(data, needle)) {
canPipeline = FALSE;
}
/* Look up the bundle with all the connections to this
particular host */
bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache);
if(bundle) {
size_t max_pipe_len = Curl_multi_max_pipeline_length(data->multi);
size_t best_pipe_len = max_pipe_len;
struct curl_llist_element *curr;
infof(data, "Found bundle for host %s: %p\n",
needle->host.name, (void *)bundle);
Daniel Stenberg
committed
/* We can't pipe if we don't know anything about the server */
if(canPipeline && !bundle->server_supports_pipelining) {
infof(data, "Server doesn't support pipelining\n");
canPipeline = FALSE;
}
curr = bundle->conn_list->head;
while(curr) {
bool match = FALSE;
#if defined(USE_NTLM)
bool credentialsMatch = FALSE;
size_t pipeLen;
Daniel Stenberg
committed
/*
* Note that if we use a HTTP proxy, we check connections to that
* proxy and not to the actual remote server.
*/
check = curr->ptr;
curr = curr->next;
if(disconnect_if_dead(check, data))
continue;
Daniel Stenberg
committed
pipeLen = check->send_pipe->size + check->recv_pipe->size;
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);
if(sh) {
if(!IsPipeliningPossible(sh, check))
continue;
}
else if(rh) {
if(!IsPipeliningPossible(rh, check))
continue;
}
Daniel Stenberg
committed
}
else {
if(pipeLen > 0) {
/* can only happen within multi handles, and means that another easy
handle is using this connection */
Daniel Stenberg
committed
if(Curl_resolver_asynch()) {
/* ip_addr_str[0] is NUL only if the resolving of the name hasn't
completed yet and until then we don't re-use this connection */
if(!check->ip_addr_str[0]) {
infof(data,
"Connection #%ld is still name resolving, can't reuse\n",
check->connection_id);
continue;
}
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->connection_id);
#ifdef DEBUGBUILD
if(check->recv_pipe->size > 0) {
infof(data,
"BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
check->connection_id);
}
Daniel Stenberg
committed
#endif
continue;
}
Daniel Stenberg
committed
}
if((needle->handler->flags&PROTOPT_SSL) !=
(check->handler->flags&PROTOPT_SSL))
/* don't do mixed SSL and non-SSL connections */
if(!(needle->handler->protocol & check->handler->protocol))
/* except protocols that have been upgraded via TLS */
continue;
if(needle->handler->flags&PROTOPT_SSL) {
if((data->set.ssl.verifypeer != check->verifypeer) ||
(data->set.ssl.verifyhost != check->verifyhost))
continue;
}
if(needle->bits.proxy != check->bits.proxy)
/* don't do mixed proxy and non-proxy connections */
if(!canPipeline && check->inuse)
/* this request can't be pipelined but the checked connection is
already in use so we skip it */
if(needle->localdev || needle->localport) {
/* If we are bound to a specific local end (IP+port), we must not
re-use a random other one, although if we didn't ask for a
particular one we can reuse one that was bound.
This comparison is a bit rough and too strict. Since the input
parameters can be specified in numerous ways and still end up the
same it would take a lot of processing to make it really accurate.
Instead, this matching will assume that re-uses of bound connections
will most likely also re-use the exact same binding parameters and
missing out a few edge cases shouldn't hurt anyone very much.
*/
if((check->localport != needle->localport) ||
(check->localportrange != needle->localportrange) ||
!check->localdev ||
!needle->localdev ||
strcmp(check->localdev, needle->localdev))
continue;
}
if((!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) ||
wantNTLMhttp) {
/* This protocol requires credentials per connection or is HTTP+NTLM,
so 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;
}
#if defined(USE_NTLM)
credentialsMatch = TRUE;
}
3221
3222
3223
3224
3225
3226
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
if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL ||
(needle->bits.httpproxy && check->bits.httpproxy &&
needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
Curl_raw_equal(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 or it is a non-SSL protocol which is allowed to be
upgraded via TLS */
if((Curl_raw_equal(needle->handler->scheme, check->handler->scheme) ||
needle->handler->protocol & check->handler->protocol) &&
Curl_raw_equal(needle->host.name, check->host.name) &&
needle->remote_port == check->remote_port) {
if(needle->handler->flags & PROTOPT_SSL) {
/* This is a SSL connection so verify that we're using the same
SSL options as well */
if(!Curl_ssl_config_matches(&needle->ssl_config,
&check->ssl_config)) {
DEBUGF(infof(data,
"Connection #%ld has different SSL parameters, "
"can't reuse\n",
check->connection_id));
continue;
}
else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
DEBUGF(infof(data,
"Connection #%ld has not started SSL connect, "
"can't reuse\n",
check->connection_id));
continue;
}
Daniel Stenberg
committed
}
match = TRUE;
else { /* The requested needle connection is using a proxy,
is the checked one using the same host, port and type? */
if(check->bits.proxy &&
(needle->proxytype == check->proxytype) &&
(needle->bits.tunnel_proxy == check->bits.tunnel_proxy) &&
Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
needle->port == check->port) {
/* This is the same proxy connection, use it! */
match = TRUE;
}
}
if(match) {
#if defined(USE_NTLM)
/* If we are looking for an HTTP+NTLM connection, check if this is
already authenticating with the right credentials. If not, keep
looking so that we can reuse NTLM connections if
possible. (Especially we must not reuse the same connection if
partway through a handshake!) */
if(wantNTLMhttp) {
if(credentialsMatch && check->ntlm.state != NTLMSTATE_NONE) {
chosen = check;
/* We must use this connection, no other */
*force_reuse = TRUE;
break;
}
else if(credentialsMatch)
/* this is a backup choice */
chosen = check;
continue;
if(canPipeline) {
/* We can pipeline if we want to. Let's continue looking for
the optimal connection to use, i.e the shortest pipe that is not
blacklisted. */
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
if(pipeLen == 0) {
/* We have the optimal connection. Let's stop looking. */
chosen = check;
break;
}
/* We can't use the connection if the pipe is full */
if(pipeLen >= max_pipe_len)
continue;
/* We can't use the connection if the pipe is penalized */
if(Curl_pipeline_penalized(data, check))
continue;
if(pipeLen < best_pipe_len) {
/* This connection has a shorter pipe so far. We'll pick this
and continue searching */
chosen = check;
best_pipe_len = pipeLen;
continue;
}
}
else {
/* We have found a connection. Let's stop searching. */
chosen = check;
break;
}
Daniel Stenberg
committed
if(chosen) {
*usethis = chosen;
return TRUE; /* yes, we found one to use! */
}
return FALSE; /* no matching connecting exists */
}
/* Mark the connection as 'idle', or close it if the cache is full.
Returns TRUE if the connection is kept, or FALSE if it was closed. */
static bool
ConnectionDone(struct SessionHandle *data, struct connectdata *conn)
{
/* data->multi->maxconnects can be negative, deal with it. */
size_t maxconnects =
(data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
data->multi->maxconnects;
struct connectdata *conn_candidate = NULL;
/* Mark the current connection as 'unused' */
conn->inuse = FALSE;
if(maxconnects > 0 &&
data->state.conn_cache->num_connections > maxconnects) {
infof(data, "Connection cache is full, closing the oldest one.\n");
conn_candidate = find_oldest_idle_connection(data);
if(conn_candidate) {
/* Set the connection's owner correctly */
conn_candidate->data = data;
/* the winner gets the honour of being disconnected */
(void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE);
}
}
return (conn_candidate == conn) ? FALSE : TRUE;
}
* The given input connection struct pointer is to be stored in the connection
* cache. If the cache is already full, least interesting existing connection
* (if any) gets closed.
*
* The given connection should be unique. That must've been checked prior to
* this call.
*/
static CURLcode ConnectionStore(struct SessionHandle *data,
struct connectdata *conn)
return Curl_conncache_add_conn(data->state.conn_cache, conn);
/* after a TCP connection to the proxy has been verified, this function does
the next magic step.
Note: this function's sub-functions call failf()
CURLcode Curl_connected_proxy(struct connectdata *conn,
int sockindex)
if(!conn->bits.proxy || sockindex)
/* this magic only works for the primary socket as the secondary is used
for FTP only and it has FTP specific magic in ftp.c */
switch(conn->proxytype) {
#ifndef CURL_DISABLE_PROXY
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
return Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
conn->host.name, conn->remote_port,
FIRSTSOCKET, conn);
case CURLPROXY_SOCKS4:
return Curl_SOCKS4(conn->proxyuser, conn->host.name,
conn->remote_port, FIRSTSOCKET, conn, FALSE);
case CURLPROXY_SOCKS4A:
return Curl_SOCKS4(conn->proxyuser, conn->host.name,
conn->remote_port, FIRSTSOCKET, conn, TRUE);
#endif /* CURL_DISABLE_PROXY */
case CURLPROXY_HTTP:
case CURLPROXY_HTTP_1_0:
/* do nothing here. handled later. */
break;
default:
Daniel Stenberg
committed
break;
} /* switch proxytype */
* verboseconnect() displays verbose information after a connect
void Curl_verboseconnect(struct connectdata *conn)
Daniel Stenberg
committed
{
if(conn->data->set.verbose)
infof(conn->data, "Connected to %s (%s) port %ld (#%ld)\n",
conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname,
conn->ip_addr_str, conn->port, conn->connection_id);
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;
*protocol_done = FALSE;
if(conn->bits.tcpconnect[FIRSTSOCKET] && 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.protoconnstart) {
result = Curl_proxy_connect(conn);
if(result)
return result;
if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
(conn->tunnel_state[FIRSTSOCKET] != TUNNEL_COMPLETE))
/* when using an HTTP tunnel proxy, await complete tunnel establishment
before proceeding further. Return CURLE_OK so we'll be called again */
return CURLE_OK;
Patrick Monnerat
committed
if(conn->handler->connect_it) {
/* is there a protocol-specific connect() procedure? */
/* 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.
*/
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)
const char *tld_errmsg = "<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)
tld_errmsg = tld_strerror((Tld_rc)rc);
#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)
if(rc != TLD_SUCCESS)
return FALSE;
return TRUE;
#endif
/*
* Perform any necessary IDN conversion of hostname
*/
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 */
len = strlen(host->name);
if(host->name[len-1] == '.')
/* strip off a single trailing dot if present, primarily for SNI but
there's no use for it */
host->name[len-1]=0;
#ifdef USE_LIBIDN
/*************************************************************
* Check name for non-ASCII and convert hostname to ACE form.
*************************************************************/
if(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;
}
}
#elif defined(USE_WIN32_IDN)
/*************************************************************
* Check name for non-ASCII and convert hostname to ACE form.
*************************************************************/
char *ace_hostname = NULL;
int rc = curl_win32_idn_to_ascii(host->name, &ace_hostname);
if(rc == 0)
infof(data, "Failed to convert %s to ACE;\n",
host->name);
else {
host->encalloc = ace_hostname;
/* change the name pointer to point to the encoded hostname */
host->name = host->encalloc;
}
#else
infof(data, "IDN support not present, can't parse Unicode domains\n");
}
static void llist_dtor(void *user, void *element)
{
(void)user;
(void)element;
/* Do nothing */
}
/*
* Allocate and initialize a new connectdata object.
*/
static struct connectdata *allocate_conn(struct SessionHandle *data)
struct connectdata *conn = calloc(1, sizeof(struct connectdata));
if(!conn)
return NULL;
conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined
already from start to avoid NULL
situations and checks */
/* and we setup a few fields in case we end up actually using this struct */
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */
conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
conn->connection_id = -1; /* no ID */
Daniel Stenberg
committed
conn->port = -1; /* unknown at this point */
conn->remote_port = -1; /* unknown */
/* Default protocol-independent behavior doesn't support persistent
connections, so we set this to force-close. Protocols that support
this need to set this to FALSE in their "curl_do" functions. */
connclose(conn, "Default to force-close");
/* Store creation time to help future close decision making */
conn->created = Curl_tvnow();
conn->data = data; /* Setup the association between this connection
and the SessionHandle */
conn->proxytype = data->set.proxytype; /* type */
#ifdef CURL_DISABLE_PROXY
conn->bits.proxy = FALSE;
conn->bits.httpproxy = FALSE;
conn->bits.proxy_user_passwd = FALSE;
conn->bits.tunnel_proxy = FALSE;
#else /* CURL_DISABLE_PROXY */
/* note that these two proxy bits are now just on what looks to be
requested, they may be altered down the road */
conn->bits.proxy = (data->set.str[STRING_PROXY] &&
*data->set.str[STRING_PROXY])?TRUE:FALSE;
conn->bits.httpproxy = (conn->bits.proxy &&
(conn->proxytype == CURLPROXY_HTTP ||
conn->proxytype == CURLPROXY_HTTP_1_0))?TRUE:FALSE;
conn->bits.proxy_user_passwd =
(NULL != data->set.str[STRING_PROXYUSERNAME])?TRUE:FALSE;
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
#endif /* CURL_DISABLE_PROXY */
conn->bits.user_passwd = (NULL != data->set.str[STRING_USERNAME])?TRUE:FALSE;
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
conn->verifypeer = data->set.ssl.verifypeer;
conn->verifyhost = data->set.ssl.verifyhost;
conn->ip_version = data->set.ipver;
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
defined(NTLM_WB_ENABLED)
conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
conn->ntlm_auth_hlpr_pid = 0;
conn->challenge_header = NULL;
conn->response_header = NULL;
#endif
if(Curl_multi_pipeline_enabled(data->multi) &&
!conn->master_buffer) {
/* Allocate master_buffer to be used for pipelining */
conn->master_buffer = calloc(BUFSIZE, sizeof (char));
if(!conn->master_buffer)
goto error;
}
/* Initialize the pipeline lists */
conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
if(!conn->send_pipe || !conn->recv_pipe)
conn->data_prot = PROT_CLEAR;
/* Store the local bind parameters that will be used for this connection */
if(data->set.str[STRING_DEVICE]) {
conn->localdev = strdup(data->set.str[STRING_DEVICE]);
if(!conn->localdev)
goto error;
}
conn->localportrange = data->set.localportrange;
conn->localport = data->set.localport;
/* the close socket stuff needs to be copied to the connection struct as
it may live on without (this specific) SessionHandle */
conn->fclosesocket = data->set.fclosesocket;
conn->closesocket_client = data->set.closesocket_client;
return conn;
Curl_llist_destroy(conn->send_pipe, NULL);
Curl_llist_destroy(conn->recv_pipe, NULL);
conn->send_pipe = NULL;
conn->recv_pipe = NULL;
free(conn->master_buffer);
free(conn->localdev);
free(conn);
}
Daniel Stenberg
committed
static CURLcode findprotocol(struct SessionHandle *data,
Daniel Stenberg
committed
struct connectdata *conn,
const char *protostr)
Daniel Stenberg
committed
{
const struct Curl_handler * const *pp;
const struct Curl_handler *p;
/* Scan protocol handler table and match against 'protostr' to set a few
variables based on the URL. Now that the handler may be changed later
when the protocol specific setup function is called. */
for(pp = protocols; (p = *pp) != NULL; pp++) {
Daniel Stenberg
committed
if(Curl_raw_equal(p->scheme, protostr)) {
Daniel Stenberg
committed
/* Protocol found in table. Check if allowed */
if(!(data->set.allowed_protocols & p->protocol))
/* nope, get out */
break;
/* it is allowed for "normal" request, now do an extra check if this is
the result of a redirect */
if(data->state.this_is_a_follow &&
!(data->set.redir_protocols & p->protocol))
/* nope, get out */
break;
/* Perform setup complement if some. */
Daniel Stenberg
committed
/* 'port' and 'remote_port' are set in setup_connection_internals() */
return CURLE_OK;
}
}
/* The protocol was not found in the table, but we don't have to assign it
to anything since it is already assigned to a dummy-struct in the
create_conn() function when the connectdata struct is allocated. */
failf(data, "Protocol \"%s\" not supported or disabled in " LIBCURL_NAME,
Daniel Stenberg
committed
protostr);
Daniel Stenberg
committed
return CURLE_UNSUPPORTED_PROTOCOL;
}
/*
* Parse URL and fill in the relevant members of the connection struct.
Daniel Stenberg
committed
*/
Daniel Stenberg
committed
static CURLcode parseurlandfillconn(struct SessionHandle *data,
struct connectdata *conn,
char **userp, char **passwdp,
char **optionsp)
Daniel Stenberg
committed
char *at;
Claes Jakobsson
committed
char *fragment;
Daniel Stenberg
committed
char *path = data->state.path;
Claes Jakobsson
committed
char *query;
int rc;
char protobuf[16] = "";
const char *protop = "";
/* We might pass the entire URL into the request so we need to make sure
* there are no bad characters in there.*/
if(strpbrk(data->change.url, "\r\n")) {
failf(data, "Illegal characters found in URL");
return CURLE_URL_MALFORMAT;
}
/*************************************************************
* 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]",
Daniel Stenberg
committed
protobuf, path)) &&
Curl_raw_equal(protobuf, "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);
Daniel Stenberg
committed
protop = "file"; /* protocol string */
/* clear path */
path[0]=0;
Daniel Stenberg
committed
if(2 > sscanf(data->change.url,
Daniel Stenberg
committed
protobuf,
conn->host.name, path)) {
/*
* The URL was badly formatted, let's try the browser-style _without_
* protocol specified like 'http://'.
*/
rc = sscanf(data->change.url, "%[^\n/?]%[^\n]", conn->host.name, path);
* djgpp 2.04 has a sscanf() bug where 'conn->host.name' is
* assigned, but the return value is EOF!
#if defined(__DJGPP__) && (DJGPP_MINOR == 4)
if(!(rc == -1 && *conn->host.name))
#endif
{
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.
*/
/* Note: if you add a new protocol, please update the list in
* lib/version.c too! */
if(checkprefix("FTP.", conn->host.name))
Daniel Stenberg
committed
protop = "ftp";
Daniel Stenberg
committed
else if(checkprefix("DICT.", conn->host.name))
Daniel Stenberg
committed
protop = "DICT";
Daniel Stenberg
committed
else if(checkprefix("LDAP.", conn->host.name))
Daniel Stenberg
committed
protop = "LDAP";
else if(checkprefix("IMAP.", conn->host.name))
Daniel Stenberg
committed
protop = "IMAP";
else if(checkprefix("SMTP.", conn->host.name))
protop = "smtp";
else if(checkprefix("POP3.", conn->host.name))
protop = "pop3";
Daniel Stenberg
committed
protop = "http";
*prot_missing = TRUE; /* not given in URL */
Daniel Stenberg
committed
else
protop = protobuf;
Daniel Stenberg
committed
/* We search for '?' in the host name (but only on the right side of a
* @-letter to allow ?-letters in username and password) to handle things
* like http://example.com?param= (notice the missing '/').
*/
at = strchr(conn->host.name, '@');
if(at)
Claes Jakobsson
committed
query = strchr(at+1, '?');
Daniel Stenberg
committed
else
Claes Jakobsson
committed
query = strchr(conn->host.name, '?');
Daniel Stenberg
committed