urlapi: allow setting port number zero

Also set and check errno when strtoul() parsing numbers for better error
checking.

Updated test 1560

Closes #13427
This commit is contained in:
Daniel Stenberg 2024-04-19 14:42:39 +02:00
parent ac49152e26
commit fe17c162d0
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
2 changed files with 37 additions and 35 deletions

View file

@ -79,7 +79,7 @@ struct Curl_URL {
char *path; char *path;
char *query; char *query;
char *fragment; char *fragment;
unsigned short portnum; /* the numerical version */ unsigned short portnum; /* the numerical version (if 'port' is set) */
BIT(query_present); /* to support blank */ BIT(query_present); /* to support blank */
BIT(fragment_present); /* to support blank */ BIT(fragment_present); /* to support blank */
}; };
@ -537,7 +537,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
if(portptr) { if(portptr) {
char *rest = NULL; char *rest = NULL;
long port; unsigned long port;
size_t keep = portptr - hostname; size_t keep = portptr - hostname;
/* Browser behavior adaptation. If there's a colon with no digits after, /* Browser behavior adaptation. If there's a colon with no digits after,
@ -555,12 +555,10 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
if(!ISDIGIT(*portptr)) if(!ISDIGIT(*portptr))
return CURLUE_BAD_PORT_NUMBER; return CURLUE_BAD_PORT_NUMBER;
port = strtol(portptr, &rest, 10); /* Port number must be decimal */ errno = 0;
port = strtoul(portptr, &rest, 10); /* Port number must be decimal */
if(port > 0xffff) if(errno || (port > 0xffff) || *rest)
return CURLUE_BAD_PORT_NUMBER;
if(rest[0])
return CURLUE_BAD_PORT_NUMBER; return CURLUE_BAD_PORT_NUMBER;
u->portnum = (unsigned short) port; u->portnum = (unsigned short) port;
@ -685,6 +683,7 @@ static int ipv4_normalize(struct dynbuf *host)
if(*c == '[') if(*c == '[')
return HOST_IPV6; return HOST_IPV6;
errno = 0; /* for strtoul */
while(!done) { while(!done) {
char *endp = NULL; char *endp = NULL;
unsigned long l; unsigned long l;
@ -692,6 +691,13 @@ static int ipv4_normalize(struct dynbuf *host)
/* most importantly this doesn't allow a leading plus or minus */ /* most importantly this doesn't allow a leading plus or minus */
return HOST_NAME; return HOST_NAME;
l = strtoul(c, &endp, 0); l = strtoul(c, &endp, 0);
if(errno)
return HOST_NAME;
#if SIZEOF_LONG > 4
/* a value larger than 32 bits */
if(l > UINT_MAX)
return HOST_NAME;
#endif
parts[n] = l; parts[n] = l;
c = endp; c = endp;
@ -711,16 +717,6 @@ static int ipv4_normalize(struct dynbuf *host)
default: default:
return HOST_NAME; return HOST_NAME;
} }
/* overflow */
if((l == ULONG_MAX) && (errno == ERANGE))
return HOST_NAME;
#if SIZEOF_LONG > 4
/* a value larger than 32 bits */
if(l > UINT_MAX)
return HOST_NAME;
#endif
} }
switch(n) { switch(n) {
@ -1707,7 +1703,6 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
const char *part, unsigned int flags) const char *part, unsigned int flags)
{ {
char **storep = NULL; char **storep = NULL;
long port = 0;
bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
bool plusencode = FALSE; bool plusencode = FALSE;
bool urlskipslash = FALSE; bool urlskipslash = FALSE;
@ -1816,18 +1811,26 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
storep = &u->zoneid; storep = &u->zoneid;
break; break;
case CURLUPART_PORT: case CURLUPART_PORT:
{ if(!ISDIGIT(part[0]))
char *endp; /* not a number */
urlencode = FALSE; /* never */
port = strtol(part, &endp, 10); /* Port number must be decimal */
if((port <= 0) || (port > 0xffff))
return CURLUE_BAD_PORT_NUMBER; return CURLUE_BAD_PORT_NUMBER;
if(*endp) else {
/* weirdly provided number, not good! */ char *tmp;
return CURLUE_BAD_PORT_NUMBER; char *endp;
storep = &u->port; unsigned long port;
} errno = 0;
break; port = strtoul(part, &endp, 10); /* must be decimal */
if(errno || (port > 0xffff) || *endp)
/* weirdly provided number, not good! */
return CURLUE_BAD_PORT_NUMBER;
tmp = strdup(part);
if(!tmp)
return CURLUE_OUT_OF_MEMORY;
free(u->port);
u->port = tmp;
u->portnum = (unsigned short)port;
return CURLUE_OK;
}
case CURLUPART_PATH: case CURLUPART_PATH:
urlskipslash = TRUE; urlskipslash = TRUE;
leadingslash = TRUE; /* enforce */ leadingslash = TRUE; /* enforce */
@ -1990,9 +1993,5 @@ nomem:
free(*storep); free(*storep);
*storep = (char *)newp; *storep = (char *)newp;
} }
/* set after the string, to make it not assigned if the allocation above
fails */
if(port)
u->portnum = (unsigned short)port;
return CURLUE_OK; return CURLUE_OK;
} }

View file

@ -151,6 +151,9 @@ struct clearurlcase {
}; };
static const struct testcase get_parts_list[] ={ static const struct testcase get_parts_list[] ={
{"https://curl.se:0/#",
"https | [11] | [12] | [13] | curl.se | 0 | / | [16] | ",
0, CURLU_GET_EMPTY, CURLUE_OK},
{"https://curl.se/#", {"https://curl.se/#",
"https | [11] | [12] | [13] | curl.se | [15] | / | [16] | ", "https | [11] | [12] | [13] | curl.se | [15] | / | [16] | ",
0, CURLU_GET_EMPTY, CURLUE_OK}, 0, CURLU_GET_EMPTY, CURLUE_OK},
@ -941,8 +944,8 @@ static const struct setcase set_parts_list[] = {
0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER}, 0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER},
{"https://host:1234/", {"https://host:1234/",
"port=0,", "port=0,",
"https://host:1234/", "https://host:0/",
0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER}, 0, 0, CURLUE_OK, CURLUE_OK},
{"https://host:1234/", {"https://host:1234/",
"port=65535,", "port=65535,",
"https://host:65535/", "https://host:65535/",