Newer
Older
Daniel Stenberg
committed
Daniel Stenberg
committed
/*
* This function logs in to a SOCKS5 proxy and sends the specifics to the final
* destination server.
Daniel Stenberg
committed
*/
Daniel Stenberg
committed
static int handleSock5Proxy(const char *proxy_name,
const char *proxy_password,
struct connectdata *conn)
Daniel Stenberg
committed
{
Daniel Stenberg
committed
/*
According to the RFC1928, section "6. Replies". This is what a SOCK5
replies:
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
Where:
o VER protocol version: X'05'
o REP Reply field:
o X'00' succeeded
*/
Daniel Stenberg
committed
unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
ssize_t actualread;
ssize_t written;
int result;
CURLcode code;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
Daniel Stenberg
committed
Curl_nonblock(sock, FALSE);
socksreq[0] = 5; /* version */
Daniel Stenberg
committed
socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
Daniel Stenberg
committed
socksreq[2] = 0; /* no authentication */
socksreq[3] = 2; /* username/password */
code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
Daniel Stenberg
committed
&written);
if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
Daniel Stenberg
committed
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
failf(conn->data, "Unable to send initial SOCKS5 request.");
return 1;
}
result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
if ((result != CURLE_OK) || (actualread != 2)) {
failf(conn->data, "Unable to receive initial SOCKS5 response.");
return 1;
}
if (socksreq[0] != 5) {
failf(conn->data, "Received invalid version in initial SOCKS5 response.");
return 1;
}
if (socksreq[1] == 0) {
/* Nothing to do, no authentication needed */
;
}
else if (socksreq[1] == 2) {
/* Needs user name and password */
int userlen, pwlen, len;
userlen = (int)strlen(proxy_name);
pwlen = proxy_password?(int)strlen(proxy_password):0;
Daniel Stenberg
committed
/* username/password request looks like
* +----+------+----------+------+----------+
* |VER | ULEN | UNAME | PLEN | PASSWD |
* +----+------+----------+------+----------+
* | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
* +----+------+----------+------+----------+
*/
len = 0;
socksreq[len++] = 1; /* username/pw subnegotiation version */
socksreq[len++] = (char) userlen;
memcpy(socksreq + len, proxy_name, (int) userlen);
len += userlen;
socksreq[len++] = (char) pwlen;
memcpy(socksreq + len, proxy_password, (int) pwlen);
len += pwlen;
code = Curl_write(conn, sock, (char *)socksreq, len, &written);
if ((code != CURLE_OK) || (len != written)) {
Daniel Stenberg
committed
failf(conn->data, "Failed to send SOCKS5 sub-negotiation request.");
return 1;
}
result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
if ((result != CURLE_OK) || (actualread != 2)) {
failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response.");
return 1;
}
/* ignore the first (VER) byte */
if (socksreq[1] != 0) { /* status */
Daniel Stenberg
committed
failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).",
socksreq[0], socksreq[1]);
return 1;
}
/* Everything is good so far, user was authenticated! */
}
else {
/* error */
if (socksreq[1] == 1) {
failf(conn->data,
"SOCKS5 GSSAPI per-message authentication is not supported.");
return 1;
}
else if (socksreq[1] == 255) {
if (!proxy_name || !*proxy_name) {
Daniel Stenberg
committed
failf(conn->data,
"No authentication method was acceptable. (It is quite likely"
" that the SOCKS5 server wanted a username/password, since none"
" was supplied to the server on this connection.)");
}
Daniel Stenberg
committed
failf(conn->data, "No authentication method was acceptable.");
}
return 1;
}
else {
failf(conn->data,
"Undocumented SOCKS5 mode attempted to be used by server.");
return 1;
}
}
/* Authentication is complete, now specify destination to the proxy */
socksreq[0] = 5; /* version (SOCKS5) */
socksreq[1] = 1; /* connect */
socksreq[2] = 0; /* must be zero */
socksreq[3] = 1; /* IPv4 = 1 */
Daniel Stenberg
committed
{
struct Curl_dns_entry *dns;
Curl_addrinfo *hp=NULL;
int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);
Daniel Stenberg
committed
if(rc == CURLRESOLV_ERROR)
return 1;
Daniel Stenberg
committed
Daniel Stenberg
committed
if(rc == CURLRESOLV_PENDING)
Daniel Stenberg
committed
/* this requires that we're in "wait for resolve" state */
rc = Curl_wait_for_resolv(conn, &dns);
Daniel Stenberg
committed
/*
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It
* returns a Curl_addrinfo pointer that may not always look the same.
*/
if(dns)
hp=dns->addr;
Daniel Stenberg
committed
if (hp) {
char buf[64];
unsigned short ip[4];
Curl_printable_address(hp, buf, sizeof(buf));
if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
&ip[0], &ip[1], &ip[2], &ip[3])) {
socksreq[4] = (unsigned char)ip[0];
socksreq[5] = (unsigned char)ip[1];
socksreq[6] = (unsigned char)ip[2];
socksreq[7] = (unsigned char)ip[3];
Daniel Stenberg
committed
}
else
hp = NULL; /* fail! */
Curl_resolv_unlock(conn->data, dns); /* not used anymore from now on */
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(!hp) {
Daniel Stenberg
committed
failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.",
conn->host.name);
Daniel Stenberg
committed
return 1;
}
}
*((unsigned short*)&socksreq[8]) = htons(conn->remote_port);
{
const int packetsize = 10;
code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
if ((code != CURLE_OK) || (written != packetsize)) {
Daniel Stenberg
committed
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
failf(conn->data, "Failed to send SOCKS5 connect request.");
return 1;
}
result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread);
if ((result != CURLE_OK) || (actualread != packetsize)) {
failf(conn->data, "Failed to receive SOCKS5 connect request ack.");
return 1;
}
if (socksreq[0] != 5) { /* version */
failf(conn->data,
"SOCKS5 reply has wrong version, version should be 5.");
return 1;
}
if (socksreq[1] != 0) { /* Anything besides 0 is an error */
failf(conn->data,
"Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
(unsigned char)socksreq[4], (unsigned char)socksreq[5],
(unsigned char)socksreq[6], (unsigned char)socksreq[7],
(unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
socksreq[1]);
return 1;
}
}
Curl_nonblock(sock, TRUE);
return 0; /* Proxy was successful! */
}
static CURLcode ConnectPlease(struct connectdata *conn,
struct Curl_dns_entry *hostaddr,
bool *connected)
Daniel Stenberg
committed
Curl_addrinfo *addr;
Daniel Stenberg
committed
struct SessionHandle *data = conn->data;
char *hostname = data->change.proxy?conn->proxy.name:conn->host.name;
Daniel Stenberg
committed
infof(data, "About to connect() to %s%s port %d\n",
data->change.proxy?"proxy ":"",
Daniel Stenberg
committed
hostname, conn->port);
/*************************************************************
*************************************************************/
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
Curl_store_ip_addr(conn);
Daniel Stenberg
committed
switch(conn->data->set.proxytype) {
case CURLPROXY_SOCKS5:
return handleSock5Proxy(conn->proxyuser,
conn->proxypasswd,
Daniel Stenberg
committed
conn) ?
Daniel Stenberg
committed
CURLE_COULDNT_CONNECT : CURLE_OK;
Daniel Stenberg
committed
case CURLPROXY_HTTP:
Daniel Stenberg
committed
/* do nothing here. handled later. */
Daniel Stenberg
committed
break;
case CURLPROXY_SOCKS4:
return handleSock4Proxy(conn) ? CURLE_COULDNT_CONNECT : CURLE_OK;
default:
Daniel Stenberg
committed
failf(conn->data, "unknown proxytype option given");
Daniel Stenberg
committed
}
}
* verboseconnect() displays verbose information after a connect
static void verboseconnect(struct connectdata *conn)
Daniel Stenberg
committed
{
infof(conn->data, "Connected to %s (%s) port %d\n",
Daniel Stenberg
committed
conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname,
conn->ip_addr_str, conn->port);
Daniel Stenberg
committed
}
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
CURLcode Curl_protocol_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
{
CURLcode res = CURLE_OK;
if(conn->curl_proto_fdset)
res = conn->curl_proto_fdset(conn, read_fd_set, write_fd_set, max_fdp);
return res;
}
CURLcode Curl_doing_fdset(struct connectdata *conn,
fd_set *read_fd_set,
fd_set *write_fd_set,
int *max_fdp)
{
CURLcode res = CURLE_OK;
if(conn && conn->curl_doing_fdset)
res = conn->curl_doing_fdset(conn, read_fd_set, write_fd_set, max_fdp);
return res;
}
/*
* 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;
if(conn && conn->curl_connecting) {
*done = FALSE;
result = conn->curl_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;
if(conn && conn->curl_doing) {
*done = FALSE;
result = conn->curl_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)
{
struct SessionHandle *data = conn->data;
CURLcode result=CURLE_OK;
*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. */
if(!conn->curl_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) {
if(conn->curl_connect) {
/* 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 */
result = conn->curl_connect(conn, protocol_done);
}
else
*protocol_done = TRUE;
/* it has started, possibly even completed but that knowledge isn't stored
in this bit! */
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;
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)
{
size_t err_pos;
char *uc_name = NULL;
int rc;
/* Convert (and downcase) ACE-name back into locale's character set */
rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0);
if (rc != IDNA_SUCCESS)
rc = tld_check_lz(uc_name, &err_pos, NULL);
if (rc == TLD_INVALID)
infof(data, "WARNING: %s; pos %u = `%c'/0x%02X\n",
#ifdef HAVE_TLD_STRERROR
tld_strerror(rc),
#else
"<no msg>",
#endif
err_pos, uc_name[err_pos],
uc_name[err_pos] & 255);
else if (rc != TLD_SUCCESS)
infof(data, "WARNING: TLD check for %s failed; %s\n",
uc_name,
#ifdef HAVE_TLD_STRERROR
tld_strerror(rc)
#else
"<no msg>"
#endif
);
if (uc_name)
idn_free(uc_name);
return (rc == TLD_SUCCESS);
}
#endif
static void fix_hostname(struct connectdata *conn, struct hostname *host)
{
/* 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) &&
stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
char *ace_hostname = NULL;
struct SessionHandle *data = conn->data;
int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0);
infof (data, "Input domain encoded as `%s'\n",
stringprep_locale_charset ());
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;
}
}
Daniel Stenberg
committed
#else
(void)conn; /* never used */
#endif
}
Daniel Stenberg
committed
/**
Daniel Stenberg
committed
* CreateConnection() sets up a new connectdata struct, or re-uses an already
* existing one, and resolves host name.
*
* if this function returns CURLE_OK and *async is set to TRUE, the resolve
* response will be coming asynchronously. If *async is FALSE, the name is
* already resolved.
Daniel Stenberg
committed
*
* @param data The sessionhandle pointer
* @param in_connect is set to the next connection data pointer
* @param addr is set to the new dns entry for this connection. If this
* connection is re-used it will be NULL.
Daniel Stenberg
committed
* @param async is set TRUE/FALSE depending on the nature of this lookup
* @return CURLcode
* @see SetupConnection()
Daniel Stenberg
committed
*/
static CURLcode CreateConnection(struct SessionHandle *data,
Daniel Stenberg
committed
struct connectdata **in_connect,
struct Curl_dns_entry **addr,
bool *async)
Daniel Stenberg
committed
char *at;
CURLcode result=CURLE_OK;
struct connectdata *conn_temp = NULL;
struct Curl_dns_entry *hostaddr;
#if defined(HAVE_ALARM) && !defined(USE_ARES)
Daniel Stenberg
committed
#endif
char endbracket;
char user[MAX_CURL_USER_LENGTH];
char passwd[MAX_CURL_PASSWORD_LENGTH];
Daniel Stenberg
committed
int rc;
Daniel Stenberg
committed
bool reuse;
Daniel Stenberg
committed
#ifdef SIGALRM
Daniel Stenberg
committed
#ifdef HAVE_SIGACTION
struct sigaction keep_sigact; /* store the old struct here */
bool keep_copysig=FALSE; /* did copy it? */
#else
#ifdef HAVE_SIGNAL
void *keep_sigact; /* store the old handler here */
#endif /* HAVE_SIGNAL */
#endif /* HAVE_SIGACTION */
#endif /* SIGALRM */
#endif /* USE_ARES */
Daniel Stenberg
committed
*addr = NULL; /* nothing yet */
*async = FALSE;
/*************************************************************
* Check input data
*************************************************************/
Daniel Stenberg
committed
if(!data->change.url)
return CURLE_URL_MALFORMAT;
/* First, split up the current URL in parts so that we can use the
parts for checking against the already present connections. In order
to not have to modify everything at once, we allocate a temporary
connection data struct and fill in for comparison purposes. */
conn = (struct connectdata *)calloc(sizeof(struct connectdata), 1);
if(!conn) {
*in_connect = NULL; /* clear the pointer */
return CURLE_OUT_OF_MEMORY;
}
/* We must set the return variable as soon as possible, so that our
parent can cleanup any possible allocs we may have done before
any failure */
*in_connect = conn;
/* and we setup a few fields in case we end up actually using this struct */
conn->data = data; /* remember our daddy */
Daniel Stenberg
committed
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
conn->connectindex = -1; /* no index */
Daniel Stenberg
committed
conn->bits.httpproxy = (data->change.proxy && *data->change.proxy &&
(data->set.proxytype == CURLPROXY_HTTP))?
TRUE:FALSE; /* http proxy or not */
/* 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. */
conn->bits.close = TRUE;
/* maxdownload must be -1 on init, as 0 is a valid value! */
conn->maxdownload = -1; /* might have been used previously! */
/* Store creation time to help future close decision making */
conn->created = Curl_tvnow();
conn->bits.use_range = data->set.set_range?TRUE:FALSE; /* range status */
conn->range = data->set.set_range; /* clone the range setting */
conn->resume_from = data->set.set_resume_from; /* inherit resume_from */
conn->bits.user_passwd = data->set.userpwd?1:0;
conn->bits.proxy_user_passwd = data->set.proxyuserpwd?1:0;
Daniel Stenberg
committed
conn->bits.no_body = data->set.opt_no_body;
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
Daniel Stenberg
committed
/* This initing continues below, see the comment "Continue connectdata
* initialization here" */
/***********************************************************
* We need to allocate memory to store the path in. We get the size of the
* full URL to be sure, and we need to make it at least 256 bytes since
* other parts of the code will rely on this fact
***********************************************************/
Daniel Stenberg
committed
urllen=strlen(data->change.url);
if(urllen < LEAST_PATH_ALLOC)
urllen=LEAST_PATH_ALLOC;
/*
* We malloc() the buffers below urllen+2 to make room for to possibilities:
* 1 - an extra terminating zero
* 2 - an extra slash (in case a syntax like "www.host.com?moo" is used)
*/
conn->pathbuffer=(char *)malloc(urllen+2);
Daniel Stenberg
committed
if(NULL == conn->pathbuffer)
return CURLE_OUT_OF_MEMORY; /* really bad error */
Daniel Stenberg
committed
conn->path = conn->pathbuffer;
conn->host.rawalloc=(char *)malloc(urllen+2);
if(NULL == conn->host.rawalloc)
Daniel Stenberg
committed
return CURLE_OUT_OF_MEMORY;
conn->host.name = conn->host.rawalloc;
conn->host.name[0] = 0;
/*************************************************************
* 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]",
conn->protostr,
conn->path)) && strequal(conn->protostr, "file")) {
if(conn->path[0] == '/' && conn->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(conn->path, conn->path + 2, strlen(conn->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(conn->path[0] != '/') {
/* the URL included a host name, we ignore host names in file:// URLs
as the standards don't define what to do with them */
char *ptr=strchr(conn->path, '/');
if(ptr) {
/* there was a slash present
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(conn->path, ptr, strlen(ptr)+1);
strcpy(conn->protostr, "file"); /* store protocol string lowercase */
/* clear path */
conn->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, conn->path)) {
/*
* The URL was badly formatted, let's try the browser-style _without_
* protocol specified like 'http://'.
*/
Daniel Stenberg
committed
if((1 > sscanf(data->change.url, "%[^\n/]%[^\n]",
conn->host.name, conn->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.
*/
/* Note: if you add a new protocol, please update the list in
* lib/version.c too! */
if(checkprefix("FTP.", conn->host.name))
strcpy(conn->protostr, "ftp");
Daniel Stenberg
committed
#ifdef USE_SSL
else if(checkprefix("FTPS", conn->host.name))
Daniel Stenberg
committed
#endif /* USE_SSL */
Daniel Stenberg
committed
else if(checkprefix("TELNET.", conn->host.name))
strcpy(conn->protostr, "telnet");
Daniel Stenberg
committed
else if (checkprefix("DICT.", conn->host.name))
strcpy(conn->protostr, "DICT");
Daniel Stenberg
committed
else if (checkprefix("LDAP.", conn->host.name))
strcpy(conn->protostr, "LDAP");
strcpy(conn->protostr, "http");
}
conn->protocol |= PROT_MISSING; /* not given in URL */
}
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)
tmp = strchr(at+1, '?');
else
tmp = strchr(conn->host.name, '?');
if(tmp) {
/* We must insert a slash before the '?'-letter in the URL. If the URL had
a slash after the '?', that is where the path currently begins and the
'?string' is still part of the host name.
We must move the trailing part from the host name and put it first in
the path. And have it all prefixed with a slash.
*/
size_t hostlen = strlen(tmp);
size_t pathlen = strlen(conn->path);
/* move the existing path plus the zero byte forward, to make room for
the host-name part */
memmove(conn->path+hostlen+1, conn->path, pathlen+1);
/* now copy the trailing host part in front of the existing path */
memcpy(conn->path+1, tmp, hostlen);
Daniel Stenberg
committed
conn->path[0]='/'; /* prepend the missing slash */
Daniel Stenberg
committed
*tmp=0; /* now cut off the hostname at the ? */
}
else if(!conn->path[0]) {
/* if there's no path set, use a single slash */
strcpy(conn->path, "/");
}
Daniel Stenberg
committed
Daniel Stenberg
committed
/* If the URL is malformatted (missing a '/' after hostname before path) we
* insert a slash here. The only letter except '/' we accept to start a path
* is '?'.
*/
if(conn->path[0] == '?') {
/* We need this function to deal with overlapping memory areas. We know
that the memory area 'path' points to is 'urllen' bytes big and that
is bigger than the path. Use +1 to move the zero byte too. */
memmove(&conn->path[1], conn->path, strlen(conn->path)+1);
Daniel Stenberg
committed
conn->path[0] = '/';
}
/*
* So if the URL was A://B/C,
* conn->protostr is A
* conn->host.name is B
* conn->path is /C
*/
/*************************************************************
* Take care of proxy authentication stuff
*************************************************************/
Daniel Stenberg
committed
if(conn->bits.proxy_user_passwd) {
char proxyuser[MAX_CURL_USER_LENGTH]="";
char proxypasswd[MAX_CURL_PASSWORD_LENGTH]="";
Daniel Stenberg
committed
sscanf(data->set.proxyuserpwd,
"%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
"%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
proxyuser, proxypasswd);
Daniel Stenberg
committed
conn->proxyuser = curl_unescape(proxyuser,0);
if(!conn->proxyuser)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
conn->proxypasswd = curl_unescape(proxypasswd,0);
if(!conn->proxypasswd)
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
#ifndef CURL_DISABLE_HTTP
/*************************************************************
* Detect what (if any) proxy to use
*************************************************************/
Daniel Stenberg
committed
if(!data->change.proxy) {
/* If proxy was not specified, we check for default proxy environment
* variables, to enable i.e Lynx compliance:
*
* http_proxy=http://some.server.dom:port/
* https_proxy=http://some.server.dom:port/
* ftp_proxy=http://some.server.dom:port/
* no_proxy=domain1.dom,host.domain2.dom
* (a comma-separated list of hosts which should
* not be proxied, or an asterisk to override
* all proxy variables)
* all_proxy=http://some.server.dom:port/
* (seems to exist for the CERN www lib. Probably
* the first to check for.)
*
* For compatibility, the all-uppercase versions of these variables are
* checked if the lowercase versions don't exist.
*/
char *no_proxy=NULL;
no_proxy=curl_getenv("no_proxy");
if(!no_proxy)
no_proxy=curl_getenv("NO_PROXY");
if(!no_proxy || !strequal("*", no_proxy)) {
/* NO_PROXY wasn't specified or it wasn't just an asterisk */
char *nope;
nope=no_proxy?strtok_r(no_proxy, ", ", &no_proxy_tok_buf):NULL;
char *endptr = strchr(conn->host.name, ':');
Daniel Stenberg
committed
if(endptr)
namelen=endptr-conn->host.name;
Daniel Stenberg
committed
else
namelen=strlen(conn->host.name);
Daniel Stenberg
committed
if(strlen(nope) <= namelen) {
conn->host.name + namelen - strlen(nope);
if(checkprefix(nope, checkn)) {
/* no proxy for this host! */
break;
}
}
nope=strtok_r(NULL, ", ", &no_proxy_tok_buf);
/* It was not listed as without proxy */
char *protop = conn->protostr;
char *envp = proxy_env;
char *prox;
/* Now, build <protocol>_proxy and check for such a one to use */
while(*protop)
*envp++ = tolower((int)*protop++);
/* append _proxy */
strcpy(envp, "_proxy");
/* read the protocol proxy: */
prox=curl_getenv(proxy_env);
Daniel Stenberg
committed
/*
* We don't try the uppercase version of HTTP_PROXY because of
* security reasons:
*
* When curl is used in a webserver application
* environment (cgi or php), this environment variable can
* be controlled by the web server user by setting the
* http header 'Proxy:' to some value.
Daniel Stenberg
committed
* This can cause 'internal' http/ftp requests to be
* arbitrarily redirected by any external attacker.
*/
if(!prox && !strequal("http_proxy", proxy_env)) {
/* There was no lowercase variable, try the uppercase version: */
for(envp = proxy_env; *envp; envp++)
*envp = toupper((int)*envp);
prox=curl_getenv(proxy_env);
}
if(prox && *prox) { /* don't count "" strings */
proxy = prox; /* use this */
}
else {
proxy = curl_getenv("all_proxy"); /* default proxy to use */
if(!proxy)
proxy=curl_getenv("ALL_PROXY");
Daniel Stenberg
committed
long bits = conn->protocol & (PROT_HTTPS|PROT_SSL);
Daniel Stenberg
committed
data->change.proxy = proxy;
data->change.proxy_alloc=TRUE; /* this needs to be freed later */
conn->bits.httpproxy = TRUE;
Daniel Stenberg
committed
/* force this to become HTTP */
conn->protocol = PROT_HTTP | bits;
} /* if (!nope) - it wasn't specified non-proxy */
if(no_proxy)
free(no_proxy);
Daniel Stenberg
committed
#endif /* CURL_DISABLE_HTTP */
/*************************************************************
Daniel Stenberg
committed
* No protocol part in URL was used, add it!
*************************************************************/
Daniel Stenberg
committed
if(conn->protocol&PROT_MISSING) {
/* We're guessing prefixes here and if we're told to use a proxy or if
we're gonna follow a Location: later or... then we need the protocol
part added so that we have a valid URL. */
Daniel Stenberg
committed
reurl = aprintf("%s://%s", conn->protostr, data->change.url);
Daniel Stenberg
committed
data->change.url = reurl;
data->change.url_alloc = TRUE; /* free this later */
conn->protocol &= ~PROT_MISSING; /* switch that one off again */
#ifndef CURL_DISABLE_HTTP
/************************************************************
* RESUME on a HTTP page is a tricky business. First, let's just check that
* 'range' isn't used, then set the range parameter and leave the resume as
* it is to inform about this situation for later use. We will then
* "attempt" to resume, and if we're talking to a HTTP/1.1 (or later)
* server, we will get the document resumed. If we talk to a HTTP/1.0
* server, we just fail since we can't rewind the file writing from within
* this function.
***********************************************************/
if(conn->resume_from) {
if(!conn->bits.use_range) {
Daniel Stenberg
committed
conn->range = aprintf("%" FORMAT_OFF_T "-", conn->resume_from);
if(!conn->range)
return CURLE_OUT_OF_MEMORY;
conn->bits.rangestringalloc = TRUE; /* mark as allocated */
conn->bits.use_range = 1; /* switch on range usage */
#endif
/*************************************************************
* Setup internals depending on protocol
*************************************************************/
Daniel Stenberg
committed
conn->socktype = SOCK_STREAM; /* most of them are TCP streams */
if (strequal(conn->protostr, "HTTP")) {
#ifndef CURL_DISABLE_HTTP
Daniel Stenberg
committed
conn->port = PORT_HTTP;
conn->remote_port = PORT_HTTP;
conn->curl_do = Curl_http;
conn->curl_do_more = NULL;
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
#else
failf(data, LIBCURL_NAME
" was built with HTTP disabled, http: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
else if (strequal(conn->protostr, "HTTPS")) {
Daniel Stenberg
committed
#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
Daniel Stenberg
committed
conn->port = PORT_HTTPS;
conn->remote_port = PORT_HTTPS;
conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
conn->curl_do = Curl_http;
conn->curl_do_more = NULL;
conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect;
Daniel Stenberg
committed
#else /* USE_SS */
failf(data, LIBCURL_NAME
" was built with SSL disabled, https: not supported!");
Daniel Stenberg
committed
#endif /* !USE_SSL */
else if(strequal(conn->protostr, "FTP") ||
strequal(conn->protostr, "FTPS")) {
#ifndef CURL_DISABLE_FTP