socks: make the connect phase non-blocking

Removes two entries from KNOWN_BUGS.

Closes #4907
This commit is contained in:
Daniel Stenberg 2020-02-14 16:16:54 +01:00
parent d60b1b37a1
commit 4a4b63daaa
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
13 changed files with 830 additions and 566 deletions

View File

@ -87,8 +87,6 @@ problems may have been fixed or changed somewhat since this was written!
9.1 SFTP doesn't do CURLOPT_POSTQUOTE correct
10. SOCKS
10.1 SOCKS proxy connections are done blocking
10.2 SOCKS don't support timeouts
10.3 FTPS over SOCKS
10.4 active FTP over a SOCKS
@ -621,21 +619,6 @@ problems may have been fixed or changed somewhat since this was written!
10. SOCKS
10.1 SOCKS proxy connections are done blocking
Both SOCKS5 and SOCKS4 proxy connections are done blocking, which is very bad
when used with the multi interface.
10.2 SOCKS don't support timeouts
The SOCKS4 connection codes don't properly acknowledge (connect) timeouts.
According to bug #1556528, even the SOCKS5 connect code does not do it right:
https://curl.haxx.se/bug/view.cgi?id=604
When connecting to a SOCK proxy, the (connect) timeout is not properly
acknowledged after the actual TCP connect (during the SOCKS "negotiate"
phase).
10.3 FTPS over SOCKS
libcurl doesn't support FTPS over a SOCKS proxy.

View File

@ -405,7 +405,6 @@
EWOULDBLOCK or similar. Blocking cases include:
- Name resolves on non-windows unless c-ares or the threaded resolver is used
- SOCKS proxy handshakes
- file:// transfers
- TELNET transfers
- The "DONE" operation (post transfer protocol-specific actions) for the

View File

@ -745,58 +745,82 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
Curl_persistconninfo(conn);
}
/* after a TCP connection to the proxy has been verified, this function does
the next magic step.
/* After a TCP connection to the proxy has been verified, this function does
the next magic steps. If 'done' isn't set TRUE, it is not done yet and
must be called again.
Note: this function's sub-functions call failf()
*/
static CURLcode connected_proxy(struct connectdata *conn, int sockindex)
static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex,
bool *done)
{
CURLcode result = CURLE_OK;
infof(conn->data, "connect_SOCKS is called [socks state %d]\n",
conn->cnnct.state);
if(conn->bits.socksproxy) {
#ifndef CURL_DISABLE_PROXY
/* for the secondary socket (FTP), use the "connect to host"
* but ignore the "connect to port" (use the secondary port)
*/
const char * const host = conn->bits.httpproxy ?
const char * const host =
conn->bits.httpproxy ?
conn->http_proxy.host.name :
conn->bits.conn_to_host ?
conn->conn_to_host.name :
sockindex == SECONDARYSOCKET ?
conn->secondaryhostname : conn->host.name;
const int port = conn->bits.httpproxy ? (int)conn->http_proxy.port :
const int port =
conn->bits.httpproxy ? (int)conn->http_proxy.port :
sockindex == SECONDARYSOCKET ? conn->secondary_port :
conn->bits.conn_to_port ? conn->conn_to_port :
conn->remote_port;
conn->bits.socksproxy_connecting = TRUE;
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);
host, port, sockindex, conn, done);
break;
case CURLPROXY_SOCKS4:
case CURLPROXY_SOCKS4A:
result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
conn);
conn, done);
break;
default:
failf(conn->data, "unknown proxytype option given");
result = CURLE_COULDNT_CONNECT;
} /* switch proxytype */
conn->bits.socksproxy_connecting = FALSE;
#else
(void)sockindex;
#endif /* CURL_DISABLE_PROXY */
}
else
*done = TRUE; /* no SOCKS proxy, so consider us connected */
return result;
}
/*
* post_SOCKS() is called after a successful connect to the peer, which
* *could* be a SOCKS proxy
*/
static void post_SOCKS(struct connectdata *conn,
int sockindex,
bool *connected)
{
conn->bits.tcpconnect[sockindex] = TRUE;
*connected = TRUE;
if(sockindex == FIRSTSOCKET)
Curl_pgrsTime(conn->data, TIMER_CONNECT); /* connect done */
Curl_updateconninfo(conn, conn->sock[sockindex]);
Curl_verboseconnect(conn);
}
/*
* Curl_is_connected() checks if the socket has connected.
*/
@ -834,6 +858,14 @@ CURLcode Curl_is_connected(struct connectdata *conn,
return CURLE_OPERATION_TIMEDOUT;
}
if(SOCKS_STATE(conn->cnnct.state)) {
/* still doing SOCKS */
result = connect_SOCKS(conn, sockindex, connected);
if(!result && *connected)
post_SOCKS(conn, sockindex, connected);
return result;
}
for(i = 0; i<2; i++) {
const int other = i ^ 1;
if(conn->tempsock[i] == CURL_SOCKET_BAD)
@ -900,18 +932,13 @@ CURLcode Curl_is_connected(struct connectdata *conn,
conn->tempsock[other] = CURL_SOCKET_BAD;
}
/* see if we need to do any proxy magic first once we connected */
result = connected_proxy(conn, sockindex);
if(result)
/* see if we need to kick off any SOCKS proxy magic once we
connected */
result = connect_SOCKS(conn, sockindex, connected);
if(result || !*connected)
return result;
conn->bits.tcpconnect[sockindex] = TRUE;
*connected = TRUE;
if(sockindex == FIRSTSOCKET)
Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
Curl_updateconninfo(conn, conn->sock[sockindex]);
Curl_verboseconnect(conn);
post_SOCKS(conn, sockindex, connected);
return CURLE_OK;
}

