Merge commit 'public/socks-client'

Resolved conflict in:
	src/or/or.h
This commit is contained in:
Nick Mathewson 2009-08-26 11:27:19 -04:00
commit 707a6bd659
8 changed files with 695 additions and 112 deletions

View File

@ -299,6 +299,25 @@ HTTPS proxy authentication that Tor supports; feel free to submit a
patch if you want it to support others.
.LP
.TP
\fBSocks4Proxy\fR \fIhost\fR[:\fIport\fR]\fP
Tor will make all OR connections through the SOCKS 4 proxy at host:port
(or host:1080 if port is not specified).
.LP
.TP
\fBSocks5Proxy\fR \fIhost\fR[:\fIport\fR]\fP
Tor will make all OR connections through the SOCKS 5 proxy at host:port
(or host:1080 if port is not specified).
.LP
.TP
\fBSocks5ProxyUsername\fR \fIusername\fP
.LP
.TP
\fBSocks5ProxyPassword\fR \fIpassword\fP
If defined, authenticate to the SOCKS 5 server using username and password
in accordance to RFC 1929. Both username and password must be between 1 and 255
characters.
.LP
.TP
\fBKeepalivePeriod \fR\fINUM\fP
To keep firewalls from expiring connections, send a padding keepalive
cell every NUM seconds on open connections that are in use. If the

View File

@ -1611,6 +1611,132 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
}
}
/** Inspect a reply from SOCKS server stored in <b>buf</b> according
* to <b>state</b>, removing the protocol data upon success. Return 0 on
* incomplete response, 1 on success and -1 on error, in which case
* <b>reason</b> is set to a descriptive message (free() when finished
* with it).
*
* As a special case, 2 is returned when user/pass is required
* during SOCKS5 handshake and user/pass is configured.
*/
int
fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
{
unsigned char *data;
size_t addrlen;
if (buf->datalen < 2)
return 0;
buf_pullup(buf, 128, 0);
tor_assert(buf->head && buf->head->datalen >= 2);
data = (unsigned char *) buf->head->data;
switch (state) {
case PROXY_SOCKS4_WANT_CONNECT_OK:
/* Wait for the complete response */
if (buf->head->datalen < 8)
return 0;
if (data[1] != 0x5a) {
*reason = tor_strdup(socks4_response_code_to_string(data[1]));
return -1;
}
/* Success */
buf_remove_from_front(buf, 8);
return 1;
case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
/* we don't have any credentials */
if (data[1] != 0x00) {
*reason = tor_strdup("server doesn't support any of our "
"available authentication methods");
return -1;
}
log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
buf_clear(buf);
return 1;
case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
/* we have a username and password. return 1 if we can proceed without
* providing authentication, or 2 otherwise. */
switch (data[1]) {
case 0x00:
log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
"doesn't require authentication.");
buf_clear(buf);
return 1;
case 0x02:
log_info(LD_NET, "SOCKS 5 client: need authentication.");
buf_clear(buf);
return 2;
/* fall through */
}
*reason = tor_strdup("server doesn't support any of our available "
"authentication methods");
return -1;
case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
/* handle server reply to rfc1929 authentication */
if (data[1] != 0x00) {
*reason = tor_strdup("authentication failed");
return -1;
}
log_info(LD_NET, "SOCKS 5 client: authentication successful.");
buf_clear(buf);
return 1;
case PROXY_SOCKS5_WANT_CONNECT_OK:
/* response is variable length. BND.ADDR, etc, isn't needed
* (don't bother with buf_pullup()), but make sure to eat all
* the data used */
/* wait for address type field to arrive */
if (buf->datalen < 4)
return 0;
switch (data[3]) {
case 0x01: /* ip4 */
addrlen = 4;
break;
case 0x04: /* ip6 */
addrlen = 16;
break;
case 0x03: /* fqdn (can this happen here?) */
if (buf->datalen < 5)
return 0;
addrlen = 1 + data[4];
break;
default:
*reason = tor_strdup("invalid response to connect request");
return -1;
}
/* wait for address and port */
if (buf->datalen < 6 + addrlen)
return 0;
if (data[1] != 0x00) {
*reason = tor_strdup(socks5_response_code_to_string(data[1]));
return -1;
}
buf_remove_from_front(buf, 6 + addrlen);
return 1;
}
/* shouldn't get here... */
tor_assert(0);
return -1;
}
/** Return 1 iff buf looks more like it has an (obsolete) v0 controller
* command on it than any valid v1 controller command. */
int

