mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
Add support for HTTP Connect tunnels
This commit is contained in:
parent
eda79c2f78
commit
4b30ae1581
@ -372,6 +372,7 @@ static config_var_t option_vars_[] = {
|
||||
V(HTTPProxyAuthenticator, STRING, NULL),
|
||||
V(HTTPSProxy, STRING, NULL),
|
||||
V(HTTPSProxyAuthenticator, STRING, NULL),
|
||||
VPORT(HTTPTunnelPort),
|
||||
V(IPv6Exit, BOOL, "0"),
|
||||
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
|
||||
V(ServerTransportListenAddr, LINELIST, NULL),
|
||||
@ -2910,7 +2911,8 @@ options_validate_single_onion(or_options_t *options, char **msg)
|
||||
const int client_port_set = (options->SocksPort_set ||
|
||||
options->TransPort_set ||
|
||||
options->NATDPort_set ||
|
||||
options->DNSPort_set);
|
||||
options->DNSPort_set ||
|
||||
options->HTTPTunnelPort_set);
|
||||
if (rend_service_non_anonymous_mode_enabled(options) && client_port_set &&
|
||||
!options->Tor2webMode) {
|
||||
REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as "
|
||||
@ -6976,6 +6978,15 @@ parse_ports(or_options_t *options, int validate_only,
|
||||
*msg = tor_strdup("Invalid NatdPort configuration");
|
||||
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 |
|
||||
CL_PORT_WARN_NONLOCAL;
|
||||
@ -7053,6 +7064,8 @@ parse_ports(or_options_t *options, int validate_only,
|
||||
!! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1);
|
||||
options->NATDPort_set =
|
||||
!! 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 */
|
||||
options->ControlPort_set =
|
||||
!! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0);
|
||||
|
@ -158,7 +158,8 @@ static smartlist_t *outgoing_addrs = NULL;
|
||||
case CONN_TYPE_CONTROL_LISTENER: \
|
||||
case CONN_TYPE_AP_TRANS_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
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@ -185,6 +186,7 @@ conn_type_to_string(int type)
|
||||
case CONN_TYPE_CONTROL: return "Control";
|
||||
case CONN_TYPE_EXT_OR: return "Extended OR";
|
||||
case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener";
|
||||
case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: return "HTTP tunnel listener";
|
||||
default:
|
||||
log_warn(LD_BUG, "unknown connection type %d", type);
|
||||
tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
|
||||
@ -1702,6 +1704,8 @@ connection_init_accepted_conn(connection_t *conn,
|
||||
TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
|
||||
conn->state = AP_CONN_STATE_NATD_WAIT;
|
||||
break;
|
||||
case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
|
||||
conn->state = AP_CONN_STATE_HTTP_CONNECT_WAIT;
|
||||
}
|
||||
break;
|
||||
case CONN_TYPE_DIR:
|
||||
@ -3394,6 +3398,7 @@ connection_handle_read_impl(connection_t *conn)
|
||||
case CONN_TYPE_AP_LISTENER:
|
||||
case CONN_TYPE_AP_TRANS_LISTENER:
|
||||
case CONN_TYPE_AP_NATD_LISTENER:
|
||||
case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
|
||||
return connection_handle_listener_read(conn, CONN_TYPE_AP);
|
||||
case CONN_TYPE_DIR_LISTENER:
|
||||
return connection_handle_listener_read(conn, CONN_TYPE_DIR);
|
||||
@ -4286,6 +4291,7 @@ connection_is_listener(connection_t *conn)
|
||||
conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
|
||||
conn->type == CONN_TYPE_AP_DNS_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_CONTROL_LISTENER)
|
||||
return 1;
|
||||
|
@ -127,6 +127,7 @@
|
||||
|
||||
static int connection_ap_handshake_process_socks(entry_connection_t *conn);
|
||||
static int connection_ap_process_natd(entry_connection_t *conn);
|
||||
static int connection_ap_process_http_connect(entry_connection_t *conn);
|
||||
static int connection_exit_connect_dir(edge_connection_t *exitconn);
|
||||
static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port);
|
||||
static int connection_ap_supports_optimistic_data(const entry_connection_t *);
|
||||
@ -237,6 +238,11 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
|
||||
return -1;
|
||||
}
|
||||
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 EXIT_CONN_STATE_OPEN:
|
||||
if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
|
||||
@ -486,6 +492,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
|
||||
case AP_CONN_STATE_CONNECT_WAIT:
|
||||
case AP_CONN_STATE_CONTROLLER_WAIT:
|
||||
case AP_CONN_STATE_RESOLVE_WAIT:
|
||||
case AP_CONN_STATE_HTTP_CONNECT_WAIT:
|
||||
return 0;
|
||||
default:
|
||||
log_warn(LD_BUG, "Called in unexpected state %d.",conn->base_.state);
|
||||
@ -2349,6 +2356,95 @@ connection_ap_process_natd(entry_connection_t *conn)
|
||||
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;
|
||||
}
|
||||
|
||||
/* XXXX Look at headers */
|
||||
|
||||
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
|
||||
* already in use; return it. Return 0 if can't get a unique stream_id.
|
||||
*/
|
||||
@ -2972,7 +3068,14 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
|
||||
conn->socks_request->has_finished = 1;
|
||||
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);
|
||||
buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT);
|
||||
/* leave version, destport, destip zero */
|
||||
|
@ -1683,7 +1683,8 @@ any_client_port_set(const or_options_t *options)
|
||||
options->TransPort_set ||
|
||||
options->NATDPort_set ||
|
||||
options->ControlPort_set ||
|
||||
options->DNSPort_set);
|
||||
options->DNSPort_set ||
|
||||
options->HTTPTunnelPort_set);
|
||||
}
|
||||
|
||||
/**
|
||||
|
18
src/or/or.h
18
src/or/or.h
@ -226,8 +226,10 @@ typedef enum {
|
||||
#define CONN_TYPE_EXT_OR 16
|
||||
/** Type for sockets listening for Extended ORPort connections. */
|
||||
#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
|
||||
* connection_t. */
|
||||
|
||||
@ -348,7 +350,9 @@ typedef enum {
|
||||
/** State for a transparent natd connection: waiting for original
|
||||
* destination. */
|
||||
#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
|
||||
* edge connection is not attached to any circuit. */
|
||||
@ -645,6 +649,10 @@ typedef enum {
|
||||
/** 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 */
|
||||
#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. */
|
||||
#define END_STREAM_REASON_MASK 511
|
||||
@ -3693,6 +3701,8 @@ typedef struct {
|
||||
} TransProxyType_parsed;
|
||||
config_line_t *NATDPort_lines; /**< Ports to listen on for transparent natd
|
||||
* 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
|
||||
* connections. */
|
||||
config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
|
||||
@ -3719,7 +3729,8 @@ typedef struct {
|
||||
* configured in one of the _lines options above.
|
||||
* For client ports, also true if there is a unix socket configured.
|
||||
* 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.
|
||||
*
|
||||
* @{
|
||||
@ -3732,6 +3743,7 @@ typedef struct {
|
||||
unsigned int DirPort_set : 1;
|
||||
unsigned int DNSPort_set : 1;
|
||||
unsigned int ExtORPort_set : 1;
|
||||
unsigned int HTTPTunnelPort_set : 1;
|
||||
/**@}*/
|
||||
|
||||
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
|
||||
|
@ -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_NET_UNREACHABLE: return "NET_UNREACHABLE";
|
||||
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";
|
||||
|
||||
@ -138,6 +140,11 @@ stream_end_reason_to_socks5_response(int reason)
|
||||
return SOCKS5_NET_UNREACHABLE;
|
||||
case END_STREAM_REASON_SOCKSPROTOCOL:
|
||||
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:
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 *bandwidth_weight_rule_to_string(enum bandwidth_weight_rule_t rule);
|
||||
const char *end_reason_to_http_connect_response_line(int endreason);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1467,8 +1467,9 @@ connection_edge_process_relay_cell_not_open(
|
||||
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
|
||||
/* don't send a socks reply to transparent conns */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Was it a linked dir conn? If so, a dir request just started to
|
||||
* fetch something; this could be a bootstrap status milestone. */
|
||||
|
Loading…
Reference in New Issue
Block a user