From fe17c162d02de0ea46b847c486055ad39fef4eee Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 19 Apr 2024 14:42:39 +0200 Subject: [PATCH] urlapi: allow setting port number zero Also set and check errno when strtoul() parsing numbers for better error checking. Updated test 1560 Closes #13427 --- lib/urlapi.c | 65 ++++++++++++++++++++--------------------- tests/libtest/lib1560.c | 7 +++-- 2 files changed, 37 insertions(+), 35 deletions(-) diff --git a/lib/urlapi.c b/lib/urlapi.c index 308bc2745a..8e8625d6f2 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -79,7 +79,7 @@ struct Curl_URL { char *path; char *query; 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(fragment_present); /* to support blank */ }; @@ -537,7 +537,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, if(portptr) { char *rest = NULL; - long port; + unsigned long port; size_t keep = portptr - hostname; /* 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)) 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) - return CURLUE_BAD_PORT_NUMBER; - - if(rest[0]) + if(errno || (port > 0xffff) || *rest) return CURLUE_BAD_PORT_NUMBER; u->portnum = (unsigned short) port; @@ -685,6 +683,7 @@ static int ipv4_normalize(struct dynbuf *host) if(*c == '[') return HOST_IPV6; + errno = 0; /* for strtoul */ while(!done) { char *endp = NULL; 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 */ return HOST_NAME; 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; c = endp; @@ -711,16 +717,6 @@ static int ipv4_normalize(struct dynbuf *host) default: 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) { @@ -1707,7 +1703,6 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags) { char **storep = NULL; - long port = 0; bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0; bool plusencode = FALSE; bool urlskipslash = FALSE; @@ -1816,18 +1811,26 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, storep = &u->zoneid; break; case CURLUPART_PORT: - { - char *endp; - urlencode = FALSE; /* never */ - port = strtol(part, &endp, 10); /* Port number must be decimal */ - if((port <= 0) || (port > 0xffff)) + if(!ISDIGIT(part[0])) + /* not a number */ return CURLUE_BAD_PORT_NUMBER; - if(*endp) - /* weirdly provided number, not good! */ - return CURLUE_BAD_PORT_NUMBER; - storep = &u->port; - } - break; + else { + char *tmp; + char *endp; + unsigned long port; + errno = 0; + 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: urlskipslash = TRUE; leadingslash = TRUE; /* enforce */ @@ -1990,9 +1993,5 @@ nomem: free(*storep); *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; } diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 0a872e9496..7e44de940d 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -151,6 +151,9 @@ struct clearurlcase { }; 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 | [11] | [12] | [13] | curl.se | [15] | / | [16] | ", 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}, {"https://host:1234/", "port=0,", - "https://host:1234/", - 0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER}, + "https://host:0/", + 0, 0, CURLUE_OK, CURLUE_OK}, {"https://host:1234/", "port=65535,", "https://host:65535/", -- 2.47.3