mirror of
https://github.com/curl/curl.git
synced 2026-08-26 21:13:34 +03:00
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:
parent
89ee5cfb38
commit
dafdb20a26
49 changed files with 3325 additions and 1969 deletions
417
lib/socks.c
417
lib/socks.c
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue