host names: allow trailing dot in name resolve, then strip it

Delays stripping of trailing dots to after resolving the hostname.

Fixes #3022
Closes #3222
This commit is contained in:
Tobias Hintze 2018-11-02 21:24:14 +01:00 committed by Daniel Stenberg
parent 2366697806
commit 5b4cce2e36
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
3 changed files with 61 additions and 33 deletions

View File

@ -167,7 +167,8 @@ problems may have been fixed or changed somewhat since this was written!
When given a URL with a trailing dot for the host name part:
"https://example.com./", libcurl will strip off the dot and use the name
without a dot internally and send it dot-less in HTTP Host: headers and in
the TLS SNI field.
the TLS SNI field. For the purpose of resolving the name to an address
the hostname is used as is without any change.
The HTTP part violates RFC 7230 section 5.4 but the SNI part is accordance
with RFC 6066 section 3.
@ -186,10 +187,10 @@ problems may have been fixed or changed somewhat since this was written!
Our current approach allows a knowing client to send a custom HTTP header
with the dot added.
It can also be noted that while adding a trailing dot to the host name in
most (all?) cases will make the name resolve to the same set of IP addresses,
many HTTP servers will not happily accept the trailing dot there unless that
has been specifically configured to be a fine virtual host.
In a few cases there is a difference in name resolving to IP addresses with
a trailing dot, but it can be noted that many HTTP servers will not happily
accept the trailing dot there unless that has been specifically configured
to be a fine virtual host.
If URLs with trailing dots for host names become more popular or even just
used more than for just plain fun experiments, I'm sure we will have reason

View File