View File

@ -246,6 +246,10 @@ static config_var_t _option_vars[] = {
V(HttpProxyAuthenticator, STRING, NULL),
V(HttpsProxy, STRING, NULL),
V(HttpsProxyAuthenticator, STRING, NULL),
V(Socks4Proxy, STRING, NULL),
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
V(Socks5ProxyPassword, STRING, NULL),
OBSOLETE("IgnoreVersion"),
V(KeepalivePeriod, INTERVAL, "5 minutes"),
VAR("Log", LINELIST, Logs, NULL),
@ -3482,7 +3486,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Failed to parse accounting options. See logs for details.");
if (options->HttpProxy) { /* parse it now */
if (parse_addr_port(LOG_WARN, options->HttpProxy, NULL,
if (tor_addr_port_parse(options->HttpProxy,
&options->HttpProxyAddr, &options->HttpProxyPort) < 0)
REJECT("HttpProxy failed to parse or resolve. Please fix.");
if (options->HttpProxyPort == 0) { /* give it a default */
@ -3496,7 +3500,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (options->HttpsProxy) { /* parse it now */
if (parse_addr_port(LOG_WARN, options->HttpsProxy, NULL,
if (tor_addr_port_parse(options->HttpsProxy,
&options->HttpsProxyAddr, &options->HttpsProxyPort) <0)
REJECT("HttpsProxy failed to parse or resolve. Please fix.");
if (options->HttpsProxyPort == 0) { /* give it a default */
@ -3509,6 +3513,42 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("HttpsProxyAuthenticator is too long (>= 48 chars).");
}
if (options->Socks4Proxy) { /* parse it now */
if (tor_addr_port_parse(options->Socks4Proxy,
&options->Socks4ProxyAddr,
&options->Socks4ProxyPort) <0)
REJECT("Socks4Proxy failed to parse or resolve. Please fix.");
if (options->Socks4ProxyPort == 0) { /* give it a default */
options->Socks4ProxyPort = 1080;
}
}
if (options->Socks5Proxy) { /* parse it now */
if (tor_addr_port_parse(options->Socks5Proxy,
&options->Socks5ProxyAddr,
&options->Socks5ProxyPort) <0)
REJECT("Socks5Proxy failed to parse or resolve. Please fix.");
if (options->Socks5ProxyPort == 0) { /* give it a default */
options->Socks5ProxyPort = 1080;
}
}
if (options->Socks5ProxyUsername) {
size_t len;
len = strlen(options->Socks5ProxyUsername);
if (len < 1 || len > 255)
REJECT("Socks5ProxyUsername must be between 1 and 255 characters.");
if (!options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
len = strlen(options->Socks5ProxyPassword);
if (len < 1 || len > 255)
REJECT("Socks5ProxyPassword must be between 1 and 255 characters.");
} else if (options->Socks5ProxyPassword)
REJECT("Socks5ProxyPassword must be included with Socks5ProxyUsername.");
if (options->HashedControlPassword) {
smartlist_t *sl = decode_hashed_passwords(options->HashedControlPassword);
if (!sl) {
@ -3657,6 +3697,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->PreferTunneledDirConns && !options->TunnelDirConns)
REJECT("Must set TunnelDirConns if PreferTunneledDirConns is set.");
if ((options->Socks4Proxy || options->Socks5Proxy) &&
!options->HttpProxy && !options->PreferTunneledDirConns)
REJECT("When Socks4Proxy or Socks5Proxy is configured, "
"PreferTunneledDirConns and TunnelDirConns must both be "
"set to 1, or HttpProxy must be configured.");
if (options->AutomapHostsSuffixes) {
SMARTLIST_FOREACH(options->AutomapHostsSuffixes, char *, suf,
{

View File

@ -32,6 +32,10 @@ static int connection_process_inbuf(connection_t *conn, int package_partial);
static void client_check_address_changed(int sock);
static void set_constrained_socket_buffers(int sock, int size);
static const char *connection_proxy_state_to_string(int state);
static int connection_read_https_proxy_response(connection_t *conn);
static void connection_send_socks5_connect(connection_t *conn);
/** The last IPv4 address that our network interface seemed to have been
* binding to, in host order. We use this to detect when our IP changes. */
static uint32_t last_interface_ip = 0;
@ -92,8 +96,7 @@ conn_state_to_string(int type, int state)
case CONN_TYPE_OR:
switch (state) {
case OR_CONN_STATE_CONNECTING: return "connect()ing";
case OR_CONN_STATE_PROXY_FLUSHING: return "proxy flushing";
case OR_CONN_STATE_PROXY_READING: return "proxy reading";
case OR_CONN_STATE_PROXY_HANDSHAKING: return "handshaking (proxy)";
case OR_CONN_STATE_TLS_HANDSHAKING: return "handshaking (TLS)";
case OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING:
return "renegotiating (TLS)";
@ -1289,6 +1292,353 @@ connection_connect(connection_t *conn, const char *address,
return inprogress ? 0 : 1;
}
/** Convert state number to string representation for logging purposes.
*/
static const char *
connection_proxy_state_to_string(int state)
{
static const char *unknown = "???";
static const char *states[] = {
"PROXY_NONE",
"PROXY_HTTPS_WANT_CONNECT_OK",
"PROXY_SOCKS4_WANT_CONNECT_OK",
"PROXY_SOCKS5_WANT_AUTH_METHOD_NONE",
"PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929",
"PROXY_SOCKS5_WANT_AUTH_RFC1929_OK",
"PROXY_SOCKS5_WANT_CONNECT_OK",
"PROXY_CONNECTED",
};
if (state < PROXY_NONE || state > PROXY_CONNECTED)
return unknown;
return states[state];
}
/** Write a proxy request of <b>type</b> (socks4, socks5, https) to conn
* for conn->addr:conn->port, authenticating with the auth details given
* in the configuration (if available). SOCKS 5 and HTTP CONNECT proxies
* support authentication.
*
* Returns -1 if conn->addr is incompatible with the proxy protocol, and
* 0 otherwise.
*
* Use connection_read_proxy_handshake() to complete the handshake.
*/
int
connection_proxy_connect(connection_t *conn, int type)
{
or_options_t *options;
tor_assert(conn);
options = get_options();
switch (type) {
case PROXY_CONNECT: {
char buf[1024];
char *base64_authenticator=NULL;
const char *authenticator = options->HttpsProxyAuthenticator;
/* Send HTTP CONNECT and authentication (if available) in
* one request */
if (authenticator) {
base64_authenticator = alloc_http_authenticator(authenticator);
if (!base64_authenticator)
log_warn(LD_OR, "Encoding https authenticator failed");
}
if (base64_authenticator) {
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n"
"Proxy-Authorization: Basic %s\r\n\r\n",
fmt_addr(&conn->addr),
conn->port, base64_authenticator);
tor_free(base64_authenticator);
} else {
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n",
fmt_addr(&conn->addr), conn->port);
}
connection_write_to_buf(buf, strlen(buf), conn);
conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
break;
}
case PROXY_SOCKS4: {
unsigned char buf[9];
uint16_t portn;
uint32_t ip4addr;
/* Send a SOCKS4 connect request with empty user id */
if (tor_addr_family(&conn->addr) != AF_INET) {
log_warn(LD_NET, "SOCKS4 client is incompatible with with IPv6");
return -1;
}
ip4addr = tor_addr_to_ipv4n(&conn->addr);
portn = htons(conn->port);
buf[0] = 4; /* version */
buf[1] = SOCKS_COMMAND_CONNECT; /* command */
memcpy(buf + 2, &portn, 2); /* port */
memcpy(buf + 4, &ip4addr, 4); /* addr */
buf[8] = 0; /* userid (empty) */
connection_write_to_buf((char *)buf, sizeof(buf), conn);
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
break;
}
case PROXY_SOCKS5: {
unsigned char buf[4]; /* fields: vers, num methods, method list */
/* Send a SOCKS5 greeting (connect request must wait) */
buf[0] = 5; /* version */
/* number of auth methods */
if (options->Socks5ProxyUsername) {
buf[1] = 2;
buf[2] = 0x00; /* no authentication */
buf[3] = 0x02; /* rfc1929 Username/Passwd auth */
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929;
} else {
buf[1] = 1;
buf[2] = 0x00; /* no authentication */
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE;
}
connection_write_to_buf((char *)buf, 2 + buf[1], conn);
break;
}
default:
log_err(LD_BUG, "Invalid proxy protocol, %d", type);
tor_fragile_assert();
return -1;
}
log_debug(LD_NET, "set state %s",
connection_proxy_state_to_string(conn->proxy_state));
return 0;
}
/** Read conn's inbuf. If the http response from the proxy is all
* here, make sure it's good news, then return 1. If it's bad news,
* return -1. Else return 0 and hope for better luck next time.
*/
static int
connection_read_https_proxy_response(connection_t *conn)
{
char *headers;
char *reason=NULL;
int status_code;
time_t date_header;
switch (fetch_from_buf_http(conn->inbuf,
&headers, MAX_HEADERS_SIZE,
NULL, NULL, 10000, 0)) {
case -1: /* overflow */
log_warn(LD_PROTOCOL,
"Your https proxy sent back an oversized response. Closing.");
return -1;
case 0:
log_info(LD_NET,"https proxy response not all here yet. Waiting.");
return 0;
/* case 1, fall through */
}
if (parse_http_response(headers, &status_code, &date_header,
NULL, &reason) < 0) {
log_warn(LD_NET,
"Unparseable headers from proxy (connecting to '%s'). Closing.",
conn->address);
tor_free(headers);
return -1;
}
if (!reason) reason = tor_strdup("[no reason given]");
if (status_code == 200) {
log_info(LD_NET,
"HTTPS connect to '%s' successful! (200 %s) Starting TLS.",
conn->address, escaped(reason));
tor_free(reason);
return 1;
}
/* else, bad news on the status code */
log_warn(LD_NET,
"The https proxy sent back an unexpected status code %d (%s). "
"Closing.",
status_code, escaped(reason));
tor_free(reason);
return -1;
}
/** Send SOCKS5 CONNECT command to <b>conn</b>, copying <b>conn->addr</b>
* and <b>conn->port</b> into the request.
*/
static void
connection_send_socks5_connect(connection_t *conn)
{
unsigned char buf[1024];
size_t reqsize = 6;
uint16_t port = htons(conn->port);
buf[0] = 5; /* version */
buf[1] = SOCKS_COMMAND_CONNECT; /* command */
buf[2] = 0; /* reserved */
if (tor_addr_family(&conn->addr) == AF_INET) {
uint32_t addr = tor_addr_to_ipv4n(&conn->addr);
buf[3] = 1;
reqsize += 4;
memcpy(buf + 4, &addr, 4);
memcpy(buf + 8, &port, 2);
} else { /* AF_INET6 */
buf[3] = 4;
reqsize += 16;
memcpy(buf + 4, tor_addr_to_in6(&conn->addr), 16);
memcpy(buf + 20, &port, 2);
}
connection_write_to_buf((char *)buf, reqsize, conn);
conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK;
}
/** Call this from connection_*_process_inbuf() to advance the proxy
* handshake.
*
* No matter what proxy protocol is used, if this function returns 1, the
* handshake is complete, and the data remaining on inbuf may contain the
* start of the communication with the requested server.
*
* Returns 0 if the current buffer contains an incomplete response, and -1
* on error.
*/
int
connection_read_proxy_handshake(connection_t *conn)
{
int ret = 0;
char *reason = NULL;
log_debug(LD_NET, "enter state %s",
connection_proxy_state_to_string(conn->proxy_state));
switch (conn->proxy_state) {
case PROXY_HTTPS_WANT_CONNECT_OK:
ret = connection_read_https_proxy_response(conn);
if (ret == 1)
conn->proxy_state = PROXY_CONNECTED;
break;
case PROXY_SOCKS4_WANT_CONNECT_OK:
ret = fetch_from_buf_socks_client(conn->inbuf,
conn->proxy_state,
&reason);
if (ret == 1)
conn->proxy_state = PROXY_CONNECTED;
break;
case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
ret = fetch_from_buf_socks_client(conn->inbuf,
conn->proxy_state,
&reason);
/* no auth needed, do connect */
if (ret == 1) {
connection_send_socks5_connect(conn);
ret = 0;
}
break;
case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
ret = fetch_from_buf_socks_client(conn->inbuf,
conn->proxy_state,
&reason);
/* send auth if needed, otherwise do connect */
if (ret == 1) {
connection_send_socks5_connect(conn);
ret = 0;
} else if (ret == 2) {
unsigned char buf[1024];
size_t reqsize, usize, psize;
const char *user, *pass;
user = get_options()->Socks5ProxyUsername;
pass = get_options()->Socks5ProxyPassword;
tor_assert(user && pass);
/* XXX len of user and pass must be <= 255 !!! */
usize = strlen(user);
psize = strlen(pass);
tor_assert(usize <= 255 && psize <= 255);
reqsize = 3 + usize + psize;
buf[0] = 1; /* negotiation version */
buf[1] = usize;
memcpy(buf + 2, user, usize);
buf[2 + usize] = psize;
memcpy(buf + 3 + usize, pass, psize);
connection_write_to_buf((char *)buf, reqsize, conn);
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK;
ret = 0;
}
break;
case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
ret = fetch_from_buf_socks_client(conn->inbuf,
conn->proxy_state,
&reason);
/* send the connect request */
if (ret == 1) {
connection_send_socks5_connect(conn);
ret = 0;
}
break;
case PROXY_SOCKS5_WANT_CONNECT_OK:
ret = fetch_from_buf_socks_client(conn->inbuf,
conn->proxy_state,
&reason);
if (ret == 1)
conn->proxy_state = PROXY_CONNECTED;
break;
default:
log_err(LD_BUG, "Invalid proxy_state for reading, %d",
conn->proxy_state);
tor_fragile_assert();
ret = -1;
break;
}
log_debug(LD_NET, "leaving state %s",
connection_proxy_state_to_string(conn->proxy_state));
if (ret < 0) {
if (reason) {
log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d (%s)",
conn->address, conn->port, escaped(reason));
tor_free(reason);
} else {
log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d",
conn->address, conn->port);
}
} else if (ret == 1) {
log_info(LD_NET, "Proxy Client: connection to %s:%d successful",
conn->address, conn->port);
}
return ret;
}
/**
* Launch any configured listener connections of type <b>type</b>. (A
* listener is configured if <b>port_option</b> is non-zero. If any
@ -2055,7 +2405,7 @@ connection_read_to_buf(connection_t *conn, int *max_to_read, int *socket_error)
}
if (connection_speaks_cells(conn) &&
conn->state > OR_CONN_STATE_PROXY_READING) {
conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) {
int pending;
or_connection_t *or_conn = TO_OR_CONN(conn);
size_t initial_size;
@ -2283,7 +2633,7 @@ connection_handle_write(connection_t *conn, int force)
: connection_bucket_write_limit(conn, now);
if (connection_speaks_cells(conn) &&
conn->state > OR_CONN_STATE_PROXY_READING) {
conn->state > OR_CONN_STATE_PROXY_HANDSHAKING) {
or_connection_t *or_conn = TO_OR_CONN(conn);
if (conn->state == OR_CONN_STATE_TLS_HANDSHAKING ||
conn->state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) {
@ -3016,7 +3366,7 @@ assert_connection_ok(connection_t *conn, time_t now)
}
// tor_assert(conn->addr && conn->port);
tor_assert(conn->address);
if (conn->state > OR_CONN_STATE_PROXY_READING)
if (conn->state > OR_CONN_STATE_PROXY_HANDSHAKING)
tor_assert(or_conn->tls);
}

View File

@ -187,66 +187,6 @@ connection_or_reached_eof(or_connection_t *conn)
return 0;
}
/** Read conn's inbuf. If the http response from the proxy is all
* here, make sure it's good news, and begin the tls handshake. If
* it's bad news, close the connection and return -1. Else return 0
* and hope for better luck next time.
*/
static int
connection_or_read_proxy_response(or_connection_t *or_conn)
{
char *headers;
char *reason=NULL;
int status_code;
time_t date_header;
connection_t *conn = TO_CONN(or_conn);
switch (fetch_from_buf_http(conn->inbuf,
&headers, MAX_HEADERS_SIZE,
NULL, NULL, 10000, 0)) {
case -1: /* overflow */
log_warn(LD_PROTOCOL,
"Your https proxy sent back an oversized response. Closing.");
return -1;
case 0:
log_info(LD_OR,"https proxy response not all here yet. Waiting.");
return 0;
/* case 1, fall through */
}
if (parse_http_response(headers, &status_code, &date_header,
NULL, &reason) < 0) {
log_warn(LD_OR,
"Unparseable headers from proxy (connecting to '%s'). Closing.",
conn->address);
tor_free(headers);
return -1;
}
if (!reason) reason = tor_strdup("[no reason given]");
if (status_code == 200) {
log_info(LD_OR,
"HTTPS connect to '%s' successful! (200 %s) Starting TLS.",
conn->address, escaped(reason));
tor_free(reason);
if (connection_tls_start_handshake(or_conn, 0) < 0) {
/* TLS handshaking error of some kind. */
connection_mark_for_close(conn);
return -1;
}
return 0;
}
/* else, bad news on the status code */
log_warn(LD_OR,
"The https proxy sent back an unexpected status code %d (%s). "
"Closing.",
status_code, escaped(reason));
tor_free(reason);
connection_mark_for_close(conn);
return -1;
}
/** Handle any new bytes that have come in on connection <b>conn</b>.
* If conn is in 'open' state, hand it to
* connection_or_process_cells_from_inbuf()
@ -255,11 +195,24 @@ connection_or_read_proxy_response(or_connection_t *or_conn)
int
connection_or_process_inbuf(or_connection_t *conn)
{
int ret;
tor_assert(conn);
switch (conn->_base.state) {
case OR_CONN_STATE_PROXY_READING:
return connection_or_read_proxy_response(conn);
case OR_CONN_STATE_PROXY_HANDSHAKING:
ret = connection_read_proxy_handshake(TO_CONN(conn));
/* start TLS after handshake completion, or deal with error */
if (ret == 1) {
tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED);
if (connection_tls_start_handshake(conn, 0) < 0)
ret = -1;
}
if (ret < 0) {
connection_mark_for_close(TO_CONN(conn));
}
return ret;
case OR_CONN_STATE_OPEN:
case OR_CONN_STATE_OR_HANDSHAKING:
return connection_or_process_cells_from_inbuf(conn);
@ -312,11 +265,7 @@ connection_or_finished_flushing(or_connection_t *conn)
assert_connection_ok(TO_CONN(conn),0);
switch (conn->_base.state) {
case OR_CONN_STATE_PROXY_FLUSHING:
log_debug(LD_OR,"finished sending CONNECT to proxy.");
conn->_base.state = OR_CONN_STATE_PROXY_READING;
connection_stop_writing(TO_CONN(conn));
break;
case OR_CONN_STATE_PROXY_HANDSHAKING:
case OR_CONN_STATE_OPEN:
case OR_CONN_STATE_OR_HANDSHAKING:
connection_stop_writing(TO_CONN(conn));
@ -334,6 +283,7 @@ connection_or_finished_flushing(or_connection_t *conn)
int
connection_or_finished_connecting(or_connection_t *or_conn)
{
int proxy_type;
connection_t *conn;
tor_assert(or_conn);
conn = TO_CONN(or_conn);
@ -343,28 +293,24 @@ connection_or_finished_connecting(or_connection_t *or_conn)
conn->address,conn->port);
control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0);
if (get_options()->HttpsProxy) {
char buf[1024];
char *base64_authenticator=NULL;
const char *authenticator = get_options()->HttpsProxyAuthenticator;
proxy_type = PROXY_NONE;
if (authenticator) {
base64_authenticator = alloc_http_authenticator(authenticator);
if (!base64_authenticator)
log_warn(LD_OR, "Encoding https authenticator failed");
if (get_options()->HttpsProxy)
proxy_type = PROXY_CONNECT;
else if (get_options()->Socks4Proxy)
proxy_type = PROXY_SOCKS4;
else if (get_options()->Socks5Proxy)
proxy_type = PROXY_SOCKS5;
if (proxy_type != PROXY_NONE) {
/* start proxy handshake */
if (connection_proxy_connect(conn, proxy_type) < 0) {
connection_mark_for_close(conn);
return -1;
}
if (base64_authenticator) {
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.1\r\n"
"Proxy-Authorization: Basic %s\r\n\r\n",
fmt_addr(&conn->addr),
conn->port, base64_authenticator);
tor_free(base64_authenticator);
} else {
tor_snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n\r\n",
fmt_addr(&conn->addr), conn->port);
}
connection_write_to_buf(buf, strlen(buf), conn);
conn->state = OR_CONN_STATE_PROXY_FLUSHING;
connection_start_reading(conn);
conn->state = OR_CONN_STATE_PROXY_HANDSHAKING;
return 0;
}
@ -753,6 +699,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
or_connection_t *conn;
or_options_t *options = get_options();
int socket_error = 0;
int using_proxy = 0;
tor_addr_t addr;
tor_assert(_addr);
@ -771,19 +718,27 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
conn->_base.state = OR_CONN_STATE_CONNECTING;
control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
/* use a proxy server if available */
if (options->HttpsProxy) {
/* we shouldn't connect directly. use the https proxy instead. */
tor_addr_from_ipv4h(&addr, options->HttpsProxyAddr);
using_proxy = 1;
tor_addr_copy(&addr, &options->HttpsProxyAddr);
port = options->HttpsProxyPort;
} else if (options->Socks4Proxy) {
using_proxy = 1;
tor_addr_copy(&addr, &options->Socks4ProxyAddr);
port = options->Socks4ProxyPort;
} else if (options->Socks5Proxy) {
using_proxy = 1;
tor_addr_copy(&addr, &options->Socks5ProxyAddr);
port = options->Socks5ProxyPort;
}
switch (connection_connect(TO_CONN(conn), conn->_base.address,
&addr, port, &socket_error)) {
case -1:
/* If the connection failed immediately, and we're using
* an https proxy, our https proxy is down. Don't blame the
* Tor server. */
if (!options->HttpsProxy)
* a proxy, our proxy is down. Don't blame the Tor server. */
if (!using_proxy)
entry_guard_register_connect_status(conn->identity_digest,
0, 1, time(NULL));
connection_or_connect_failed(conn,

View File

@ -747,6 +747,15 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
/* ensure that we don't make direct connections when a SOCKS server is
* configured. */
if (!anonymized_connection && !use_begindir && !options->HttpProxy &&
(options->Socks4Proxy || options->Socks5Proxy)) {
log_warn(LD_DIR, "Cannot connect to a directory server through a "
"SOCKS proxy!");
return;
}
conn = dir_connection_new(AF_INET);
/* set up conn so it's got all the data we need to remember */
@ -772,7 +781,7 @@ directory_initiate_command_rend(const char *address, const tor_addr_t *_addr,
/* then we want to connect to dirport directly */
if (options->HttpProxy) {
tor_addr_from_ipv4h(&addr, options->HttpProxyAddr);
tor_addr_copy(&addr, &options->HttpProxyAddr);
dir_port = options->HttpProxyPort;
}

View File

@ -223,6 +223,21 @@ typedef enum {
/* !!!! If _CONN_TYPE_MAX is ever over 15, we must grow the type field in
* connection_t. */
/* Proxy client types */
#define PROXY_NONE 0
#define PROXY_CONNECT 1
#define PROXY_SOCKS4 2
#define PROXY_SOCKS5 3
/* Proxy client handshake states */
#define PROXY_HTTPS_WANT_CONNECT_OK 1
#define PROXY_SOCKS4_WANT_CONNECT_OK 2
#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 3
#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 4
#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 5
#define PROXY_SOCKS5_WANT_CONNECT_OK 6
#define PROXY_CONNECTED 7
/** True iff <b>x</b> is an edge connection. */
#define CONN_IS_EDGE(x) \
((x)->type == CONN_TYPE_EXIT || (x)->type == CONN_TYPE_AP)
@ -243,26 +258,24 @@ typedef enum {
#define _OR_CONN_STATE_MIN 1
/** State for a connection to an OR: waiting for connect() to finish. */
#define OR_CONN_STATE_CONNECTING 1
/** State for a connection to an OR: waiting for proxy command to flush. */
#define OR_CONN_STATE_PROXY_FLUSHING 2
/** State for a connection to an OR: waiting for proxy response. */
#define OR_CONN_STATE_PROXY_READING 3
/** State for a connection to an OR: waiting for proxy handshake to complete */
#define OR_CONN_STATE_PROXY_HANDSHAKING 2
/** State for a connection to an OR or client: SSL is handshaking, not done
* yet. */
#define OR_CONN_STATE_TLS_HANDSHAKING 4
#define OR_CONN_STATE_TLS_HANDSHAKING 3
/** State for a connection to an OR: We're doing a second SSL handshake for
* renegotiation purposes. */
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 5
#define OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING 4
/** State for a connection at an OR: We're waiting for the client to
* renegotiate. */
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 6
#define OR_CONN_STATE_TLS_SERVER_RENEGOTIATING 5
/** State for a connection to an OR: We're done with our SSL handshake, but we
* haven't yet negotiated link protocol versions and sent a netinfo cell.
*/
#define OR_CONN_STATE_OR_HANDSHAKING 7
#define OR_CONN_STATE_OR_HANDSHAKING 6
/** State for a connection to an OR: Ready to send/receive cells. */
#define OR_CONN_STATE_OPEN 8
#define _OR_CONN_STATE_MAX 8
#define OR_CONN_STATE_OPEN 7
#define _OR_CONN_STATE_MAX 7
#define _EXIT_CONN_STATE_MIN 1
/** State for an exit connection: waiting for response from DNS farm. */
@ -932,6 +945,9 @@ typedef struct connection_t {
* connection. */
unsigned int linked_conn_is_closed:1;
/** CONNECT/SOCKS proxy client handshake state (for outgoing connections). */
unsigned int proxy_state:4;
/** Our socket; -1 if this connection is closed, or has no socket. */
evutil_socket_t s;
int conn_array_index; /**< Index into the global connection array. */
@ -979,6 +995,7 @@ typedef struct connection_t {
/** Unique ID for measuring tunneled network status requests. */
uint64_t dirreq_id;
#endif
} connection_t;
/** Stores flags and information related to the portion of a v2 Tor OR
@ -2366,15 +2383,25 @@ typedef struct {
char *ContactInfo; /**< Contact info to be published in the directory. */
char *HttpProxy; /**< hostname[:port] to use as http proxy, if any. */
uint32_t HttpProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
tor_addr_t HttpProxyAddr; /**< Parsed IPv4 addr for http proxy, if any. */
uint16_t HttpProxyPort; /**< Parsed port for http proxy, if any. */
char *HttpProxyAuthenticator; /**< username:password string, if any. */
char *HttpsProxy; /**< hostname[:port] to use as https proxy, if any. */
uint32_t HttpsProxyAddr; /**< Parsed IPv4 addr for https proxy, if any. */
tor_addr_t HttpsProxyAddr; /**< Parsed addr for https proxy, if any. */
uint16_t HttpsProxyPort; /**< Parsed port for https proxy, if any. */
char *HttpsProxyAuthenticator; /**< username:password string, if any. */
char *Socks4Proxy;
tor_addr_t Socks4ProxyAddr;
uint16_t Socks4ProxyPort;
char *Socks5Proxy;
tor_addr_t Socks5ProxyAddr;
uint16_t Socks5ProxyPort;
char *Socks5ProxyUsername;
char *Socks5ProxyPassword;
/** List of configuration lines for replacement directory authorities.
* If you just want to replace one class of authority at a time,
* use the "Alternate*Authority" options below instead. */
@ -2738,6 +2765,7 @@ int fetch_from_buf_http(buf_t *buf,
int force_complete);
int fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
int log_sockstype, int safe_socks);
int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason);
int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
int peek_buf_has_control0_command(buf_t *buf);
@ -3000,6 +3028,10 @@ void connection_expire_held_open(void);
int connection_connect(connection_t *conn, const char *address,
const tor_addr_t *addr,
uint16_t port, int *socket_error);
int connection_proxy_connect(connection_t *conn, int type);
int connection_read_proxy_handshake(connection_t *conn);
int retry_all_listeners(smartlist_t *replaced_conns,
smartlist_t *new_conns);
@ -4037,6 +4069,8 @@ int tls_error_to_orconn_end_reason(int e);
int errno_to_orconn_end_reason(int e);
const char *circuit_end_reason_to_control_string(int reason);
const char *socks4_response_code_to_string(uint8_t code);
const char *socks5_response_code_to_string(uint8_t code);
/********************************* relay.c ***************************/

View File

@ -326,3 +326,47 @@ circuit_end_reason_to_control_string(int reason)
}
}
const char *
socks4_response_code_to_string(uint8_t code)
{
switch (code) {
case 0x5a:
return "connection accepted";
case 0x5b:
return "server rejected connection";
case 0x5c:
return "server cannot connect to identd on this client";
case 0x5d:
return "user id does not match identd";
default:
return "invalid SOCKS 4 response code";
}
}
const char *
socks5_response_code_to_string(uint8_t code)
{
switch (code) {
case 0x00:
return "connection accepted";
case 0x01:
return "general SOCKS server failure";
case 0x02:
return "connection not allowed by ruleset";
case 0x03:
return "Network unreachable";
case 0x04:
return "Host unreachable";
case 0x05:
return "Connection refused";
case 0x06:
return "TTL expired";
case 0x07:
return "Command not supported";
case 0x08:
return "Address type not supported";
default:
return "unknown reason";
}
}