✓ProveIt
Examples / Report #2604391 Saved analysis

CVE-2024-6874: macidn punycode buffer overread

curl · 2024-07-1620.1s analysis · $0.026 in model calls

What the report claims

A stack-buffer over-read in lib/idn.c:75 leads to an out-of-bounds read when punycode-encoding a URL host, causing potential information leaks.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2024-6874 macidn punycode buffer overread

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Judgment withdrawn. The answer failed the evidence check.
See the withdrawn model reasoning

The following reasoning failed the citation check and does not support the final finding.

  • The code at lib/idn.c:68 ignores the return value of uidna_nameToASCII_UTF8, which indicates the number of bytes written.
  • The 256-byte stack buffer at line 67 has no guaranteed null terminator if the ICU function writes exactly 256 bytes.
  • strdup at line 75 reads past the buffer boundary in that case, leaking adjacent stack memory.
  • The trigger path via curl_url_get with CURLU_PUNYCODE is confirmed in lib/urlapi.c.
  • The fix proposed (check return value, ensure n < sizeof(buffer), explicitly null-terminate) matches PHP's mitigation and corrects the flaw.
How this analysis ran 5 claims · 11 citations verified

Evidence check: judge quoted an external source that does not say that (https://github.com/php/php-src/blob/master/ext/intl/idn/idn.c#L69)

Checked against curl permalink 58772b0e082e (58772b0e082e). Test evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 15 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2856 tokens · $0.0003
3 · Evidence
9 code snippets
real curl source at 58772b0e082e · 3 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
20482 tokens · 7.8s · $0.0261
5 · Receipts
11 of 12 citations verified
every citation must point at code the judge was shown
Inspect the withdrawn draft reply

This draft belongs to the withdrawn judgment. It may contain unsupported claims.

Thank you for the detailed report and patch. The flaw is confirmed: mac_idn_to_ascii ignores the return value of uidna_nameToASCII_UTF8, so a 256-byte punycode output leaves the stack buffer unterminated, causing strdup to overread. The code path via curl_url_get with CURLU_PUNYCODE is valid. Your proposed fix (capture return value, verify n < sizeof(buffer), explicitly null-terminate) is correct and matches PHP's approach. We will apply this fix.

Inspect all claims and source code 5 claims · 9 code excerpts

This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.

Report claims

C1flaw supported

uidna_nameToASCII_UTF8() leaves the output buffer unterminated when the encoded length equals the buffer capacity, causing strdup() to read beyond the buffer

Read the reasoning

The code ignores the return value of uidna_nameToASCII_UTF8 (line 68) and passes the fixed-size stack buffer directly to strdup (line 75). If the ICU function writes exactly 256 bytes (filling the buffer), no null terminator remains, causing strdup to read past the buffer boundary.

C2location supported

in lib/idn.c at line 75 in the mac_idn_to_ascii() function

Read the reasoning

The function mac_idn_to_ascii is defined at line 57 and the strdup call is at line 75, matching the report's location claim.

C3trigger supported

when a URL host is converted to punycode via curl_url_get() with CURLU_PUNYCODE

Read the reasoning

The code path exists: curl_url_get with CURLU_PUNYCODE triggers punycode conversion for the host, which calls Curl_idn_decode -> idn_decode -> mac_idn_to_ascii.

C4impact supported

leaks pointers in the stackframe of mac_idn_to_ascii() leading to information disclosure

Read the reasoning

An unterminated buffer passed to strdup causes a stack buffer overread, potentially leaking stack frame pointers, return addresses, or other adjacent stack data, which can defeat ASLR/PIE.

C5reproduction supported

provide a URL with a long host name that punycode-encodes to exactly 256 bytes, causing the unterminated buffer to be passed to strdup()

Read the reasoning

An attacker-controlled hostname that punycode-encodes to exactly 256 bytes will fill the buffer completely, leaving no null terminator. The PoC demonstrates this with a long internationalized domain name.

What the report says about other projects

not addressed uidna_nameToASCII_UTF8() leaves the output buffer unterminated when the encoded length equals the buffer capacity

confirmed PHP's handling of uidna_nameToASCII_UTF8() checks the return value and ensures null-termination

“int n = uidna_nameToASCII_UTF8(idna, in, -1, buffer, sizeof(buffer), &info, &err); ... if (U_FAILURE(err) || n < 0 || n >= sizeof(buffer)) { ... } buffer[n] = 0;” source

Read the original report (excerpt)

Full report on HackerOne ↗

libcurl at commit [58772b0e082eda333e0a5fc8fb0bc7f17a3cd99c](https://github.com/curl/curl/tree/58772b0e082eda333e0a5fc8fb0bc7f17a3cd99c) contains a stack-buffer overread in [lib/idn.c:75](https://github.com/curl/curl/blob/58772b0e082eda333e0a5fc8fb0bc7f17a3cd99c/lib/idn.c#L75) that can be triggered when the host of a URL is converted to punycode. The root cause of the bug is in the function `mac_idn_to_ascii()`: ```c static CURLcode mac_idn_to_ascii(const char *in, char **out) { // --- snip --- UIDNAInfo info = UIDNA_INFO_INITIALIZER; char buffer[256] = {0}; (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer, sizeof(buffer), &info, &err); uidna_close(idna); if(U_FAILURE(err)) { return CURLE_URL_MALFORMAT; } else { *out = strdup(buffer); if(*out) return CURLE_OK; else return CURLE_OUT_OF_MEMORY; } // --- snip --- } ``` `buffer` is supposed to hold the punycode-encoded version of `in` as a NUL-terminated string. However the implementation of `uidna_nameToASCII_UTF8()` leaves the output buffer unterminated when the length of the encoded output is equal to the capacity of the output buffer ([source](https://github.com/unicode-org/icu/blob/main/icu4c/source/common/ustring.cpp#L1437)). This leaves `buffer` without a terminating NUL-byte and the subsequent call to `strdup(buffer)` includes bytes beyond `buffer`. # PoC Consider the following dummy application that takes a URL as an argument and prints its punycode-encoded version: ```c int main (int argc, char** argv) { CURLU* url = curl_url(); curl_url_set(url, CURLUPART_URL, argv[1], 0); char* encoded_url; curl_url_get(url, CURLUPART_URL, &encoded_url, CURLU_PUNYCODE); printf("%s\n", encoded_url); } ``` Then ``` ./dummy "https://öööööö-ä-üxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxöööööööööööööööööüöäüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüöööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööööxx" ``` results in the over-read: ``` ==77491==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7e649e109750 at pc 0x5852e492c7b5 bp 0x7ffec1daa250 sp 0x7ffec1da9a10 READ of size 257 at 0x7e649e109750 thread T0 #0 0x5852e492c7b4 in strlen.part.0 asan_interceptors.cpp.o #1 0x5852e4a1bf48 in curl_dbg_ …

Source code

lib/idn.clines 50–100 · lines 75-75 cited by the report
50#include "curl_memory.h"
51#include "memdebug.h"
52
53/* for macOS and iOS targets */
54#if defined(USE_APPLE_IDN)
55#include <unicode/uidna.h>
56
57static CURLcode mac_idn_to_ascii(const char *in, char **out)
C2: mac_idn_to_ascii function definition starts here
58{
59 UErrorCode err = U_ZERO_ERROR;
60 UIDNA* idna = uidna_openUTS46(
61 UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_ASCII, &err);
62 if(U_FAILURE(err)) {
63 return CURLE_OUT_OF_MEMORY;
64 }
65 else {
66 UIDNAInfo info = UIDNA_INFO_INITIALIZER;
67 char buffer[256] = {0};
C1: buffer is 256 bytes, initialized to zero
C4: buffer is a stack-allocated array of 256 bytes
C5: buffer size is 256 bytes
68 (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer,
C1: uidna_nameToASCII_UTF8 return value cast to void, ignoring the number of bytes written
C5: uidna_nameToASCII_UTF8 called with sizeof(buffer) as capacity
69 sizeof(buffer), &info, &err);
70 uidna_close(idna);
71 if(U_FAILURE(err)) {
72 return CURLE_URL_MALFORMAT;
73 }
74 else {
75 *out = strdup(buffer);
C1: strdup(buffer) called without verifying null-termination; if uidna_nameToASCII_UTF8 wrote exactly 256 bytes, buffer has no NUL terminator
C2: strdup(buffer) call at line 75 as cited
C4: strdup reads past buffer if not null-terminated, leaking adjacent stack contents
76 if(*out)
77 return CURLE_OK;
78 else
79 return CURLE_OUT_OF_MEMORY;
80 }
81 }
82}
83
84static CURLcode mac_ascii_to_idn(const char *in, char **out)
85{
86 UErrorCode err = U_ZERO_ERROR;
87 UIDNA* idna = uidna_openUTS46(
88 UIDNA_CHECK_BIDI|UIDNA_NONTRANSITIONAL_TO_UNICODE, &err);
89 if(U_FAILURE(err)) {
90 return CURLE_OUT_OF_MEMORY;
91 }
92 else {
93 UIDNAInfo info = UIDNA_INFO_INITIALIZER;
94 char buffer[256] = {0};
95 (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer,
96 sizeof(buffer), &info, &err);
97 uidna_close(idna);
98 if(U_FAILURE(err)) {
99 return CURLE_URL_MALFORMAT;
100 }
lib/urlapi.clines 1419–1579 · definition of curl_url_get (truncated)
1419CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
1420 char **part, unsigned int flags)
1421{
1422 const char *ptr;
1423 CURLUcode ifmissing = CURLUE_UNKNOWN_PART;
1424 char portbuf[7];
1425 bool urldecode = (flags & CURLU_URLDECODE)?1:0;
1426 bool urlencode = (flags & CURLU_URLENCODE)?1:0;
1427 bool punycode = FALSE;
1428 bool depunyfy = FALSE;
1429 bool plusdecode = FALSE;
1430 (void)flags;
1431 if(!u)
1432 return CURLUE_BAD_HANDLE;
1433 if(!part)
1434 return CURLUE_BAD_PARTPOINTER;
1435 *part = NULL;
1436
1437 switch(what) {
1438 case CURLUPART_SCHEME:
1439 ptr = u->scheme;
1440 ifmissing = CURLUE_NO_SCHEME;
1441 urldecode = FALSE; /* never for schemes */
1442 if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme)
1443 return CURLUE_NO_SCHEME;
1444 break;
1445 case CURLUPART_USER:
1446 ptr = u->user;
1447 ifmissing = CURLUE_NO_USER;
1448 break;
1449 case CURLUPART_PASSWORD:
1450 ptr = u->password;
1451 ifmissing = CURLUE_NO_PASSWORD;
1452 break;
1453 case CURLUPART_OPTIONS:
1454 ptr = u->options;
1455 ifmissing = CURLUE_NO_OPTIONS;
1456 break;
1457 case CURLUPART_HOST:
C3: punycode flag set from CURLU_PUNYCODE when getting host part
1458 ptr = u->host;
1459 ifmissing = CURLUE_NO_HOST;
1460 punycode = (flags & CURLU_PUNYCODE)?1:0;
1461 depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
1462 break;
1463 case CURLUPART_ZONEID:
1464 ptr = u->zoneid;
1465 ifmissing = CURLUE_NO_ZONEID;
1466 break;
1467 case CURLUPART_PORT:
1468 ptr = u->port;
1469 ifmissing = CURLUE_NO_PORT;
1470 urldecode = FALSE; /* never for port */
1471 if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) {
1472 /* there is no stored port number, but asked to deliver
1473 a default one for the scheme */
1474 const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
1475 if(h) {
1476 msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
1477 ptr = portbuf;
1478 }
1479 }
1480 else if(ptr && u->scheme) {
1481 /* there is a stored port number, but ask to inhibit if
1482 it matches the default one for the scheme */
1483 const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
1484 if(h && (h->defport == u->portnum) &&
1485 (flags & CURLU_NO_DEFAULT_PORT))
1486 ptr = NULL;
1487 }
1488 break;
1489 case CURLUPART_PATH:
1490 ptr = u->path;
1491 if(!ptr)
1492 ptr = "/";
1493 break;
1494 case CURLUPART_QUERY:
1495 ptr = u->query;
1496 ifmissing = CURLUE_NO_QUERY;
1497 plusdecode = urldecode;
1498 if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY))
1499 /* there was a blank query and the user do not ask for it */
1500 ptr = NULL;
1501 break;
1502 case CURLUPART_FRAGMENT:
1503 ptr = u->fragment;
1504 ifmissing = CURLUE_NO_FRAGMENT;
1505 if(!ptr && u->fragment_present && flags & CURLU_GET_EMPTY)
1506 /* there was a blank fragment and the user asks for it */
1507 ptr = "";
1508 break;
1509 case CURLUPART_URL: {
1510 char *url;
1511 char *scheme;
1512 char *options = u->options;
1513 char *port = u->port;
1514 char *allochost = NULL;
1515 bool show_fragment =
1516 u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY);
1517 bool show_query =
1518 (u->query && u->query[0]) ||
1519 (u->query_present && flags & CURLU_GET_EMPTY);
1520 punycode = (flags & CURLU_PUNYCODE)?1:0;
1521 depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
1522 if(u->scheme && strcasecompare("file", u->scheme)) {
1523 url = aprintf("file://%s%s%s",
1524 u->path,
1525 show_fragment ? "#": "",
1526 u->fragment ? u->fragment : "");
1527 }
1528 else if(!u->host)
1529 return CURLUE_NO_HOST;
1530 else {
1531 const struct Curl_handler *h = NULL;
1532 char schemebuf[MAX_SCHEME_LEN + 5];
1533 if(u->scheme)
1534 scheme = u->scheme;
1535 else if(flags & CURLU_DEFAULT_SCHEME)
1536 scheme = (char *) DEFAULT_SCHEME;
1537 else
1538 return CURLUE_NO_SCHEME;
1539
1540 h = Curl_get_scheme_handler(scheme);
1541 if(!port && (flags & CURLU_DEFAULT_PORT)) {
1542 /* there is no stored port number, but asked to deliver
1543 a default one for the scheme */
1544 if(h) {
1545 msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
1546 port = portbuf;
1547 }
1548 }
1549 else if(port) {
1550 /* there is a stored port number, but asked to inhibit if it matches
1551 the default one for the scheme */
1552 if(h && (h->defport == u->portnum) &&
1553 (flags & CURLU_NO_DEFAULT_PORT))
1554 port = NULL;
1555 }
1556
1557 if(h && !(h->flags & PROTOPT_URLOPTIONS))
1558 options = NULL;
1559
1560 if(u->host[0] == '[') {
1561 if(u->zoneid) {
1562 /* make it '[ host %25 zoneid ]' */
1563 struct dynbuf enc;
1564 size_t hostlen = strlen(u->host);
1565 Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
1566 if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
1567 u->zoneid))
1568 return CURLUE_OUT_OF_MEMORY;
1569 allochost = Curl_dyn_ptr(&enc);
1570 }
1571 }
1572 else if(urlencode) {
1573 allochost = curl_easy_escape(NULL, u->host, 0);
1574 if(!allochost)
1575 return CURLUE_OUT_OF_MEMORY;
1576 }
1577 else if(punycode) {
C3: Curl_idn_decode called when punycode flag is set and host is not ASCII
1578 if(!Curl_is_ASCII_name(u->host)) {
1579#ifndef USE_IDN
lib/curl_memory.hlines 153–153 · definition of strdup
Show 1 lines (not cited by the judge)
153#define strdup(ptr) Curl_cstrdup(ptr)
include/curl/urlapi.hlines 100–100 · definition of CURLU_PUNYCODE
Show 1 lines (not cited by the judge)
100#define CURLU_PUNYCODE (1<<12) /* get the hostname in punycode */
include/curl/urlapi.h@curl-8_8_0lines 100–100 · definition of CURLU_PUNYCODE as in release curl-8_8_0; differs from master
Show 1 lines (not cited by the judge)
100#define CURLU_PUNYCODE (1<<12) /* get the host name in punycode */
lib/idn.c@curl-8_8_0lines 57–81 · definition of mac_idn_to_ascii as in release curl-8_8_0; differs from master; commits that changed it since: fd0250869f IDN: fix ß with AppleIDN
Show 25 lines (not cited by the judge)
57static CURLcode mac_idn_to_ascii(const char *in, char **out)
58{
59 UErrorCode err = U_ZERO_ERROR;
60 UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err);
61 if(U_FAILURE(err)) {
62 return CURLE_OUT_OF_MEMORY;
63 }
64 else {
65 UIDNAInfo info = UIDNA_INFO_INITIALIZER;
66 char buffer[256] = {0};
67 (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer,
68 sizeof(buffer), &info, &err);
69 uidna_close(idna);
70 if(U_FAILURE(err)) {
71 return CURLE_URL_MALFORMAT;
72 }
73 else {
74 *out = strdup(buffer);
75 if(*out)
76 return CURLE_OK;
77 else
78 return CURLE_OUT_OF_MEMORY;
79 }
80 }
81}
lib/urlapi.c@curl-8_8_0lines 1417–1577 · definition of curl_url_get as in release curl-8_8_0; differs from master; commits that changed it since: c074ba64a8 code: language cleanup in comments | 655d44d139 urlapi: add CURLU_NO_GUESS_SCHEME
Show 161 lines (not cited by the judge)
1417CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
1418 char **part, unsigned int flags)
1419{
1420 const char *ptr;
1421 CURLUcode ifmissing = CURLUE_UNKNOWN_PART;
1422 char portbuf[7];
1423 bool urldecode = (flags & CURLU_URLDECODE)?1:0;
1424 bool urlencode = (flags & CURLU_URLENCODE)?1:0;
1425 bool punycode = FALSE;
1426 bool depunyfy = FALSE;
1427 bool plusdecode = FALSE;
1428 (void)flags;
1429 if(!u)
1430 return CURLUE_BAD_HANDLE;
1431 if(!part)
1432 return CURLUE_BAD_PARTPOINTER;
1433 *part = NULL;
1434
1435 switch(what) {
1436 case CURLUPART_SCHEME:
1437 ptr = u->scheme;
1438 ifmissing = CURLUE_NO_SCHEME;
1439 urldecode = FALSE; /* never for schemes */
1440 break;
1441 case CURLUPART_USER:
1442 ptr = u->user;
1443 ifmissing = CURLUE_NO_USER;
1444 break;
1445 case CURLUPART_PASSWORD:
1446 ptr = u->password;
1447 ifmissing = CURLUE_NO_PASSWORD;
1448 break;
1449 case CURLUPART_OPTIONS:
1450 ptr = u->options;
1451 ifmissing = CURLUE_NO_OPTIONS;
1452 break;
1453 case CURLUPART_HOST:
1454 ptr = u->host;
1455 ifmissing = CURLUE_NO_HOST;
1456 punycode = (flags & CURLU_PUNYCODE)?1:0;
1457 depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
1458 break;
1459 case CURLUPART_ZONEID:
1460 ptr = u->zoneid;
1461 ifmissing = CURLUE_NO_ZONEID;
1462 break;
1463 case CURLUPART_PORT:
1464 ptr = u->port;
1465 ifmissing = CURLUE_NO_PORT;
1466 urldecode = FALSE; /* never for port */
1467 if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) {
1468 /* there's no stored port number, but asked to deliver
1469 a default one for the scheme */
1470 const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
1471 if(h) {
1472 msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
1473 ptr = portbuf;
1474 }
1475 }
1476 else if(ptr && u->scheme) {
1477 /* there is a stored port number, but ask to inhibit if
1478 it matches the default one for the scheme */
1479 const struct Curl_handler *h = Curl_get_scheme_handler(u->scheme);
1480 if(h && (h->defport == u->portnum) &&
1481 (flags & CURLU_NO_DEFAULT_PORT))
1482 ptr = NULL;
1483 }
1484 break;
1485 case CURLUPART_PATH:
1486 ptr = u->path;
1487 if(!ptr)
1488 ptr = "/";
1489 break;
1490 case CURLUPART_QUERY:
1491 ptr = u->query;
1492 ifmissing = CURLUE_NO_QUERY;
1493 plusdecode = urldecode;
1494 if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY))
1495 /* there was a blank query and the user do not ask for it */
1496 ptr = NULL;
1497 break;
1498 case CURLUPART_FRAGMENT:
1499 ptr = u->fragment;
1500 ifmissing = CURLUE_NO_FRAGMENT;
1501 if(!ptr && u->fragment_present && flags & CURLU_GET_EMPTY)
1502 /* there was a blank fragment and the user asks for it */
1503 ptr = "";
1504 break;
1505 case CURLUPART_URL: {
1506 char *url;
1507 char *scheme;
1508 char *options = u->options;
1509 char *port = u->port;
1510 char *allochost = NULL;
1511 bool show_fragment =
1512 u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY);
1513 bool show_query =
1514 (u->query && u->query[0]) ||
1515 (u->query_present && flags & CURLU_GET_EMPTY);
1516 punycode = (flags & CURLU_PUNYCODE)?1:0;
1517 depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
1518 if(u->scheme && strcasecompare("file", u->scheme)) {
1519 url = aprintf("file://%s%s%s",
1520 u->path,
1521 show_fragment ? "#": "",
1522 u->fragment ? u->fragment : "");
1523 }
1524 else if(!u->host)
1525 return CURLUE_NO_HOST;
1526 else {
1527 const struct Curl_handler *h = NULL;
1528 if(u->scheme)
1529 scheme = u->scheme;
1530 else if(flags & CURLU_DEFAULT_SCHEME)
1531 scheme = (char *) DEFAULT_SCHEME;
1532 else
1533 return CURLUE_NO_SCHEME;
1534
1535 h = Curl_get_scheme_handler(scheme);
1536 if(!port && (flags & CURLU_DEFAULT_PORT)) {
1537 /* there's no stored port number, but asked to deliver
1538 a default one for the scheme */
1539 if(h) {
1540 msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
1541 port = portbuf;
1542 }
1543 }
1544 else if(port) {
1545 /* there is a stored port number, but asked to inhibit if it matches
1546 the default one for the scheme */
1547 if(h && (h->defport == u->portnum) &&
1548 (flags & CURLU_NO_DEFAULT_PORT))
1549 port = NULL;
1550 }
1551
1552 if(h && !(h->flags & PROTOPT_URLOPTIONS))
1553 options = NULL;
1554
1555 if(u->host[0] == '[') {
1556 if(u->zoneid) {
1557 /* make it '[ host %25 zoneid ]' */
1558 struct dynbuf enc;
1559 size_t hostlen = strlen(u->host);
1560 Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
1561 if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
1562 u->zoneid))
1563 return CURLUE_OUT_OF_MEMORY;
1564 allochost = Curl_dyn_ptr(&enc);
1565 }
1566 }
1567 else if(urlencode) {
1568 allochost = curl_easy_escape(NULL, u->host, 0);
1569 if(!allochost)
1570 return CURLUE_OUT_OF_MEMORY;
1571 }
1572 else if(punycode) {
1573 if(!Curl_is_ASCII_name(u->host)) {
1574#ifndef USE_IDN
1575 return CURLUE_LACKS_IDN;
1576#else
1577 CURLcode result = Curl_idn_decode(u->host, &allochost);
lib/urlapi.clines 1710–1870 · definition of curl_url_set (truncated)
Show 161 lines (not cited by the judge)
1710CURLUcode curl_url_set(CURLU *u, CURLUPart what,
1711 const char *part, unsigned int flags)
1712{
1713 char **storep = NULL;
1714 bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
1715 bool plusencode = FALSE;
1716 bool urlskipslash = FALSE;
1717 bool leadingslash = FALSE;
1718 bool appendquery = FALSE;
1719 bool equalsencode = FALSE;
1720 size_t nalloc;
1721
1722 if(!u)
1723 return CURLUE_BAD_HANDLE;
1724 if(!part) {
1725 /* setting a part to NULL clears it */
1726 switch(what) {
1727 case CURLUPART_URL:
1728 break;
1729 case CURLUPART_SCHEME:
1730 storep = &u->scheme;
1731 u->guessed_scheme = FALSE;
1732 break;
1733 case CURLUPART_USER:
1734 storep = &u->user;
1735 break;
1736 case CURLUPART_PASSWORD:
1737 storep = &u->password;
1738 break;
1739 case CURLUPART_OPTIONS:
1740 storep = &u->options;
1741 break;
1742 case CURLUPART_HOST:
1743 storep = &u->host;
1744 break;
1745 case CURLUPART_ZONEID:
1746 storep = &u->zoneid;
1747 break;
1748 case CURLUPART_PORT:
1749 u->portnum = 0;
1750 storep = &u->port;
1751 break;
1752 case CURLUPART_PATH:
1753 storep = &u->path;
1754 break;
1755 case CURLUPART_QUERY:
1756 storep = &u->query;
1757 u->query_present = FALSE;
1758 break;
1759 case CURLUPART_FRAGMENT:
1760 storep = &u->fragment;
1761 u->fragment_present = FALSE;
1762 break;
1763 default:
1764 return CURLUE_UNKNOWN_PART;
1765 }
1766 if(storep && *storep) {
1767 Curl_safefree(*storep);
1768 }
1769 else if(!storep) {
1770 free_urlhandle(u);
1771 memset(u, 0, sizeof(struct Curl_URL));
1772 }
1773 return CURLUE_OK;
1774 }
1775
1776 nalloc = strlen(part);
1777 if(nalloc > CURL_MAX_INPUT_LENGTH)
1778 /* excessive input length */
1779 return CURLUE_MALFORMED_INPUT;
1780
1781 switch(what) {
1782 case CURLUPART_SCHEME: {
1783 size_t plen = strlen(part);
1784 const char *s = part;
1785 if((plen > MAX_SCHEME_LEN) || (plen < 1))
1786 /* too long or too short */
1787 return CURLUE_BAD_SCHEME;
1788 /* verify that it is a fine scheme */
1789 if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part))
1790 return CURLUE_UNSUPPORTED_SCHEME;
1791 storep = &u->scheme;
1792 urlencode = FALSE; /* never */
1793 if(ISALPHA(*s)) {
1794 /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
1795 while(--plen) {
1796 if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
1797 s++; /* fine */
1798 else
1799 return CURLUE_BAD_SCHEME;
1800 }
1801 }
1802 else
1803 return CURLUE_BAD_SCHEME;
1804 u->guessed_scheme = FALSE;
1805 break;
1806 }
1807 case CURLUPART_USER:
1808 storep = &u->user;
1809 break;
1810 case CURLUPART_PASSWORD:
1811 storep = &u->password;
1812 break;
1813 case CURLUPART_OPTIONS:
1814 storep = &u->options;
1815 break;
1816 case CURLUPART_HOST:
1817 storep = &u->host;
1818 Curl_safefree(u->zoneid);
1819 break;
1820 case CURLUPART_ZONEID:
1821 storep = &u->zoneid;
1822 break;
1823 case CURLUPART_PORT:
1824 if(!ISDIGIT(part[0]))
1825 /* not a number */
1826 return CURLUE_BAD_PORT_NUMBER;
1827 else {
1828 char *tmp;
1829 char *endp;
1830 unsigned long port;
1831 errno = 0;
1832 port = strtoul(part, &endp, 10); /* must be decimal */
1833 if(errno || (port > 0xffff) || *endp)
1834 /* weirdly provided number, not good! */
1835 return CURLUE_BAD_PORT_NUMBER;
1836 tmp = strdup(part);
1837 if(!tmp)
1838 return CURLUE_OUT_OF_MEMORY;
1839 free(u->port);
1840 u->port = tmp;
1841 u->portnum = (unsigned short)port;
1842 return CURLUE_OK;
1843 }
1844 case CURLUPART_PATH:
1845 urlskipslash = TRUE;
1846 leadingslash = TRUE; /* enforce */
1847 storep = &u->path;
1848 break;
1849 case CURLUPART_QUERY:
1850 plusencode = urlencode;
1851 appendquery = (flags & CURLU_APPENDQUERY)?1:0;
1852 equalsencode = appendquery;
1853 storep = &u->query;
1854 u->query_present = TRUE;
1855 break;
1856 case CURLUPART_FRAGMENT:
1857 storep = &u->fragment;
1858 u->fragment_present = TRUE;
1859 break;
1860 case CURLUPART_URL: {
1861 /*
1862 * Allow a new URL to replace the existing (if any) contents.
1863 *
1864 * If the existing contents is enough for a URL, allow a relative URL to
1865 * replace it.
1866 */
1867 CURLcode result;
1868 CURLUcode uc;
1869 char *oldurl;
1870 char *redired_url;
lib/urlapi.c@curl-8_8_0lines 1700–1860 · definition of curl_url_set as in release curl-8_8_0; differs from master; commits that changed it since: c074ba64a8 code: language cleanup in comments | 655d44d139 urlapi: add CURLU_NO_GUESS_SCHEME
Show 161 lines (not cited by the judge)
1700CURLUcode curl_url_set(CURLU *u, CURLUPart what,
1701 const char *part, unsigned int flags)
1702{
1703 char **storep = NULL;
1704 bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
1705 bool plusencode = FALSE;
1706 bool urlskipslash = FALSE;
1707 bool leadingslash = FALSE;
1708 bool appendquery = FALSE;
1709 bool equalsencode = FALSE;
1710 size_t nalloc;
1711
1712 if(!u)
1713 return CURLUE_BAD_HANDLE;
1714 if(!part) {
1715 /* setting a part to NULL clears it */
1716 switch(what) {
1717 case CURLUPART_URL:
1718 break;
1719 case CURLUPART_SCHEME:
1720 storep = &u->scheme;
1721 break;
1722 case CURLUPART_USER:
1723 storep = &u->user;
1724 break;
1725 case CURLUPART_PASSWORD:
1726 storep = &u->password;
1727 break;
1728 case CURLUPART_OPTIONS:
1729 storep = &u->options;
1730 break;
1731 case CURLUPART_HOST:
1732 storep = &u->host;
1733 break;
1734 case CURLUPART_ZONEID:
1735 storep = &u->zoneid;
1736 break;
1737 case CURLUPART_PORT:
1738 u->portnum = 0;
1739 storep = &u->port;
1740 break;
1741 case CURLUPART_PATH:
1742 storep = &u->path;
1743 break;
1744 case CURLUPART_QUERY:
1745 storep = &u->query;
1746 u->query_present = FALSE;
1747 break;
1748 case CURLUPART_FRAGMENT:
1749 storep = &u->fragment;
1750 u->fragment_present = FALSE;
1751 break;
1752 default:
1753 return CURLUE_UNKNOWN_PART;
1754 }
1755 if(storep && *storep) {
1756 Curl_safefree(*storep);
1757 }
1758 else if(!storep) {
1759 free_urlhandle(u);
1760 memset(u, 0, sizeof(struct Curl_URL));
1761 }
1762 return CURLUE_OK;
1763 }
1764
1765 nalloc = strlen(part);
1766 if(nalloc > CURL_MAX_INPUT_LENGTH)
1767 /* excessive input length */
1768 return CURLUE_MALFORMED_INPUT;
1769
1770 switch(what) {
1771 case CURLUPART_SCHEME: {
1772 size_t plen = strlen(part);
1773 const char *s = part;
1774 if((plen > MAX_SCHEME_LEN) || (plen < 1))
1775 /* too long or too short */
1776 return CURLUE_BAD_SCHEME;
1777 /* verify that it is a fine scheme */
1778 if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part))
1779 return CURLUE_UNSUPPORTED_SCHEME;
1780 storep = &u->scheme;
1781 urlencode = FALSE; /* never */
1782 if(ISALPHA(*s)) {
1783 /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
1784 while(--plen) {
1785 if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
1786 s++; /* fine */
1787 else
1788 return CURLUE_BAD_SCHEME;
1789 }
1790 }
1791 else
1792 return CURLUE_BAD_SCHEME;
1793 break;
1794 }
1795 case CURLUPART_USER:
1796 storep = &u->user;
1797 break;
1798 case CURLUPART_PASSWORD:
1799 storep = &u->password;
1800 break;
1801 case CURLUPART_OPTIONS:
1802 storep = &u->options;
1803 break;
1804 case CURLUPART_HOST:
1805 storep = &u->host;
1806 Curl_safefree(u->zoneid);
1807 break;
1808 case CURLUPART_ZONEID:
1809 storep = &u->zoneid;
1810 break;
1811 case CURLUPART_PORT:
1812 if(!ISDIGIT(part[0]))
1813 /* not a number */
1814 return CURLUE_BAD_PORT_NUMBER;
1815 else {
1816 char *tmp;
1817 char *endp;
1818 unsigned long port;
1819 errno = 0;
1820 port = strtoul(part, &endp, 10); /* must be decimal */
1821 if(errno || (port > 0xffff) || *endp)
1822 /* weirdly provided number, not good! */
1823 return CURLUE_BAD_PORT_NUMBER;
1824 tmp = strdup(part);
1825 if(!tmp)
1826 return CURLUE_OUT_OF_MEMORY;
1827 free(u->port);
1828 u->port = tmp;
1829 u->portnum = (unsigned short)port;
1830 return CURLUE_OK;
1831 }
1832 case CURLUPART_PATH:
1833 urlskipslash = TRUE;
1834 leadingslash = TRUE; /* enforce */
1835 storep = &u->path;
1836 break;
1837 case CURLUPART_QUERY:
1838 plusencode = urlencode;
1839 appendquery = (flags & CURLU_APPENDQUERY)?1:0;
1840 equalsencode = appendquery;
1841 storep = &u->query;
1842 u->query_present = TRUE;
1843 break;
1844 case CURLUPART_FRAGMENT:
1845 storep = &u->fragment;
1846 u->fragment_present = TRUE;
1847 break;
1848 case CURLUPART_URL: {
1849 /*
1850 * Allow a new URL to replace the existing (if any) contents.
1851 *
1852 * If the existing contents is enough for a URL, allow a relative URL to
1853 * replace it.
1854 */
1855 CURLcode result;
1856 CURLUcode uc;
1857 char *oldurl;
1858 char *redired_url;
1859
1860 if(!nalloc)