✓ProveIt
Examples / Report #2187833 Saved analysis

CVE-2023-38545: socks5 heap buffer overflow

curl · 2023-09-3023.1s analysis · $0.029 in model calls

What the report claims

The SOCKS5 state machine can be manipulated to overflow heap memory when using socks5h with a hostname longer than 255 characters, due to a bug where remote resolution is not disabled on subsequent state machine calls.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2023-38545 SOCKS5 heap buffer overflow

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

ProveIt's finding

Code supports the claim

The code supports the reported flaw. A maintainer should review its impact and decide what to do next.

Why this finding?
  • Local variable socks5_resolve_local re-initialized on each do_SOCKS5 call, losing the >255 hostname check result from CONNECT_SOCKS_INIT
  • State machine can be delayed in CONNECT_SOCKS_READ by malicious SOCKS server
  • CONNECT_RESOLVE_REMOTE copies hostname into data->state.buffer without bounds check
  • Default buffer (16385 bytes) smaller than max hostname (65535 bytes), enabling heap overflow
  • Attacker can trigger via redirect (CURLOPT_FOLLOWLOCATION) with long hostname while SOCKS server delays hello reply
How this analysis ran 6 claims · 18 citations verified

Checked against curl permalink curl-8_3_0 (6fa1d817e5b1). Dev 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 · 49 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
6 claims · 4379 tokens · $0.0004
3 · Evidence
18 code snippets
real curl source at 6fa1d817e5b1 · 3 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
23300 tokens · 12.1s · $0.0289
5 · Receipts
18 of 18 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

The report correctly identifies a heap buffer overflow in the SOCKS5 state machine. The bug is that socks5_resolve_local is a local variable re-initialized on each do_SOCKS5 call, so the >255 hostname check in CONNECT_SOCKS_INIT (line 588-593) is lost when the state machine later reaches CONNECT_RESOLVE_REMOTE (line 904-911). An attacker controlling a SOCKS server and a redirect can delay the hello reply and supply a hostname exceeding the default 16KB buffer, causing memcpy to overflow data->state.buffer. This is a valid security vulnerability.

Inspect all claims and source code 6 claims · 18 code excerpts

Report claims

C1flaw supported

The SOCKS5 state machine fails to disable remote hostname resolution for hostnames longer than 255 characters on subsequent calls, leading to a heap buffer overflow.

Read the reasoning

The local variable socks5_resolve_local is re-initialized to FALSE on every call to do_SOCKS5 for CURLPROXY_SOCKS5_HOSTNAME. The hostname length check (>255) only runs in CONNECT_SOCKS_INIT state (first call). On subsequent calls when the state machine is in CONNECT_SOCKS_READ waiting for server reply, the check is skipped. When the server finally replies, the state transitions to CONNECT_REQ_INIT which reads the re-initialized FALSE value, then goes to CONNECT_RESOLVE_REMOTE where the hostname is copied into socksreq (data->state.buffer) without bounds checking.

C2location supported

The flaw occurs in the CONNECT_RESOLVE_REMOTE state where the hostname is copied into the socksreq buffer without length validation.

Read the reasoning

In CONNECT_RESOLVE_REMOTE (lines 904-911), the code assumes hostname_len <= 255 (per RFC1928) and copies it into socksreq which aliases data->state.buffer. No bounds check against the buffer size is performed.

C3trigger supported

An attacker controlling a SOCKS server can delay the initial hello reply and force a hostname longer than 255 characters to trigger the overflow.

Read the reasoning

An attacker controlling a SOCKS server can delay the initial hello reply (keeping state in CONNECT_SOCKS_READ). Combined with a redirect (CURLOPT_FOLLOWLOCATION) providing a hostname >255 chars (up to 65535), when the server finally replies the state machine proceeds with socks5_resolve_local=FALSE into CONNECT_RESOLVE_REMOTE.

C4impact supported

Heap buffer overflow can corrupt adjacent memory, potentially leading to remote code execution or application crash.

Read the reasoning

Default buffer is 16385 bytes. Hostname can be 65535 bytes. The memcpy in CONNECT_RESOLVE_REMOTE writes hostname_len bytes plus 2 bytes (type + length) into the buffer without checking remaining space, causing heap overflow when hostname exceeds buffer capacity.

C5reproduction supported

By using curl with CURLOPT_FOLLOWLOCATION and a malicious Location header containing a long hostname, while delaying the SOCKS server hello, the overflow can be reproduced.

Read the reasoning

The reproduction steps are plausible: CURLOPT_FOLLOWLOCATION allows attacker-controlled redirect with long hostname in Location header; socks5h proxy uses CURLPROXY_SOCKS5_HOSTNAME; delaying SOCKS server hello keeps state machine in CONNECT_SOCKS_READ until the redirect is processed.

