#define SOCKS_CHUNKS 1
-struct socks_state {
+struct socks_ctx {
enum socks_state_t state;
struct bufq iobuf;
- const char *hostname;
uint16_t remote_port;
- const char *proxy_user;
- const char *proxy_password;
+ const char *user;
+ const char *passwd;
CURLproxycode presult;
uint32_t resolv_id;
+ uint8_t ip_version;
+ uint8_t proxy_type;
unsigned char version;
BIT(resolve_local);
BIT(start_resolving);
BIT(socks4a);
+ char hostname[1];
};
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
*pnread = 0;
for(;;) {
timediff_t timeout_ms = Curl_timeleft_ms(data);
+ curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
+
if(timeout_ms < 0) {
/* we already got the timeout */
return CURLE_OPERATION_TIMEDOUT;
}
if(!timeout_ms)
timeout_ms = TIMEDIFF_T_MAX;
- if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0)
+ if(SOCKET_READABLE(sock, timeout_ms) <= 0)
return CURLE_OPERATION_TIMEDOUT;
result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread);
if(result == CURLE_AGAIN)
#endif
/* always use this function to change state, to make debugging easier */
-static void socksstate(struct socks_state *sx,
+static void socksstate(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
enum socks_state_t state
#endif
}
-static CURLproxycode socks_failed(struct socks_state *sx,
+static CURLproxycode socks_failed(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
CURLproxycode presult)
return presult;
}
-static CURLproxycode socks_flush(struct socks_state *sx,
+static CURLproxycode socks_flush(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
return CURLPX_OK;
}
-static CURLproxycode socks_recv(struct socks_state *sx,
+static CURLproxycode socks_recv(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
size_t min_bytes,
return CURLPX_OK;
}
-static CURLproxycode socks4_req_add_hd(struct socks_state *sx,
+static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx,
struct Curl_easy *data)
{
unsigned char buf[4];
return CURLPX_OK;
}
-static CURLproxycode socks4_req_add_user(struct socks_state *sx,
+static CURLproxycode socks4_req_add_user(struct socks_ctx *sx,
struct Curl_easy *data)
{
CURLcode result;
size_t nwritten;
- if(sx->proxy_user) {
- size_t plen = strlen(sx->proxy_user);
+ if(sx->user) {
+ size_t plen = strlen(sx->user);
if(plen > 255) {
/* there is no real size limit to this field in the protocol, but
SOCKS5 limits the proxy user field to 255 bytes and it seems likely
return CURLPX_LONG_USER;
}
/* add proxy name WITH trailing zero */
- result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, plen + 1,
+ result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1,
&nwritten);
if(result || (nwritten != (plen + 1)))
return CURLPX_SEND_REQUEST;
return CURLPX_OK;
}
-static CURLproxycode socks4_resolving(struct socks_state *sx,
+static CURLproxycode socks4_resolving(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
if(sx->start_resolving) {
/* need to resolve hostname to add destination address */
sx->start_resolving = FALSE;
- DEBUGASSERT(sx->hostname && *sx->hostname);
result = Curl_cf_dns_insert_after(
- cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version),
+ cf, data, Curl_resolv_dns_queries(data, sx->ip_version),
sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE);
if(result) {
failf(data, "unable to create DNS filter for socks");
return CURLPX_OK;
}
-static CURLproxycode socks4_check_resp(struct socks_state *sx,
+static CURLproxycode socks4_check_resp(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data)
{
* Nonsupport "Identification Protocol (RFC1413)"
*/
static CURLproxycode socks4_connect(struct Curl_cfilter *cf,
- struct socks_state *sx,
+ struct socks_ctx *sx,
struct Curl_easy *data)
{
size_t nwritten;
case SOCKS4_ST_START:
Curl_bufq_reset(&sx->iobuf);
sx->start_resolving = FALSE;
- sx->socks4a = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A);
+ sx->socks4a = (sx->proxy_type == CURLPROXY_SOCKS4A);
sx->resolve_local = !sx->socks4a;
sx->presult = CURLPX_OK;
/* SOCKS4 can only do IPv4, insist! */
- cf->conn->ip_version = CURL_IPRESOLVE_V4;
- CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%u",
- sx->socks4a ? "a" : "",
- cf->conn->bits.httpproxy ? " HTTP proxy" : "",
- sx->hostname, sx->remote_port);
+ sx->ip_version = CURL_IPRESOLVE_V4;
+ CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u",
+ sx->socks4a ? "a" : "", sx->hostname, sx->remote_port);
/*
* Compose socks4 request
}
static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf,
- struct socks_state *sx,
+ struct socks_ctx *sx,
struct Curl_easy *data)
{
const unsigned char auth = data->set.socks5auth;
"CURLOPT_SOCKS5_AUTH: %u", auth);
if(!(auth & CURLAUTH_BASIC))
/* disable username/password auth */
- sx->proxy_user = NULL;
+ sx->user = NULL;
req[0] = 5; /* version */
nauths = 1;
req[1 + nauths] = 1; /* GSS-API */
}
#endif
- if(sx->proxy_user) {
+ if(sx->user) {
++nauths;
req[1 + nauths] = 2; /* username/password */
}
return CURLPX_OK;
}
-static CURLproxycode socks5_check_resp0(struct socks_state *sx,
+static CURLproxycode socks5_check_resp0(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data)
{
}
static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf,
- struct socks_state *sx,
+ struct socks_ctx *sx,
struct Curl_easy *data)
{
/* Needs username and password */
unsigned char buf[2];
CURLcode result;
- if(sx->proxy_user && sx->proxy_password) {
- ulen = strlen(sx->proxy_user);
- plen = strlen(sx->proxy_password);
+ if(sx->user && sx->passwd) {
+ ulen = strlen(sx->user);
+ plen = strlen(sx->passwd);
/* the lengths must fit in a single byte */
if(ulen > 255) {
failf(data, "Excessive username length for proxy auth");
if(result || (nwritten != 2))
return CURLPX_SEND_REQUEST;
if(ulen) {
- result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, ulen, &nwritten);
+ result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten);
if(result || (nwritten != ulen))
return CURLPX_SEND_REQUEST;
}
if(result || (nwritten != 1))
return CURLPX_SEND_REQUEST;
if(plen) {
- result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_password, plen, &nwritten);
+ result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten);
if(result || (nwritten != plen))
return CURLPX_SEND_REQUEST;
}
return CURLPX_OK;
}
-static CURLproxycode socks5_check_auth_resp(struct socks_state *sx,
+static CURLproxycode socks5_check_auth_resp(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data)
{
return CURLPX_OK;
}
-static CURLproxycode socks5_req1_init(struct socks_state *sx,
+static CURLproxycode socks5_req1_init(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data)
{
return CURLPX_OK;
}
-static CURLproxycode socks5_resolving(struct socks_state *sx,
+static CURLproxycode socks5_resolving(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
if(sx->start_resolving) {
/* need to resolve hostname to add destination address */
sx->start_resolving = FALSE;
- DEBUGASSERT(sx->hostname && *sx->hostname);
result = Curl_cf_dns_insert_after(
- cf, data, Curl_resolv_dns_queries(data, cf->conn->ip_version),
+ cf, data, Curl_resolv_dns_queries(data, sx->ip_version),
sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE);
if(result) {
failf(data, "unable to create DNS filter for socks");
return presult;
}
-static CURLproxycode socks5_recv_resp1(struct socks_state *sx,
+static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
* destination server.
*/
static CURLproxycode socks5_connect(struct Curl_cfilter *cf,
- struct socks_state *sx,
+ struct socks_ctx *sx,
struct Curl_easy *data)
{
CURLproxycode presult;
switch(sx->state) {
case SOCKS_ST_INIT:
sx->version = 5;
- sx->resolve_local = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS5);
+ sx->resolve_local = (sx->proxy_type == CURLPROXY_SOCKS5);
sxstate(sx, cf, data, SOCKS5_ST_START);
FALLTHROUGH();
case SOCKS5_ST_START:
- if(cf->conn->bits.httpproxy)
- CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %u",
- sx->hostname, sx->remote_port);
+ CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u",
+ sx->hostname, sx->remote_port);
presult = socks5_req0_init(cf, sx, data);
if(presult)
return socks_failed(sx, cf, data, presult);
}
}
-static void socks_proxy_cf_free(struct Curl_cfilter *cf)
+static void socks_proxy_ctx_free(struct socks_ctx *ctx)
{
- struct socks_state *sxstate = cf->ctx;
- if(sxstate) {
- Curl_bufq_free(&sxstate->iobuf);
- curlx_free(sxstate);
- cf->ctx = NULL;
+ if(ctx) {
+ Curl_bufq_free(&ctx->iobuf);
+ curlx_free(ctx);
}
}
struct Curl_easy *data,
bool *done)
{
- CURLcode result;
- struct connectdata *conn = cf->conn;
- int sockindex = cf->sockindex;
- struct socks_state *sx = cf->ctx;
+ struct socks_ctx *ctx = cf->ctx;
CURLproxycode pxresult = CURLPX_OK;
+ CURLcode result;
if(cf->connected) {
*done = TRUE;
if(result || !*done)
return result;
- if(!sx) {
- cf->ctx = sx = curlx_calloc(1, sizeof(*sx));
- if(!sx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- /* for the secondary socket (FTP), use the "connect to host"
- * but ignore the "connect to port" (use the secondary port)
- */
- 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 ? conn->http_proxy.port :
- sockindex == SECONDARYSOCKET ? conn->secondary_port :
- conn->bits.conn_to_port ? conn->conn_to_port :
- (uint16_t)conn->remote_port;
- sx->proxy_user = conn->socks_proxy.user;
- sx->proxy_password = conn->socks_proxy.passwd;
- Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS,
- BUFQ_OPT_SOFT_LIMIT);
- }
-
- switch(conn->socks_proxy.proxytype) {
+ switch(ctx->proxy_type) {
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
- pxresult = socks5_connect(cf, sx, data);
+ pxresult = socks5_connect(cf, ctx, data);
break;
case CURLPROXY_SOCKS4:
case CURLPROXY_SOCKS4A:
- pxresult = socks4_connect(cf, sx, data);
+ pxresult = socks4_connect(cf, ctx, data);
break;
default:
- failf(data, "unknown proxytype option given");
+ DEBUGASSERT(0); /* should not come here, checked it at creation time */
result = CURLE_COULDNT_CONNECT;
goto out;
}
data->info.pxcode = pxresult;
goto out;
}
- else if(sx->state != SOCKS_ST_SUCCESS)
+ else if(ctx->state != SOCKS_ST_SUCCESS)
goto out;
#ifdef CURLVERBOSE
if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad))
infof(data, "Opened %sSOCKS connection from %s port %d to %s port %d "
"(via %s port %u)",
- (sockindex == SECONDARYSOCKET) ? "2nd " : "",
+ (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
ipquad.local_ip, ipquad.local_port,
- sx->hostname, sx->remote_port,
+ ctx->hostname, ctx->remote_port,
ipquad.remote_ip, ipquad.remote_port);
else
infof(data, "Opened %sSOCKS connection",
- (sockindex == SECONDARYSOCKET) ? "2nd " : "");
+ (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "");
}
#endif
- socks_proxy_cf_free(cf);
cf->connected = TRUE;
out:
*done = (bool)cf->connected;
+ if(*done || result) {
+ ctx->user = NULL;
+ ctx->passwd = NULL;
+ }
return result;
}
struct Curl_easy *data,
struct easy_pollset *ps)
{
- struct socks_state *sx = cf->ctx;
+ struct socks_ctx *sx = cf->ctx;
CURLcode result = CURLE_OK;
if(!cf->connected && sx) {
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->do_close(cf->next, data);
+ if(cf->next)
+ cf->next->cft->do_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);
+ socks_proxy_ctx_free(cf->ctx);
+ cf->ctx = NULL;
}
static CURLcode socks_cf_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2)
{
- struct socks_state *sx = cf->ctx;
+ struct socks_ctx *sx = cf->ctx;
switch(query) {
case CF_QUERY_HOST_PORT:
};
CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at,
- struct Curl_easy *data)
+ struct Curl_easy *data,
+ const char *hostname,
+ uint16_t port,
+ uint8_t ip_version,
+ uint8_t proxy_type,
+ const char *user,
+ const char *passwd)
{
struct Curl_cfilter *cf;
+ struct socks_ctx *ctx;
+ size_t hostlen = hostname ? strlen(hostname) : 0;
CURLcode result;
- (void)data;
- result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL);
+ if(!hostlen)
+ return CURLE_FAILED_INIT;
+
+ switch(proxy_type) {
+ case CURLPROXY_SOCKS5:
+ case CURLPROXY_SOCKS5_HOSTNAME:
+ case CURLPROXY_SOCKS4:
+ case CURLPROXY_SOCKS4A:
+ break; /* all supported */
+ default:
+ failf(data, "unknown proxytype %d option given", proxy_type);
+ return CURLE_COULDNT_CONNECT;
+ }
+
+ /* NUL byte already part of struct size */
+ ctx = curlx_calloc(1, sizeof(*ctx) + hostlen);
+ if(!ctx) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ memcpy(ctx->hostname, hostname, hostlen);
+ ctx->remote_port = port;
+ ctx->ip_version = ip_version;
+ ctx->proxy_type = proxy_type;
+ ctx->user = user;
+ ctx->passwd = passwd;
+ Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS,
+ BUFQ_OPT_SOFT_LIMIT);
+
+ result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, ctx);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
+ else
+ socks_proxy_ctx_free(ctx);
return result;
}