View File

@ -55,7 +55,6 @@
#include "transfer.h"
#include "escape.h"
#include "http.h" /* for HTTP proxy tunnel stuff */
#include "socks.h"
#include "ftp.h"
#include "fileinfo.h"
#include "ftplistparser.h"
@ -78,6 +77,7 @@
#include "warnless.h"
#include "http_proxy.h"
#include "non-ascii.h"
#include "socks.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@ -810,6 +810,9 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks)
* handle ordinary commands.
*/
if(SOCKS_STATE(conn->cnnct.state))
return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET);
if(FTP_STOP == ftpc->state) {
int bits = GETSOCK_READSOCK(0);
@ -919,7 +922,7 @@ static CURLcode ftp_state_use_port(struct connectdata *conn,
struct sockaddr_in6 * const sa6 = (void *)sa;
#endif
static const char mode[][5] = { "EPRT", "PORT" };
int rc;
enum resolve_t rc;
int error;
char *host = NULL;
char *string_ftpport = data->set.str[STRING_FTPPORT];
@ -1794,7 +1797,7 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
CURLcode result;
struct Curl_easy *data = conn->data;
struct Curl_dns_entry *addr = NULL;
int rc;
enum resolve_t rc;
unsigned short connectport; /* the local port connect() should use! */
char *str = &data->state.buffer[4]; /* start on the first letter */

View File

@ -483,7 +483,7 @@ Curl_cache_addr(struct Curl_easy *data,
* CURLRESOLV_PENDING (1) = waiting for response, no pointer
*/
int Curl_resolv(struct connectdata *conn,
enum resolve_t Curl_resolv(struct connectdata *conn,
const char *hostname,
int port,
bool allowDOH,
@ -492,7 +492,7 @@ int Curl_resolv(struct connectdata *conn,
struct Curl_dns_entry *dns = NULL;
struct Curl_easy *data = conn->data;
CURLcode result;
int rc = CURLRESOLV_ERROR; /* default to failure */
enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
*entry = NULL;
@ -642,7 +642,7 @@ RETSIGTYPE alarmfunc(int sig)
* CURLRESOLV_PENDING (1) = waiting for response, no pointer
*/
int Curl_resolv_timeout(struct connectdata *conn,
enum resolve_t Curl_resolv_timeout(struct connectdata *conn,
const char *hostname,
int port,
struct Curl_dns_entry **entry,
@ -662,7 +662,7 @@ int Curl_resolv_timeout(struct connectdata *conn,
volatile unsigned int prev_alarm = 0;
struct Curl_easy *data = conn->data;
#endif /* USE_ALARM_TIMEOUT */
int rc;
enum resolve_t rc;
*entry = NULL;

View File

@ -79,17 +79,20 @@ struct Curl_dns_entry {
* use, or we'll leak memory!
*/
/* return codes */
#define CURLRESOLV_TIMEDOUT -2
#define CURLRESOLV_ERROR -1
#define CURLRESOLV_RESOLVED 0
#define CURLRESOLV_PENDING 1
int Curl_resolv(struct connectdata *conn,
enum resolve_t {
CURLRESOLV_TIMEDOUT = -2,
CURLRESOLV_ERROR = -1,
CURLRESOLV_RESOLVED = 0,
CURLRESOLV_PENDING = 1
};
enum resolve_t Curl_resolv(struct connectdata *conn,
const char *hostname,
int port,
bool allowDOH,
struct Curl_dns_entry **dnsentry);
int Curl_resolv_timeout(struct connectdata *conn, const char *hostname,
int port, struct Curl_dns_entry **dnsentry,
enum resolve_t Curl_resolv_timeout(struct connectdata *conn,
const char *hostname, int port,
struct Curl_dns_entry **dnsentry,
timediff_t timeoutms);
#ifdef CURLRES_IPV6

View File

@ -47,6 +47,7 @@
#include "http_proxy.h"
#include "http2.h"
#include "socketpair.h"
#include "socks.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@ -856,6 +857,9 @@ static int waitconnect_getsock(struct connectdata *conn,
return Curl_ssl_getsock(conn, sock);
#endif
if(SOCKS_STATE(conn->cnnct.state))
return Curl_SOCKS_getsock(conn, sock, FIRSTSOCKET);
for(i = 0; i<2; i++) {
if(conn->tempsock[i] != CURL_SOCKET_BAD) {
sock[s] = conn->tempsock[i];

View File

@ -692,19 +692,20 @@ CURLcode Curl_read_plain(curl_socket_t sockfd,
ssize_t nread = sread(sockfd, buf, bytesfromsocket);
if(-1 == nread) {
int err = SOCKERRNO;
int return_error;
const int err = SOCKERRNO;
const bool return_error =
#ifdef USE_WINSOCK
return_error = WSAEWOULDBLOCK == err;
WSAEWOULDBLOCK == err
#else
return_error = EWOULDBLOCK == err || EAGAIN == err || EINTR == err;
EWOULDBLOCK == err || EAGAIN == err || EINTR == err
#endif
;
*n = 0; /* no data returned */
if(return_error)
return CURLE_AGAIN;
return CURLE_RECV_ERROR;
}
/* we only return number of bytes read when we return OK */
*n = nread;
return CURLE_OK;
}

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* 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
@ -37,18 +37,19 @@
#include "connect.h"
#include "timeval.h"
#include "socks.h"
#include "multiif.h" /* for getsock macros */
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
/*
* Helper read-from-socket functions. Does the same as Curl_read() but it
* blocks until all bytes amount of buffersize will be read. No more, no less.
*
* This is STUPID BLOCKING behaviour which we frown upon, but right now this
* is what we have...
* This is STUPID BLOCKING behavior. Only used by the SOCKS GSSAPI functions.
*/
int Curl_blockread_all(struct connectdata *conn, /* connection data */
curl_socket_t sockfd, /* read from this socket */
@ -94,6 +95,81 @@ int Curl_blockread_all(struct connectdata *conn, /* connection data */
}
return result;
}
#endif
#ifndef DEBUGBUILD
#define sxstate(x,y) socksstate(x,y)
#else
#define sxstate(x,y) socksstate(x,y, __LINE__)
#endif
/* always use this function to change state, to make debugging easier */
static void socksstate(struct connectdata *conn,
enum connect_t state
#ifdef DEBUGBUILD
, int lineno
#endif
)
{
enum connect_t oldstate = conn->cnnct.state;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* synced with the state list in urldata.h */
static const char * const statename[] = {
"INIT",
"SOCKS_INIT",
"SOCKS_SEND",
"SOCKS_READ_INIT",
"SOCKS_READ",
"GSSAPI_INIT",
"AUTH_INIT",
"AUTH_SEND",
"AUTH_READ",
"REQ_INIT",
"RESOLVING",
"RESOLVED",
"RESOLVE_REMOTE",
"REQ_SEND",
"REQ_SENDING",
"REQ_READ",
"REQ_READ_MORE",
"DONE"
};
#endif
if(oldstate == state)
/* don't bother when the new state is the same as the old state */
return;
conn->cnnct.state = state;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
infof(conn->data,
"SXSTATE: %s => %s conn %p; line %d\n",
statename[oldstate], statename[conn->cnnct.state], conn,
lineno);
#endif
}
int Curl_SOCKS_getsock(struct connectdata *conn, curl_socket_t *sock,
int sockindex)
{
int rc = 0;
sock[0] = conn->sock[sockindex];
switch(conn->cnnct.state) {
case CONNECT_RESOLVING:
case CONNECT_SOCKS_READ:
case CONNECT_AUTH_READ:
case CONNECT_REQ_READ:
case CONNECT_REQ_READ_MORE:
rc = GETSOCK_READSOCK(0);
break;
default:
rc = GETSOCK_WRITESOCK(0);
break;
}
return rc;
}
/*
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
@ -110,29 +186,29 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
const char *hostname,
int remote_port,
int sockindex,
struct connectdata *conn)
struct connectdata *conn,
bool *done)
{
const bool protocol4a =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
#define SOCKS4REQLEN 262
unsigned char socksreq[SOCKS4REQLEN]; /* room for SOCKS4 request incl. user
id */
CURLcode code;
curl_socket_t sock = conn->sock[sockindex];
unsigned char *socksreq = &conn->cnnct.socksreq[0];
CURLcode result;
curl_socket_t sockfd = conn->sock[sockindex];
struct Curl_easy *data = conn->data;
struct connstate *sx = &conn->cnnct;
struct Curl_dns_entry *dns = NULL;
ssize_t actualread;
ssize_t written;
if(Curl_timeleft(data, NULL, TRUE) < 0) {
/* time-out, bail out, go home */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
}
if(!SOCKS_STATE(sx->state) && !*done)
sxstate(conn, CONNECT_SOCKS_INIT);
switch(sx->state) {
case CONNECT_SOCKS_INIT:
if(conn->bits.httpproxy)
infof(conn->data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n",
protocol4a ? "a" : "", hostname, remote_port);
(void)curlx_nonblock(sock, FALSE);
infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
/*
@ -153,19 +229,46 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
/* DNS resolve only for SOCKS4, not SOCKS4a */
if(!protocol4a) {
struct Curl_dns_entry *dns;
Curl_addrinfo *hp = NULL;
int rc;
rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
enum resolve_t rc =
Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLE_COULDNT_RESOLVE_PROXY;
else if(rc == CURLRESOLV_PENDING) {
sxstate(conn, CONNECT_RESOLVING);
infof(data, "SOCKS4 non-blocking resolve of %s\n", hostname);
return CURLE_OK;
}
sxstate(conn, CONNECT_RESOLVED);
goto CONNECT_RESOLVED;
}
if(rc == CURLRESOLV_PENDING)
/* ignores the return code, but 'dns' remains NULL on failure */
(void)Curl_resolver_wait_resolv(conn, &dns);
/* socks4a doesn't resolve anything locally */
sxstate(conn, CONNECT_REQ_INIT);
goto CONNECT_REQ_INIT;
case CONNECT_RESOLVING:
/* check if we have the name resolved by now */
dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
if(dns) {
#ifdef CURLRES_ASYNCH
conn->async.dns = dns;
conn->async.done = TRUE;
#endif
infof(data, "Hostname '%s' was found\n", hostname);
sxstate(conn, CONNECT_RESOLVED);
}
else {
result = Curl_resolv_check(data->conn, &dns);
/* stay in the state or error out */
return result;
}
/* FALLTHROUGH */
CONNECT_RESOLVED:
case CONNECT_RESOLVED: {
Curl_addrinfo *hp = NULL;
char buf[64];
/*
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It
* returns a Curl_addrinfo pointer that may not always look the same.
@ -173,7 +276,6 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
if(dns)
hp = dns->addr;
if(hp) {
char buf[64];
Curl_printable_address(hp, buf, sizeof(buf));
if(hp->ai_family == AF_INET) {
@ -189,7 +291,6 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
}
else {
hp = NULL; /* fail! */
failf(data, "SOCKS4 connection to %s not supported\n", buf);
}
@ -201,14 +302,16 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
return CURLE_COULDNT_RESOLVE_HOST;
}
}
/* FALLTHROUGH */
CONNECT_REQ_INIT:
case CONNECT_REQ_INIT:
/*
* This is currently not supporting "Identification Protocol (RFC1413)".
*/
socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
if(proxy_user) {
size_t plen = strlen(proxy_user);
if(plen >= sizeof(socksreq) - 8) {
if(plen >= sizeof(sx->socksreq) - 8) {
failf(data, "Too long SOCKS proxy name, can't use!\n");
return CURLE_COULDNT_CONNECT;
}
@ -220,55 +323,72 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
* Make connection
*/
{
int result;
ssize_t actualread;
ssize_t written;
ssize_t hostnamelen = 0;
ssize_t packetsize = 9 +
strlen((char *)socksreq + 8); /* size including NUL */
/* If SOCKS4a, set special invalid IP address 0.0.0.x */
if(protocol4a) {
ssize_t hostnamelen = 0;
socksreq[4] = 0;
socksreq[5] = 0;
socksreq[6] = 0;
socksreq[7] = 1;
/* If still enough room in buffer, also append hostname */
/* append hostname */
hostnamelen = (ssize_t)strlen(hostname) + 1; /* length including NUL */
if(packetsize + hostnamelen <= SOCKS4REQLEN)
if(hostnamelen <= 255)
strcpy((char *)socksreq + packetsize, hostname);
else
hostnamelen = 0; /* Flag: hostname did not fit in buffer */
else {
failf(data, "SOCKS4: too long host name");
return CURLE_COULDNT_CONNECT;
}
packetsize += hostnamelen;
}
sx->outp = socksreq;
sx->outstanding = packetsize;
sxstate(conn, CONNECT_REQ_SENDING);
}
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
/* Send request */
code = Curl_write_plain(conn, sock, (char *)socksreq,
packetsize + hostnamelen,
&written);
if(code || (written != packetsize + hostnamelen)) {
failf(data, "Failed to send SOCKS4 connect request.");
return CURLE_COULDNT_CONNECT;
}
if(protocol4a && hostnamelen == 0) {
/* SOCKS4a with very long hostname - send that name separately */
hostnamelen = (ssize_t)strlen(hostname) + 1;
code = Curl_write_plain(conn, sock, (char *)hostname, hostnamelen,
&written);
if(code || (written != hostnamelen)) {
result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
sx->outstanding, &written);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Failed to send SOCKS4 connect request.");
return CURLE_COULDNT_CONNECT;
}
if(written != sx->outstanding) {
/* not done, remain in state */
sx->outstanding -= written;
sx->outp += written;
return CURLE_OK;
}
packetsize = 8; /* receive data size */
/* done sending! */
sx->outstanding = 8; /* receive data size */
sx->outp = socksreq;
sxstate(conn, CONNECT_SOCKS_READ);
/* FALLTHROUGH */
case CONNECT_SOCKS_READ:
/* Receive response */
result = Curl_blockread_all(conn, sock, (char *)socksreq, packetsize,
&actualread);
if(result || (actualread != packetsize)) {
failf(data, "Failed to receive SOCKS4 connect request ack.");
result = Curl_read_plain(sockfd, (char *)sx->outp,
sx->outstanding, &actualread);
if(result && (CURLE_AGAIN != result)) {
failf(data, "SOCKS4: Failed receiving connect request ack: %s",
curl_easy_strerror(result));
return CURLE_COULDNT_CONNECT;
}
else if(actualread != sx->outstanding) {
/* remain in reading state */
sx->outstanding -= actualread;
sx->outp += actualread;
return CURLE_OK;
}
sxstate(conn, CONNECT_DONE);
break;
default: /* lots of unused states in SOCKS4 */
break;
}
/*
* Response format
@ -340,10 +460,8 @@ CURLcode Curl_SOCKS4(const char *proxy_user,
(unsigned char)socksreq[1]);
return CURLE_COULDNT_CONNECT;
}
}
(void)curlx_nonblock(sock, TRUE);
*done = TRUE;
return CURLE_OK; /* Proxy was successful! */
}
@ -356,7 +474,8 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
const char *hostname,
int remote_port,
int sockindex,
struct connectdata *conn)
struct connectdata *conn,
bool *done)
{
/*
According to the RFC1928, section "6. Replies". This is what a SOCK5
@ -374,24 +493,28 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
o REP Reply field:
o X'00' succeeded
*/
#define REQUEST_BUFSIZE 600 /* room for large user/pw (255 max each) */
unsigned char socksreq[REQUEST_BUFSIZE];
char dest[REQUEST_BUFSIZE] = "unknown"; /* printable hostname:port */
unsigned char *socksreq = &conn->cnnct.socksreq[0];
char dest[256] = "unknown"; /* printable hostname:port */
int idx;
ssize_t actualread;
ssize_t written;
int result;
CURLcode code;
curl_socket_t sock = conn->sock[sockindex];
CURLcode result;
curl_socket_t sockfd = conn->sock[sockindex];
struct Curl_easy *data = conn->data;
timediff_t timeout;
bool socks5_resolve_local =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
const size_t hostname_len = strlen(hostname);
ssize_t len = 0;
const unsigned long auth = data->set.socks5auth;
bool allow_gssapi = FALSE;
struct connstate *sx = &conn->cnnct;
struct Curl_dns_entry *dns = NULL;
if(!SOCKS_STATE(sx->state) && !*done)
sxstate(conn, CONNECT_SOCKS_INIT);
switch(sx->state) {
case CONNECT_SOCKS_INIT:
if(conn->bits.httpproxy)
infof(conn->data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
hostname, remote_port);
@ -403,34 +526,6 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
socks5_resolve_local = TRUE;
}
/* get timeout */
timeout = Curl_timeleft(data, NULL, TRUE);
if(timeout < 0) {
/* time-out, bail out, go home */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
}
(void)curlx_nonblock(sock, TRUE);
/* wait until socket gets connected */
result = SOCKET_WRITABLE(sock, timeout);
if(-1 == result) {
failf(conn->data, "SOCKS5: no connection here");
return CURLE_COULDNT_CONNECT;
}
if(0 == result) {
failf(conn->data, "SOCKS5: connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
if(result & CURL_CSELECT_ERR) {
failf(conn->data, "SOCKS5: error occurred during connection");
return CURLE_COULDNT_CONNECT;
}
if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
infof(conn->data,
"warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
@ -445,7 +540,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
idx = 0;
socksreq[idx++] = 5; /* version */
idx++; /* reserve for the number of authentication methods */
idx++; /* number of authentication methods */
socksreq[idx++] = 0; /* no authentication */
if(allow_gssapi)
socksreq[idx++] = 1; /* GSS-API */
@ -454,61 +549,102 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
/* write the number of authentication methods */
socksreq[1] = (unsigned char) (idx - 2);
(void)curlx_nonblock(sock, FALSE);
infof(data, "SOCKS5 communication to %s:%d\n", hostname, remote_port);
code = Curl_write_plain(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
&written);
if(code || (written != (2 + (int)socksreq[1]))) {
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;
}
(void)curlx_nonblock(sock, TRUE);
result = SOCKET_READABLE(sock, timeout);
if(-1 == result) {
failf(conn->data, "SOCKS5 nothing to read");
if(written != idx) {
sxstate(conn, CONNECT_SOCKS_SEND);
sx->outstanding = idx - written;
sx->outp = &socksreq[written];
return CURLE_OK;
}
sxstate(conn, CONNECT_SOCKS_READ);
goto CONNECT_SOCKS_READ_INIT;
case CONNECT_SOCKS_SEND:
result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
sx->outstanding, &written);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Unable to send initial SOCKS5 request.");
return CURLE_COULDNT_CONNECT;
}
if(0 == result) {
failf(conn->data, "SOCKS5 read timeout");
return CURLE_OPERATION_TIMEDOUT;
if(written != sx->outstanding) {
/* not done, remain in state */
sx->outstanding -= written;
sx->outp += written;
return CURLE_OK;
}
if(result & CURL_CSELECT_ERR) {
failf(conn->data, "SOCKS5 read error occurred");
return CURLE_RECV_ERROR;
}
(void)curlx_nonblock(sock, FALSE);
result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
if(result || (actualread != 2)) {
/* FALLTHROUGH */
CONNECT_SOCKS_READ_INIT:
case CONNECT_SOCKS_READ_INIT:
sx->outstanding = 2; /* expect two bytes */
sx->outp = socksreq; /* store it here */
/* FALLTHROUGH */
case CONNECT_SOCKS_READ:
result = Curl_read_plain(sockfd, (char *)sx->outp,
sx->outstanding, &actualread);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Unable to receive initial SOCKS5 response.");
return CURLE_COULDNT_CONNECT;
}
if(socksreq[0] != 5) {
else if(actualread != sx->outstanding) {
/* remain in reading state */
sx->outstanding -= actualread;
sx->outp += actualread;
return CURLE_OK;
}
else if(socksreq[0] != 5) {
failf(data, "Received invalid version in initial SOCKS5 response.");
return CURLE_COULDNT_CONNECT;
}
if(socksreq[1] == 0) {
/* Nothing to do, no authentication needed */
;
else if(socksreq[1] == 0) {
/* DONE! No authentication needed. Send request. */
sxstate(conn, CONNECT_REQ_INIT);
goto CONNECT_REQ_INIT;
}
else if(socksreq[1] == 2) {
/* regular name + password authentication */
sxstate(conn, CONNECT_AUTH_INIT);
goto CONNECT_AUTH_INIT;
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
else if(allow_gssapi && (socksreq[1] == 1)) {
code = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
if(code) {
sxstate(conn, CONNECT_GSSAPI_INIT);
result = Curl_SOCKS5_gssapi_negotiate(sockindex, conn);
if(result) {
failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
return CURLE_COULDNT_CONNECT;
}
}
#endif
else if(socksreq[1] == 2) {
else {
/* error */
if(!allow_gssapi && (socksreq[1] == 1)) {
failf(data,
"SOCKS5 GSSAPI per-message authentication is not supported.");
return CURLE_COULDNT_CONNECT;
}
else if(socksreq[1] == 255) {
failf(data, "No authentication method was acceptable.");
return CURLE_COULDNT_CONNECT;
}
failf(data,
"Undocumented SOCKS5 mode attempted to be used by server.");
return CURLE_COULDNT_CONNECT;
}
break;
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
case CONNECT_GSSAPI_INIT:
/* GSSAPI stuff done non-blocking */
break;
#endif
default: /* do nothing! */
break;
CONNECT_AUTH_INIT:
case CONNECT_AUTH_INIT: {
/* Needs user name and password */
size_t proxy_user_len, proxy_password_len;
if(proxy_user && proxy_password) {
@ -549,18 +685,41 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
memcpy(socksreq + len, proxy_password, proxy_password_len);
}
len += proxy_password_len;
code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
if(code || (len != written)) {
sxstate(conn, CONNECT_AUTH_SEND);
sx->outstanding = len;
sx->outp = socksreq;
}
/* FALLTHROUGH */
case CONNECT_AUTH_SEND:
result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
sx->outstanding, &written);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Failed to send SOCKS5 sub-negotiation request.");
return CURLE_COULDNT_CONNECT;
}
result = Curl_blockread_all(conn, sock, (char *)socksreq, 2, &actualread);
if(result || (actualread != 2)) {
if(sx->outstanding != written) {
/* remain in state */
sx->outstanding -= written;
sx->outp += written;
return CURLE_OK;
}
sx->outp = socksreq;
sx->outstanding = 2;
sxstate(conn, CONNECT_AUTH_READ);
/* FALLTHROUGH */
case CONNECT_AUTH_READ:
result = Curl_read_plain(sockfd, (char *)sx->outp,
sx->outstanding, &actualread);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Unable to receive SOCKS5 sub-negotiation response.");
return CURLE_COULDNT_CONNECT;
}
if(actualread != sx->outstanding) {
/* remain in state */
sx->outstanding -= actualread;
sx->outp += actualread;
return CURLE_OK;
}
/* ignore the first (VER) byte */
if(socksreq[1] != 0) { /* status */
@ -570,69 +729,55 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
}
/* Everything is good so far, user was authenticated! */
}
else {
/* error */
if(!allow_gssapi && (socksreq[1] == 1)) {
failf(data,
"SOCKS5 GSSAPI per-message authentication is not supported.");
return CURLE_COULDNT_CONNECT;
}
if(socksreq[1] == 255) {
if(!proxy_user || !*proxy_user) {
failf(data,
"No authentication method was acceptable. (It is quite likely"
" that the SOCKS5 server wanted a username/password, since none"
" was supplied to the server on this connection.)");
}
else {
failf(data, "No authentication method was acceptable.");
}
return CURLE_COULDNT_CONNECT;
}
else {
failf(data,
"Undocumented SOCKS5 mode attempted to be used by server.");
return CURLE_COULDNT_CONNECT;
}
}
/* Authentication is complete, now specify destination to the proxy */
len = 0;
socksreq[len++] = 5; /* version (SOCKS5) */
socksreq[len++] = 1; /* connect */
socksreq[len++] = 0; /* must be zero */
if(!socks5_resolve_local) {
socksreq[len++] = 3; /* ATYP: domain name = 3 */
socksreq[len++] = (char) hostname_len; /* address length */
memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
len += hostname_len;
msnprintf(dest, sizeof(dest), "%s:%d", hostname, remote_port);
infof(data, "SOCKS5 connect to %s (remotely resolved)\n", dest);
}
else {
struct Curl_dns_entry *dns;
Curl_addrinfo *hp = NULL;
int rc = Curl_resolv(conn, hostname, remote_port, FALSE, &dns);
sxstate(conn, CONNECT_REQ_INIT);
/* FALLTHROUGH */
CONNECT_REQ_INIT:
case CONNECT_REQ_INIT:
if(socks5_resolve_local) {
enum resolve_t rc = Curl_resolv(conn, hostname, remote_port,
FALSE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLE_COULDNT_RESOLVE_HOST;
if(rc == CURLRESOLV_PENDING) {
/* this requires that we're in "wait for resolve" state */
code = Curl_resolver_wait_resolv(conn, &dns);
if(code)
return code;
sxstate(conn, CONNECT_RESOLVING);
return CURLE_OK;
}
sxstate(conn, CONNECT_RESOLVED);
goto CONNECT_RESOLVED;
}
goto CONNECT_RESOLVE_REMOTE;
case CONNECT_RESOLVING:
/* check if we have the name resolved by now */
dns = Curl_fetch_addr(conn, hostname, (int)conn->port);
if(dns) {
#ifdef CURLRES_ASYNCH
conn->async.dns = dns;
conn->async.done = TRUE;
#endif
infof(data, "SOCKS5: hostname '%s' found\n", hostname);
}
/*
* We cannot use 'hostent' as a struct that Curl_resolv() returns. It
* returns a Curl_addrinfo pointer that may not always look the same.
*/
if(!dns) {
result = Curl_resolv_check(data->conn, &dns);
/* stay in the state or error out */
return result;
}
/* FALLTHROUGH */
CONNECT_RESOLVED:
case CONNECT_RESOLVED: {
Curl_addrinfo *hp = NULL;
if(dns)
hp = dns->addr;
if(hp) {
if(!hp) {
failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
hostname);
return CURLE_COULDNT_RESOLVE_HOST;
}
if(Curl_printable_address(hp, dest, sizeof(dest))) {
size_t destlen = strlen(dest);
msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
@ -641,6 +786,10 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
strcpy(dest, "unknown");
}
len = 0;
socksreq[len++] = 5; /* version (SOCKS5) */
socksreq[len++] = 1; /* connect */
socksreq[len++] = 0; /* must be zero */
if(hp->ai_family == AF_INET) {
int i;
struct sockaddr_in *saddr_in;
@ -670,60 +819,98 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
#endif
else {
hp = NULL; /* fail! */
failf(data, "SOCKS5 connection to %s not supported\n", dest);
}
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
goto CONNECT_REQ_SEND;
}
if(!hp) {
failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
hostname);
return CURLE_COULDNT_RESOLVE_HOST;
}
}
CONNECT_RESOLVE_REMOTE:
case CONNECT_RESOLVE_REMOTE:
/* Authentication is complete, now specify destination to the proxy */
len = 0;
socksreq[len++] = 5; /* version (SOCKS5) */
socksreq[len++] = 1; /* connect */
socksreq[len++] = 0; /* must be zero */
socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
socksreq[len++] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
if(!socks5_resolve_local) {
socksreq[len++] = 3; /* ATYP: domain name = 3 */
socksreq[len++] = (char) hostname_len; /* one byte address length */
memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
len += hostname_len;
infof(data, "SOCKS5 connect to %s:5d (remotely resolved)\n",
hostname, remote_port);
}
/* FALLTHROUGH */
CONNECT_REQ_SEND:
case CONNECT_REQ_SEND:
/* PORT MSB */
socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
/* PORT LSB */
socksreq[len++] = (unsigned char)(remote_port & 0xff);
#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;
}
else
#endif
code = Curl_write_plain(conn, sock, (char *)socksreq, len, &written);
if(code || (len != written)) {
sx->outp = socksreq;
sx->outstanding = len;
sxstate(conn, CONNECT_REQ_SENDING);
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
result = Curl_write_plain(conn, sockfd, (char *)sx->outp,
sx->outstanding, &written);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Failed to send SOCKS5 connect request.");
return CURLE_COULDNT_CONNECT;
}
len = 10; /* minimum packet size is 10 */
if(sx->outstanding != written) {
/* remain in state */
sx->outstanding -= written;
sx->outp += written;
return CURLE_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;
}
else
#endif
result = Curl_blockread_all(conn, sock, (char *)socksreq,
len, &actualread);
if(result || (len != actualread)) {
sx->outstanding = 10; /* minimum packet size is 10 */
sx->outp = socksreq;
sxstate(conn, CONNECT_REQ_READ);
/* FALLTHROUGH */
case CONNECT_REQ_READ:
result = Curl_read_plain(sockfd, (char *)sx->outp,
sx->outstanding, &actualread);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Failed to receive SOCKS5 connect request ack.");
return CURLE_COULDNT_CONNECT;
}
else if(actualread != sx->outstanding) {
/* remain in state */
sx->outstanding -= actualread;
sx->outp += actualread;
return CURLE_OK;
}
if(socksreq[0] != 5) { /* version */
failf(data,
"SOCKS5 reply has wrong version, version should be 5.");
return CURLE_COULDNT_CONNECT;
}
else if(socksreq[1] != 0) { /* Anything besides 0 is an error */
failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
hostname, (unsigned char)socksreq[1]);
return CURLE_COULDNT_CONNECT;
}
/* Fix: in general, returned BND.ADDR is variable length parameter by RFC
1928, so the reply packet should be read until the end to avoid errors at
subsequent protocol level.
1928, so the reply packet should be read until the end to avoid errors
at subsequent protocol level.
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
@ -754,25 +941,36 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
/* decrypt_gssapi_blockread already read the whole packet */
#endif
if(len > 10) {
result = Curl_blockread_all(conn, sock, (char *)&socksreq[10],
len - 10, &actualread);
if(result || ((len - 10) != actualread)) {
failf(data, "Failed to receive SOCKS5 connect request ack.");
return CURLE_COULDNT_CONNECT;
sx->outstanding = len - 10; /* get the rest */
sx->outp = &socksreq[10];
sxstate(conn, CONNECT_REQ_READ_MORE);
}
else {
sxstate(conn, CONNECT_DONE);
break;
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
}
#endif
if(socksreq[1] != 0) { /* Anything besides 0 is an error */
failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
dest, (unsigned char)socksreq[1]);
/* FALLTHROUGH */
case CONNECT_REQ_READ_MORE:
result = Curl_read_plain(sockfd, (char *)sx->outp,
sx->outstanding, &actualread);
if(result && (CURLE_AGAIN != result)) {
failf(data, "Failed to receive SOCKS5 connect request ack.");
return CURLE_COULDNT_CONNECT;
}
if(actualread != sx->outstanding) {
/* remain in state */
sx->outstanding -= actualread;
sx->outp += actualread;
return CURLE_OK;
}
sxstate(conn, CONNECT_DONE);
}
infof(data, "SOCKS5 request granted.\n");
(void)curlx_nonblock(sock, TRUE);
*done = TRUE;
return CURLE_OK; /* Proxy was successful! */
}

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* 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
@ -27,13 +27,13 @@
#ifdef CURL_DISABLE_PROXY
#define Curl_SOCKS4(a,b,c,d,e) CURLE_NOT_BUILT_IN
#define Curl_SOCKS5(a,b,c,d,e,f) CURLE_NOT_BUILT_IN
#define Curl_SOCKS_getsock(x,y,z) 0
#else
/*
* Helper read-from-socket functions. Does the same as Curl_read() but it
* blocks until all bytes amount of buffersize will be read. No more, no less.
*
* This is STUPID BLOCKING behaviour which we frown upon, but right now this
* is what we have...
* This is STUPID BLOCKING behavior
*/
int Curl_blockread_all(struct connectdata *conn,
curl_socket_t sockfd,
@ -41,6 +41,9 @@ int Curl_blockread_all(struct connectdata *conn,
ssize_t buffersize,
ssize_t *n);
int Curl_SOCKS_getsock(struct connectdata *conn,
curl_socket_t *sock,
int sockindex);
/*
* This function logs in to a SOCKS4(a) proxy and sends the specifics to the
* final destination server.
@ -49,7 +52,8 @@ CURLcode Curl_SOCKS4(const char *proxy_name,
const char *hostname,
int remote_port,
int sockindex,
struct connectdata *conn);
struct connectdata *conn,
bool *done);
/*
* This function logs in to a SOCKS5 proxy and sends the specifics to the
@ -60,7 +64,8 @@ CURLcode Curl_SOCKS5(const char *proxy_name,
const char *hostname,
int remote_port,
int sockindex,
struct connectdata *conn);
struct connectdata *conn,
bool *done);
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
/*

View File

@ -5,8 +5,8 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2009, Markus Moeller, <markus_moeller@compuserve.com>
* Copyright (C) 2012 - 2018, 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
@ -167,6 +167,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_COULDNT_CONNECT;
}
(void)curlx_nonblock(sock, FALSE);
/* As long as we need to keep sending some context info, and there's no */
/* errors, keep sending it... */
for(;;) {
@ -513,6 +515,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
gss_release_buffer(&gss_status, &gss_recv_token);
}
(void)curlx_nonblock(sock, TRUE);
infof(data, "SOCKS5 access with%s protection granted.\n",
(socksreq[0] == 0)?"out GSS-API data":
((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));

View File

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) 2009, 2011, Markus Moeller, <markus_moeller@compuserve.com>
*
* This software is licensed as described in the file COPYING, which
@ -153,6 +153,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
return CURLE_COULDNT_CONNECT;
}
(void)curlx_nonblock(sock, FALSE);
/* As long as we need to keep sending some context info, and there's no */
/* errors, keep sending it... */
for(;;) {
@ -587,6 +589,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer);
s_pSecFn->FreeContextBuffer(sspi_w_token[0].pvBuffer);
}
(void)curlx_nonblock(sock, TRUE);
infof(data, "SOCKS5 access with%s protection granted.\n",
(socksreq[0] == 0)?"out GSS-API data":

View File

@ -476,7 +476,6 @@ struct ConnectBits {
BIT(tcp_fastopen); /* use TCP Fast Open */
BIT(tls_enable_npn); /* TLS NPN extension? */
BIT(tls_enable_alpn); /* TLS ALPN extension? */
BIT(socksproxy_connecting); /* connecting through a socks proxy */
BIT(connect_only);
};
@ -817,6 +816,41 @@ struct http_connect_state {
struct ldapconninfo;
/* for the (SOCKS) connect state machine */
enum connect_t {
CONNECT_INIT,
CONNECT_SOCKS_INIT, /* 1 */
CONNECT_SOCKS_SEND, /* 2 waiting to send more first data */
CONNECT_SOCKS_READ_INIT, /* 3 set up read */
CONNECT_SOCKS_READ, /* 4 read server response */
CONNECT_GSSAPI_INIT, /* 5 */
CONNECT_AUTH_INIT, /* 6 setup outgoing auth buffer */
CONNECT_AUTH_SEND, /* 7 send auth */
CONNECT_AUTH_READ, /* 8 read auth response */
CONNECT_REQ_INIT, /* 9 init SOCKS "request" */
CONNECT_RESOLVING, /* 10 */
CONNECT_RESOLVED, /* 11 */
CONNECT_RESOLVE_REMOTE, /* 12 */
CONNECT_REQ_SEND, /* 13 */
CONNECT_REQ_SENDING, /* 14 */
CONNECT_REQ_READ, /* 15 */
CONNECT_REQ_READ_MORE, /* 16 */
CONNECT_DONE /* 17 connected fine to the remote or the SOCKS proxy */
};
#define SOCKS_STATE(x) (((x) >= CONNECT_SOCKS_INIT) && \
((x) < CONNECT_DONE))
#define SOCKS_REQUEST_BUFSIZE 600 /* room for large user/pw (255 max each) */
struct connstate {
enum connect_t state;
unsigned char socksreq[SOCKS_REQUEST_BUFSIZE];
/* CONNECT_SOCKS_SEND */
ssize_t outstanding; /* send this many bytes more */
unsigned char *outp; /* send from this pointer */
};
/*
* The connectdata struct contains all fields and variables that should be
* unique for an entire connection.
@ -826,7 +860,7 @@ struct connectdata {
caution that this might very well vary between different times this
connection is used! */
struct Curl_easy *data;
struct connstate cnnct;
struct curl_llist_element bundle_node; /* conncache */
/* chunk is for HTTP chunked encoding, but is in the general connectdata