Newer
Older
score = Curl_tvdiff(now, conn->now);
break;
case CURLCLOSEPOLICY_OLDEST:
/*
* Set higher score for the age passed since the connection
* was created.
*/
score = Curl_tvdiff(now, conn->created);
break;
}
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;
conn->data = NULL;
if (conn->send_pipe == 0 &&
conn->recv_pipe == 0)
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);
infof(data, "Connection (#%d) was killed to make room\n", i);
}
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;
Daniel Stenberg
committed
/*
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
* destination server.
Daniel Stenberg
committed
*
* Reference :
* http://socks.permeo.com/protocol/socks4.protocol
*
* Note :
* Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
* Nonsupport "Identification Protocol (RFC1413)"
*/
static int handleSock4Proxy(const char *proxy_name,
struct SessionHandle *data,
struct connectdata *conn)
Daniel Stenberg
committed
{
unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
Daniel Stenberg
committed
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
int result;
CURLcode code;
curl_socket_t sock = conn->sock[FIRSTSOCKET];
Curl_nonblock(sock, FALSE);
/*
* Compose socks4 request
*
* Request format
*
* +----+----+----+----+----+----+----+----+----+----+....+----+
* | VN | CD | DSTPORT | DSTIP | USERID |NULL|
* +----+----+----+----+----+----+----+----+----+----+....+----+
* # of bytes: 1 1 2 4 variable 1
*/
socksreq[0] = 4; /* version (SOCKS4) */
socksreq[1] = 1; /* connect */
*((unsigned short*)&socksreq[2]) = htons(conn->remote_port);
/* DNS resolve */
{
struct Curl_dns_entry *dns;
Curl_addrinfo *hp=NULL;
int rc;
rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);
if(rc == CURLRESOLV_ERROR)
return 1;
if(rc == CURLRESOLV_PENDING)
/* this requires that we're in "wait for resolve" state */
rc = Curl_wait_for_resolv(conn, &dns);
/*
* 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;
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])) {
/* Set DSTIP */
socksreq[4] = (unsigned char)ip[0];
socksreq[5] = (unsigned char)ip[1];
socksreq[6] = (unsigned char)ip[2];
socksreq[7] = (unsigned char)ip[3];
}
else
hp = NULL; /* fail! */
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
Daniel Stenberg
committed
}
if(!hp) {
failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
Daniel Stenberg
committed
conn->host.name);
return 1;
}
}
/*
* This is currently not supporting "Identification Protocol (RFC1413)".
*/
socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
if (proxy_name)
strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
Daniel Stenberg
committed
{
ssize_t actualread;
ssize_t written;
int packetsize = 9 +
(int)strlen((char*)socksreq + 8); /* size including NUL */
Daniel Stenberg
committed
/* Send request */
code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
if ((code != CURLE_OK) || (written != packetsize)) {
failf(data, "Failed to send SOCKS4 connect request.");
Daniel Stenberg
committed
return 1;
}
packetsize = 8; /* receive data size */
/* Receive response */
result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread);
if ((result != CURLE_OK) || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS4 connect request ack.");
Daniel Stenberg
committed
return 1;
}
/*
* Response format
*
* +----+----+----+----+----+----+----+----+
* | VN | CD | DSTPORT | DSTIP |
* +----+----+----+----+----+----+----+----+
* # of bytes: 1 1 2 4
*
* VN is the version of the reply code and should be 0. CD is the result
* code with one of the following values:
*
* 90: request granted
* 91: request rejected or failed
* 92: request rejected because SOCKS server cannot connect to
Daniel Stenberg
committed
* identd on the client
* 93: request rejected because the client program and identd
* report different user-ids
*/
/* wrong version ? */
if (socksreq[0] != 0) {
failf(data,
"SOCKS4 reply has wrong version, version should be 4.");
Daniel Stenberg
committed
return 1;
}
/* Result */
switch(socksreq[1])
{
case 90:
infof(data, "SOCKS4 request granted.\n");
break;
case 91:
failf(data,
Daniel Stenberg
committed
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", request rejected or failed.",
Daniel Stenberg
committed
(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;
case 92:
failf(data,
Daniel Stenberg
committed
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", request rejected because SOCKS server cannot connect to "
"identd on the client.",
Daniel Stenberg
committed
(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;
case 93:
failf(data,
Daniel Stenberg
committed
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", request rejected because the client program and identd "
"report different user-ids.",
Daniel Stenberg
committed
(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;
default :
failf(data,
Daniel Stenberg
committed
"Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
", Unknown.",
Daniel Stenberg
committed
(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! */
}
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
struct SessionHandle *data = conn->data;
long timeout;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* get timeout */
if(data->set.timeout && data->set.connecttimeout) {
if (data->set.timeout < data->set.connecttimeout)
timeout = data->set.timeout*1000;
else
timeout = data->set.connecttimeout*1000;
}
else if(data->set.timeout)
timeout = data->set.timeout*1000;
else if(data->set.connecttimeout)
timeout = data->set.connecttimeout*1000;
else
timeout = DEFAULT_CONNECT_TIMEOUT;
Curl_nonblock(sock, TRUE);
/* wait until socket gets connected */
result = Curl_select(CURL_SOCKET_BAD, sock, (int)timeout);
Daniel Stenberg
committed
if(-1 == result) {
failf(conn->data, "SOCKS5: no connection here");
return 1;
}
else if(0 == result) {
failf(conn->data, "SOCKS5: connection timeout");
return 1;
}
if(result & CSELECT_ERR) {
failf(conn->data, "SOCKS5: error occured during connection");
return 1;
}
Daniel Stenberg
committed
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 */
Daniel Stenberg
committed
Curl_nonblock(sock, FALSE);
code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
Daniel Stenberg
committed
&written);
if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
failf(data, "Unable to send initial SOCKS5 request.");
Daniel Stenberg
committed
return 1;
}
Daniel Stenberg
committed
Curl_nonblock(sock, TRUE);
result = Curl_select(sock, CURL_SOCKET_BAD, (int)timeout);
Daniel Stenberg
committed
if(-1 == result) {
failf(conn->data, "SOCKS5 nothing to read");
return 1;
}
else if(0 == result) {
failf(conn->data, "SOCKS5 read timeout");
return 1;
}
if(result & CSELECT_ERR) {
failf(conn->data, "SOCKS5 read error occured");
return 1;
}
Curl_nonblock(sock, FALSE);
Daniel Stenberg
committed
result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
if ((result != CURLE_OK) || (actualread != 2)) {
failf(data, "Unable to receive initial SOCKS5 response.");
Daniel Stenberg
committed
return 1;
}
if (socksreq[0] != 5) {
failf(data, "Received invalid version in initial SOCKS5 response.");
Daniel Stenberg
committed
return 1;
}
if (socksreq[1] == 0) {
/* Nothing to do, no authentication needed */
;
}
else if (socksreq[1] == 2) {
/* Needs user name and password */
Daniel Stenberg
committed
size_t userlen, pwlen;
int len;
if(proxy_name && proxy_password) {
userlen = strlen(proxy_name);
pwlen = proxy_password?strlen(proxy_password):0;
}
else {
userlen = 0;
pwlen = 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)) {
failf(data, "Failed to send SOCKS5 sub-negotiation request.");
Daniel Stenberg
committed
return 1;
}
result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
if ((result != CURLE_OK) || (actualread != 2)) {
failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
Daniel Stenberg
committed
return 1;
}
/* ignore the first (VER) byte */
if (socksreq[1] != 0) { /* status */
failf(data, "User was rejected by the SOCKS5 server (%d %d).",
Daniel Stenberg
committed
socksreq[0], socksreq[1]);
return 1;
}
/* Everything is good so far, user was authenticated! */
}
else {
/* error */
if (socksreq[1] == 1) {
failf(data,
Daniel Stenberg
committed
"SOCKS5 GSSAPI per-message authentication is not supported.");
return 1;
}
else if (socksreq[1] == 255) {
if (!proxy_name || !*proxy_name) {
failf(data,
Daniel Stenberg
committed
"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.)");
}
failf(data, "No authentication method was acceptable.");
Daniel Stenberg
committed
}
return 1;
}
else {
failf(data,
Daniel Stenberg
committed
"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(data, dns); /* not used anymore from now on */
Daniel Stenberg
committed
}
Daniel Stenberg
committed
if(!hp) {
failf(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)) {
failf(data, "Failed to send SOCKS5 connect request.");
Daniel Stenberg
committed
return 1;
}
result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread);
if ((result != CURLE_OK) || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS5 connect request ack.");
Daniel Stenberg
committed
return 1;
}
if (socksreq[0] != 5) { /* version */
failf(data,
Daniel Stenberg
committed
"SOCKS5 reply has wrong version, version should be 5.");
return 1;
}
if (socksreq[1] != 0) { /* Anything besides 0 is an error */
failf(data,
Daniel Stenberg
committed
"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 SessionHandle *data,
struct connectdata *conn,
struct Curl_dns_entry *hostaddr,
bool *connected)
Daniel Stenberg
committed
Curl_addrinfo *addr;
Daniel Stenberg
committed
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);
switch(data->set.proxytype) {
Daniel Stenberg
committed
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->proxyuser, data, conn) ?
CURLE_COULDNT_CONNECT : CURLE_OK;
Daniel Stenberg
committed
default:
failf(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
}
Daniel Stenberg
committed
int Curl_protocol_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
Daniel Stenberg
committed
if(conn->curl_proto_getsock)
return conn->curl_proto_getsock(conn, socks, numsocks);
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->curl_doing_getsock)
return conn->curl_doing_getsock(conn, socks, numsocks);
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)
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
{
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)
{
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. */
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((Tld_rc)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((Tld_rc)rc)
#else
"<no msg>"
#endif
);
if (uc_name)
idn_free(uc_name);
return (rc == TLD_SUCCESS);
}
#endif
static void fix_hostname(struct SessionHandle *data,
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;
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)data; /* never used */
Daniel Stenberg
committed
(void)conn; /* never used */
#endif
}
/*
* 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;
char *path = data->reqdata.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.
*/
/* 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(path);
/* move the existing path plus the zero byte forward, to make room for
the host-name part */
memmove(path+hostlen+1, path, pathlen+1);
/* now copy the trailing host part in front of the existing path */
memcpy(path+1, tmp, hostlen);
path[0]='/'; /* prepend the missing slash */
Daniel Stenberg
committed
*tmp=0; /* now cut off the hostname at the ? */
}
else if(!path[0]) {
/* if there's no path set, use a single slash */
strcpy(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(path[0] == '?') {
Daniel Stenberg
committed
/* 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(&path[1], path, strlen(path)+1);
path[0] = '/';
Daniel Stenberg
committed
}
/*
* So if the URL was A://B/C,
* conn->protostr is A
* conn->host.name is B
* data->reqdata.path is /C
*/
return CURLE_OK;
}