--- /dev/null
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, 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 <curl/curl.h>
+
+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), "
* 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;
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;
}
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:
if(!hp) {
failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
hostname);
- return CURLE_COULDNT_RESOLVE_HOST;
+ return CURLPX_RESOLVE_HOST;
}
}
/* FALLTHROUGH */
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);
strcpy((char *)socksreq + packetsize, hostname);
else {
failf(data, "SOCKS4: too long host name");
- return CURLE_COULDNT_CONNECT;
+ return CURLPX_LONG_HOSTNAME;
}
packetsize += hostnamelen;
}
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! */
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;
if(socksreq[0] != 0) {
failf(data,
"SOCKS4 reply has wrong version, version should be 0.");
- return CURLE_COULDNT_CONNECT;
+ return CURLPX_BAD_VERSION;
}
/* Result */
(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)"
(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)"
(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)"
(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
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;
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:
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. */
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
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 */
/* 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);
}
/* 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);
}
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;
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! */
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;
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:
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));
#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;
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 */
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
}
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 */
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 */