urlapi: strip off scope id from numerical IPv6 addresses

... to make the host name "usable". Store the scope id and put it back
when extracting a URL out of it.

Also makes curl_url_set() syntax check CURLUPART_HOST.

Fixes #3817
Closes #3822
This commit is contained in:
Daniel Stenberg 2019-04-30 16:59:08 +02:00
parent 0281262819
commit bdb2dbc103
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
5 changed files with 192 additions and 12 deletions

View File

@ -35,6 +35,7 @@
1.16 Try to URL encode given URL
1.17 Add support for IRIs
1.18 try next proxy if one doesn't work
1.19 add CURLUPART_SCOPEID
1.20 SRV and URI DNS records
1.21 Have the URL API offer IDN decoding
1.22 CURLINFO_PAUSE_STATE
@ -372,6 +373,11 @@
https://github.com/curl/curl/issues/896
1.19 add CURLUPART_SCOPEID
Add support for CURLUPART_SCOPEID to curl_url_set() and curl_url_get(). It is
only really used when the host name is an IPv6 numerical address.
1.20 SRV and URI DNS records
Offer support for resolving SRV and URI DNS records for libcurl to know which

View File

@ -56,6 +56,7 @@ struct Curl_URL {
char *password;
char *options; /* IMAP only? */
char *host;
char *scopeid; /* for numerical IPv6 addresses */
char *port;
char *path;
char *query;
@ -74,6 +75,7 @@ static void free_urlhandle(struct Curl_URL *u)
free(u->password);
free(u->options);
free(u->host);
free(u->scopeid);
free(u->port);
free(u->path);
free(u->query);
@ -504,7 +506,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname)
portptr = &hostname[len];
else if('%' == endbracket) {
int zonelen = len;
if(1 == sscanf(hostname + zonelen, "25%*[^]]%c%n", &endbracket, &len)) {
if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
if(']' != endbracket)
return CURLUE_MALFORMED_INPUT;
portptr = &hostname[--zonelen + len + 1];
@ -587,25 +589,45 @@ static CURLUcode junkscan(char *part)
return CURLUE_OK;
}
static CURLUcode hostname_check(char *hostname, unsigned int flags)
static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
{
const char *l = NULL; /* accepted characters */
size_t len;
size_t hlen = strlen(hostname);
(void)flags;
if(hostname[0] == '[') {
hostname++;
l = "0123456789abcdefABCDEF::.%";
l = "0123456789abcdefABCDEF::.";
hlen -= 2;
}
if(l) {
/* only valid letters are ok */
len = strspn(hostname, l);
if(hlen != len)
/* hostname with bad content */
return CURLUE_MALFORMED_INPUT;
if(hlen != len) {
/* this could now be '%[zone id]' */
char scopeid[16];
if(hostname[len] == '%') {
int i = 0;
char *h = &hostname[len + 1];
/* pass '25' if present and is a url encoded percent sign */
if(!strncmp(h, "25", 2) && h[2] && (h[2] != ']'))
h += 2;
while(*h && (*h != ']') && (i < 15))
scopeid[i++] = *h++;
if(!i || (']' != *h))
return CURLUE_MALFORMED_INPUT;
scopeid[i] = 0;
u->scopeid = strdup(scopeid);
if(!u->scopeid)
return CURLUE_OUT_OF_MEMORY;
hostname[len] = ']'; /* insert end bracket */
hostname[len + 1] = 0; /* terminate the hostname */
}
else
return CURLUE_MALFORMED_INPUT;
/* hostname is fine */
}
}
else {
/* letters from the second string is not ok */
@ -856,7 +878,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
if(result)
return result;
result = hostname_check(hostname, flags);
result = hostname_check(u, hostname);
if(result)
return result;
@ -1021,6 +1043,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
char *scheme;
char *options = u->options;
char *port = u->port;
char *allochost = NULL;
if(u->scheme && strcasecompare("file", u->scheme)) {
url = aprintf("file://%s%s%s",
u->path,
@ -1059,6 +1082,18 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
if(h && !(h->flags & PROTOPT_URLOPTIONS))
options = NULL;
if((u->host[0] == '[') && u->scopeid) {
/* make it '[ host %25 scopeid ]' */
size_t hostlen = strlen(u->host);
size_t alen = hostlen + 3 + strlen(u->scopeid) + 1;
allochost = malloc(alen);
if(!allochost)
return CURLUE_OUT_OF_MEMORY;
memcpy(allochost, u->host, hostlen - 1);
msnprintf(&allochost[hostlen - 1], alen - hostlen + 1,
"%%25%s]", u->scopeid);
}
url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
scheme,
u->user ? u->user : "",
@ -1067,7 +1102,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
options ? ";" : "",
options ? options : "",
(u->user || u->password || options) ? "@": "",
u->host,
allochost ? allochost : u->host,
port ? ":": "",
port ? port : "",
(u->path && (u->path[0] != '/')) ? "/": "",
@ -1076,6 +1111,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
(u->query && u->query[0]) ? u->query : "",
u->fragment? "#": "",
u->fragment? u->fragment : "");
free(allochost);
}
if(!url)
return CURLUE_OUT_OF_MEMORY;
@ -1191,6 +1227,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
break;
case CURLUPART_HOST:
storep = &u->host;
free(u->scopeid);
u->scopeid = NULL;
break;
case CURLUPART_PORT:
{
@ -1370,6 +1408,13 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
}
}
if(what == CURLUPART_HOST) {
if(hostname_check(u, (char *)newp)) {
free((char *)newp);
return CURLUE_MALFORMED_INPUT;
}
}
free(*storep);
*storep = (char *)newp;
}

View File

@ -31,4 +31,14 @@ lib1560
</tool>
</client>
<verify>
<stdout>
we got [fe80::20c:29ff:fe9c:409b]
we got https://[::1]/hello.html
we got https://example.com/hello.html
we got https://[fe80::20c:29ff:fe9c:409b%25eth0]/hello.html
we got [fe80::20c:29ff:fe9c:409b]
success
</stdout>
</verify>
</testcase>

View File

@ -153,7 +153,13 @@ static struct testcase get_parts_list[] ={
"http | [11] | [12] | [13] | [fd00:a41::50] | [15] | / | [16] | [17]",
CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
{"https://[::1%252]:1234",
"https | [11] | [12] | [13] | [::1%252] | 1234 | / | [16] | [17]",
"https | [11] | [12] | [13] | [::1] | 1234 | / | [16] | [17]",
CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
/* here's "bad" zone id */
{"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234",
"https | [11] | [12] | [13] | [fe80::20c:29ff:fe9c:409b] | 1234 "
"| / | [16] | [17]",
CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
{"https://127.0.0.1:443",
"https | [11] | [12] | [13] | 127.0.0.1 | [15] | / | [16] | [17]",
@ -273,6 +279,18 @@ static struct testcase get_parts_list[] ={
};
static struct urltestcase get_url_list[] = {
{"https://[fe80::20c:29ff:fe9c:409b%]:1234",
"",
0, 0, CURLUE_MALFORMED_INPUT},
{"https://[fe80::20c:29ff:fe9c:409b%25]:1234",
"https://[fe80::20c:29ff:fe9c:409b%2525]:1234/",
0, 0, CURLUE_OK},
{"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234",
"https://[fe80::20c:29ff:fe9c:409b%25eth0]:1234/",
0, 0, CURLUE_OK},
{"https://[::%25fakeit]/moo",
"https://[::%25fakeit]/moo",
0, 0, CURLUE_OK},
{"smtp.example.com/path/html",
"smtp://smtp.example.com/path/html",
CURLU_GUESS_SCHEME, 0, CURLUE_OK},
@ -831,10 +849,111 @@ static int append(void)
return error;
}
static int scopeid(void)
{
CURLU *u;
int error = 0;
CURLUcode rc;
char *url;
u = curl_url();
rc = curl_url_set(u, CURLUPART_URL,
"https://[fe80::20c:29ff:fe9c:409b%25eth0]/hello.html", 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_set returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
rc = curl_url_get(u, CURLUPART_HOST, &url, 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
else {
printf("we got %s\n", url);
curl_free(url);
}
rc = curl_url_set(u, CURLUPART_HOST, "[::1]", 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
rc = curl_url_get(u, CURLUPART_URL, &url, 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
else {
printf("we got %s\n", url);
curl_free(url);
}
rc = curl_url_set(u, CURLUPART_HOST, "example.com", 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
rc = curl_url_get(u, CURLUPART_URL, &url, 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
else {
printf("we got %s\n", url);
curl_free(url);
}
rc = curl_url_set(u, CURLUPART_HOST,
"[fe80::20c:29ff:fe9c:409b%25eth0]", 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
rc = curl_url_get(u, CURLUPART_URL, &url, 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
else {
printf("we got %s\n", url);
curl_free(url);
}
rc = curl_url_get(u, CURLUPART_HOST, &url, 0);
if(rc != CURLUE_OK) {
fprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d\n",
__FILE__, __LINE__, (int)rc);
error++;
}
else {
printf("we got %s\n", url);
curl_free(url);
}
curl_url_cleanup(u);
return error;
}
int test(char *URL)
{
(void)URL; /* not used */
if(scopeid())
return 6;
if(append())
return 5;

View File

@ -168,7 +168,7 @@ UNITTEST_START
u = curl_url();
if(!u)
goto fail;
ipv6port = strdup("[fe80::250:56ff:fea7:da15%!25eth3]:80");
ipv6port = strdup("[fe80::250:56ff:fea7:da15!25eth3]:80");
if(!ipv6port)
goto fail;
ret = Curl_parse_port(u, ipv6port);
@ -184,7 +184,7 @@ UNITTEST_START
if(!ipv6port)
goto fail;
ret = Curl_parse_port(u, ipv6port);
fail_unless(ret != CURLUE_OK, "Curl_parse_port returned non-error");
fail_unless(ret == CURLUE_OK, "Curl_parse_port returned error");
fail:
free(ipv6port);
curl_url_cleanup(u);