C6reproduction supported

The exploit requires knowledge of the buffer size (e.g., 16384+1) and can be triggered with a hostname exceeding the remaining buffer space.

Read the reasoning

The exploit requires hostname length exceeding the remaining buffer space. With default 16384+1 buffer, a hostname >~16KB triggers overflow. The report correctly identifies buffer size configurations.

Read the original report (excerpt)

Full report on HackerOne ↗

# Summary: The SOCKS5 state machine can be manipulated by a remote attacker to overflow heap memory if four conditions are met: 1. The request is made via socks5h. 2. The state machine's negotiation buffer is smaller than ~65k. 3. The SOCKS server's "hello" reply is delayed. 4. The attacker sets a final destination hostname larger than the negotiation buffer. libcurl is supposed to disable SOCKS5 remote hostname resolution for hostnames larger than 255 but will not due to a state machine bug. For example tor user running libcurl app with follow location that connects to rogue onion server that replies with payload in `Location:` header which causes crash or worse. # Walkthrough: `do_SOCKS` initializes local variable `socks5_resolve_local` depending on the `CURLPROXY_` name. There are two relevant names for this state machine: - `CURLPROXY_SOCKS5` (SOCKS5 with local resolve of dest host) - `CURLPROXY_SOCKS5_HOSTNAME` (SOCKS5 with remote resolve of dest host) [Code:](https://github.com/curl/curl/blob/curl-8_3_0/lib/socks.c#L573-L574) ~~~c bool socks5_resolve_local = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; ~~~ For this scenario, `CURLPROXY_SOCKS5_HOSTNAME` is the name and `socks5_resolve_local` is initialized FALSE. The `do_SOCKS` state machine is entered for the first time for the connection. `sx->state` is `CONNECT_SOCKS_INIT` (which happens to be the first label). In that state the hostname length is checked and if too long to resolve remotely (>255) then it sets `socks5_resolve_local` to TRUE. [Code:](https://github.com/curl/curl/blob/curl-8_3_0/lib/socks.c#L588-L593) ~~~c /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ if(!socks5_resolve_local && hostname_len > 255) { infof(data, "SOCKS5: server resolving disabled for hostnames of " "length > 255 [actual len=%zu]", hostname_len); socks5_resolve_local = TRUE; } ~~~ The local variable `socks5_resolve_local` is changed but, because this is a state machine, subsequent calls to `do_SOCKS` are in a different state and do not make the same change. ==**This is the bug.**== For this scenario, the hostname is longer than 255 characters and `do_SOCKS` is on a subsequent call, which means `socks5_resolve_local` remains FALSE. This can happen by chance or be forced by an attacker. The client "hello" SOCKS packet …

Source code