@ -127,7 +127,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "memdebug.h"
static void conn_free(struct connectdata *conn);
static void free_fixed_hostname(struct hostname *host);
static void free_idnconverted_hostname(struct hostname *host);
static unsigned int get_protocol_family(unsigned int protocol);
/* Some parts of the code (e.g. chunked encoding) assume this buffer has at
@ -708,6 +708,7 @@ static void conn_free(struct connectdata *conn)
Curl_safefree(conn->trailer);
Curl_safefree(conn->host.rawalloc); /* host name buffer */
Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */
Curl_safefree(conn->hostname_resolve);
Curl_safefree(conn->secondaryhostname);
Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
@ -788,10 +789,10 @@ CURLcode Curl_disconnect(struct Curl_easy *data,
infof(data, "Closing connection %ld\n", conn->connection_id);
Curl_conncache_remove_conn(conn, TRUE);
free_fixed_hostname(&conn->host);
free_fixed_hostname(&conn->conn_to_host);
free_fixed_hostname(&conn->http_proxy.host);
free_fixed_hostname(&conn->socks_proxy.host);
free_idnconverted_hostname(&conn->host);
free_idnconverted_hostname(&conn->conn_to_host);
free_idnconverted_hostname(&conn->http_proxy.host);
free_idnconverted_hostname(&conn->socks_proxy.host);
DEBUGASSERT(conn->data == data);
/* this assumes that the pointer is still there after the connection was
@ -1679,11 +1680,23 @@ static bool is_ASCII_name(const char *hostname)
}
/*
* Perform any necessary IDN conversion of hostname
* Strip single trailing dot in the hostname,
* primarily for SNI and http host header.
*/
static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host)
static void strip_trailing_dot(struct hostname *host)
{
size_t len;
len = strlen(host->name);
if(len && (host->name[len-1] == '.'))
host->name[len-1] = 0;
}
/*
* Perform any necessary IDN conversion of hostname
*/
static CURLcode idnconvert_hostname(struct connectdata *conn,
struct hostname *host)
{
struct Curl_easy *data = conn->data;
#ifndef USE_LIBIDN2
@ -1696,12 +1709,6 @@ static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host)
/* set the name we use to display the host name */
host->dispname = host->name;
len = strlen(host->name);
if(len && (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;
/* Check name for non-ASCII and convert hostname to ACE form if we can */
if(!is_ASCII_name(host->name)) {
#ifdef USE_LIBIDN2
@ -1756,9 +1763,9 @@ static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host)
}
/*
* Frees data allocated by fix_hostname()
* Frees data allocated by idnconvert_hostname()
*/
static void free_fixed_hostname(struct hostname *host)
static void free_idnconverted_hostname(struct hostname *host)
{
#if defined(USE_LIBIDN2)
if(host->encalloc) {
@ -3373,7 +3380,7 @@ static CURLcode resolve_server(struct Curl_easy *data,
*************************************************************/
if(conn->bits.reuse)
/* We're reusing the connection - no need to resolve anything, and
fix_hostname() was called already in create_conn() for the re-use
idnconvert_hostname() was called already in create_conn() for the re-use
case. */
*async = FALSE;
@ -3428,7 +3435,10 @@ static CURLcode resolve_server(struct Curl_easy *data,
conn->port = conn->remote_port;
/* Resolve target host right on */
rc = Curl_resolv_timeout(conn, connhost->name, (int)conn->port,
conn->hostname_resolve = strdup(connhost->name);
if(!conn->hostname_resolve)
return CURLE_OUT_OF_MEMORY;
rc = Curl_resolv_timeout(conn, conn->hostname_resolve, (int)conn->port,
&hostaddr, timeout_ms);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
@ -3449,7 +3459,10 @@ static CURLcode resolve_server(struct Curl_easy *data,
&conn->socks_proxy.host : &conn->http_proxy.host;
/* resolve proxy */
rc = Curl_resolv_timeout(conn, host->name, (int)conn->port,
conn->hostname_resolve = strdup(host->name);
if(!conn->hostname_resolve)
return CURLE_OUT_OF_MEMORY;
rc = Curl_resolv_timeout(conn, conn->hostname_resolve, (int)conn->port,
&hostaddr, timeout_ms);
if(rc == CURLRESOLV_PENDING)
@ -3479,8 +3492,8 @@ static CURLcode resolve_server(struct Curl_easy *data,
static void reuse_conn(struct connectdata *old_conn,
struct connectdata *conn)
{
free_fixed_hostname(&old_conn->http_proxy.host);
free_fixed_hostname(&old_conn->socks_proxy.host);
free_idnconverted_hostname(&old_conn->http_proxy.host);
free_idnconverted_hostname(&old_conn->socks_proxy.host);
free(old_conn->http_proxy.host.rawalloc);
free(old_conn->socks_proxy.host.rawalloc);
@ -3524,14 +3537,18 @@ static void reuse_conn(struct connectdata *old_conn,
/* host can change, when doing keepalive with a proxy or if the case is
different this time etc */
free_fixed_hostname(&conn->host);
free_fixed_hostname(&conn->conn_to_host);
free_idnconverted_hostname(&conn->host);
free_idnconverted_hostname(&conn->conn_to_host);
Curl_safefree(conn->host.rawalloc);
Curl_safefree(conn->conn_to_host.rawalloc);
conn->host = old_conn->host;
conn->conn_to_host = old_conn->conn_to_host;
conn->conn_to_port = old_conn->conn_to_port;
conn->remote_port = old_conn->remote_port;
Curl_safefree(conn->hostname_resolve);
conn->hostname_resolve = old_conn->hostname_resolve;
old_conn->hostname_resolve = NULL;
/* persist connection info in session handle */
Curl_persistconninfo(conn);
@ -3681,30 +3698,30 @@ static CURLcode create_conn(struct Curl_easy *data,
goto out;
/*************************************************************
* IDN-fix the hostnames
* IDN-convert the hostnames
*************************************************************/
result = fix_hostname(conn, &conn->host);
result = idnconvert_hostname(conn, &conn->host);
if(result)
goto out;
if(conn->bits.conn_to_host) {
result = fix_hostname(conn, &conn->conn_to_host);
result = idnconvert_hostname(conn, &conn->conn_to_host);
if(result)
goto out;
}
if(conn->bits.httpproxy) {
result = fix_hostname(conn, &conn->http_proxy.host);
result = idnconvert_hostname(conn, &conn->http_proxy.host);
if(result)
goto out;
}
if(conn->bits.socksproxy) {
result = fix_hostname(conn, &conn->socks_proxy.host);
result = idnconvert_hostname(conn, &conn->socks_proxy.host);
if(result)
goto out;
}
/*************************************************************
* Check whether the host and the "connect to host" are equal.
* Do this after the hostnames have been IDN-fixed.
* Do this after the hostnames have been IDN-converted.
*************************************************************/
if(conn->bits.conn_to_host &&
strcasecompare(conn->conn_to_host.name, conn->host.name)) {
@ -4032,6 +4049,15 @@ static CURLcode create_conn(struct Curl_easy *data,
*************************************************************/
result = resolve_server(data, conn, async);
/* Strip trailing dots. resolve_server copied the name. */
strip_trailing_dot(&conn->host);
if(conn->bits.httpproxy)
strip_trailing_dot(&conn->http_proxy.host);
if(conn->bits.socksproxy)
strip_trailing_dot(&conn->socks_proxy.host);
if(conn->bits.conn_to_host)
strip_trailing_dot(&conn->conn_to_host);
out:
return result;
}

View File

@ -828,6 +828,7 @@ struct connectdata {
int socktype; /* SOCK_STREAM or SOCK_DGRAM */
struct hostname host;
char *hostname_resolve; /* host name to resolve to address, allocated */
char *secondaryhostname; /* secondary socket host name (ftp) */
struct hostname conn_to_host; /* the host to connect to. valid only if
bits.conn_to_host is set */