From 88b1ca7cba5c94b11c3a6510146ba410e8858771 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 24 Aug 2020 08:39:29 +0200 Subject: [PATCH] CURLE_PROXY: new error code Failures clearly returned from a (SOCKS) proxy now causes this return code. Previously the situation was not very clear as what would be returned and when. In addition: when this error code is returned, an application can use CURLINFO_PROXY_ERROR to query libcurl for the detailed error, which then returns a value from the new 'CURLproxycode' enum. Closes #5770 --- docs/libcurl/curl_easy_getinfo.3 | 3 + docs/libcurl/opts/CURLINFO_PROXY_ERROR.3 | 104 ++++++++++++++ docs/libcurl/opts/Makefile.inc | 3 +- docs/libcurl/symbols-in-versions | 36 +++++ include/curl/curl.h | 46 ++++++- lib/connect.c | 14 +- lib/getinfo.c | 3 + lib/socks.c | 165 +++++++++++++---------- lib/socks.h | 26 ++-- lib/strerror.c | 3 + lib/urldata.h | 1 + tests/data/test1538 | 3 +- tests/data/test702 | 2 +- tests/data/test703 | 2 +- tests/data/test716 | 2 +- 15 files changed, 318 insertions(+), 95 deletions(-) create mode 100644 docs/libcurl/opts/CURLINFO_PROXY_ERROR.3 diff --git a/docs/libcurl/curl_easy_getinfo.3 b/docs/libcurl/curl_easy_getinfo.3 index 27c2779659..68c77e5ca5 100644 --- a/docs/libcurl/curl_easy_getinfo.3 +++ b/docs/libcurl/curl_easy_getinfo.3 @@ -141,6 +141,9 @@ See \fICURLINFO_REQUEST_SIZE(3)\fP .IP CURLINFO_SSL_VERIFYRESULT Certificate verification result. See \fICURLINFO_SSL_VERIFYRESULT(3)\fP +.IP CURLINFO_PROXY_ERROR +Detailed proxy error. +See \fICURLINFO_PROXY_ERROR(3)\fP .IP CURLINFO_PROXY_SSL_VERIFYRESULT Proxy certificate verification result. See \fICURLINFO_PROXY_SSL_VERIFYRESULT(3)\fP diff --git a/docs/libcurl/opts/CURLINFO_PROXY_ERROR.3 b/docs/libcurl/opts/CURLINFO_PROXY_ERROR.3 new file mode 100644 index 0000000000..c6420b9b3e --- /dev/null +++ b/docs/libcurl/opts/CURLINFO_PROXY_ERROR.3 @@ -0,0 +1,104 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2020, Daniel Stenberg, , et al. +.\" * +.\" * This software is licensed as described in the file COPYING, which +.\" * you should have received as part of this distribution. The terms +.\" * are also available at https://curl.haxx.se/docs/copyright.html. +.\" * +.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell +.\" * copies of the Software, and permit persons to whom the Software is +.\" * furnished to do so, under the terms of the COPYING file. +.\" * +.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY +.\" * KIND, either express or implied. +.\" * +.\" ************************************************************************** +.\" +.TH CURLINFO_PROXY_ERROR 3 "3 Aug 2020" "libcurl 7.73.0" "curl_easy_getinfo options" +.SH NAME +CURLINFO_PROXY_ERROR \- get the detailed (SOCKS) proxy error +.SH SYNOPSIS +.nf +#include + +typedef enum { + CURLPX_OK, + CURLPX_BAD_ADDRESS_TYPE, + CURLPX_BAD_VERSION, + CURLPX_CLOSED, + CURLPX_GSSAPI, + CURLPX_GSSAPI_PERMSG, + CURLPX_GSSAPI_PROTECTION, + CURLPX_IDENTD, + CURLPX_IDENTD_DIFFER, + CURLPX_LONG_HOSTNAME, + CURLPX_LONG_PASSWD, + CURLPX_LONG_USER, + CURLPX_NO_AUTH, + CURLPX_RECV_ADDRESS, + CURLPX_RECV_AUTH, + CURLPX_RECV_CONNECT, + CURLPX_RECV_REQACK, + CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, + CURLPX_REPLY_COMMAND_NOT_SUPPORTED, + CURLPX_REPLY_CONNECTION_REFUSED, + CURLPX_REPLY_GENERAL_SERVER_FAILURE, + CURLPX_REPLY_HOST_UNREACHABLE, + CURLPX_REPLY_NETWORK_UNREACHABLE, + CURLPX_REPLY_NOT_ALLOWED, + CURLPX_REPLY_TTL_EXPIRED, + CURLPX_REPLY_UNASSIGNED, + CURLPX_REQUEST_FAILED, + CURLPX_RESOLVE_HOST, + CURLPX_SEND_AUTH, + CURLPX_SEND_CONNECT, + CURLPX_SEND_REQUEST, + CURLPX_UNKNOWN_FAIL, + CURLPX_UNKNOWN_MODE, + CURLPX_USER_REJECTED, + CURLPX_LAST /* never use */ +} CURLproxycode; + +CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_PROXY_ERROR, long *detail); +.fi +.SH DESCRIPTION +Pass a pointer to a long to receive a detailed error code when the most recent +transfer returned a CURLE_PROXY error. + +The return value will match the CURLproxycode set. + +The returned value will be zero (equal to CURLPX_OK) if no such response code +was available. +.SH PROTOCOLS +All that can be done over SOCKS +.SH EXAMPLE +.nf +CURL *curl = curl_easy_init(); +if(curl) { + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); + + curl_easy_setopt(curl, CURLOPT_PROXY, "socks5://127.0.0.1"); + res = curl_easy_perform(curl); + if(res == CURLE_PROXY) { + long proxycode; + res = curl_easy_getinfo(curl, CURLINFO_PROXY_ERROR, &proxycode); + if(!res && proxycode) + printf("The detailed proxy error: %ld\\n", proxycode); + } + curl_easy_cleanup(curl); +} +.fi +.SH AVAILABILITY +Added in 7.73.0 +.SH RETURN VALUE +Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not. +.SH "SEE ALSO" +.BR CURLINFO_RESPONSE_CODE "(3), " libcurl-errors "(3), " +.BR curl_easy_getinfo "(3), " curl_easy_setopt "(3), " diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 8708f32ed1..ebf2340020 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -57,8 +57,9 @@ man_MANS = \ CURLINFO_PRIMARY_PORT.3 \ CURLINFO_PRIVATE.3 \ CURLINFO_PROTOCOL.3 \ - CURLINFO_PROXYAUTH_AVAIL.3 \ + CURLINFO_PROXY_ERROR.3 \ CURLINFO_PROXY_SSL_VERIFYRESULT.3 \ + CURLINFO_PROXYAUTH_AVAIL.3 \ CURLINFO_REDIRECT_COUNT.3 \ CURLINFO_REDIRECT_TIME.3 \ CURLINFO_REDIRECT_TIME_T.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index c0cdbdc044..797e8ab332 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -105,6 +105,7 @@ CURLE_OPERATION_TIMEOUTED 7.1 7.17.0 CURLE_OUT_OF_MEMORY 7.1 CURLE_PARTIAL_FILE 7.1 CURLE_PEER_FAILED_VERIFICATION 7.17.1 +CURLE_PROXY 7.73.0 CURLE_QUIC_CONNECT_ERROR 7.69.0 CURLE_QUOTE_ERROR 7.17.0 CURLE_RANGE_ERROR 7.17.0 @@ -261,6 +262,7 @@ CURLINFO_PRIMARY_PORT 7.21.0 CURLINFO_PRIVATE 7.10.3 CURLINFO_PROTOCOL 7.52.0 CURLINFO_PROXYAUTH_AVAIL 7.10.8 +CURLINFO_PROXY_ERROR 7.73.0 CURLINFO_PROXY_SSL_VERIFYRESULT 7.52.0 CURLINFO_PTR 7.54.1 CURLINFO_REDIRECT_COUNT 7.9.7 @@ -710,6 +712,40 @@ CURLPROXY_SOCKS4 7.10 CURLPROXY_SOCKS4A 7.18.0 CURLPROXY_SOCKS5 7.10 CURLPROXY_SOCKS5_HOSTNAME 7.18.0 +CURLPX_BAD_ADDRESS_TYPE 7.73.0 +CURLPX_BAD_VERSION 7.73.0 +CURLPX_CLOSED 7.73.0 +CURLPX_GSSAPI 7.73.0 +CURLPX_GSSAPI_PERMSG 7.73.0 +CURLPX_GSSAPI_PROTECTION 7.73.0 +CURLPX_IDENTD 7.73.0 +CURLPX_IDENTD_DIFFER 7.73.0 +CURLPX_LONG_HOSTNAME 7.73.0 +CURLPX_LONG_PASSWD 7.73.0 +CURLPX_LONG_USER 7.73.0 +CURLPX_NO_AUTH 7.73.0 +CURLPX_OK 7.73.0 +CURLPX_RECV_ADDRESS 7.73.0 +CURLPX_RECV_AUTH 7.73.0 +CURLPX_RECV_CONNECT 7.73.0 +CURLPX_RECV_REQACK 7.73.0 +CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED 7.73.0 +CURLPX_REPLY_COMMAND_NOT_SUPPORTED 7.73.0 +CURLPX_REPLY_CONNECTION_REFUSED 7.73.0 +CURLPX_REPLY_GENERAL_SERVER_FAILURE 7.73.0 +CURLPX_REPLY_HOST_UNREACHABLE 7.73.0 +CURLPX_REPLY_NETWORK_UNREACHABLE 7.73.0 +CURLPX_REPLY_NOT_ALLOWED 7.73.0 +CURLPX_REPLY_TTL_EXPIRED 7.73.0 +CURLPX_REPLY_UNASSIGNED 7.73.0 +CURLPX_REQUEST_FAILED 7.73.0 +CURLPX_RESOLVE_HOST 7.73.0 +CURLPX_SEND_AUTH 7.73.0 +CURLPX_SEND_CONNECT 7.73.0 +CURLPX_SEND_REQUEST 7.73.0 +CURLPX_UNKNOWN_FAIL 7.73.0 +CURLPX_UNKNOWN_MODE 7.73.0 +CURLPX_USER_REJECTED 7.73.0 CURLSHE_BAD_OPTION 7.10.3 CURLSHE_INVALID 7.10.3 CURLSHE_IN_USE 7.10.3 diff --git a/include/curl/curl.h b/include/curl/curl.h index 5aeaca91d0..f4d5bf645d 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -610,6 +610,7 @@ typedef enum { error */ CURLE_HTTP3, /* 95 - An HTTP/3 layer problem */ CURLE_QUIC_CONNECT_ERROR, /* 96 - QUIC connection error */ + CURLE_PROXY, /* 97 - proxy handshake error */ CURL_LAST /* never use! */ } CURLcode; @@ -689,6 +690,48 @@ typedef enum { #endif /*!CURL_NO_OLDIES*/ +/* + * Proxy error codes. Returned in CURLINFO_PROXY_ERROR if CURLE_PROXY was + * return for the transfers. + */ +typedef enum { + CURLPX_OK, + CURLPX_BAD_ADDRESS_TYPE, + CURLPX_BAD_VERSION, + CURLPX_CLOSED, + CURLPX_GSSAPI, + CURLPX_GSSAPI_PERMSG, + CURLPX_GSSAPI_PROTECTION, + CURLPX_IDENTD, + CURLPX_IDENTD_DIFFER, + CURLPX_LONG_HOSTNAME, + CURLPX_LONG_PASSWD, + CURLPX_LONG_USER, + CURLPX_NO_AUTH, + CURLPX_RECV_ADDRESS, + CURLPX_RECV_AUTH, + CURLPX_RECV_CONNECT, + CURLPX_RECV_REQACK, + CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, + CURLPX_REPLY_COMMAND_NOT_SUPPORTED, + CURLPX_REPLY_CONNECTION_REFUSED, + CURLPX_REPLY_GENERAL_SERVER_FAILURE, + CURLPX_REPLY_HOST_UNREACHABLE, + CURLPX_REPLY_NETWORK_UNREACHABLE, + CURLPX_REPLY_NOT_ALLOWED, + CURLPX_REPLY_TTL_EXPIRED, + CURLPX_REPLY_UNASSIGNED, + CURLPX_REQUEST_FAILED, + CURLPX_RESOLVE_HOST, + CURLPX_SEND_AUTH, + CURLPX_SEND_CONNECT, + CURLPX_SEND_REQUEST, + CURLPX_UNKNOWN_FAIL, + CURLPX_UNKNOWN_MODE, + CURLPX_USER_REJECTED, + CURLPX_LAST /* never use */ +} CURLproxycode; + /* This prototype applies to all conversion callbacks */ typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); @@ -2643,8 +2686,9 @@ typedef enum { CURLINFO_APPCONNECT_TIME_T = CURLINFO_OFF_T + 56, CURLINFO_RETRY_AFTER = CURLINFO_OFF_T + 57, CURLINFO_EFFECTIVE_METHOD = CURLINFO_STRING + 58, + CURLINFO_PROXY_ERROR = CURLINFO_LONG + 59, - CURLINFO_LASTONE = 58 + CURLINFO_LASTONE = 59 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as diff --git a/lib/connect.c b/lib/connect.c index b000b1b2c2..f53266054c 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -746,8 +746,8 @@ static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex, bool *done) { CURLcode result = CURLE_OK; - #ifndef CURL_DISABLE_PROXY + CURLproxycode pxresult = CURLPX_OK; if(conn->bits.socksproxy) { /* for the secondary socket (FTP), use the "connect to host" * but ignore the "connect to port" (use the secondary port) @@ -767,20 +767,24 @@ static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex, switch(conn->socks_proxy.proxytype) { case CURLPROXY_SOCKS5: case CURLPROXY_SOCKS5_HOSTNAME: - result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, - host, port, sockindex, conn, done); + pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, + host, port, sockindex, conn, done); break; case CURLPROXY_SOCKS4: case CURLPROXY_SOCKS4A: - result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, - conn, done); + pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, + conn, done); break; default: failf(conn->data, "unknown proxytype option given"); result = CURLE_COULDNT_CONNECT; } /* switch proxytype */ + if(pxresult) { + result = CURLE_PROXY; + conn->data->info.pxcode = pxresult; + } } else #else diff --git a/lib/getinfo.c b/lib/getinfo.c index 82691dcc71..49e79e476f 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -269,6 +269,9 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info, /* Return the local port of the most recent (primary) connection */ *param_longp = data->info.conn_local_port; break; + case CURLINFO_PROXY_ERROR: + *param_longp = (long)data->info.pxcode; + break; case CURLINFO_CONDITION_UNMET: if(data->info.httpcode == 304) *param_longp = 1L; diff --git a/lib/socks.c b/lib/socks.c index 44783d015c..a41b67d5a9 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -184,12 +184,12 @@ int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock, * Set protocol4a=true for "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)" * Nonsupport "Identification Protocol (RFC1413)" */ -CURLcode Curl_SOCKS4(const char *proxy_user, - const char *hostname, - int remote_port, - int sockindex, - struct connectdata *conn, - bool *done) +CURLproxycode Curl_SOCKS4(const char *proxy_user, + const char *hostname, + int remote_port, + int sockindex, + struct connectdata *conn, + bool *done) { const bool protocol4a = (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; @@ -237,11 +237,11 @@ CURLcode Curl_SOCKS4(const char *proxy_user, Curl_resolv(conn, hostname, remote_port, FALSE, &dns); if(rc == CURLRESOLV_ERROR) - return CURLE_COULDNT_RESOLVE_PROXY; + return CURLPX_RESOLVE_HOST; else if(rc == CURLRESOLV_PENDING) { sxstate(conn, CONNECT_RESOLVING); infof(data, "SOCKS4 non-blocking resolve of %s\n", hostname); - return CURLE_OK; + return CURLPX_OK; } sxstate(conn, CONNECT_RESOLVED); goto CONNECT_RESOLVED; @@ -265,8 +265,11 @@ CURLcode Curl_SOCKS4(const char *proxy_user, } else { result = Curl_resolv_check(data->conn, &dns); - if(!dns) - return result; + if(!dns) { + if(result) + return CURLPX_RESOLVE_HOST; + return CURLPX_OK; + } } /* FALLTHROUGH */ CONNECT_RESOLVED: @@ -303,7 +306,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user, if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", hostname); - return CURLE_COULDNT_RESOLVE_HOST; + return CURLPX_RESOLVE_HOST; } } /* FALLTHROUGH */ @@ -316,8 +319,8 @@ CURLcode Curl_SOCKS4(const char *proxy_user, if(proxy_user) { size_t plen = strlen(proxy_user); if(plen >= sizeof(sx->socksreq) - 8) { - failf(data, "Too long SOCKS proxy name, can't use!\n"); - return CURLE_COULDNT_CONNECT; + failf(data, "Too long SOCKS proxy user name, can't use!\n"); + return CURLPX_LONG_USER; } /* copy the proxy name WITH trailing zero */ memcpy(socksreq + 8, proxy_user, plen + 1); @@ -343,7 +346,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user, strcpy((char *)socksreq + packetsize, hostname); else { failf(data, "SOCKS4: too long host name"); - return CURLE_COULDNT_CONNECT; + return CURLPX_LONG_HOSTNAME; } packetsize += hostnamelen; } @@ -358,13 +361,13 @@ CURLcode Curl_SOCKS4(const char *proxy_user, sx->outstanding, &written); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to send SOCKS4 connect request."); - return CURLE_COULDNT_CONNECT; + return CURLPX_SEND_CONNECT; } if(written != sx->outstanding) { /* not done, remain in state */ sx->outstanding -= written; sx->outp += written; - return CURLE_OK; + return CURLPX_OK; } /* done sending! */ @@ -380,18 +383,18 @@ CURLcode Curl_SOCKS4(const char *proxy_user, if(result && (CURLE_AGAIN != result)) { failf(data, "SOCKS4: Failed receiving connect request ack: %s", curl_easy_strerror(result)); - return CURLE_COULDNT_CONNECT; + return CURLPX_RECV_CONNECT; } else if(!result && !actualread) { /* connection closed */ failf(data, "connection to proxy closed"); - return CURLE_COULDNT_CONNECT; + return CURLPX_CLOSED; } else if(actualread != sx->outstanding) { /* remain in reading state */ sx->outstanding -= actualread; sx->outp += actualread; - return CURLE_OK; + return CURLPX_OK; } sxstate(conn, CONNECT_DONE); break; @@ -422,7 +425,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user, if(socksreq[0] != 0) { failf(data, "SOCKS4 reply has wrong version, version should be 0."); - return CURLE_COULDNT_CONNECT; + return CURLPX_BAD_VERSION; } /* Result */ @@ -438,7 +441,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user, (unsigned char)socksreq[6], (unsigned char)socksreq[7], (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; + return CURLPX_REQUEST_FAILED; case 92: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" @@ -448,7 +451,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user, (unsigned char)socksreq[6], (unsigned char)socksreq[7], (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; + return CURLPX_IDENTD; case 93: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" @@ -458,7 +461,7 @@ CURLcode Curl_SOCKS4(const char *proxy_user, (unsigned char)socksreq[6], (unsigned char)socksreq[7], (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; + return CURLPX_IDENTD_DIFFER; default: failf(data, "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)" @@ -467,24 +470,24 @@ CURLcode Curl_SOCKS4(const char *proxy_user, (unsigned char)socksreq[6], (unsigned char)socksreq[7], (((unsigned char)socksreq[2] << 8) | (unsigned char)socksreq[3]), (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; + return CURLPX_UNKNOWN_FAIL; } *done = TRUE; - return CURLE_OK; /* Proxy was successful! */ + return CURLPX_OK; /* Proxy was successful! */ } /* * This function logs in to a SOCKS5 proxy and sends the specifics to the final * destination server. */ -CURLcode Curl_SOCKS5(const char *proxy_user, - const char *proxy_password, - const char *hostname, - int remote_port, - int sockindex, - struct connectdata *conn, - bool *done) +CURLproxycode Curl_SOCKS5(const char *proxy_user, + const char *proxy_password, + const char *hostname, + int remote_port, + int sockindex, + struct connectdata *conn, + bool *done) { /* According to the RFC1928, section "6. Replies". This is what a SOCK5 @@ -561,13 +564,13 @@ CURLcode Curl_SOCKS5(const char *proxy_user, result = Curl_write_plain(conn, sockfd, (char *)socksreq, idx, &written); if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to send initial SOCKS5 request."); - return CURLE_COULDNT_CONNECT; + return CURLPX_SEND_CONNECT; } if(written != idx) { sxstate(conn, CONNECT_SOCKS_SEND); sx->outstanding = idx - written; sx->outp = &socksreq[written]; - return CURLE_OK; + return CURLPX_OK; } sxstate(conn, CONNECT_SOCKS_READ); goto CONNECT_SOCKS_READ_INIT; @@ -576,13 +579,13 @@ CURLcode Curl_SOCKS5(const char *proxy_user, sx->outstanding, &written); if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to send initial SOCKS5 request."); - return CURLE_COULDNT_CONNECT; + return CURLPX_SEND_CONNECT; } if(written != sx->outstanding) { /* not done, remain in state */ sx->outstanding -= written; sx->outp += written; - return CURLE_OK; + return CURLPX_OK; } /* FALLTHROUGH */ CONNECT_SOCKS_READ_INIT: @@ -595,22 +598,22 @@ CURLcode Curl_SOCKS5(const char *proxy_user, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to receive initial SOCKS5 response."); - return CURLE_COULDNT_CONNECT; + return CURLPX_RECV_CONNECT; } else if(!result && !actualread) { /* connection closed */ failf(data, "Connection to proxy closed"); - return CURLE_COULDNT_CONNECT; + return CURLPX_CLOSED; } else if(actualread != sx->outstanding) { /* remain in reading state */ sx->outstanding -= actualread; sx->outp += actualread; - return CURLE_OK; + return CURLPX_OK; } else if(socksreq[0] != 5) { failf(data, "Received invalid version in initial SOCKS5 response."); - return CURLE_COULDNT_CONNECT; + return CURLPX_BAD_VERSION; } else if(socksreq[1] == 0) { /* DONE! No authentication needed. Send request. */ @@ -628,7 +631,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user, result = Curl_SOCKS5_gssapi_negotiate(sockindex, conn); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); - return CURLE_COULDNT_CONNECT; + return CURLPX_GSSAPI; } } #endif @@ -637,16 +640,16 @@ CURLcode Curl_SOCKS5(const char *proxy_user, if(!allow_gssapi && (socksreq[1] == 1)) { failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); - return CURLE_COULDNT_CONNECT; + return CURLPX_GSSAPI_PERMSG; } else if(socksreq[1] == 255) { failf(data, "No authentication method was acceptable."); - return CURLE_COULDNT_CONNECT; + return CURLPX_NO_AUTH; } } failf(data, "Undocumented SOCKS5 mode attempted to be used by server."); - return CURLE_COULDNT_CONNECT; + return CURLPX_UNKNOWN_MODE; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) case CONNECT_GSSAPI_INIT: /* GSSAPI stuff done non-blocking */ @@ -683,7 +686,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user, /* the length must fit in a single byte */ if(proxy_user_len >= 255) { failf(data, "Excessive user name length for proxy auth"); - return CURLE_BAD_FUNCTION_ARGUMENT; + return CURLPX_LONG_USER; } memcpy(socksreq + len, proxy_user, proxy_user_len); } @@ -693,7 +696,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user, /* the length must fit in a single byte */ if(proxy_password_len > 255) { failf(data, "Excessive password length for proxy auth"); - return CURLE_BAD_FUNCTION_ARGUMENT; + return CURLPX_LONG_PASSWD; } memcpy(socksreq + len, proxy_password, proxy_password_len); } @@ -708,13 +711,13 @@ CURLcode Curl_SOCKS5(const char *proxy_user, sx->outstanding, &written); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to send SOCKS5 sub-negotiation request."); - return CURLE_COULDNT_CONNECT; + return CURLPX_SEND_AUTH; } if(sx->outstanding != written) { /* remain in state */ sx->outstanding -= written; sx->outp += written; - return CURLE_OK; + return CURLPX_OK; } sx->outp = socksreq; sx->outstanding = 2; @@ -725,24 +728,24 @@ CURLcode Curl_SOCKS5(const char *proxy_user, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Unable to receive SOCKS5 sub-negotiation response."); - return CURLE_COULDNT_CONNECT; + return CURLPX_RECV_AUTH; } else if(!result && !actualread) { /* connection closed */ failf(data, "connection to proxy closed"); - return CURLE_COULDNT_CONNECT; + return CURLPX_CLOSED; } else if(actualread != sx->outstanding) { /* remain in state */ sx->outstanding -= actualread; sx->outp += actualread; - return CURLE_OK; + return CURLPX_OK; } /* ignore the first (VER) byte */ else if(socksreq[1] != 0) { /* status */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - return CURLE_COULDNT_CONNECT; + return CURLPX_USER_REJECTED; } /* Everything is good so far, user was authenticated! */ @@ -755,11 +758,11 @@ CURLcode Curl_SOCKS5(const char *proxy_user, FALSE, &dns); if(rc == CURLRESOLV_ERROR) - return CURLE_COULDNT_RESOLVE_HOST; + return CURLPX_RESOLVE_HOST; if(rc == CURLRESOLV_PENDING) { sxstate(conn, CONNECT_RESOLVING); - return CURLE_OK; + return CURLPX_OK; } sxstate(conn, CONNECT_RESOLVED); goto CONNECT_RESOLVED; @@ -780,8 +783,11 @@ CURLcode Curl_SOCKS5(const char *proxy_user, if(!dns) { result = Curl_resolv_check(data->conn, &dns); - if(!dns) - return result; + if(!dns) { + if(result) + return CURLPX_RESOLVE_HOST; + return CURLPX_OK; + } } /* FALLTHROUGH */ CONNECT_RESOLVED: @@ -793,7 +799,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user, if(!hp) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", hostname); - return CURLE_COULDNT_RESOLVE_HOST; + return CURLPX_RESOLVE_HOST; } Curl_printable_address(hp, dest, sizeof(dest)); @@ -867,7 +873,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user, #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) if(conn->socks5_gssapi_enctype) { failf(data, "SOCKS5 GSS-API protection not yet implemented."); - return CURLE_COULDNT_CONNECT; + return CURLPX_GSSAPI_PROTECTION; } #endif sx->outp = socksreq; @@ -879,18 +885,18 @@ CURLcode Curl_SOCKS5(const char *proxy_user, sx->outstanding, &written); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to send SOCKS5 connect request."); - return CURLE_COULDNT_CONNECT; + return CURLPX_SEND_REQUEST; } if(sx->outstanding != written) { /* remain in state */ sx->outstanding -= written; sx->outp += written; - return CURLE_OK; + return CURLPX_OK; } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) if(conn->socks5_gssapi_enctype) { failf(data, "SOCKS5 GSS-API protection not yet implemented."); - return CURLE_COULDNT_CONNECT; + return CURLPX_GSSAPI_PROTECTION; } #endif sx->outstanding = 10; /* minimum packet size is 10 */ @@ -902,29 +908,46 @@ CURLcode Curl_SOCKS5(const char *proxy_user, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLE_COULDNT_CONNECT; + return CURLPX_RECV_REQACK; } else if(!result && !actualread) { /* connection closed */ failf(data, "connection to proxy closed"); - return CURLE_COULDNT_CONNECT; + return CURLPX_CLOSED; } else if(actualread != sx->outstanding) { /* remain in state */ sx->outstanding -= actualread; sx->outp += actualread; - return CURLE_OK; + return CURLPX_OK; } if(socksreq[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); - return CURLE_COULDNT_CONNECT; + return CURLPX_BAD_VERSION; } else if(socksreq[1] != 0) { /* Anything besides 0 is an error */ + CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; + int code = socksreq[1]; failf(data, "Can't complete SOCKS5 connection to %s. (%d)", hostname, (unsigned char)socksreq[1]); - return CURLE_COULDNT_CONNECT; + if(code < 9) { + /* RFC 1928 section 6 lists: */ + static const CURLproxycode lookup[] = { + CURLPX_OK, + CURLPX_REPLY_GENERAL_SERVER_FAILURE, + CURLPX_REPLY_NOT_ALLOWED, + CURLPX_REPLY_NETWORK_UNREACHABLE, + CURLPX_REPLY_HOST_UNREACHABLE, + CURLPX_REPLY_CONNECTION_REFUSED, + CURLPX_REPLY_TTL_EXPIRED, + CURLPX_REPLY_COMMAND_NOT_SUPPORTED, + CURLPX_REPLY_ADDRESS_TYPE_NOT_SUPPORTED, + }; + rc = lookup[code]; + } + return rc; } /* Fix: in general, returned BND.ADDR is variable length parameter by RFC @@ -958,7 +981,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user, } else { failf(data, "SOCKS5 reply has wrong address type."); - return CURLE_COULDNT_CONNECT; + return CURLPX_BAD_ADDRESS_TYPE; } /* At this point we already read first 10 bytes */ @@ -984,25 +1007,25 @@ CURLcode Curl_SOCKS5(const char *proxy_user, sx->outstanding, &actualread); if(result && (CURLE_AGAIN != result)) { failf(data, "Failed to receive SOCKS5 connect request ack."); - return CURLE_COULDNT_CONNECT; + return CURLPX_RECV_ADDRESS; } else if(!result && !actualread) { /* connection closed */ failf(data, "connection to proxy closed"); - return CURLE_COULDNT_CONNECT; + return CURLPX_CLOSED; } else if(actualread != sx->outstanding) { /* remain in state */ sx->outstanding -= actualread; sx->outp += actualread; - return CURLE_OK; + return CURLPX_OK; } sxstate(conn, CONNECT_DONE); } infof(data, "SOCKS5 request granted.\n"); *done = TRUE; - return CURLE_OK; /* Proxy was successful! */ + return CURLPX_OK; /* Proxy was successful! */ } #endif /* CURL_DISABLE_PROXY */ diff --git a/lib/socks.h b/lib/socks.h index 64a7563373..17e6f46101 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -48,24 +48,24 @@ int Curl_SOCKS_getsock(struct connectdata *conn, * This function logs in to a SOCKS4(a) proxy and sends the specifics to the * final destination server. */ -CURLcode Curl_SOCKS4(const char *proxy_name, - const char *hostname, - int remote_port, - int sockindex, - struct connectdata *conn, - bool *done); +CURLproxycode Curl_SOCKS4(const char *proxy_name, + const char *hostname, + int remote_port, + int sockindex, + struct connectdata *conn, + bool *done); /* * This function logs in to a SOCKS5 proxy and sends the specifics to the * final destination server. */ -CURLcode Curl_SOCKS5(const char *proxy_name, - const char *proxy_password, - const char *hostname, - int remote_port, - int sockindex, - struct connectdata *conn, - bool *done); +CURLproxycode Curl_SOCKS5(const char *proxy_name, + const char *proxy_password, + const char *hostname, + int remote_port, + int sockindex, + struct connectdata *conn, + bool *done); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* diff --git a/lib/strerror.c b/lib/strerror.c index 015e588cf7..5893f49a90 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -320,6 +320,9 @@ curl_easy_strerror(CURLcode error) case CURLE_QUIC_CONNECT_ERROR: return "QUIC connection error"; + case CURLE_PROXY: + return "proxy handshake error"; + /* error codes not used by current libcurl */ case CURLE_OBSOLETE20: case CURLE_OBSOLETE24: diff --git a/lib/urldata.h b/lib/urldata.h index 0ae9269275..86f8af42c4 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1134,6 +1134,7 @@ struct PureInfo { OpenSSL, GnuTLS, Schannel, NSS and GSKit builds. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ + CURLproxycode pxcode; BIT(timecond); /* set to TRUE if the time condition didn't match, which thus made the document NOT get fetched */ }; diff --git a/tests/data/test1538 b/tests/data/test1538 index ea8eddbb8c..03481d56e6 100644 --- a/tests/data/test1538 +++ b/tests/data/test1538 @@ -129,7 +129,8 @@ e93: API function called from within callback e94: An authentication function returned an error e95: HTTP/3 error e96: QUIC connection error -e97: Unknown error +e97: proxy handshake error +e98: Unknown error m-1: Please call curl_multi_perform() soon m0: No error m1: Invalid multi handle diff --git a/tests/data/test702 b/tests/data/test702 index 1c18d09769..dd84ffe263 100644 --- a/tests/data/test702 +++ b/tests/data/test702 @@ -38,7 +38,7 @@ Attempt connect to non-listening HTTP server via SOCKS4 proxy # Verify data after the test has been "shot" -7 +97 diff --git a/tests/data/test703 b/tests/data/test703 index d7be71393c..3c3487aeec 100644 --- a/tests/data/test703 +++ b/tests/data/test703 @@ -38,7 +38,7 @@ Attempt connect to non-listening HTTP server via SOCKS5 proxy # Verify data after the test has been "shot" -7 +97 diff --git a/tests/data/test716 b/tests/data/test716 index 96167de5cf..531e272fa8 100644 --- a/tests/data/test716 +++ b/tests/data/test716 @@ -39,7 +39,7 @@ http://hohoho.example.com:99/716 -x socks5://AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA # Verify data after the test has been "shot" -43 +97