lib/socks.clines 548–687 · lines 573-574 cited by the report; lines 588-593 cited by the report; lines 640-662 cited by the report
548static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
549 struct socks_state *sx,
550 struct Curl_easy *data)
551{
552 /*
553 According to the RFC1928, section "6. Replies". This is what a SOCK5
554 replies:
555
556 +----+-----+-------+------+----------+----------+
557 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
558 +----+-----+-------+------+----------+----------+
559 | 1 | 1 | X'00' | 1 | Variable | 2 |
560 +----+-----+-------+------+----------+----------+
561
562 Where:
563
564 o VER protocol version: X'05'
565 o REP Reply field:
566 o X'00' succeeded
567 */
568 struct connectdata *conn = cf->conn;
569 unsigned char *socksreq = (unsigned char *)data->state.buffer;
C2: socksreq points to data->state.buffer
570 int idx;
571 CURLcode result;
572 CURLproxycode presult;
573 bool socks5_resolve_local =
C1: socks5_resolve_local initialized as local variable based on proxy type
C5: CURLPROXY_SOCKS5_HOSTNAME triggers the vulnerable path
574 (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
575 const size_t hostname_len = strlen(sx->hostname);
576 ssize_t len = 0;
577 const unsigned char auth = data->set.socks5auth;
578 bool allow_gssapi = FALSE;
579 struct Curl_dns_entry *dns = NULL;
580
581 DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
582 switch(sx->state) {
583 case CONNECT_SOCKS_INIT:
C1: CONNECT_SOCKS_INIT state where hostname length check runs
584 if(conn->bits.httpproxy)
585 infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
586 sx->hostname, sx->remote_port);
587
588 /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
C1: Length check only executes in CONNECT_SOCKS_INIT state
589 if(!socks5_resolve_local && hostname_len > 255) {
590 infof(data, "SOCKS5: server resolving disabled for hostnames of "
591 "length > 255 [actual len=%zu]", hostname_len);
592 socks5_resolve_local = TRUE;
593 }
594
595 if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
596 infof(data,
597 "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %u",
598 auth);
599 if(!(auth & CURLAUTH_BASIC))
600 /* disable username/password auth */
601 sx->proxy_user = NULL;
602#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
603 if(auth & CURLAUTH_GSSAPI)
604 allow_gssapi = TRUE;
605#endif
606
607 idx = 0;
608 socksreq[idx++] = 5; /* version */
609 idx++; /* number of authentication methods */
610 socksreq[idx++] = 0; /* no authentication */
611 if(allow_gssapi)
612 socksreq[idx++] = 1; /* GSS-API */
613 if(sx->proxy_user)
614 socksreq[idx++] = 2; /* username/password */
615 /* write the number of authentication methods */
616 socksreq[1] = (unsigned char) (idx - 2);
617
618 sx->outp = socksreq;
619 sx->outstanding = idx;
620 presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
621 "initial SOCKS5 request");
622 if(CURLPX_OK != presult)
623 return presult;
624 else if(sx->outstanding) {
625 /* remain in sending state */
626 return CURLPX_OK;
627 }
628 sxstate(sx, data, CONNECT_SOCKS_READ);
629 goto CONNECT_SOCKS_READ_INIT;
630 case CONNECT_SOCKS_SEND:
631 presult = socks_state_send(cf, sx, data, CURLPX_SEND_CONNECT,
632 "initial SOCKS5 request");
633 if(CURLPX_OK != presult)
634 return presult;
635 else if(sx->outstanding) {
636 /* remain in sending state */
637 return CURLPX_OK;
638 }
639 /* FALLTHROUGH */
640CONNECT_SOCKS_READ_INIT:
641 case CONNECT_SOCKS_READ_INIT:
642 sx->outstanding = 2; /* expect two bytes */
643 sx->outp = socksreq; /* store it here */
644 /* FALLTHROUGH */
645 case CONNECT_SOCKS_READ:
C3: CONNECT_SOCKS_READ state waits for server reply via socks_state_recv
C5: State machine delay in CONNECT_SOCKS_READ
646 presult = socks_state_recv(cf, sx, data, CURLPX_RECV_CONNECT,
647 "initial SOCKS5 response");
648 if(CURLPX_OK != presult)
649 return presult;
650 else if(sx->outstanding) {
651 /* remain in reading state */
652 return CURLPX_OK;
653 }
654 else if(socksreq[0] != 5) {
655 failf(data, "Received invalid version in initial SOCKS5 response.");
656 return CURLPX_BAD_VERSION;
657 }
658 else if(socksreq[1] == 0) {
659 /* DONE! No authentication needed. Send request. */
660 sxstate(sx, data, CONNECT_REQ_INIT);
C3: On valid reply (method 0), transitions to CONNECT_REQ_INIT
661 goto CONNECT_REQ_INIT;
662 }
663 else if(socksreq[1] == 2) {
664 /* regular name + password authentication */
665 sxstate(sx, data, CONNECT_AUTH_INIT);
666 goto CONNECT_AUTH_INIT;
667 }
668#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
669 else if(allow_gssapi && (socksreq[1] == 1)) {
670 sxstate(sx, data, CONNECT_GSSAPI_INIT);
671 result = Curl_SOCKS5_gssapi_negotiate(cf, data);
672 if(result) {
673 failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
674 return CURLPX_GSSAPI;
675 }
676 }
677#endif
678 else {
679 /* error */
680 if(!allow_gssapi && (socksreq[1] == 1)) {
681 failf(data,
682 "SOCKS5 GSSAPI per-message authentication is not supported.");
683 return CURLPX_GSSAPI_PERMSG;
684 }
685 else if(socksreq[1] == 255) {
686 failf(data, "No authentication method was acceptable.");
687 return CURLPX_NO_AUTH;
lib/socks.clines 756–822 · lines 781-797 cited by the report
756 return CURLPX_OK;
757 }
758 sx->outp = socksreq;
759 sx->outstanding = 2;
760 sxstate(sx, data, CONNECT_AUTH_READ);
761 /* FALLTHROUGH */
762 case CONNECT_AUTH_READ:
763 presult = socks_state_recv(cf, sx, data, CURLPX_RECV_AUTH,
764 "SOCKS5 sub-negotiation response");
765 if(CURLPX_OK != presult)
766 return presult;
767 else if(sx->outstanding) {
768 /* remain in reading state */
769 return CURLPX_OK;
770 }
771 /* ignore the first (VER) byte */
772 else if(socksreq[1]) { /* status */
773 failf(data, "User was rejected by the SOCKS5 server (%d %d).",
774 socksreq[0], socksreq[1]);
775 return CURLPX_USER_REJECTED;
776 }
777
778 /* Everything is good so far, user was authenticated! */
779 sxstate(sx, data, CONNECT_REQ_INIT);
780 /* FALLTHROUGH */
781CONNECT_REQ_INIT:
C1: CONNECT_REQ_INIT reads socks5_resolve_local which was re-initialized to FALSE
782 case CONNECT_REQ_INIT:
783 if(socks5_resolve_local) {
784 enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
785 TRUE, &dns);
786
787 if(rc == CURLRESOLV_ERROR)
788 return CURLPX_RESOLVE_HOST;
789
790 if(rc == CURLRESOLV_PENDING) {
791 sxstate(sx, data, CONNECT_RESOLVING);
792 return CURLPX_OK;
793 }
794 sxstate(sx, data, CONNECT_RESOLVED);
795 goto CONNECT_RESOLVED;
796 }
797 goto CONNECT_RESOLVE_REMOTE;
798
799 case CONNECT_RESOLVING:
800 /* check if we have the name resolved by now */
801 dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
802
803 if(dns) {
804#ifdef CURLRES_ASYNCH
805 data->state.async.dns = dns;
806 data->state.async.done = TRUE;
807#endif
808 infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
809 }
810
811 if(!dns) {
812 result = Curl_resolv_check(data, &dns);
813 if(!dns) {
814 if(result)
815 return CURLPX_RESOLVE_HOST;
816 return CURLPX_OK;
817 }
818 }
819 /* FALLTHROUGH */
820CONNECT_RESOLVED:
821 case CONNECT_RESOLVED: {
822 char dest[MAX_IPADR_LEN] = "unknown"; /* printable address */
lib/socks.clines 879–936 · lines 904-911 cited by the report
879 socksreq[len++] = 5; /* version (SOCKS5) */
880 socksreq[len++] = 1; /* connect */
881 socksreq[len++] = 0; /* must be zero */
882
883 if(!socks5_resolve_local) {
884 /* ATYP: domain name = 3,
885 IPv6 == 4,
886 IPv4 == 1 */
887 unsigned char ip4[4];
888#ifdef ENABLE_IPV6
889 if(conn->bits.ipv6_ip) {
890 char ip6[16];
891 if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
892 return CURLPX_BAD_ADDRESS_TYPE;
893 socksreq[len++] = 4;
894 memcpy(&socksreq[len], ip6, sizeof(ip6));
895 len += sizeof(ip6);
896 }
897 else
898#endif
899 if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
900 socksreq[len++] = 1;
901 memcpy(&socksreq[len], ip4, sizeof(ip4));
902 len += sizeof(ip4);
903 }
904 else {
C1: CONNECT_RESOLVE_REMOTE copies hostname without length validation
C2: CONNECT_RESOLVE_REMOTE state copies hostname via memcpy
905 socksreq[len++] = 3;
906 socksreq[len++] = (char) hostname_len; /* one byte address length */
C2: hostname_len used as one-byte length and memcpy size
907 memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */
908 len += hostname_len;
909 }
910 infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
911 sx->hostname, sx->remote_port);
912 }
913 /* FALLTHROUGH */
914
915CONNECT_REQ_SEND:
916 case CONNECT_REQ_SEND:
917 /* PORT MSB */
918 socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
919 /* PORT LSB */
920 socksreq[len++] = (unsigned char)(sx->remote_port & 0xff);
921
922#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
923 if(conn->socks5_gssapi_enctype) {
924 failf(data, "SOCKS5 GSS-API protection not yet implemented.");
925 return CURLPX_GSSAPI_PROTECTION;
926 }
927#endif
928 sx->outp = socksreq;
929 sx->outstanding = len;
930 sxstate(sx, data, CONNECT_REQ_SENDING);
931 /* FALLTHROUGH */
932 case CONNECT_REQ_SENDING:
933 presult = socks_state_send(cf, sx, data, CURLPX_SEND_REQUEST,
934 "SOCKS5 connect request");
935 if(CURLPX_OK != presult)
936 return presult;
lib/url.clines 1783–1836 · lines 1808-1811 cited by the report
1783 (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
1784 if(uc) {
1785 failf(data, "URL rejected: %s", curl_url_strerror(uc));
1786 return Curl_uc_to_curlcode(uc);
1787 }
1788
1789 /* after it was parsed, get the generated normalized version */
1790 uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0);
1791 if(uc)
1792 return Curl_uc_to_curlcode(uc);
1793 if(data->state.url_alloc)
1794 free(data->state.url);
1795 data->state.url = newurl;
1796 data->state.url_alloc = TRUE;
1797 }
1798
1799 uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
1800 if(uc)
1801 return Curl_uc_to_curlcode(uc);
1802
1803 uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0);
1804 if(uc) {
1805 if(!strcasecompare("file", data->state.up.scheme))
1806 return CURLE_OUT_OF_MEMORY;
1807 }
1808 else if(strlen(data->state.up.hostname) > MAX_URL_LEN) {
C3: Hostname can be up to MAX_URL_LEN (65535) characters
1809 failf(data, "Too long host name (maximum is %d)", MAX_URL_LEN);
1810 return CURLE_URL_MALFORMAT;
1811 }
1812 hostname = data->state.up.hostname;
1813
1814 if(hostname && hostname[0] == '[') {
1815 /* This looks like an IPv6 address literal. See if there is an address
1816 scope. */
1817 size_t hlen;
1818 conn->bits.ipv6_ip = TRUE;
1819 /* cut off the brackets! */
1820 hostname++;
1821 hlen = strlen(hostname);
1822 hostname[hlen - 1] = 0;
1823
1824 zonefrom_url(uh, data, conn);
1825 }
1826
1827 /* make sure the connect struct gets its own copy of the host name */
1828 conn->host.rawalloc = strdup(hostname ? hostname : "");
1829 if(!conn->host.rawalloc)
1830 return CURLE_OUT_OF_MEMORY;
1831 conn->host.name = conn->host.rawalloc;
1832
1833 /*************************************************************
1834 * IDN-convert the hostnames
1835 *************************************************************/
1836 result = Curl_idnconvert_hostname(&conn->host);
lib/multi.clines 1833–1886 · lines 1858-1861 cited by the report
1833 }
1834 }
1835 else {
1836 /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
1837 given FILE * stream and we can actually attempt to rewind that
1838 ourselves with fseek() */
1839 if(data->state.fread_func == (curl_read_callback)fread) {
1840 if(-1 != fseek(data->state.in, 0, SEEK_SET))
1841 /* successful rewind */
1842 return CURLE_OK;
1843 }
1844
1845 /* no callback set or failure above, makes us fail at once */
1846 failf(data, "necessary data rewind wasn't possible");
1847 return CURLE_SEND_FAIL_REWIND;
1848 }
1849 }
1850 return CURLE_OK;
1851}
1852
1853/*
1854 * Curl_preconnect() is called immediately before a connect starts. When a
1855 * redirect is followed, this is then called multiple times during a single
1856 * transfer.
1857 */
1858CURLcode Curl_preconnect(struct Curl_easy *data)
1859{
1860 if(!data->state.buffer) {
1861 data->state.buffer = malloc(data->set.buffer_size + 1);
C4: data->state.buffer allocated as buffer_size + 1 bytes
C6: buffer_size determines allocation size
1862 if(!data->state.buffer)
1863 return CURLE_OUT_OF_MEMORY;
1864 }
1865
1866 return CURLE_OK;
1867}
1868
1869static void set_in_callback(struct Curl_multi *multi, bool value)
1870{
1871 multi->in_callback = value;
1872}
1873
1874static CURLMcode multi_runsingle(struct Curl_multi *multi,
1875 struct curltime *nowp,
1876 struct Curl_easy *data)
1877{
1878 struct Curl_message *msg = NULL;
1879 bool connected;
1880 bool async;
1881 bool protocol_connected = FALSE;
1882 bool dophase_done = FALSE;
1883 bool done = FALSE;
1884 CURLMcode rc;
1885 CURLcode result = CURLE_OK;
1886 timediff_t recv_timeout_ms;
lib/url.clines 151–151 · definition of MAX_URL_LEN
151#define MAX_URL_LEN 0xffff
C4: MAX_URL_LEN 0xffff (65535) maximum hostname length
C6: MAX_URL_LEN maximum hostname length
include/curl/curl.hlines 263–263 · definition of CURL_MAX_WRITE_SIZE
263#define CURL_MAX_WRITE_SIZE 16384
C4: CURL_MAX_WRITE_SIZE 16384 default buffer size
lib/idn.clines 106–183 · lines 131-144 cited by the report; definition of idn_decode
Show 78 lines (not cited by the judge)
106 IDN_MAX_LENGTH);
107 if(chars) {
108 /* 'chars' is "the number of characters retrieved" */
109 char *mstr = curlx_convert_wchar_to_UTF8(idn);
110 if(mstr) {
111 out = strdup(mstr);
112 curlx_unicodefree(mstr);
113 if(!out)
114 return CURLE_OUT_OF_MEMORY;
115 }
116 }
117 else
118 return CURLE_URL_MALFORMAT;
119 }
120 else
121 return CURLE_URL_MALFORMAT;
122 *output = out;
123 return CURLE_OK;
124}
125
126#endif /* USE_WIN32_IDN */
127
128/*
129 * Helpers for IDNA conversions.
130 */
131bool Curl_is_ASCII_name(const char *hostname)
132{
133 /* get an UNSIGNED local version of the pointer */
134 const unsigned char *ch = (const unsigned char *)hostname;
135
136 if(!hostname) /* bad input, consider it ASCII! */
137 return TRUE;
138
139 while(*ch) {
140 if(*ch++ & 0x80)
141 return FALSE;
142 }
143 return TRUE;
144}
145
146#ifdef USE_IDN
147/*
148 * Curl_idn_decode() returns an allocated IDN decoded string if it was
149 * possible. NULL on error.
150 *
151 * CURLE_URL_MALFORMAT - the host name could not be converted
152 * CURLE_OUT_OF_MEMORY - memory problem
153 *
154 */
155static CURLcode idn_decode(const char *input, char **output)
156{
157 char *decoded = NULL;
158 CURLcode result = CURLE_OK;
159#ifdef USE_LIBIDN2
160 if(idn2_check_version(IDN2_VERSION)) {
161 int flags = IDN2_NFC_INPUT
162#if IDN2_VERSION_NUMBER >= 0x00140000
163 /* IDN2_NFC_INPUT: Normalize input string using normalization form C.
164 IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional
165 processing. */
166 | IDN2_NONTRANSITIONAL
167#endif
168 ;
169 int rc = IDN2_LOOKUP(input, &decoded, flags);
170 if(rc != IDN2_OK)
171 /* fallback to TR46 Transitional mode for better IDNA2003
172 compatibility */
173 rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL);
174 if(rc != IDN2_OK)
175 result = CURLE_URL_MALFORMAT;
176 }
177#elif defined(USE_WIN32_IDN)
178 result = win32_idn_to_ascii(input, &decoded);
179#endif
180 if(!result)
181 *output = decoded;
182 return result;
183}
lib/idn.clines 236–282 · lines 261-265 cited by the report
Show 47 lines (not cited by the judge)
236 return result;
237}
238
239/*
240 * Frees data allocated by idnconvert_hostname()
241 */
242void Curl_free_idnconverted_hostname(struct hostname *host)
243{
244 if(host->encalloc) {
245 /* must be freed with idn2_free() if allocated by libidn */
246 Curl_idn_free(host->encalloc);
247 host->encalloc = NULL;
248 }
249}
250
251#endif /* USE_IDN */
252
253/*
254 * Perform any necessary IDN conversion of hostname
255 */
256CURLcode Curl_idnconvert_hostname(struct hostname *host)
257{
258 /* set the name we use to display the host name */
259 host->dispname = host->name;
260
261#ifdef USE_IDN
262 /* Check name for non-ASCII and convert hostname if we can */
263 if(!Curl_is_ASCII_name(host->name)) {
264 char *decoded;
265 CURLcode result = idn_decode(host->name, &decoded);
266 if(!result) {
267 if(!*decoded) {
268 /* zero length is a bad host name */
269 Curl_idn_free(decoded);
270 return CURLE_URL_MALFORMAT;
271 }
272 /* successful */
273 host->encalloc = decoded;
274 /* change the name pointer to point to the encoded hostname */
275 host->name = host->encalloc;
276 }
277 else
278 return result;
279 }
280#endif
281 return CURLE_OK;
282}
lib/openldap.clines 206–230 · definition of state
Show 25 lines (not cited by the judge)
206static void state(struct Curl_easy *data, ldapstate newstate)
207{
208 struct ldapconninfo *ldapc = data->conn->proto.ldapc;
209
210#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
211 /* for debug purposes */
212 static const char * const names[] = {
213 "STOP",
214 "SSL",
215 "STARTTLS",
216 "TLS",
217 "MECHS",
218 "SASL",
219 "BIND",
220 "BINDV2",
221 /* LAST */
222 };
223
224 if(ldapc->state != newstate)
225 infof(data, "LDAP %p state change from %s to %s",
226 (void *)ldapc, names[ldapc->state], names[newstate]);
227#endif
228
229 ldapc->state = newstate;
230}
lib/CMakeLists.txtlines 24–97 · definition of set
Show 74 lines (not cited by the judge)
24set(LIB_NAME libcurl)
25set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library")
26add_definitions(-DBUILDING_LIBCURL)
27
28configure_file(curl_config.h.cmake
29 ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h)
30
31transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
32include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake)
33
34list(APPEND HHEADERS
35 ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h
36 )
37
38# The rest of the build
39
40include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include)
41include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
42include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include)
43include_directories(${CMAKE_CURRENT_BINARY_DIR}/..)
44include_directories(${CMAKE_CURRENT_SOURCE_DIR})
45include_directories(${CMAKE_CURRENT_BINARY_DIR})
46if(USE_ARES)
47 include_directories(${CARES_INCLUDE_DIR})
48endif()
49
50add_library(
51 curlu # special libcurlu library just for unittests
52 STATIC
53 EXCLUDE_FROM_ALL
54 ${HHEADERS} ${CSOURCES}
55)
56target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
57
58if(ENABLE_CURLDEBUG)
59 # We must compile memdebug.c separately to avoid memdebug.h redefinitions
60 # being applied to memdebug.c itself.
61 set_source_files_properties(memdebug.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
62endif()
63target_link_libraries(curlu PRIVATE ${CURL_LIBS})
64
65transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
66include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake)
67
68if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
69 CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
70 CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
71 CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR
72 CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
73
74 # FreeBSD comes with the a.out and elf flavours
75 # but a.out was supported up to version 3.x and
76 # elf from 3.x. I cannot imagine someone running
77 # CMake on those ancient systems
78 CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
79
80 CMAKE_SYSTEM_NAME STREQUAL "Haiku")
81
82 math(EXPR CMAKESONAME "${VERSIONCHANGE} - ${VERSIONDEL}")
83 set(CMAKEVERSION "${CMAKESONAME}.${VERSIONDEL}.${VERSIONADD}")
84else()
85 unset(CMAKESONAME)
86endif()
87
88if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING)
89 # on not-Windows and not-crosscompiling, check for writable argv[]
90 include(CheckCSourceRuns)
91 check_c_source_runs("
92int main(int argc, char **argv)
93{
94 (void)argc;
95 argv[0][0] = ' ';
96 return (argv[0][0] == ' ')?0:1;
97}"
lib/hostip.clines 676–836 · definition of Curl_resolv (truncated)
Show 161 lines (not cited by the judge)
676enum resolve_t Curl_resolv(struct Curl_easy *data,
677 const char *hostname,
678 int port,
679 bool allowDOH,
680 struct Curl_dns_entry **entry)
681{
682 struct Curl_dns_entry *dns = NULL;
683 CURLcode result;
684 enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
685 struct connectdata *conn = data->conn;
686 /* We should intentionally error and not resolve .onion TLDs */
687 size_t hostname_len = strlen(hostname);
688 if(hostname_len >= 7 &&
689 (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
690 curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
691 failf(data, "Not resolving .onion address (RFC 7686)");
692 return CURLRESOLV_ERROR;
693 }
694 *entry = NULL;
695#ifndef CURL_DISABLE_DOH
696 conn->bits.doh = FALSE; /* default is not */
697#else
698 (void)allowDOH;
699#endif
700
701 if(data->share)
702 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
703
704 dns = fetch_addr(data, hostname, port);
705
706 if(dns) {
707 infof(data, "Hostname %s was found in DNS cache", hostname);
708 dns->inuse++; /* we use it! */
709 rc = CURLRESOLV_RESOLVED;
710 }
711
712 if(data->share)
713 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
714
715 if(!dns) {
716 /* The entry was not in the cache. Resolve it to IP address */
717
718 struct Curl_addrinfo *addr = NULL;
719 int respwait = 0;
720#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
721 struct in_addr in;
722#endif
723#ifndef CURL_DISABLE_DOH
724#ifndef USE_RESOLVE_ON_IPS
725 const
726#endif
727 bool ipnum = FALSE;
728#endif
729
730 /* notify the resolver start callback */
731 if(data->set.resolver_start) {
732 int st;
733 Curl_set_in_callback(data, true);
734 st = data->set.resolver_start(
735#ifdef USE_CURL_ASYNC
736 data->state.async.resolver,
737#else
738 NULL,
739#endif
740 NULL,
741 data->set.resolver_start_client);
742 Curl_set_in_callback(data, false);
743 if(st)
744 return CURLRESOLV_ERROR;
745 }
746
747#ifndef USE_RESOLVE_ON_IPS
748 /* First check if this is an IPv4 address string */
749 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
750 /* This is a dotted IP address 123.123.123.123-style */
751 addr = Curl_ip2addr(AF_INET, &in, hostname, port);
752#ifdef ENABLE_IPV6
753 if(!addr) {
754 struct in6_addr in6;
755 /* check if this is an IPv6 address string */
756 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
757 /* This is an IPv6 address literal */
758 addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
759 }
760#endif /* ENABLE_IPV6 */
761
762#else /* if USE_RESOLVE_ON_IPS */
763#ifndef CURL_DISABLE_DOH
764 /* First check if this is an IPv4 address string */
765 if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
766 /* This is a dotted IP address 123.123.123.123-style */
767 ipnum = TRUE;
768#ifdef ENABLE_IPV6
769 else {
770 struct in6_addr in6;
771 /* check if this is an IPv6 address string */
772 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
773 /* This is an IPv6 address literal */
774 ipnum = TRUE;
775 }
776#endif /* ENABLE_IPV6 */
777#endif /* CURL_DISABLE_DOH */
778
779#endif /* !USE_RESOLVE_ON_IPS */
780
781 if(!addr) {
782 if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
783 return CURLRESOLV_ERROR;
784
785 if(strcasecompare(hostname, "localhost") ||
786 tailmatch(hostname, ".localhost"))
787 addr = get_localhost(port, hostname);
788#ifndef CURL_DISABLE_DOH
789 else if(allowDOH && data->set.doh && !ipnum)
790 addr = Curl_doh(data, hostname, port, &respwait);
791#endif
792 else {
793 /* Check what IP specifics the app has requested and if we can provide
794 * it. If not, bail out. */
795 if(!Curl_ipvalid(data, conn))
796 return CURLRESOLV_ERROR;
797 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
798 non-zero value indicating that we need to wait for the response to
799 the resolve call */
800 addr = Curl_getaddrinfo(data, hostname, port, &respwait);
801 }
802 }
803 if(!addr) {
804 if(respwait) {
805 /* the response to our resolve call will come asynchronously at
806 a later time, good or bad */
807 /* First, check that we haven't received the info by now */
808 result = Curl_resolv_check(data, &dns);
809 if(result) /* error detected */
810 return CURLRESOLV_ERROR;
811 if(dns)
812 rc = CURLRESOLV_RESOLVED; /* pointer provided */
813 else
814 rc = CURLRESOLV_PENDING; /* no info yet */
815 }
816 }
817 else {
818 if(data->share)
819 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
820
821 /* we got a response, store it in the cache */
822 dns = Curl_cache_addr(data, addr, hostname, 0, port);
823
824 if(data->share)
825 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
826
827 if(!dns)
828 /* returned failure, bail out nicely */
829 Curl_freeaddrinfo(addr);
830 else
831 rc = CURLRESOLV_RESOLVED;
832 }
833 }
834
835 *entry = dns;
836
lib/urldata.hlines 201–201 · definition of READBUFFER_SIZE
Show 1 lines (not cited by the judge)
201#define READBUFFER_SIZE CURL_MAX_WRITE_SIZE
lib/curl_trc.hlines 83–83 · definition of failf
Show 1 lines (not cited by the judge)
83#define failf Curl_failf
lib/curl_trc.hlines 118–120 · definition of infof
Show 3 lines (not cited by the judge)
118#define infof(data, ...) \
119 do { if(Curl_trc_is_verbose(data)) \
120 Curl_infof(data, __VA_ARGS__); } while(0)
lib/socks.clines 232–261 · definition of socks_state_recv
Show 30 lines (not cited by the judge)
232static CURLproxycode socks_state_recv(struct Curl_cfilter *cf,
233 struct socks_state *sx,
234 struct Curl_easy *data,
235 CURLproxycode failcode,
236 const char *description)
237{
238 ssize_t nread;
239 CURLcode result;
240
241 nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp,
242 sx->outstanding, &result);
243 if(nread <= 0) {
244 if(CURLE_AGAIN == result) {
245 return CURLPX_OK;
246 }
247 else if(CURLE_OK == result) {
248 /* connection closed */
249 failf(data, "connection to proxy closed");
250 return CURLPX_CLOSED;
251 }
252 failf(data, "SOCKS4: Failed receiving %s: %s", description,
253 curl_easy_strerror(result));
254 return failcode;
255 }
256 /* remain in reading state */
257 DEBUGASSERT(sx->outstanding >= nread);
258 sx->outstanding -= nread;
259 sx->outp += nread;
260 return CURLPX_OK;
261}
lib/socks.clines 148–148 · definition of sxstate
Show 1 lines (not cited by the judge)
148#define sxstate(x,d,y) socksstate(x,d,y, __LINE__)
lib/optiontable.pllines 74–115 · definition of while
Show 42 lines (not cited by the judge)
74while(<STDIN>) {
75 my $l = $_;
76 if($fl) {
77 # continued deprecation
78 if($l =~ /(.*)\),/) {
79 $fl .= $1;
80
81 # the end
82 my @p=split(/, */, $fl);
83 add($p[0], $p[1], $p[2]);
84 undef $fl;
85 }
86 else {
87 # another line to append
88 chomp $l;
89 $fl .= $l;
90 }
91 }
92
93 if(/^ *CURLOPTDEPRECATED\((.*)/) {
94 $fl = $1;
95 chomp $fl;
96 }
97
98 if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) {
99 my($opt, $type, $num)=($1,$2,$3);
100 add($opt, $type, $num);
101 }
102
103 # alias for an older option
104 # old = new
105 if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) {
106 my ($o, $n)=($1, $2);
107 # skip obsolete ones
108 if($n !~ /OBSOLETE/) {
109 $o =~ s/^CURLOPT_//;
110 $n =~ s/^CURLOPT_//;
111 $alias{$o} = $n;
112 push @names, $o,
113 }
114 }
115}