summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2017-09-05 14:34:29 -0400
committerNick Mathewson <nickm@torproject.org>2017-09-05 14:34:29 -0400
commit73b0e2e6fd02140d349ebc6d4ae44c6a28a0f39c (patch)
tree4520ec8590003e12db93503b7b5973938b63f2a1 /src/or
parentc0b9f594b65f410cf219673d53226ed4eeeedc19 (diff)
parentad9190404b1cbba3f7e17f8db20034e986093f21 (diff)
downloadtor-73b0e2e6fd02140d349ebc6d4ae44c6a28a0f39c.tar.gz
tor-73b0e2e6fd02140d349ebc6d4ae44c6a28a0f39c.zip
Merge branch 'http_tunnel_squashed'
Diffstat (limited to 'src/or')
-rw-r--r--src/or/config.c15
-rw-r--r--src/or/connection.c8
-rw-r--r--src/or/connection_edge.c125
-rw-r--r--src/or/connection_edge.h9
-rw-r--r--src/or/directory.c38
-rw-r--r--src/or/directory.h3
-rw-r--r--src/or/networkstatus.c3
-rw-r--r--src/or/or.h18
-rw-r--r--src/or/reasons.c52
-rw-r--r--src/or/reasons.h1
-rw-r--r--src/or/relay.c3
11 files changed, 250 insertions, 25 deletions
diff --git a/src/or/config.c b/src/or/config.c
index ff70a4465e..eb89d6f5ee 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -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),
@@ -2915,7 +2916,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 "
@@ -7000,6 +7002,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;
@@ -7077,6 +7088,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);
diff --git a/src/or/connection.c b/src/or/connection.c
index 86ea999be0..e2392b52e3 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -162,7 +162,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
/**************************************************************/
@@ -189,6 +190,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);
@@ -1706,6 +1708,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:
@@ -3398,6 +3402,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);
@@ -4313,6 +4318,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;
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 9f47f41179..f6c5895396 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -242,6 +242,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) {
@@ -491,6 +496,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);
@@ -1182,10 +1188,10 @@ consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
* See connection_ap_handshake_rewrite_and_attach()'s
* documentation for arguments and return value.
*/
-int
-connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
- origin_circuit_t *circ,
- crypt_path_t *cpath)
+MOCK_IMPL(int,
+connection_ap_rewrite_and_attach_if_allowed,(entry_connection_t *conn,
+ origin_circuit_t *circ,
+ crypt_path_t *cpath))
{
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);
}
+/** 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
* 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;
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 */
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index 914238fc1f..e47043f7fe 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -89,9 +89,10 @@ int connection_ap_process_transparent(entry_connection_t *conn);
int address_is_invalid_destination(const char *address, int client);
-int connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
- origin_circuit_t *circ,
- crypt_path_t *cpath);
+MOCK_DECL(int, connection_ap_rewrite_and_attach_if_allowed,
+ (entry_connection_t *conn,
+ origin_circuit_t *circ,
+ crypt_path_t *cpath));
int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath);
@@ -188,6 +189,8 @@ typedef struct {
STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn,
rewrite_result_t *out);
+
+STATIC int connection_ap_process_http_connect(entry_connection_t *conn);
#endif
#endif
diff --git a/src/or/directory.c b/src/or/directory.c
index b1094634ff..1baf053903 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -104,7 +104,6 @@ static void directory_send_command(dir_connection_t *conn,
int direct,
const directory_request_t *request);
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 connection_dir_download_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
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;
s = (char *)eat_whitespace_no_nl(headers);
if (!*s) return -1;
+ command = s;
s = (char *)find_whitespace(s); /* get past GET/POST */
if (!*s) return -1;
+ end_of_command = s;
s = (char *)eat_whitespace_no_nl(s);
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);
if (!*s) return -1;
@@ -2005,13 +2028,8 @@ parse_http_url(const char *headers, char **url)
return -1;
}
- if (s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */
- *url = tor_malloc(s - start + 5);
- strlcpy(*url,"/tor", s-start+5);
- strlcat((*url)+4, start, s-start+1);
- } else {
- *url = tor_strndup(start, s-start);
- }
+ *url_out = tor_memdup_nulterm(start, s-start);
+ *command_out = tor_memdup_nulterm(command, end_of_command - command);
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;
* this function copies everything after, up to but not including the
* following \\r\\n. */
-static char *
+char *
http_get_header(const char *headers, const char *which)
{
const char *cp = headers;
diff --git a/src/or/directory.h b/src/or/directory.h
index 42bcb55eb2..1200728411 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -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,
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_reached_eof(dir_connection_t *conn);
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 7136ab2968..e25a3d316a 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -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);
}
/**
diff --git a/src/or/or.h b/src/or/or.h
index 11d3bb31cc..b3cd83f245 100644
--- a/src/or/or.h
+++ b/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. */
@@ -648,6 +652,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
@@ -3696,6 +3704,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
@@ -3722,7 +3732,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.
*
* @{
@@ -3735,6 +3746,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. */
diff --git a/src/or/reasons.c b/src/or/reasons.c
index e6c325f1b3..717cf57c24 100644
--- a/src/or/reasons.c
+++ b/src/or/reasons.c
@@ -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";
+ }
+}
+
diff --git a/src/or/reasons.h b/src/or/reasons.h
index 1cadf4e89e..f98db55bdd 100644
--- a/src/or/reasons.h
+++ b/src/or/reasons.h
@@ -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
diff --git a/src/or/relay.c b/src/or/relay.c
index e362348bb9..d8f09d320d 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -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. */