Merge branch 'http_tunnel_squashed'

This commit is contained in:
Nick Mathewson 2017-09-05 14:34:29 -04:00
commit 73b0e2e6fd
16 changed files with 397 additions and 27 deletions

5
changes/feature22407 Normal file
View File

@ -0,0 +1,5 @@
o Minor features (client):
- You can now use Tor as a tunneled HTTP proxy: use the HTTPTunnelPort
option to open a port that accepts HTTP CONNECT requests.
Closes ticket 22407.

View File

@ -786,7 +786,8 @@ CLIENT OPTIONS
-------------- --------------
The following options are useful only for clients (that is, if The following options are useful only for clients (that is, if
**SocksPort**, **TransPort**, **DNSPort**, or **NATDPort** is non-zero): **SocksPort**, **HTTPTunnelPort**, **TransPort**, **DNSPort**, or
**NATDPort** is non-zero):
[[Bridge]] **Bridge** [__transport__] __IP__:__ORPort__ [__fingerprint__]:: [[Bridge]] **Bridge** [__transport__] __IP__:__ORPort__ [__fingerprint__]::
When set along with UseBridges, instructs Tor to use the relay at When set along with UseBridges, instructs Tor to use the relay at
@ -1110,7 +1111,9 @@ The following options are useful only for clients (that is, if
Unsupported and force-disabled when using Unix domain sockets.) Unsupported and force-disabled when using Unix domain sockets.)
**IsolateSOCKSAuth**;; **IsolateSOCKSAuth**;;
Don't share circuits with streams for which different Don't share circuits with streams for which different
SOCKS authentication was provided. (On by default; SOCKS authentication was provided. (For HTTPTunnelPort
connections, this option looks at the Proxy-Authorization and
X-Tor-Stream-Isolation headers. On by default;
you can disable it with **NoIsolateSOCKSAuth**.) you can disable it with **NoIsolateSOCKSAuth**.)
**IsolateClientProtocol**;; **IsolateClientProtocol**;;
Don't share circuits with streams using a different protocol. Don't share circuits with streams using a different protocol.
@ -1331,6 +1334,14 @@ The following options are useful only for clients (that is, if
the node "foo". Disabled by default since attacking websites and exit the node "foo". Disabled by default since attacking websites and exit
relays can use it to manipulate your path selection. (Default: 0) relays can use it to manipulate your path selection. (Default: 0)
[[HTTPTunnelPort]] **HTTPTunnelPort** \['address':]__port__|**auto** [_isolation flags_]::
Open this port to listen for proxy connections using the "HTTP CONNECT"
protocol instead of SOCKS. Set this to 0
0 if you don't want to allow "HTTP CONNECT" connections. Set the port
to "auto" to have Tor pick a port for you. This directive can be
specified multiple times to bind to multiple addresses/ports. See
SOCKSPort for an explanation of isolation flags. (Default: 0)
[[TransPort]] **TransPort** \['address':]__port__|**auto** [_isolation flags_]:: [[TransPort]] **TransPort** \['address':]__port__|**auto** [_isolation flags_]::
Open this port to listen for transparent proxy connections. Set this to Open this port to listen for transparent proxy connections. Set this to
0 if you don't want to allow transparent proxy connections. Set the port 0 if you don't want to allow transparent proxy connections. Set the port

View File

@ -8,6 +8,7 @@ FUZZERS = """
extrainfo extrainfo
hsdescv2 hsdescv2
http http
http-connect
iptsv2 iptsv2
microdesc microdesc
vrs vrs

View File

@ -372,6 +372,7 @@ static config_var_t option_vars_[] = {
V(HTTPProxyAuthenticator, STRING, NULL), V(HTTPProxyAuthenticator, STRING, NULL),
V(HTTPSProxy, STRING, NULL), V(HTTPSProxy, STRING, NULL),
V(HTTPSProxyAuthenticator, STRING, NULL), V(HTTPSProxyAuthenticator, STRING, NULL),
VPORT(HTTPTunnelPort),
V(IPv6Exit, BOOL, "0"), V(IPv6Exit, BOOL, "0"),
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
V(ServerTransportListenAddr, LINELIST, NULL), V(ServerTransportListenAddr, LINELIST, NULL),
@ -2915,7 +2916,8 @@ options_validate_single_onion(or_options_t *options, char **msg)
const int client_port_set = (options->SocksPort_set || const int client_port_set = (options->SocksPort_set ||
options->TransPort_set || options->TransPort_set ||
options->NATDPort_set || options->NATDPort_set ||
options->DNSPort_set); options->DNSPort_set ||
options->HTTPTunnelPort_set);
if (rend_service_non_anonymous_mode_enabled(options) && client_port_set && if (rend_service_non_anonymous_mode_enabled(options) && client_port_set &&
!options->Tor2webMode) { !options->Tor2webMode) {
REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as " REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as "
@ -7000,6 +7002,15 @@ parse_ports(or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid NatdPort configuration"); *msg = tor_strdup("Invalid NatdPort configuration");
goto err; goto err;
} }
if (parse_port_config(ports,
options->HTTPTunnelPort_lines,
"HTTP Tunnel", CONN_TYPE_AP_HTTP_CONNECT_LISTENER,
"127.0.0.1", 0,
((validate_only ? 0 : CL_PORT_WARN_NONLOCAL)
| CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) {
*msg = tor_strdup("Invalid HTTPTunnelPort configuration");
goto err;
}
{ {
unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS | unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS |
CL_PORT_WARN_NONLOCAL; CL_PORT_WARN_NONLOCAL;
@ -7077,6 +7088,8 @@ parse_ports(or_options_t *options, int validate_only,
!! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1); !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1);
options->NATDPort_set = options->NATDPort_set =
!! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1); !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1);
options->HTTPTunnelPort_set =
!! count_real_listeners(ports, CONN_TYPE_AP_HTTP_CONNECT_LISTENER, 1);
/* Use options->ControlSocket to test if a control socket is set */ /* Use options->ControlSocket to test if a control socket is set */
options->ControlPort_set = options->ControlPort_set =
!! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0); !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0);

View File

@ -162,7 +162,8 @@ static smartlist_t *outgoing_addrs = NULL;
case CONN_TYPE_CONTROL_LISTENER: \ case CONN_TYPE_CONTROL_LISTENER: \
case CONN_TYPE_AP_TRANS_LISTENER: \ case CONN_TYPE_AP_TRANS_LISTENER: \
case CONN_TYPE_AP_NATD_LISTENER: \ case CONN_TYPE_AP_NATD_LISTENER: \
case CONN_TYPE_AP_DNS_LISTENER case CONN_TYPE_AP_DNS_LISTENER: \
case CONN_TYPE_AP_HTTP_CONNECT_LISTENER
/**************************************************************/ /**************************************************************/
@ -189,6 +190,7 @@ conn_type_to_string(int type)
case CONN_TYPE_CONTROL: return "Control"; case CONN_TYPE_CONTROL: return "Control";
case CONN_TYPE_EXT_OR: return "Extended OR"; case CONN_TYPE_EXT_OR: return "Extended OR";
case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener"; case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener";
case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: return "HTTP tunnel listener";
default: default:
log_warn(LD_BUG, "unknown connection type %d", type); log_warn(LD_BUG, "unknown connection type %d", type);
tor_snprintf(buf, sizeof(buf), "unknown [%d]", type); tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
@ -1706,6 +1708,8 @@ connection_init_accepted_conn(connection_t *conn,
TO_ENTRY_CONN(conn)->is_transparent_ap = 1; TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
conn->state = AP_CONN_STATE_NATD_WAIT; conn->state = AP_CONN_STATE_NATD_WAIT;
break; break;
case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
conn->state = AP_CONN_STATE_HTTP_CONNECT_WAIT;
} }
break; break;
case CONN_TYPE_DIR: case CONN_TYPE_DIR:
@ -3398,6 +3402,7 @@ connection_handle_read_impl(connection_t *conn)
case CONN_TYPE_AP_LISTENER: case CONN_TYPE_AP_LISTENER:
case CONN_TYPE_AP_TRANS_LISTENER: case CONN_TYPE_AP_TRANS_LISTENER:
case CONN_TYPE_AP_NATD_LISTENER: case CONN_TYPE_AP_NATD_LISTENER:
case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_AP); return connection_handle_listener_read(conn, CONN_TYPE_AP);
case CONN_TYPE_DIR_LISTENER: case CONN_TYPE_DIR_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_DIR); return connection_handle_listener_read(conn, CONN_TYPE_DIR);
@ -4313,6 +4318,7 @@ connection_is_listener(connection_t *conn)
conn->type == CONN_TYPE_AP_TRANS_LISTENER || conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
conn->type == CONN_TYPE_AP_DNS_LISTENER || conn->type == CONN_TYPE_AP_DNS_LISTENER ||
conn->type == CONN_TYPE_AP_NATD_LISTENER || conn->type == CONN_TYPE_AP_NATD_LISTENER ||
conn->type == CONN_TYPE_AP_HTTP_CONNECT_LISTENER ||
conn->type == CONN_TYPE_DIR_LISTENER || conn->type == CONN_TYPE_DIR_LISTENER ||
conn->type == CONN_TYPE_CONTROL_LISTENER) conn->type == CONN_TYPE_CONTROL_LISTENER)
return 1; return 1;

View File

@ -242,6 +242,11 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
return -1; return -1;
} }
return 0; return 0;
case AP_CONN_STATE_HTTP_CONNECT_WAIT:
if (connection_ap_process_http_connect(EDGE_TO_ENTRY_CONN(conn)) < 0) {
return -1;
}
return 0;
case AP_CONN_STATE_OPEN: case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN: case EXIT_CONN_STATE_OPEN:
if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) { if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
@ -491,6 +496,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
case AP_CONN_STATE_CONNECT_WAIT: case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT: case AP_CONN_STATE_CONTROLLER_WAIT:
case AP_CONN_STATE_RESOLVE_WAIT: case AP_CONN_STATE_RESOLVE_WAIT:
case AP_CONN_STATE_HTTP_CONNECT_WAIT:
return 0; return 0;
default: default:
log_warn(LD_BUG, "Called in unexpected state %d.",conn->base_.state); log_warn(LD_BUG, "Called in unexpected state %d.",conn->base_.state);
@ -1182,10 +1188,10 @@ consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
* See connection_ap_handshake_rewrite_and_attach()'s * See connection_ap_handshake_rewrite_and_attach()'s
* documentation for arguments and return value. * documentation for arguments and return value.
*/ */
int MOCK_IMPL(int,
connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn, connection_ap_rewrite_and_attach_if_allowed,(entry_connection_t *conn,
origin_circuit_t *circ, origin_circuit_t *circ,
crypt_path_t *cpath) crypt_path_t *cpath))
{ {
const or_options_t *options = get_options(); const or_options_t *options = get_options();
@ -2422,6 +2428,108 @@ connection_ap_process_natd(entry_connection_t *conn)
return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL); return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
} }
/** Called on an HTTP CONNECT entry connection when some bytes have arrived,
* but we have not yet received a full HTTP CONNECT request. Try to parse an
* HTTP CONNECT request from the connection's inbuf. On success, set up the
* connection's socks_request field and try to attach the connection. On
* failure, send an HTTP reply, and mark the connection.
*/
STATIC int
connection_ap_process_http_connect(entry_connection_t *conn)
{
if (BUG(ENTRY_TO_CONN(conn)->state != AP_CONN_STATE_HTTP_CONNECT_WAIT))
return -1;
char *headers = NULL, *body = NULL;
char *command = NULL, *addrport = NULL;
char *addr = NULL;
size_t bodylen = 0;
const char *errmsg = NULL;
int rv = 0;
const int http_status =
fetch_from_buf_http(ENTRY_TO_CONN(conn)->inbuf, &headers, 8192,
&body, &bodylen, 1024, 0);
if (http_status < 0) {
/* Bad http status */
errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
goto err;
} else if (http_status == 0) {
/* no HTTP request yet. */
goto done;
}
const int cmd_status = parse_http_command(headers, &command, &addrport);
if (cmd_status < 0) {
errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
goto err;
}
tor_assert(command);
tor_assert(addrport);
if (strcasecmp(command, "connect")) {
errmsg = "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
goto err;
}
tor_assert(conn->socks_request);
socks_request_t *socks = conn->socks_request;
uint16_t port;
if (tor_addr_port_split(LOG_WARN, addrport, &addr, &port) < 0) {
errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
goto err;
}
if (strlen(addr) >= MAX_SOCKS_ADDR_LEN) {
errmsg = "HTTP/1.0 414 Request-URI Too Long\r\n\r\n";
goto err;
}
/* Abuse the 'username' and 'password' fields here. They are already an
* abuse. */
{
char *authorization = http_get_header(headers, "Proxy-Authorization: ");
if (authorization) {
socks->username = authorization; // steal reference
socks->usernamelen = strlen(authorization);
}
char *isolation = http_get_header(headers, "X-Tor-Stream-Isolation: ");
if (isolation) {
socks->password = isolation; // steal reference
socks->passwordlen = strlen(isolation);
}
}
socks->command = SOCKS_COMMAND_CONNECT;
socks->listener_type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER;
strlcpy(socks->address, addr, sizeof(socks->address));
socks->port = port;
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
rv = connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
// XXXX send a "100 Continue" message?
goto done;
err:
if (BUG(errmsg == NULL))
errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
log_warn(LD_EDGE, "Saying %s", escaped(errmsg));
connection_write_to_buf(errmsg, strlen(errmsg), ENTRY_TO_CONN(conn));
connection_mark_unattached_ap(conn,
END_STREAM_REASON_HTTPPROTOCOL|
END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
done:
tor_free(headers);
tor_free(body);
tor_free(command);
tor_free(addrport);
tor_free(addr);
return rv;
}
/** Iterate over the two bytes of stream_id until we get one that is not /** Iterate over the two bytes of stream_id until we get one that is not
* already in use; return it. Return 0 if can't get a unique stream_id. * already in use; return it. Return 0 if can't get a unique stream_id.
*/ */
@ -3045,7 +3153,14 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
conn->socks_request->has_finished = 1; conn->socks_request->has_finished = 1;
return; return;
} }
if (conn->socks_request->socks_version == 4) { if (conn->socks_request->listener_type ==
CONN_TYPE_AP_HTTP_CONNECT_LISTENER) {
const char *response = end_reason_to_http_connect_response_line(endreason);
if (!response) {
response = "HTTP/1.0 400 Bad Request\r\n\r\n";
}
connection_write_to_buf(response, strlen(response), ENTRY_TO_CONN(conn));
} else if (conn->socks_request->socks_version == 4) {
memset(buf,0,SOCKS4_NETWORK_LEN); memset(buf,0,SOCKS4_NETWORK_LEN);
buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT); buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT);
/* leave version, destport, destip zero */ /* leave version, destport, destip zero */

View File

@ -89,9 +89,10 @@ int connection_ap_process_transparent(entry_connection_t *conn);
int address_is_invalid_destination(const char *address, int client); int address_is_invalid_destination(const char *address, int client);
int connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn, MOCK_DECL(int, connection_ap_rewrite_and_attach_if_allowed,
origin_circuit_t *circ, (entry_connection_t *conn,
crypt_path_t *cpath); origin_circuit_t *circ,
crypt_path_t *cpath));
int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
origin_circuit_t *circ, origin_circuit_t *circ,
crypt_path_t *cpath); crypt_path_t *cpath);
@ -188,6 +189,8 @@ typedef struct {
STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn, STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn,
rewrite_result_t *out); rewrite_result_t *out);
STATIC int connection_ap_process_http_connect(entry_connection_t *conn);
#endif #endif
#endif #endif

View File

@ -104,7 +104,6 @@ static void directory_send_command(dir_connection_t *conn,
int direct, int direct,
const directory_request_t *request); const directory_request_t *request);
static int body_is_plausible(const char *body, size_t body_len, int purpose); static int body_is_plausible(const char *body, size_t body_len, int purpose);
static char *http_get_header(const char *headers, const char *which);
static void http_set_address_origin(const char *headers, connection_t *conn); static void http_set_address_origin(const char *headers, connection_t *conn);
static void connection_dir_download_routerdesc_failed(dir_connection_t *conn); static void connection_dir_download_routerdesc_failed(dir_connection_t *conn);
static void connection_dir_bridge_routerdesc_failed(dir_connection_t *conn); static void connection_dir_bridge_routerdesc_failed(dir_connection_t *conn);
@ -1966,15 +1965,39 @@ directory_send_command(dir_connection_t *conn,
STATIC int STATIC int
parse_http_url(const char *headers, char **url) parse_http_url(const char *headers, char **url)
{ {
char *command = NULL;
if (parse_http_command(headers, &command, url) < 0) {
return -1;
}
if (!strcmpstart(*url, "/tor/")) {
char *new_url = NULL;
tor_asprintf(&new_url, "/tor/%s", *url);
tor_free(*url);
*url = new_url;
}
tor_free(command);
return 0;
}
/** Parse an HTTP request line at the start of a headers string. On failure,
* return -1. On success, set *<b>command_out</b> to a copy of the HTTP
* command ("get", "post", etc), set *<b>url_out</b> to a copy of the URL, and
* return 0. */
int
parse_http_command(const char *headers, char **command_out, char **url_out)
{
const char *command, *end_of_command;
char *s, *start, *tmp; char *s, *start, *tmp;
s = (char *)eat_whitespace_no_nl(headers); s = (char *)eat_whitespace_no_nl(headers);
if (!*s) return -1; if (!*s) return -1;
command = s;
s = (char *)find_whitespace(s); /* get past GET/POST */ s = (char *)find_whitespace(s); /* get past GET/POST */
if (!*s) return -1; if (!*s) return -1;
end_of_command = s;
s = (char *)eat_whitespace_no_nl(s); s = (char *)eat_whitespace_no_nl(s);
if (!*s) return -1; if (!*s) return -1;
start = s; /* this is it, assuming it's valid */ start = s; /* this is the URL, assuming it's valid */
s = (char *)find_whitespace(start); s = (char *)find_whitespace(start);
if (!*s) return -1; if (!*s) return -1;
@ -2005,13 +2028,8 @@ parse_http_url(const char *headers, char **url)
return -1; return -1;
} }
if (s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */ *url_out = tor_memdup_nulterm(start, s-start);
*url = tor_malloc(s - start + 5); *command_out = tor_memdup_nulterm(command, end_of_command - command);
strlcpy(*url,"/tor", s-start+5);
strlcat((*url)+4, start, s-start+1);
} else {
*url = tor_strndup(start, s-start);
}
return 0; return 0;
} }
@ -2019,7 +2037,7 @@ parse_http_url(const char *headers, char **url)
* <b>which</b>. The key should be given with a terminating colon and space; * <b>which</b>. The key should be given with a terminating colon and space;
* this function copies everything after, up to but not including the * this function copies everything after, up to but not including the
* following \\r\\n. */ * following \\r\\n. */
static char * char *
http_get_header(const char *headers, const char *which) http_get_header(const char *headers, const char *which)
{ {
const char *cp = headers; const char *cp = headers;

View File

@ -87,6 +87,9 @@ MOCK_DECL(void, directory_initiate_request, (directory_request_t *request));
int parse_http_response(const char *headers, int *code, time_t *date, int parse_http_response(const char *headers, int *code, time_t *date,
compress_method_t *compression, char **response); compress_method_t *compression, char **response);
int parse_http_command(const char *headers,
char **command_out, char **url_out);
char *http_get_header(const char *headers, const char *which);
int connection_dir_is_encrypted(const dir_connection_t *conn); int connection_dir_is_encrypted(const dir_connection_t *conn);
int connection_dir_reached_eof(dir_connection_t *conn); int connection_dir_reached_eof(dir_connection_t *conn);

View File

@ -1683,7 +1683,8 @@ any_client_port_set(const or_options_t *options)
options->TransPort_set || options->TransPort_set ||
options->NATDPort_set || options->NATDPort_set ||
options->ControlPort_set || options->ControlPort_set ||
options->DNSPort_set); options->DNSPort_set ||
options->HTTPTunnelPort_set);
} }
/** /**

View File

@ -226,8 +226,10 @@ typedef enum {
#define CONN_TYPE_EXT_OR 16 #define CONN_TYPE_EXT_OR 16
/** Type for sockets listening for Extended ORPort connections. */ /** Type for sockets listening for Extended ORPort connections. */
#define CONN_TYPE_EXT_OR_LISTENER 17 #define CONN_TYPE_EXT_OR_LISTENER 17
/** Type for sockets listening for HTTP CONNECT tunnel connections. */
#define CONN_TYPE_AP_HTTP_CONNECT_LISTENER 18
#define CONN_TYPE_MAX_ 17 #define CONN_TYPE_MAX_ 19
/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in /* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in
* connection_t. */ * connection_t. */
@ -348,7 +350,9 @@ typedef enum {
/** State for a transparent natd connection: waiting for original /** State for a transparent natd connection: waiting for original
* destination. */ * destination. */
#define AP_CONN_STATE_NATD_WAIT 12 #define AP_CONN_STATE_NATD_WAIT 12
#define AP_CONN_STATE_MAX_ 12 /** State for an HTTP tunnel: waiting for an HTTP CONNECT command. */
#define AP_CONN_STATE_HTTP_CONNECT_WAIT 13
#define AP_CONN_STATE_MAX_ 13
/** True iff the AP_CONN_STATE_* value <b>s</b> means that the corresponding /** True iff the AP_CONN_STATE_* value <b>s</b> means that the corresponding
* edge connection is not attached to any circuit. */ * edge connection is not attached to any circuit. */
@ -648,6 +652,10 @@ typedef enum {
/** The target address is in a private network (like 127.0.0.1 or 10.0.0.1); /** The target address is in a private network (like 127.0.0.1 or 10.0.0.1);
* you don't want to do that over a randomly chosen exit */ * you don't want to do that over a randomly chosen exit */
#define END_STREAM_REASON_PRIVATE_ADDR 262 #define END_STREAM_REASON_PRIVATE_ADDR 262
/** This is an HTTP tunnel connection and the client used or misused HTTP in a
* way we can't handle.
*/
#define END_STREAM_REASON_HTTPPROTOCOL 263
/** Bitwise-and this value with endreason to mask out all flags. */ /** Bitwise-and this value with endreason to mask out all flags. */
#define END_STREAM_REASON_MASK 511 #define END_STREAM_REASON_MASK 511
@ -3696,6 +3704,8 @@ typedef struct {
} TransProxyType_parsed; } TransProxyType_parsed;
config_line_t *NATDPort_lines; /**< Ports to listen on for transparent natd config_line_t *NATDPort_lines; /**< Ports to listen on for transparent natd
* connections. */ * connections. */
/** Ports to listen on for HTTP Tunnel connections. */
config_line_t *HTTPTunnelPort_lines;
config_line_t *ControlPort_lines; /**< Ports to listen on for control config_line_t *ControlPort_lines; /**< Ports to listen on for control
* connections. */ * connections. */
config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
@ -3722,7 +3732,8 @@ typedef struct {
* configured in one of the _lines options above. * configured in one of the _lines options above.
* For client ports, also true if there is a unix socket configured. * For client ports, also true if there is a unix socket configured.
* If you are checking for client ports, you may want to use: * If you are checking for client ports, you may want to use:
* SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set * SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set ||
* HTTPTunnelPort_set
* rather than SocksPort_set. * rather than SocksPort_set.
* *
* @{ * @{
@ -3735,6 +3746,7 @@ typedef struct {
unsigned int DirPort_set : 1; unsigned int DirPort_set : 1;
unsigned int DNSPort_set : 1; unsigned int DNSPort_set : 1;
unsigned int ExtORPort_set : 1; unsigned int ExtORPort_set : 1;
unsigned int HTTPTunnelPort_set : 1;
/**@}*/ /**@}*/
int AssumeReachable; /**< Whether to publish our descriptor regardless. */ int AssumeReachable; /**< Whether to publish our descriptor regardless. */

View File

@ -45,6 +45,8 @@ stream_end_reason_to_control_string(int reason)
case END_STREAM_REASON_CANT_ATTACH: return "CANT_ATTACH"; case END_STREAM_REASON_CANT_ATTACH: return "CANT_ATTACH";
case END_STREAM_REASON_NET_UNREACHABLE: return "NET_UNREACHABLE"; case END_STREAM_REASON_NET_UNREACHABLE: return "NET_UNREACHABLE";
case END_STREAM_REASON_SOCKSPROTOCOL: return "SOCKS_PROTOCOL"; case END_STREAM_REASON_SOCKSPROTOCOL: return "SOCKS_PROTOCOL";
// XXXX Controlspec
case END_STREAM_REASON_HTTPPROTOCOL: return "HTTP_PROTOCOL";
case END_STREAM_REASON_PRIVATE_ADDR: return "PRIVATE_ADDR"; case END_STREAM_REASON_PRIVATE_ADDR: return "PRIVATE_ADDR";
@ -138,6 +140,11 @@ stream_end_reason_to_socks5_response(int reason)
return SOCKS5_NET_UNREACHABLE; return SOCKS5_NET_UNREACHABLE;
case END_STREAM_REASON_SOCKSPROTOCOL: case END_STREAM_REASON_SOCKSPROTOCOL:
return SOCKS5_GENERAL_ERROR; return SOCKS5_GENERAL_ERROR;
case END_STREAM_REASON_HTTPPROTOCOL:
// LCOV_EXCL_START
tor_assert_nonfatal_unreached();
return SOCKS5_GENERAL_ERROR;
// LCOV_EXCL_STOP
case END_STREAM_REASON_PRIVATE_ADDR: case END_STREAM_REASON_PRIVATE_ADDR:
return SOCKS5_GENERAL_ERROR; return SOCKS5_GENERAL_ERROR;
@ -442,3 +449,48 @@ bandwidth_weight_rule_to_string(bandwidth_weight_rule_t rule)
} }
} }
/** Given a RELAY_END reason value, convert it to an HTTP response to be
* send over an HTTP tunnel connection. */
const char *
end_reason_to_http_connect_response_line(int endreason)
{
endreason &= END_STREAM_REASON_MASK;
/* XXXX these are probably all wrong. Should they all be 502? */
switch (endreason) {
case 0:
return "HTTP/1.0 200 OK\r\n\r\n";
case END_STREAM_REASON_MISC:
return "HTTP/1.0 500 Internal Server Error\r\n\r\n";
case END_STREAM_REASON_RESOLVEFAILED:
return "HTTP/1.0 404 Not Found (resolve failed)\r\n\r\n";
case END_STREAM_REASON_NOROUTE:
return "HTTP/1.0 404 Not Found (no route)\r\n\r\n";
case END_STREAM_REASON_CONNECTREFUSED:
return "HTTP/1.0 403 Forbidden (connection refused)\r\n\r\n";
case END_STREAM_REASON_EXITPOLICY:
return "HTTP/1.0 403 Forbidden (exit policy)\r\n\r\n";
case END_STREAM_REASON_DESTROY:
return "HTTP/1.0 502 Bad Gateway (destroy cell received)\r\n\r\n";
case END_STREAM_REASON_DONE:
return "HTTP/1.0 502 Bad Gateway (unexpected close)\r\n\r\n";
case END_STREAM_REASON_TIMEOUT:
return "HTTP/1.0 504 Gateway Timeout\r\n\r\n";
case END_STREAM_REASON_HIBERNATING:
return "HTTP/1.0 502 Bad Gateway (hibernating server)\r\n\r\n";
case END_STREAM_REASON_INTERNAL:
return "HTTP/1.0 502 Bad Gateway (internal error)\r\n\r\n";
case END_STREAM_REASON_RESOURCELIMIT:
return "HTTP/1.0 502 Bad Gateway (resource limit)\r\n\r\n";
case END_STREAM_REASON_CONNRESET:
return "HTTP/1.0 403 Forbidden (connection reset)\r\n\r\n";
case END_STREAM_REASON_TORPROTOCOL:
return "HTTP/1.0 502 Bad Gateway (tor protocol violation)\r\n\r\n";
case END_STREAM_REASON_ENTRYPOLICY:
return "HTTP/1.0 403 Forbidden (entry policy violation)\r\n\r\n";
case END_STREAM_REASON_NOTDIRECTORY: /* Fall Through */
default:
tor_assert_nonfatal_unreached();
return "HTTP/1.0 500 Internal Server Error (weird end reason)\r\n\r\n";
}
}

View File

@ -26,6 +26,7 @@ const char *socks4_response_code_to_string(uint8_t code);
const char *socks5_response_code_to_string(uint8_t code); const char *socks5_response_code_to_string(uint8_t code);
const char *bandwidth_weight_rule_to_string(enum bandwidth_weight_rule_t rule); const char *bandwidth_weight_rule_to_string(enum bandwidth_weight_rule_t rule);
const char *end_reason_to_http_connect_response_line(int endreason);
#endif #endif

View File

@ -1467,8 +1467,9 @@ connection_edge_process_relay_cell_not_open(
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ)); circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */ /* don't send a socks reply to transparent conns */
tor_assert(entry_conn->socks_request != NULL); tor_assert(entry_conn->socks_request != NULL);
if (!entry_conn->socks_request->has_finished) if (!entry_conn->socks_request->has_finished) {
connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0); connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0);
}
/* Was it a linked dir conn? If so, a dir request just started to /* Was it a linked dir conn? If so, a dir request just started to
* fetch something; this could be a bootstrap status milestone. */ * fetch something; this could be a bootstrap status milestone. */

View File

@ -0,0 +1,105 @@
/* Copyright (c) 2016-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "orconfig.h"
#define BUFFERS_PRIVATE
#define CONNECTION_EDGE_PRIVATE
#include "or.h"
#include "backtrace.h"
#include "buffers.h"
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
#include "torlog.h"
#include "fuzzing.h"
static void
mock_connection_write_to_buf_impl_(const char *string, size_t len,
connection_t *conn, int compressed)
{
log_debug(LD_GENERAL, "%sResponse:\n%u\nConnection: %p\n%s\n",
compressed ? "Compressed " : "", (unsigned)len, conn, string);
}
static void
mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
int line, const char *file)
{
(void)conn;
(void)endreason;
(void)line;
(void)file;
}
static int
mock_connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath)
{
(void)conn;
(void)circ;
(void)cpath;
return 0;
}
int
fuzz_init(void)
{
/* Set up fake response handler */
MOCK(connection_write_to_buf_impl_, mock_connection_write_to_buf_impl_);
/* Set up the fake handler functions */
MOCK(connection_mark_unattached_ap_, mock_connection_mark_unattached_ap_);
MOCK(connection_ap_rewrite_and_attach_if_allowed,
mock_connection_ap_rewrite_and_attach_if_allowed);
return 0;
}
int
fuzz_cleanup(void)
{
UNMOCK(connection_write_to_buf_impl_);
UNMOCK(connection_mark_unattached_ap_);
UNMOCK(connection_ap_rewrite_and_attach_if_allowed);
return 0;
}
int
fuzz_main(const uint8_t *stdin_buf, size_t data_size)
{
entry_connection_t conn;
/* Set up the fake connection */
memset(&conn, 0, sizeof(conn));
conn.edge_.base_.type = CONN_TYPE_AP;
conn.edge_.base_.state = AP_CONN_STATE_HTTP_CONNECT_WAIT;
conn.socks_request = tor_malloc_zero(sizeof(socks_request_t));
conn.socks_request->listener_type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER;
conn.edge_.base_.inbuf = buf_new_with_data((char*)stdin_buf, data_size);
if (!conn.edge_.base_.inbuf) {
log_debug(LD_GENERAL, "Zero-Length-Input\n");
goto done;
}
/* Parse the headers */
int rv = connection_ap_process_http_connect(&conn);
/* TODO: check the output is correctly parsed based on the input */
log_debug(LD_GENERAL, "Result:\n%d\n", rv);
goto done;
done:
/* Reset. */
socks_request_free(conn.socks_request);
buf_free(conn.edge_.base_.inbuf);
conn.edge_.base_.inbuf = NULL;
return 0;
}

View File

@ -103,6 +103,14 @@ src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS)
src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG) src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG)
src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS) src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS)
src_test_fuzz_fuzz_http_connect_SOURCES = \
src/test/fuzz/fuzzing_common.c \
src/test/fuzz/fuzz_http_connect.c
src_test_fuzz_fuzz_http_connect_CPPFLAGS = $(FUZZING_CPPFLAGS)
src_test_fuzz_fuzz_http_connect_CFLAGS = $(FUZZING_CFLAGS)
src_test_fuzz_fuzz_http_connect_LDFLAGS = $(FUZZING_LDFLAG)
src_test_fuzz_fuzz_http_connect_LDADD = $(FUZZING_LIBS)
src_test_fuzz_fuzz_iptsv2_SOURCES = \ src_test_fuzz_fuzz_iptsv2_SOURCES = \
src/test/fuzz/fuzzing_common.c \ src/test/fuzz/fuzzing_common.c \
src/test/fuzz/fuzz_iptsv2.c src/test/fuzz/fuzz_iptsv2.c
@ -135,6 +143,7 @@ FUZZERS = \
src/test/fuzz/fuzz-extrainfo \ src/test/fuzz/fuzz-extrainfo \
src/test/fuzz/fuzz-hsdescv2 \ src/test/fuzz/fuzz-hsdescv2 \
src/test/fuzz/fuzz-http \ src/test/fuzz/fuzz-http \
src/test/fuzz/fuzz-http-connect \
src/test/fuzz/fuzz-iptsv2 \ src/test/fuzz/fuzz-iptsv2 \
src/test/fuzz/fuzz-microdesc \ src/test/fuzz/fuzz-microdesc \
src/test/fuzz/fuzz-vrs src/test/fuzz/fuzz-vrs
@ -191,6 +200,13 @@ src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS)
src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG) src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG)
src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS) src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS)
src_test_fuzz_lf_fuzz_http_connect_SOURCES = \
$(src_test_fuzz_fuzz_http_connect_SOURCES)
src_test_fuzz_lf_fuzz_http_connect_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
src_test_fuzz_lf_fuzz_http_connect_CFLAGS = $(LIBFUZZER_CFLAGS)
src_test_fuzz_lf_fuzz_http_connect_LDFLAGS = $(LIBFUZZER_LDFLAG)
src_test_fuzz_lf_fuzz_http_connect_LDADD = $(LIBFUZZER_LIBS)
src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \ src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \
$(src_test_fuzz_fuzz_iptsv2_SOURCES) $(src_test_fuzz_fuzz_iptsv2_SOURCES)
src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
@ -220,6 +236,7 @@ LIBFUZZER_FUZZERS = \
src/test/fuzz/lf-fuzz-extrainfo \ src/test/fuzz/lf-fuzz-extrainfo \
src/test/fuzz/lf-fuzz-hsdescv2 \ src/test/fuzz/lf-fuzz-hsdescv2 \
src/test/fuzz/lf-fuzz-http \ src/test/fuzz/lf-fuzz-http \
src/test/fuzz/lf-fuzz-http-connect \
src/test/fuzz/lf-fuzz-iptsv2 \ src/test/fuzz/lf-fuzz-iptsv2 \
src/test/fuzz/lf-fuzz-microdesc \ src/test/fuzz/lf-fuzz-microdesc \
src/test/fuzz/lf-fuzz-vrs src/test/fuzz/lf-fuzz-vrs
@ -266,6 +283,11 @@ src_test_fuzz_liboss_fuzz_http_a_SOURCES = \
src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
src_test_fuzz_liboss_fuzz_http_connect_a_SOURCES = \
$(src_test_fuzz_fuzz_http_connect_SOURCES)
src_test_fuzz_liboss_fuzz_http_connect_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
src_test_fuzz_liboss_fuzz_http_connect_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \ src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \
$(src_test_fuzz_fuzz_iptsv2_SOURCES) $(src_test_fuzz_fuzz_iptsv2_SOURCES)
src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
@ -289,6 +311,7 @@ OSS_FUZZ_FUZZERS = \
src/test/fuzz/liboss-fuzz-extrainfo.a \ src/test/fuzz/liboss-fuzz-extrainfo.a \
src/test/fuzz/liboss-fuzz-hsdescv2.a \ src/test/fuzz/liboss-fuzz-hsdescv2.a \
src/test/fuzz/liboss-fuzz-http.a \ src/test/fuzz/liboss-fuzz-http.a \
src/test/fuzz/liboss-fuzz-http-connect.a \
src/test/fuzz/liboss-fuzz-iptsv2.a \ src/test/fuzz/liboss-fuzz-iptsv2.a \
src/test/fuzz/liboss-fuzz-microdesc.a \ src/test/fuzz/liboss-fuzz-microdesc.a \
src/test/fuzz/liboss-fuzz-vrs.a src/test/fuzz/liboss-fuzz-vrs.a