lib: connection filters (cfilter) addition to curl:

- general construct/destroy in connectdata
 - default implementations of callback functions
 - connect: cfilters for connect and accept
 - socks: cfilter for socks proxying
 - http_proxy: cfilter for http proxy tunneling
 - vtls: cfilters for primary and proxy ssl
 - change in general handling of data/conn
 - Curl_cfilter_setup() sets up filter chain based on data settings,
   if none are installed by the protocol handler setup
 - Curl_cfilter_connect() boot straps filters into `connected` status,
   used by handlers and multi to reach further stages
 - Curl_cfilter_is_connected() to check if a conn is connected,
   e.g. all filters have done their work
 - Curl_cfilter_get_select_socks() gets the sockets and READ/WRITE
   indicators for multi select to work
 - Curl_cfilter_data_pending() asks filters if the have incoming
   data pending for recv
 - Curl_cfilter_recv()/Curl_cfilter_send are the general callbacks
   installed in conn->recv/conn->send for io handling
 - Curl_cfilter_attach_data()/Curl_cfilter_detach_data() inform filters
   and addition/removal of a `data` from their connection
 - adding vtl functions to prevent use of Curl_ssl globals directly
   in other parts of the code.

Reviewed-by: Daniel Stenberg
Closes #9855
This commit is contained in:
Stefan Eissing 2022-11-11 11:45:34 +01:00 committed by Daniel Stenberg
parent 89ee5cfb38
commit dafdb20a26
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
49 changed files with 3325 additions and 1969 deletions

View file

