CVE-2023-38545: socks5 heap buffer overflow
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
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.
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
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.
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.
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.
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.
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.
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)
Source code
| 548 | static 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 */ |
| 640 | CONNECT_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; |
| 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 */ |
| 781 | CONNECT_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 */ |
| 820 | CONNECT_RESOLVED: |
| 821 | case CONNECT_RESOLVED: { |
| 822 | char dest[MAX_IPADR_LEN] = "unknown"; /* printable address */ |
| 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 | |
| 915 | CONNECT_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; |
| 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); |
| 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 | */ |
| 1858 | CURLcode 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 | |
| 1869 | static void set_in_callback(struct Curl_multi *multi, bool value) |
| 1870 | { |
| 1871 | multi->in_callback = value; |
| 1872 | } |
| 1873 | |
| 1874 | static 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; |
| 151 | #define MAX_URL_LEN 0xffff |
| C4: MAX_URL_LEN 0xffff (65535) maximum hostname length | |
| C6: MAX_URL_LEN maximum hostname length |
| 263 | #define CURL_MAX_WRITE_SIZE 16384 |
| C4: CURL_MAX_WRITE_SIZE 16384 default buffer size |
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 | */ |
| 131 | bool 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 | */ |
| 155 | static 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 | } |
Show 47 lines (not cited by the judge)
| 236 | return result; |
| 237 | } |
| 238 | |
| 239 | /* |
| 240 | * Frees data allocated by idnconvert_hostname() |
| 241 | */ |
| 242 | void 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 | */ |
| 256 | CURLcode 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 | } |
Show 25 lines (not cited by the judge)
| 206 | static 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 | } |
Show 74 lines (not cited by the judge)
| 24 | set(LIB_NAME libcurl) |
| 25 | set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library") |
| 26 | add_definitions(-DBUILDING_LIBCURL) |
| 27 | |
| 28 | configure_file(curl_config.h.cmake |
| 29 | ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h) |
| 30 | |
| 31 | transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") |
| 32 | include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake) |
| 33 | |
| 34 | list(APPEND HHEADERS |
| 35 | ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h |
| 36 | ) |
| 37 | |
| 38 | # The rest of the build |
| 39 | |
| 40 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include) |
| 41 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) |
| 42 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../include) |
| 43 | include_directories(${CMAKE_CURRENT_BINARY_DIR}/..) |
| 44 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) |
| 45 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) |
| 46 | if(USE_ARES) |
| 47 | include_directories(${CARES_INCLUDE_DIR}) |
| 48 | endif() |
| 49 | |
| 50 | add_library( |
| 51 | curlu # special libcurlu library just for unittests |
| 52 | STATIC |
| 53 | EXCLUDE_FROM_ALL |
| 54 | ${HHEADERS} ${CSOURCES} |
| 55 | ) |
| 56 | target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB) |
| 57 | |
| 58 | if(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) |
| 62 | endif() |
| 63 | target_link_libraries(curlu PRIVATE ${CURL_LIBS}) |
| 64 | |
| 65 | transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") |
| 66 | include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake) |
| 67 | |
| 68 | if(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}") |
| 84 | else() |
| 85 | unset(CMAKESONAME) |
| 86 | endif() |
| 87 | |
| 88 | if(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(" |
| 92 | int main(int argc, char **argv) |
| 93 | { |
| 94 | (void)argc; |
| 95 | argv[0][0] = ' '; |
| 96 | return (argv[0][0] == ' ')?0:1; |
| 97 | }" |
Show 161 lines (not cited by the judge)
| 676 | enum 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 |
Show 1 lines (not cited by the judge)
| 201 | #define READBUFFER_SIZE CURL_MAX_WRITE_SIZE |
Show 1 lines (not cited by the judge)
| 83 | #define failf Curl_failf |
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) |
Show 30 lines (not cited by the judge)
| 232 | static 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 | } |
Show 1 lines (not cited by the judge)
| 148 | #define sxstate(x,d,y) socksstate(x,d,y, __LINE__) |
Show 42 lines (not cited by the judge)
| 74 | while(<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 | } |