urlapi: do the port number extraction without using sscanf()

- sscanf() is rather complex and slow, strchr() much simpler

- the port number function does not need to fully verify the IPv6 address
  anyway as it is done later in the hostname_check() function and doing
  it twice is unnecessary.

Closes #10541
This commit is contained in:
Daniel Stenberg 2023-02-17 00:16:39 +01:00
parent 72bb489543
commit 8b27799f8c
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
3 changed files with 40 additions and 39 deletions

View file

@ -493,35 +493,21 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
bool has_scheme)
{
char *portptr = NULL;
char endbracket;
int len;
char *portptr;
char *hostname = Curl_dyn_ptr(host);
/*
* Find the end of an IPv6 address, either on the ']' ending bracket or
* a percent-encoded zone index.
*/
if(1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.]%c%n",
&endbracket, &len)) {
if(']' == endbracket)
portptr = &hostname[len];
else if('%' == endbracket) {
int zonelen = len;
if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
if(']' != endbracket)
return CURLUE_BAD_IPV6;
portptr = &hostname[--zonelen + len + 1];
}
else
return CURLUE_BAD_IPV6;
}
else
if(hostname[0] == '[') {
portptr = strchr(hostname, ']');
if(!portptr)
return CURLUE_BAD_IPV6;
portptr++;
/* this is a RFC2732-style specified IP-address */
if(portptr && *portptr) {
if(*portptr) {
if(*portptr != ':')
return CURLUE_BAD_IPV6;
return CURLUE_BAD_PORT_NUMBER;
}
else
portptr = NULL;
@ -585,11 +571,9 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
hostname++;
hlen -= 2;
if(hostname[hlen] != ']')
return CURLUE_BAD_IPV6;
/* only valid letters are ok */
/* only valid IPv6 letters are ok */
len = strspn(hostname, l);
if(hlen != len) {
hlen = len;
if(hostname[len] == '%') {
@ -603,8 +587,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
while(*h && (*h != ']') && (i < 15))
zoneid[i++] = *h++;
if(!i || (']' != *h))
/* impossible to reach? */
return CURLUE_MALFORMED_INPUT;
return CURLUE_BAD_IPV6;
zoneid[i] = 0;
u->zoneid = strdup(zoneid);
if(!u->zoneid)

View file

@ -466,6 +466,12 @@ static const struct testcase get_parts_list[] ={
};
static const struct urltestcase get_url_list[] = {
{"https://[::%25fakeit];80/moo",
"",
0, 0, CURLUE_BAD_PORT_NUMBER},
{"https://[fe80::20c:29ff:fe9c:409b]-80/moo",
"",
0, 0, CURLUE_BAD_PORT_NUMBER},
#ifdef USE_IDN
{"https://räksmörgås.se/path?q#frag",
"https://xn--rksmrgs-5wao1o.se/path?q#frag", 0, CURLU_PUNYCODE, CURLUE_OK},
@ -658,11 +664,14 @@ static const struct urltestcase get_url_list[] = {
{NULL, NULL, 0, 0, CURLUE_OK}
};
static int checkurl(const char *url, const char *out)
static int checkurl(const char *org, const char *url, const char *out)
{
if(strcmp(out, url)) {
fprintf(stderr, "Wanted: %s\nGot : %s\n",
out, url);
fprintf(stderr,
"Org: %s\n"
"Wanted: %s\n"
"Got : %s\n",
org, out, url);
return 1;
}
return 0;
@ -967,7 +976,7 @@ static int set_url(void)
error++;
}
else {
if(checkurl(url, set_url_list[i].out)) {
if(checkurl(set_url_list[i].in, url, set_url_list[i].out)) {
error++;
}
}
@ -1020,7 +1029,7 @@ static int set_parts(void)
__FILE__, __LINE__, (int)rc, curl_url_strerror(rc));
error++;
}
else if(checkurl(url, set_parts_list[i].out)) {
else if(checkurl(set_parts_list[i].in, url, set_parts_list[i].out)) {
error++;
}
}
@ -1060,7 +1069,7 @@ static int get_url(void)
error++;
}
else {
if(checkurl(url, get_url_list[i].out)) {
if(checkurl(get_url_list[i].in, url, get_url_list[i].out)) {
error++;
}
}
@ -1163,7 +1172,7 @@ static int append(void)
error++;
}
else {
if(checkurl(url, append_list[i].out)) {
if(checkurl(append_list[i].in, url, append_list[i].out)) {
error++;
}
curl_free(url);

View file

@ -92,11 +92,16 @@ UNITTEST_START
u = curl_url();
if(!u)
goto fail;
ipv6port = strdup("[fe80::250:56ff;fea7:da15]:80");
ipv6port = strdup("[fe80::250:56ff;fea7:da15]:808");
if(!ipv6port)
goto fail;
ret = parse_port(u, ipv6port, FALSE);
fail_unless(ret != CURLUE_OK, "parse_port true on error");
fail_unless(ret == CURLUE_OK, "parse_port returned error");
ret = curl_url_get(u, CURLUPART_PORT, &portnum, 0);
fail_unless(ret == CURLUE_OK, "curl_url_get portnum returned error");
fail_unless(portnum && !strcmp(portnum, "808"), "Check portnumber");
curl_free(portnum);
free_and_clear(ipv6port);
curl_url_cleanup(u);
@ -180,15 +185,19 @@ UNITTEST_START
free_and_clear(ipv6port);
curl_url_cleanup(u);
/* Incorrect zone index syntax */
/* Incorrect zone index syntax, but the port extractor doesn't care */
u = curl_url();
if(!u)
goto fail;
ipv6port = strdup("[fe80::250:56ff:fea7:da15!25eth3]:80");
ipv6port = strdup("[fe80::250:56ff:fea7:da15!25eth3]:180");
if(!ipv6port)
goto fail;
ret = parse_port(u, ipv6port, FALSE);
fail_unless(ret != CURLUE_OK, "parse_port returned non-error");
fail_unless(ret == CURLUE_OK, "parse_port returned error");
ret = curl_url_get(u, CURLUPART_PORT, &portnum, 0);
fail_unless(ret == CURLUE_OK, "curl_url_get portnum returned error");
fail_unless(portnum && !strcmp(portnum, "180"), "Check portnumber");
curl_free(portnum);
free_and_clear(ipv6port);
curl_url_cleanup(u);