@ -36,17 +36,52 @@
#include "urldata.h"
#include "sendf.h"
#include "select.h"
#include "cfilters.h"
#include "connect.h"
#include "timeval.h"
#include "socks.h"
#include "multiif.h" /* for getsock macros */
#include "inet_pton.h"
#include "url.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
/* 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 */
};
struct socks_state {
enum connect_t state;
ssize_t outstanding; /* send this many bytes more */
unsigned char *outp; /* send from this pointer */
const char *hostname;
int remote_port;
const char *proxy_user;
const char *proxy_password;
};
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
/*
* Helper read-from-socket functions. Does the same as Curl_read() but it
@ -104,21 +139,20 @@ int Curl_blockread_all(struct Curl_easy *data, /* transfer */
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
#define DEBUG_AND_VERBOSE
#define sxstate(x,y) socksstate(x,y, __LINE__)
#define sxstate(x,d,y) socksstate(x,d,y, __LINE__)
#else
#define sxstate(x,y) socksstate(x,y)
#define sxstate(x,d,y) socksstate(x,d,y)
#endif
/* always use this function to change state, to make debugging easier */
static void socksstate(struct Curl_easy *data,
static void socksstate(struct socks_state *sx, struct Curl_easy *data,
enum connect_t state
#ifdef DEBUG_AND_VERBOSE
, int lineno
#endif
)
{
struct connectdata *conn = data->conn;
enum connect_t oldstate = conn->cnnct.state;
enum connect_t oldstate = sx->state;
#ifdef DEBUG_AND_VERBOSE
/* synced with the state list in urldata.h */
static const char * const statename[] = {
@ -143,40 +177,21 @@ static void socksstate(struct Curl_easy *data,
};
#endif
(void)data;
if(oldstate == state)
/* don't bother when the new state is the same as the old state */
return;
conn->cnnct.state = state;
sx->state = state;
#ifdef DEBUG_AND_VERBOSE
infof(data,
"SXSTATE: %s => %s conn %p; line %d",
statename[oldstate], statename[conn->cnnct.state], conn,
"SXSTATE: %s => %s; line %d",
statename[oldstate], statename[sx->state],
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
* destination server.
@ -188,20 +203,16 @@ 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)"
*/
CURLproxycode Curl_SOCKS4(const char *proxy_user,
const char *hostname,
int remote_port,
int sockindex,
struct Curl_easy *data,
bool *done)
static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
struct socks_state *sx,
struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
struct connectdata *conn = cf->conn;
const bool protocol4a =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE;
unsigned char *socksreq = (unsigned char *)data->state.buffer;
CURLcode result;
curl_socket_t sockfd = conn->sock[sockindex];
struct connstate *sx = &conn->cnnct;
curl_socket_t sockfd = conn->sock[cf->sockindex];
struct Curl_dns_entry *dns = NULL;
ssize_t actualread;
ssize_t written;
@ -209,18 +220,16 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
/* make sure that the buffer is at least 600 bytes */
DEBUGASSERT(READBUFFER_MIN >= 600);
if(!SOCKS_STATE(sx->state) && !*done)
sxstate(data, CONNECT_SOCKS_INIT);
switch(sx->state) {
case CONNECT_SOCKS_INIT:
/* SOCKS4 can only do IPv4, insist! */
conn->ip_version = CURL_IPRESOLVE_V4;
if(conn->bits.httpproxy)
infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
protocol4a ? "a" : "", hostname, remote_port);
protocol4a ? "a" : "", sx->hostname, sx->remote_port);
infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port);
infof(data, "SOCKS4 communication to %s:%d",
sx->hostname, sx->remote_port);
/*
* Compose socks4 request
@ -235,40 +244,40 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
socksreq[0] = 4; /* version (SOCKS4) */
socksreq[1] = 1; /* connect */
socksreq[2] = (unsigned char)((remote_port >> 8) & 0xff); /* PORT MSB */
socksreq[3] = (unsigned char)(remote_port & 0xff); /* PORT LSB */
socksreq[2] = (unsigned char)((sx->remote_port >> 8) & 0xff); /* MSB */
socksreq[3] = (unsigned char)(sx->remote_port & 0xff); /* LSB */
/* DNS resolve only for SOCKS4, not SOCKS4a */
if(!protocol4a) {
enum resolve_t rc =
Curl_resolv(data, hostname, remote_port, FALSE, &dns);
Curl_resolv(data, sx->hostname, sx->remote_port, FALSE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLPX_RESOLVE_HOST;
else if(rc == CURLRESOLV_PENDING) {
sxstate(data, CONNECT_RESOLVING);
infof(data, "SOCKS4 non-blocking resolve of %s", hostname);
sxstate(sx, data, CONNECT_RESOLVING);
infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname);
return CURLPX_OK;
}
sxstate(data, CONNECT_RESOLVED);
sxstate(sx, data, CONNECT_RESOLVED);
goto CONNECT_RESOLVED;
}
/* socks4a doesn't resolve anything locally */
sxstate(data, CONNECT_REQ_INIT);
sxstate(sx, data, CONNECT_REQ_INIT);
goto CONNECT_REQ_INIT;
case CONNECT_RESOLVING:
/* check if we have the name resolved by now */
dns = Curl_fetch_addr(data, hostname, (int)conn->port);
dns = Curl_fetch_addr(data, sx->hostname, (int)conn->port);
if(dns) {
#ifdef CURLRES_ASYNCH
data->state.async.dns = dns;
data->state.async.done = TRUE;
#endif
infof(data, "Hostname '%s' was found", hostname);
sxstate(data, CONNECT_RESOLVED);
infof(data, "Hostname '%s' was found", sx->hostname);
sxstate(sx, data, CONNECT_RESOLVED);
}
else {
result = Curl_resolv_check(data, &dns);
@ -309,11 +318,11 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
}
else
failf(data, "SOCKS4 connection to %s not supported", hostname);
failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
}
else
failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
hostname);
sx->hostname);
if(!hp)
return CURLPX_RESOLVE_HOST;
@ -325,14 +334,14 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
* 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(sx->proxy_user) {
size_t plen = strlen(sx->proxy_user);
if(plen >= (size_t)data->set.buffer_size - 8) {
failf(data, "Too long SOCKS proxy user name, can't use");
return CURLPX_LONG_USER;
}
/* copy the proxy name WITH trailing zero */
memcpy(socksreq + 8, proxy_user, plen + 1);
memcpy(socksreq + 8, sx->proxy_user, plen + 1);
}
/*
@ -350,9 +359,9 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
socksreq[6] = 0;
socksreq[7] = 1;
/* append hostname */
hostnamelen = strlen(hostname) + 1; /* length including NUL */
hostnamelen = strlen(sx->hostname) + 1; /* length including NUL */
if(hostnamelen <= 255)
strcpy((char *)socksreq + packetsize, hostname);
strcpy((char *)socksreq + packetsize, sx->hostname);
else {
failf(data, "SOCKS4: too long host name");
return CURLPX_LONG_HOSTNAME;
@ -361,7 +370,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
}
sx->outp = socksreq;
sx->outstanding = packetsize;
sxstate(data, CONNECT_REQ_SENDING);
sxstate(sx, data, CONNECT_REQ_SENDING);
}
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
@ -382,7 +391,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
/* done sending! */
sx->outstanding = 8; /* receive data size */
sx->outp = socksreq;
sxstate(data, CONNECT_SOCKS_READ);
sxstate(sx, data, CONNECT_SOCKS_READ);
/* FALLTHROUGH */
case CONNECT_SOCKS_READ:
@ -405,7 +414,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
sx->outp += actualread;
return CURLPX_OK;
}
sxstate(data, CONNECT_DONE);
sxstate(sx, data, CONNECT_DONE);
break;
default: /* lots of unused states in SOCKS4 */
break;
@ -478,7 +487,6 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
return CURLPX_UNKNOWN_FAIL;
}
*done = TRUE;
return CURLPX_OK; /* Proxy was successful! */
}
@ -486,13 +494,9 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
* This function logs in to a SOCKS5 proxy and sends the specifics to the final
* destination server.
*/
CURLproxycode Curl_SOCKS5(const char *proxy_user,
const char *proxy_password,
const char *hostname,
int remote_port,
int sockindex,
struct Curl_easy *data,
bool *done)
static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
struct socks_state *sx,
struct Curl_easy *data)
{
/*
According to the RFC1928, section "6. Replies". This is what a SOCK5
@ -510,31 +514,27 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
o REP Reply field:
o X'00' succeeded
*/
struct connectdata *conn = data->conn;
struct connectdata *conn = cf->conn;
unsigned char *socksreq = (unsigned char *)data->state.buffer;
char dest[256] = "unknown"; /* printable hostname:port */
int idx;
ssize_t actualread;
ssize_t written;
CURLcode result;
curl_socket_t sockfd = conn->sock[sockindex];
curl_socket_t sockfd = conn->sock[cf->sockindex];
bool socks5_resolve_local =
(conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE;
const size_t hostname_len = strlen(hostname);
const size_t hostname_len = strlen(sx->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(data, CONNECT_SOCKS_INIT);
switch(sx->state) {
case CONNECT_SOCKS_INIT:
if(conn->bits.httpproxy)
infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
hostname, remote_port);
sx->hostname, sx->remote_port);
/* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
if(!socks5_resolve_local && hostname_len > 255) {
@ -549,7 +549,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
auth);
if(!(auth & CURLAUTH_BASIC))
/* disable username/password auth */
proxy_user = NULL;
sx->proxy_user = NULL;
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
if(auth & CURLAUTH_GSSAPI)
allow_gssapi = TRUE;
@ -561,7 +561,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
socksreq[idx++] = 0; /* no authentication */
if(allow_gssapi)
socksreq[idx++] = 1; /* GSS-API */
if(proxy_user)
if(sx->proxy_user)
socksreq[idx++] = 2; /* username/password */
/* write the number of authentication methods */
socksreq[1] = (unsigned char) (idx - 2);
@ -572,12 +572,12 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
return CURLPX_SEND_CONNECT;
}
if(written != idx) {
sxstate(data, CONNECT_SOCKS_SEND);
sxstate(sx, data, CONNECT_SOCKS_SEND);
sx->outstanding = idx - written;
sx->outp = &socksreq[written];
return CURLPX_OK;
}
sxstate(data, CONNECT_SOCKS_READ);
sxstate(sx, data, CONNECT_SOCKS_READ);
goto CONNECT_SOCKS_READ_INIT;
case CONNECT_SOCKS_SEND:
result = Curl_write_plain(data, sockfd, (char *)sx->outp,
@ -622,18 +622,18 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
}
else if(socksreq[1] == 0) {
/* DONE! No authentication needed. Send request. */
sxstate(data, CONNECT_REQ_INIT);
sxstate(sx, data, CONNECT_REQ_INIT);
goto CONNECT_REQ_INIT;
}
else if(socksreq[1] == 2) {
/* regular name + password authentication */
sxstate(data, CONNECT_AUTH_INIT);
sxstate(sx, data, CONNECT_AUTH_INIT);
goto CONNECT_AUTH_INIT;
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
else if(allow_gssapi && (socksreq[1] == 1)) {
sxstate(data, CONNECT_GSSAPI_INIT);
result = Curl_SOCKS5_gssapi_negotiate(sockindex, data);
sxstate(sx, data, CONNECT_GSSAPI_INIT);
result = Curl_SOCKS5_gssapi_negotiate(cf->sockindex, data);
if(result) {
failf(data, "Unable to negotiate SOCKS5 GSS-API context.");
return CURLPX_GSSAPI;
@ -668,9 +668,9 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
case CONNECT_AUTH_INIT: {
/* Needs user name and password */
size_t proxy_user_len, proxy_password_len;
if(proxy_user && proxy_password) {
proxy_user_len = strlen(proxy_user);
proxy_password_len = strlen(proxy_password);
if(sx->proxy_user && sx->proxy_password) {
proxy_user_len = strlen(sx->proxy_user);
proxy_password_len = strlen(sx->proxy_password);
}
else {
proxy_user_len = 0;
@ -687,26 +687,26 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
len = 0;
socksreq[len++] = 1; /* username/pw subnegotiation version */
socksreq[len++] = (unsigned char) proxy_user_len;
if(proxy_user && proxy_user_len) {
if(sx->proxy_user && proxy_user_len) {
/* the length must fit in a single byte */
if(proxy_user_len >= 255) {
failf(data, "Excessive user name length for proxy auth");
return CURLPX_LONG_USER;
}
memcpy(socksreq + len, proxy_user, proxy_user_len);
memcpy(socksreq + len, sx->proxy_user, proxy_user_len);
}
len += proxy_user_len;
socksreq[len++] = (unsigned char) proxy_password_len;
if(proxy_password && proxy_password_len) {
if(sx->proxy_password && proxy_password_len) {
/* the length must fit in a single byte */
if(proxy_password_len > 255) {
failf(data, "Excessive password length for proxy auth");
return CURLPX_LONG_PASSWD;
}
memcpy(socksreq + len, proxy_password, proxy_password_len);
memcpy(socksreq + len, sx->proxy_password, proxy_password_len);
}
len += proxy_password_len;
sxstate(data, CONNECT_AUTH_SEND);
sxstate(sx, data, CONNECT_AUTH_SEND);
sx->outstanding = len;
sx->outp = socksreq;
}
@ -726,7 +726,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
}
sx->outp = socksreq;
sx->outstanding = 2;
sxstate(data, CONNECT_AUTH_READ);
sxstate(sx, data, CONNECT_AUTH_READ);
/* FALLTHROUGH */
case CONNECT_AUTH_READ:
result = Curl_read_plain(sockfd, (char *)sx->outp,
@ -754,36 +754,36 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
}
/* Everything is good so far, user was authenticated! */
sxstate(data, CONNECT_REQ_INIT);
sxstate(sx, data, CONNECT_REQ_INIT);
/* FALLTHROUGH */
CONNECT_REQ_INIT:
case CONNECT_REQ_INIT:
if(socks5_resolve_local) {
enum resolve_t rc = Curl_resolv(data, hostname, remote_port,
enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
FALSE, &dns);
if(rc == CURLRESOLV_ERROR)
return CURLPX_RESOLVE_HOST;
if(rc == CURLRESOLV_PENDING) {
sxstate(data, CONNECT_RESOLVING);
sxstate(sx, data, CONNECT_RESOLVING);
return CURLPX_OK;
}
sxstate(data, CONNECT_RESOLVED);
sxstate(sx, data, 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(data, hostname, remote_port);
dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
if(dns) {
#ifdef CURLRES_ASYNCH
data->state.async.dns = dns;
data->state.async.done = TRUE;
#endif
infof(data, "SOCKS5: hostname '%s' found", hostname);
infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
}
if(!dns) {
@ -803,13 +803,13 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
hp = dns->addr;
if(!hp) {
failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.",
hostname);
sx->hostname);
return CURLPX_RESOLVE_HOST;
}
Curl_printable_address(hp, dest, sizeof(dest));
destlen = strlen(dest);
msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port);
len = 0;
socksreq[len++] = 5; /* version (SOCKS5) */
@ -866,7 +866,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip) {
char ip6[16];
if(1 != Curl_inet_pton(AF_INET6, hostname, ip6))
if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
return CURLPX_BAD_ADDRESS_TYPE;
socksreq[len++] = 4;
memcpy(&socksreq[len], ip6, sizeof(ip6));
@ -874,7 +874,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
}
else
#endif
if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) {
if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
socksreq[len++] = 1;
memcpy(&socksreq[len], ip4, sizeof(ip4));
len += sizeof(ip4);
@ -882,20 +882,20 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
else {
socksreq[len++] = 3;
socksreq[len++] = (char) hostname_len; /* one byte address length */
memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
memcpy(&socksreq[len], sx->hostname, hostname_len); /* w/o NULL */
len += hostname_len;
}
infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
hostname, remote_port);
sx->hostname, sx->remote_port);
}
/* FALLTHROUGH */
CONNECT_REQ_SEND:
case CONNECT_REQ_SEND:
/* PORT MSB */
socksreq[len++] = (unsigned char)((remote_port >> 8) & 0xff);
socksreq[len++] = (unsigned char)((sx->remote_port >> 8) & 0xff);
/* PORT LSB */
socksreq[len++] = (unsigned char)(remote_port & 0xff);
socksreq[len++] = (unsigned char)(sx->remote_port & 0xff);
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
if(conn->socks5_gssapi_enctype) {
@ -905,7 +905,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
#endif
sx->outp = socksreq;
sx->outstanding = len;
sxstate(data, CONNECT_REQ_SENDING);
sxstate(sx, data, CONNECT_REQ_SENDING);
/* FALLTHROUGH */
case CONNECT_REQ_SENDING:
result = Curl_write_plain(data, sockfd, (char *)sx->outp,
@ -928,7 +928,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
#endif
sx->outstanding = 10; /* minimum packet size is 10 */
sx->outp = socksreq;
sxstate(data, CONNECT_REQ_READ);
sxstate(sx, data, CONNECT_REQ_READ);
/* FALLTHROUGH */
case CONNECT_REQ_READ:
result = Curl_read_plain(sockfd, (char *)sx->outp,
@ -958,7 +958,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
CURLproxycode rc = CURLPX_REPLY_UNASSIGNED;
int code = socksreq[1];
failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
hostname, (unsigned char)socksreq[1]);
sx->hostname, (unsigned char)socksreq[1]);
if(code < 9) {
/* RFC 1928 section 6 lists: */
static const CURLproxycode lookup[] = {
@ -1019,10 +1019,10 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
if(len > 10) {
sx->outstanding = len - 10; /* get the rest */
sx->outp = &socksreq[10];
sxstate(data, CONNECT_REQ_READ_MORE);
sxstate(sx, data, CONNECT_REQ_READ_MORE);
}
else {
sxstate(data, CONNECT_DONE);
sxstate(sx, data, CONNECT_DONE);
break;
}
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
@ -1047,12 +1047,197 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
sx->outp += actualread;
return CURLPX_OK;
}
sxstate(data, CONNECT_DONE);
sxstate(sx, data, CONNECT_DONE);
}
infof(data, "SOCKS5 request granted.");
*done = TRUE;
return CURLPX_OK; /* Proxy was successful! */
}
static CURLcode connect_SOCKS(struct Curl_cfilter *cf,
struct socks_state *sxstate,
struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
CURLproxycode pxresult = CURLPX_OK;
struct connectdata *conn = cf->conn;
switch(conn->socks_proxy.proxytype) {
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
pxresult = do_SOCKS5(cf, sxstate, data);
break;
case CURLPROXY_SOCKS4:
case CURLPROXY_SOCKS4A:
pxresult = do_SOCKS4(cf, sxstate, data);
break;
default:
failf(data, "unknown proxytype option given");
result = CURLE_COULDNT_CONNECT;
} /* switch proxytype */
if(pxresult) {
result = CURLE_PROXY;
data->info.pxcode = pxresult;
}
return result;
}
static void socks_proxy_cf_free(struct Curl_cfilter *cf)
{
struct socks_state *sxstate = cf->ctx;
if(sxstate) {
free(sxstate);
cf->ctx = NULL;
}
}
/* 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 socks_proxy_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool blocking, bool *done)
{
CURLcode result;
struct connectdata *conn = cf->conn;
int sockindex = cf->sockindex;
struct socks_state *sx = cf->ctx;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
result = cf->next->cft->connect(cf->next, data, blocking, done);
if(result || !*done)
return result;
if(!sx) {
sx = calloc(sizeof(*sx), 1);
if(!sx)
return CURLE_OUT_OF_MEMORY;
cf->ctx = sx;
}
if(sx->state == CONNECT_INIT) {
/* for the secondary socket (FTP), use the "connect to host"
* but ignore the "connect to port" (use the secondary port)
*/
sxstate(sx, data, CONNECT_SOCKS_INIT);
sx->hostname =
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;
sx->remote_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;
sx->proxy_user = conn->socks_proxy.user;
sx->proxy_password = conn->socks_proxy.passwd;
}
result = connect_SOCKS(cf, sx, data);
if(!result && sx->state == CONNECT_DONE) {
cf->connected = TRUE;
Curl_updateconninfo(data, conn, conn->sock[cf->sockindex]);
Curl_verboseconnect(data, conn);
socks_proxy_cf_free(cf);
}
*done = cf->connected;
return result;
}
static int socks_cf_get_select_socks(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *socks)
{
struct socks_state *sx = cf->ctx;
int fds;
fds = cf->next->cft->get_select_socks(cf->next, data, socks);
if(!fds && cf->next->connected && !cf->connected && sx) {
/* If we are not connected, the filter below is and has nothing
* to wait on, we determine what to wait for. */
socks[0] = cf->conn->sock[cf->sockindex];
switch(sx->state) {
case CONNECT_RESOLVING:
case CONNECT_SOCKS_READ:
case CONNECT_AUTH_READ:
case CONNECT_REQ_READ:
case CONNECT_REQ_READ_MORE:
fds = GETSOCK_READSOCK(0);
break;
default:
fds = GETSOCK_WRITESOCK(0);
break;
}
}
return fds;
}
static void socks_proxy_cf_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
DEBUGASSERT(cf->next);
cf->connected = FALSE;
socks_proxy_cf_free(cf);
cf->next->cft->close(cf->next, data);
}
static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)data;
socks_proxy_cf_free(cf);
}
static void socks_proxy_cf_detach_data(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)data;
socks_proxy_cf_free(cf);
}
static const struct Curl_cftype cft_socks_proxy = {
"SOCKS-PROXYY",
socks_proxy_cf_destroy,
Curl_cf_def_attach_data,
socks_proxy_cf_detach_data,
Curl_cf_def_setup,
socks_proxy_cf_close,
socks_proxy_cf_connect,
socks_cf_get_select_socks,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
};
CURLcode Curl_cfilter_socks_proxy_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
struct Curl_cfilter *cf;
CURLcode result;
result = Curl_cfilter_create(&cf, data, conn, sockindex,
&cft_socks_proxy, NULL);
if(!result)
Curl_cfilter_add(data, conn, sockindex, cf);
return result;
}
#endif /* CURL_DISABLE_PROXY */