aboutsummaryrefslogtreecommitdiff
path: root/src/or/connection_edge.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/connection_edge.c')
-rw-r--r--src/or/connection_edge.c693
1 files changed, 474 insertions, 219 deletions
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 8480a35458..f178917f0b 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -76,9 +76,15 @@
#include "dirserv.h"
#include "hibernate.h"
#include "hs_common.h"
+#include "hs_cache.h"
+#include "hs_client.h"
+#include "hs_circuit.h"
#include "main.h"
+#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "proto_http.h"
+#include "proto_socks.h"
#include "reasons.h"
#include "relay.h"
#include "rendclient.h"
@@ -109,7 +115,7 @@
#define TRANS_NETFILTER
#define TRANS_NETFILTER_IPV6
#endif
-#endif
+#endif /* defined(HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H) */
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
#include <net/if.h>
@@ -152,7 +158,9 @@ connection_mark_unattached_ap_,(entry_connection_t *conn, int endreason,
* but we should fix it someday anyway. */
if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) &&
connection_edge_is_rendezvous_stream(edge_conn)) {
- rend_client_note_connection_attempt_ended(edge_conn->rend_data);
+ if (edge_conn->rend_data) {
+ rend_client_note_connection_attempt_ended(edge_conn->rend_data);
+ }
}
if (base_conn->marked_for_close) {
@@ -236,6 +244,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) {
@@ -485,6 +498,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);
@@ -647,7 +661,7 @@ connection_ap_about_to_close(entry_connection_t *entry_conn)
connection_ap_warn_and_unmark_if_pending_circ(entry_conn,
"about_to_close");
}
-#endif
+#endif /* 1 */
control_event_stream_bandwidth(edge_conn);
control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
@@ -857,9 +871,9 @@ connection_ap_rescan_and_attach_pending(void)
entry_conn->marked_pending_circ_line = 0; \
entry_conn->marked_pending_circ_file = 0; \
} while (0)
-#else
+#else /* !(defined(DEBUGGING_17659)) */
#define UNMARK() do { } while (0)
-#endif
+#endif /* defined(DEBUGGING_17659) */
/** Tell any AP streams that are listed as waiting for a new circuit to try
* again. If there is an available circuit for a stream, attach it. Otherwise,
@@ -965,7 +979,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
log_warn(LD_BUG, "(Previously called from %s:%d.)\n",
f2 ? f2 : "<NULL>",
entry_conn->marked_pending_circ_line);
-#endif
+#endif /* defined(DEBUGGING_17659) */
log_backtrace(LOG_WARN, LD_BUG, "To debug, this may help");
return;
}
@@ -1073,7 +1087,8 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
if (!entry_conn->chosen_exit_optional &&
!entry_conn->chosen_exit_retries)
continue;
- r1 = node_get_by_nickname(entry_conn->chosen_exit_name, 0);
+ r1 = node_get_by_nickname(entry_conn->chosen_exit_name,
+ NNF_NO_WARN_UNNAMED);
r2 = node_get_by_id(info->identity_digest);
if (!r1 || !r2 || r1 != r2)
continue;
@@ -1176,10 +1191,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();
@@ -1222,10 +1237,9 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
/* Check for whether this is a .exit address. By default, those are
* disallowed when they're coming straight from the client, but you're
* allowed to have them in MapAddress commands and so forth. */
- if (!strcmpend(socks->address, ".exit") && !options->AllowDotExit) {
+ if (!strcmpend(socks->address, ".exit")) {
log_warn(LD_APP, "The \".exit\" notation is disabled in Tor due to "
- "security risks. Set AllowDotExit in your torrc to enable "
- "it (at your own risk).");
+ "security risks.");
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
out->end_reason = END_STREAM_REASON_TORPROTOCOL;
@@ -1391,6 +1405,199 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
}
}
+/** We just received a SOCKS request in <b>conn</b> to an onion address of type
+ * <b>addresstype</b>. Start connecting to the onion service. */
+static int
+connection_ap_handle_onion(entry_connection_t *conn,
+ socks_request_t *socks,
+ origin_circuit_t *circ,
+ hostname_type_t addresstype)
+{
+ time_t now = approx_time();
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
+
+ /* If .onion address requests are disabled, refuse the request */
+ if (!conn->entry_cfg.onion_traffic) {
+ log_warn(LD_APP, "Onion address %s requested from a port with .onion "
+ "disabled", safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ }
+
+ /* Check whether it's RESOLVE or RESOLVE_PTR. We don't handle those
+ * for hidden service addresses. */
+ if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
+ /* if it's a resolve request, fail it right now, rather than
+ * building all the circuits and then realizing it won't work. */
+ log_warn(LD_APP,
+ "Resolve requests to hidden services not allowed. Failing.");
+ connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,
+ 0,NULL,-1,TIME_MAX);
+ connection_mark_unattached_ap(conn,
+ END_STREAM_REASON_SOCKSPROTOCOL |
+ END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+ return -1;
+ }
+
+ /* If we were passed a circuit, then we need to fail. .onion addresses
+ * only work when we launch our own circuits for now. */
+ if (circ) {
+ log_warn(LD_CONTROL, "Attachstream to a circuit is not "
+ "supported for .onion addresses currently. Failing.");
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ return -1;
+ }
+
+ /* Interface: Regardless of HS version after the block below we should have
+ set onion_address, rend_cache_lookup_result, and descriptor_is_usable. */
+ const char *onion_address = NULL;
+ int rend_cache_lookup_result = -ENOENT;
+ int descriptor_is_usable = 0;
+
+ if (addresstype == ONION_V2_HOSTNAME) { /* it's a v2 hidden service */
+ rend_cache_entry_t *entry = NULL;
+ /* Look up if we have client authorization configured for this hidden
+ * service. If we do, associate it with the rend_data. */
+ rend_service_authorization_t *client_auth =
+ rend_client_lookup_service_authorization(socks->address);
+
+ const uint8_t *cookie = NULL;
+ rend_auth_type_t auth_type = REND_NO_AUTH;
+ if (client_auth) {
+ log_info(LD_REND, "Using previously configured client authorization "
+ "for hidden service request.");
+ auth_type = client_auth->auth_type;
+ cookie = client_auth->descriptor_cookie;
+ }
+
+ /* Fill in the rend_data field so we can start doing a connection to
+ * a hidden service. */
+ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
+ rend_data_client_create(socks->address, NULL, (char *) cookie,
+ auth_type);
+ if (rend_data == NULL) {
+ return -1;
+ }
+ onion_address = rend_data_get_address(rend_data);
+ log_info(LD_REND,"Got a hidden service request for ID '%s'",
+ safe_str_client(onion_address));
+
+ rend_cache_lookup_result = rend_cache_lookup_entry(onion_address,-1,
+ &entry);
+ if (!rend_cache_lookup_result && entry) {
+ descriptor_is_usable = rend_client_any_intro_points_usable(entry);
+ }
+ } else { /* it's a v3 hidden service */
+ tor_assert(addresstype == ONION_V3_HOSTNAME);
+ const hs_descriptor_t *cached_desc = NULL;
+ int retval;
+ /* Create HS conn identifier with HS pubkey */
+ hs_ident_edge_conn_t *hs_conn_ident =
+ tor_malloc_zero(sizeof(hs_ident_edge_conn_t));
+
+ retval = hs_parse_address(socks->address, &hs_conn_ident->identity_pk,
+ NULL, NULL);
+ if (retval < 0) {
+ log_warn(LD_GENERAL, "failed to parse hs address");
+ tor_free(hs_conn_ident);
+ return -1;
+ }
+ ENTRY_TO_EDGE_CONN(conn)->hs_ident = hs_conn_ident;
+
+ onion_address = socks->address;
+
+ /* Check the v3 desc cache */
+ cached_desc = hs_cache_lookup_as_client(&hs_conn_ident->identity_pk);
+ if (cached_desc) {
+ rend_cache_lookup_result = 0;
+ descriptor_is_usable =
+ hs_client_any_intro_points_usable(&hs_conn_ident->identity_pk,
+ cached_desc);
+ log_info(LD_GENERAL, "Found %s descriptor in cache for %s. %s.",
+ (descriptor_is_usable) ? "usable" : "unusable",
+ safe_str_client(onion_address),
+ (descriptor_is_usable) ? "Not fetching." : "Refecting.");
+ } else {
+ rend_cache_lookup_result = -ENOENT;
+ }
+ }
+
+ /* Lookup the given onion address. If invalid, stop right now.
+ * Otherwise, we might have it in the cache or not. */
+ unsigned int refetch_desc = 0;
+ if (rend_cache_lookup_result < 0) {
+ switch (-rend_cache_lookup_result) {
+ case EINVAL:
+ /* We should already have rejected this address! */
+ log_warn(LD_BUG,"Invalid service name '%s'",
+ safe_str_client(onion_address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ return -1;
+ case ENOENT:
+ /* We didn't have this; we should look it up. */
+ log_info(LD_REND, "No descriptor found in our cache for %s. Fetching.",
+ safe_str_client(onion_address));
+ refetch_desc = 1;
+ break;
+ default:
+ log_warn(LD_BUG, "Unknown cache lookup error %d",
+ rend_cache_lookup_result);
+ return -1;
+ }
+ }
+
+ /* Help predict that we'll want to do hidden service circuits in the
+ * future. We're not sure if it will need a stable circuit yet, but
+ * we know we'll need *something*. */
+ rep_hist_note_used_internal(now, 0, 1);
+
+ /* Now we have a descriptor but is it usable or not? If not, refetch.
+ * Also, a fetch could have been requested if the onion address was not
+ * found in the cache previously. */
+ if (refetch_desc || !descriptor_is_usable) {
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ connection_ap_mark_as_non_pending_circuit(conn);
+ base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+ if (addresstype == ONION_V2_HOSTNAME) {
+ tor_assert(edge_conn->rend_data);
+ rend_client_refetch_v2_renddesc(edge_conn->rend_data);
+ /* Whatever the result of the refetch, we don't go further. */
+ return 0;
+ } else {
+ tor_assert(addresstype == ONION_V3_HOSTNAME);
+ tor_assert(edge_conn->hs_ident);
+ /* Attempt to fetch the hsv3 descriptor. Check the retval to see how it
+ * went and act accordingly. */
+ int ret = hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk);
+ switch (ret) {
+ case HS_CLIENT_FETCH_MISSING_INFO:
+ /* Keeping the connection in descriptor wait state is fine because
+ * once we get enough dirinfo or a new live consensus, the HS client
+ * subsystem is notified and every connection in that state will
+ * trigger a fetch for the service key. */
+ case HS_CLIENT_FETCH_LAUNCHED:
+ case HS_CLIENT_FETCH_PENDING:
+ case HS_CLIENT_FETCH_HAVE_DESC:
+ return 0;
+ case HS_CLIENT_FETCH_ERROR:
+ case HS_CLIENT_FETCH_NO_HSDIRS:
+ case HS_CLIENT_FETCH_NOT_ALLOWED:
+ /* Can't proceed further and better close the SOCKS request. */
+ return -1;
+ }
+ }
+ }
+
+ /* We have the descriptor! So launch a connection to the HS. */
+ log_info(LD_REND, "Descriptor is here. Great.");
+
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ /* We'll try to attach it at the next event loop, or whenever
+ * we call connection_ap_attach_pending() */
+ connection_ap_mark_as_pending_circuit(conn);
+ return 0;
+}
+
/** Connection <b>conn</b> just finished its socks handshake, or the
* controller asked us to take care of it. If <b>circ</b> is defined,
* then that's where we'll want to attach it. Otherwise we have to
@@ -1466,23 +1673,23 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
const node_t *node = NULL;
/* If this .exit was added by an AUTOMAP, then it came straight from
- * a user. Make sure that options->AllowDotExit permits that! */
- if (exit_source == ADDRMAPSRC_AUTOMAP && !options->AllowDotExit) {
- /* Whoops; this one is stale. It must have gotten added earlier,
- * when AllowDotExit was on. */
- log_warn(LD_APP,"Stale automapped address for '%s.exit', with "
- "AllowDotExit disabled. Refusing.",
+ * a user. That's not safe. */
+ if (exit_source == ADDRMAPSRC_AUTOMAP) {
+ /* Whoops; this one is stale. It must have gotten added earlier?
+ * (Probably this is not possible, since AllowDotExit no longer
+ * exists.) */
+ log_warn(LD_APP,"Stale automapped address for '%s.exit'. Refusing.",
safe_str_client(socks->address));
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ tor_assert_nonfatal_unreached();
return -1;
}
/* Double-check to make sure there are no .exits coming from
* impossible/weird sources. */
- if (exit_source == ADDRMAPSRC_DNS ||
- (exit_source == ADDRMAPSRC_NONE && !options->AllowDotExit)) {
+ if (exit_source == ADDRMAPSRC_DNS || exit_source == ADDRMAPSRC_NONE) {
/* It shouldn't be possible to get a .exit address from any of these
* sources. */
log_warn(LD_BUG,"Address '%s.exit', with impossible source for the "
@@ -1507,7 +1714,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
if (s[1] != '\0') {
/* Looks like a real .exit one. */
conn->chosen_exit_name = tor_strdup(s+1);
- node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 0);
if (exit_source == ADDRMAPSRC_TRACKEXIT) {
/* We 5 tries before it expires the addressmap */
@@ -1528,7 +1735,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
* form that means (foo's address).foo.exit. */
conn->chosen_exit_name = tor_strdup(socks->address);
- node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 0);
if (node) {
*socks->address = 0;
node_get_address_string(node, socks->address, sizeof(socks->address));
@@ -1557,7 +1764,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
/* Now, we handle everything that isn't a .onion address. */
- if (addresstype != ONION_HOSTNAME) {
+ if (addresstype != ONION_V2_HOSTNAME && addresstype != ONION_V3_HOSTNAME) {
/* Not a hidden-service request. It's either a hostname or an IP,
* possibly with a .exit that we stripped off. We're going to check
* if we're allowed to connect/resolve there, and then launch the
@@ -1584,7 +1791,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1;
}
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) */
/* socks->address is a non-onion hostname or IP address.
* If we can't do any non-onion requests, refuse the connection.
@@ -1835,116 +2042,10 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return 0;
} else {
/* If we get here, it's a request for a .onion address! */
+ tor_assert(addresstype == ONION_V2_HOSTNAME ||
+ addresstype == ONION_V3_HOSTNAME);
tor_assert(!automap);
-
- /* If .onion address requests are disabled, refuse the request */
- if (!conn->entry_cfg.onion_traffic) {
- log_warn(LD_APP, "Onion address %s requested from a port with .onion "
- "disabled", safe_str_client(socks->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
- return -1;
- }
-
- /* Check whether it's RESOLVE or RESOLVE_PTR. We don't handle those
- * for hidden service addresses. */
- if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
- /* if it's a resolve request, fail it right now, rather than
- * building all the circuits and then realizing it won't work. */
- log_warn(LD_APP,
- "Resolve requests to hidden services not allowed. Failing.");
- connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,
- 0,NULL,-1,TIME_MAX);
- connection_mark_unattached_ap(conn,
- END_STREAM_REASON_SOCKSPROTOCOL |
- END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
- return -1;
- }
-
- /* If we were passed a circuit, then we need to fail. .onion addresses
- * only work when we launch our own circuits for now. */
- if (circ) {
- log_warn(LD_CONTROL, "Attachstream to a circuit is not "
- "supported for .onion addresses currently. Failing.");
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
- return -1;
- }
-
- /* Look up if we have client authorization configured for this hidden
- * service. If we do, associate it with the rend_data. */
- rend_service_authorization_t *client_auth =
- rend_client_lookup_service_authorization(socks->address);
-
- const uint8_t *cookie = NULL;
- rend_auth_type_t auth_type = REND_NO_AUTH;
- if (client_auth) {
- log_info(LD_REND, "Using previously configured client authorization "
- "for hidden service request.");
- auth_type = client_auth->auth_type;
- cookie = client_auth->descriptor_cookie;
- }
-
- /* Fill in the rend_data field so we can start doing a connection to
- * a hidden service. */
- rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
- rend_data_client_create(socks->address, NULL, (char *) cookie,
- auth_type);
- if (rend_data == NULL) {
- return -1;
- }
- const char *onion_address = rend_data_get_address(rend_data);
- log_info(LD_REND,"Got a hidden service request for ID '%s'",
- safe_str_client(onion_address));
-
- /* Lookup the given onion address. If invalid, stop right now.
- * Otherwise, we might have it in the cache or not. */
- unsigned int refetch_desc = 0;
- rend_cache_entry_t *entry = NULL;
- const int rend_cache_lookup_result =
- rend_cache_lookup_entry(onion_address, -1, &entry);
- if (rend_cache_lookup_result < 0) {
- switch (-rend_cache_lookup_result) {
- case EINVAL:
- /* We should already have rejected this address! */
- log_warn(LD_BUG,"Invalid service name '%s'",
- safe_str_client(onion_address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
- return -1;
- case ENOENT:
- /* We didn't have this; we should look it up. */
- refetch_desc = 1;
- break;
- default:
- log_warn(LD_BUG, "Unknown cache lookup error %d",
- rend_cache_lookup_result);
- return -1;
- }
- }
-
- /* Help predict that we'll want to do hidden service circuits in the
- * future. We're not sure if it will need a stable circuit yet, but
- * we know we'll need *something*. */
- rep_hist_note_used_internal(now, 0, 1);
-
- /* Now we have a descriptor but is it usable or not? If not, refetch.
- * Also, a fetch could have been requested if the onion address was not
- * found in the cache previously. */
- if (refetch_desc || !rend_client_any_intro_points_usable(entry)) {
- connection_ap_mark_as_non_pending_circuit(conn);
- base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
- log_info(LD_REND, "Unknown descriptor %s. Fetching.",
- safe_str_client(onion_address));
- rend_client_refetch_v2_renddesc(rend_data);
- return 0;
- }
-
- /* We have the descriptor! So launch a connection to the HS. */
- base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- log_info(LD_REND, "Descriptor is here. Great.");
-
- /* We'll try to attach it at the next event loop, or whenever
- * we call connection_ap_attach_pending() */
- connection_ap_mark_as_pending_circuit(conn);
- return 0;
+ return connection_ap_handle_onion(conn, socks, circ, addresstype);
}
return 0; /* unreached but keeps the compiler happy */
@@ -1966,7 +2067,7 @@ get_pf_socket(void)
#else
/* works on NetBSD and FreeBSD */
pf = tor_open_cloexec("/dev/pf", O_RDWR, 0);
-#endif
+#endif /* defined(OpenBSD) */
if (pf < 0) {
log_warn(LD_NET, "open(\"/dev/pf\") failed: %s", strerror(errno));
@@ -1976,9 +2077,10 @@ get_pf_socket(void)
pf_socket = pf;
return pf_socket;
}
-#endif
+#endif /* defined(TRANS_PF) */
-#if defined(TRANS_NETFILTER) || defined(TRANS_PF) || defined(TRANS_TPROXY)
+#if defined(TRANS_NETFILTER) || defined(TRANS_PF) || \
+ defined(TRANS_TPROXY)
/** Try fill in the address of <b>req</b> from the socket configured
* with <b>conn</b>. */
static int
@@ -1998,7 +2100,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
}
goto done;
}
-#endif
+#endif /* defined(TRANS_TPROXY) */
#ifdef TRANS_NETFILTER
int rv = -1;
@@ -2008,13 +2110,13 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
rv = getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST,
(struct sockaddr*)&orig_dst, &orig_dst_len);
break;
-#endif
+#endif /* defined(TRANS_NETFILTER_IPV4) */
#ifdef TRANS_NETFILTER_IPV6
case AF_INET6:
rv = getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IPV6, IP6T_SO_ORIGINAL_DST,
(struct sockaddr*)&orig_dst, &orig_dst_len);
break;
-#endif
+#endif /* defined(TRANS_NETFILTER_IPV6) */
default:
log_warn(LD_BUG,
"Received transparent data from an unsuported socket family %d",
@@ -2040,7 +2142,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
(void)req;
log_warn(LD_BUG, "Unable to determine destination from socket.");
return -1;
-#endif
+#endif /* defined(TRANS_NETFILTER) || ... */
done:
tor_addr_from_sockaddr(&addr, (struct sockaddr*)&orig_dst, &req->port);
@@ -2048,7 +2150,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
return 0;
}
-#endif
+#endif /* defined(TRANS_NETFILTER) || defined(TRANS_PF) || ... */
#ifdef TRANS_PF
static int
@@ -2082,7 +2184,7 @@ destination_from_pf(entry_connection_t *conn, socks_request_t *req)
return 0;
}
-#endif
+#endif /* defined(__FreeBSD__) */
memset(&pnl, 0, sizeof(pnl));
pnl.proto = IPPROTO_TCP;
@@ -2131,7 +2233,7 @@ destination_from_pf(entry_connection_t *conn, socks_request_t *req)
return 0;
}
-#endif
+#endif /* defined(TRANS_PF) */
/** Fetch the original destination address and port from a
* system-specific interface and put them into a
@@ -2167,7 +2269,7 @@ connection_ap_get_original_destination(entry_connection_t *conn,
log_warn(LD_BUG, "Called connection_ap_get_original_destination, but no "
"transparent proxy method was configured.");
return -1;
-#endif
+#endif /* defined(TRANS_NETFILTER) || ... */
}
/** connection_edge_process_inbuf() found a conn in state
@@ -2202,7 +2304,7 @@ connection_ap_handshake_process_socks(entry_connection_t *conn)
if (socks->replylen) {
had_reply = 1;
- connection_write_to_buf((const char*)socks->reply, socks->replylen,
+ connection_buf_add((const char*)socks->reply, socks->replylen,
base_conn);
socks->replylen = 0;
if (sockshere == -1) {
@@ -2299,7 +2401,7 @@ connection_ap_process_natd(entry_connection_t *conn)
/* look for LF-terminated "[DEST ip_addr port]"
* where ip_addr is a dotted-quad and port is in string form */
- err = connection_fetch_from_buf_line(ENTRY_TO_CONN(conn), tmp_buf, &tlen);
+ err = connection_buf_get_line(ENTRY_TO_CONN(conn), tmp_buf, &tlen);
if (err == 0)
return 0;
if (err < 0) {
@@ -2348,6 +2450,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_buf_add(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.
*/
@@ -2455,8 +2659,8 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn)
*
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
-int
-connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
+MOCK_IMPL(int,
+connection_ap_handshake_send_begin,(entry_connection_t *ap_conn))
{
char payload[CELL_PAYLOAD_SIZE];
int payload_len;
@@ -2967,15 +3171,22 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
return;
}
if (replylen) { /* we already have a reply in mind */
- connection_write_to_buf(reply, replylen, ENTRY_TO_CONN(conn));
+ connection_buf_add(reply, replylen, ENTRY_TO_CONN(conn));
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_buf_add(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 */
- connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, ENTRY_TO_CONN(conn));
+ connection_buf_add(buf, SOCKS4_NETWORK_LEN, ENTRY_TO_CONN(conn));
} else if (conn->socks_request->socks_version == 5) {
size_t buf_len;
memset(buf,0,sizeof(buf));
@@ -2994,7 +3205,7 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
/* 4 bytes for the header, 2 bytes for the port, 16 for the address. */
buf_len = 22;
}
- connection_write_to_buf(buf,buf_len,ENTRY_TO_CONN(conn));
+ connection_buf_add(buf,buf_len,ENTRY_TO_CONN(conn));
}
/* If socks_version isn't 4 or 5, don't send anything.
* This can happen in the case of AP bridges. */
@@ -3007,7 +3218,7 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
* <0 and set *<b>end_reason_out</b> to the end reason we should send back to
* the client.
*
- * Return -1 in the case where want to send a RELAY_END cell, and < -1 when
+ * Return -1 in the case where we want to send a RELAY_END cell, and < -1 when
* we don't.
**/
STATIC int
@@ -3066,6 +3277,88 @@ begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
return 0;
}
+/** For the given <b>circ</b> and the edge connection <b>conn</b>, setup the
+ * connection, attach it to the circ and connect it. Return 0 on success
+ * or END_CIRC_AT_ORIGIN if we can't find the requested hidden service port
+ * where the caller should close the circuit. */
+static int
+handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
+{
+ int ret;
+ origin_circuit_t *origin_circ;
+
+ assert_circuit_ok(circ);
+ tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
+ tor_assert(conn);
+
+ log_debug(LD_REND, "Connecting the hidden service rendezvous circuit "
+ "to the service destination.");
+
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ conn->base_.address = tor_strdup("(rendezvous)");
+ conn->base_.state = EXIT_CONN_STATE_CONNECTING;
+
+ /* The circuit either has an hs identifier for v3+ or a rend_data for legacy
+ * service. */
+ if (origin_circ->rend_data) {
+ conn->rend_data = rend_data_dup(origin_circ->rend_data);
+ tor_assert(connection_edge_is_rendezvous_stream(conn));
+ ret = rend_service_set_connection_addr_port(conn, origin_circ);
+ } else if (origin_circ->hs_ident) {
+ /* Setup the identifier to be the one for the circuit service. */
+ conn->hs_ident =
+ hs_ident_edge_conn_new(&origin_circ->hs_ident->identity_pk);
+ tor_assert(connection_edge_is_rendezvous_stream(conn));
+ ret = hs_service_set_conn_addr_port(origin_circ, conn);
+ } else {
+ /* We should never get here if the circuit's purpose is rendezvous. */
+ tor_assert_nonfatal_unreached();
+ return -1;
+ }
+ if (ret < 0) {
+ log_info(LD_REND, "Didn't find rendezvous service (addr%s, port %d)",
+ fmt_addr(&TO_CONN(conn)->addr), TO_CONN(conn)->port);
+ /* Send back reason DONE because we want to make hidden service port
+ * scanning harder thus instead of returning that the exit policy
+ * didn't match, which makes it obvious that the port is closed,
+ * return DONE and kill the circuit. That way, a user (malicious or
+ * not) needs one circuit per bad port unless it matches the policy of
+ * the hidden service. */
+ relay_send_end_cell_from_edge(conn->stream_id, circ,
+ END_STREAM_REASON_DONE,
+ origin_circ->cpath->prev);
+ connection_free(TO_CONN(conn));
+
+ /* Drop the circuit here since it might be someone deliberately
+ * scanning the hidden service ports. Note that this mitigates port
+ * scanning by adding more work on the attacker side to successfully
+ * scan but does not fully solve it. */
+ if (ret < -1) {
+ return END_CIRC_AT_ORIGIN;
+ } else {
+ return 0;
+ }
+ }
+
+ /* Link the circuit and the connection crypt path. */
+ conn->cpath_layer = origin_circ->cpath->prev;
+
+ /* Add it into the linked list of p_streams on this circuit */
+ conn->next_stream = origin_circ->p_streams;
+ origin_circ->p_streams = conn;
+ conn->on_circuit = circ;
+ assert_circuit_ok(circ);
+
+ hs_inc_rdv_stream_counter(origin_circ);
+
+ /* Connect tor to the hidden service destination. */
+ connection_exit_connect(conn);
+
+ /* For path bias: This circuit was used successfully */
+ pathbias_mark_use_success(origin_circ);
+ return 0;
+}
+
/** A relay 'begin' or 'begin_dir' cell has arrived, and either we are
* an exit hop for the circuit, or we are the origin and it is a
* rendezvous begin.
@@ -3141,7 +3434,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
port = bcell.port;
if (or_circ && or_circ->p_chan) {
- if ((or_circ->is_first_hop ||
+ const int client_chan = channel_is_client(or_circ->p_chan);
+ if ((client_chan ||
(!connection_or_digest_is_known_relay(
or_circ->p_chan->identity_digest) &&
should_refuse_unknown_exits(options)))) {
@@ -3151,10 +3445,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Attempt by %s to open a stream %s. Closing.",
safe_str(channel_get_canonical_remote_descr(or_circ->p_chan)),
- or_circ->is_first_hop ? "on first hop of circuit" :
- "from unknown relay");
+ client_chan ? "on first hop of circuit" :
+ "from unknown relay");
relay_send_end_cell_from_edge(rh.stream_id, circ,
- or_circ->is_first_hop ?
+ client_chan ?
END_STREAM_REASON_TORPROTOCOL :
END_STREAM_REASON_MISC,
NULL);
@@ -3217,58 +3511,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->deliver_window = STREAMWINDOW_START;
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
- tor_assert(origin_circ);
- log_info(LD_REND,"begin is for rendezvous. configuring stream.");
- n_stream->base_.address = tor_strdup("(rendezvous)");
- n_stream->base_.state = EXIT_CONN_STATE_CONNECTING;
- n_stream->rend_data = rend_data_dup(origin_circ->rend_data);
- tor_assert(connection_edge_is_rendezvous_stream(n_stream));
- assert_circuit_ok(circ);
-
- const int r = rend_service_set_connection_addr_port(n_stream, origin_circ);
- if (r < 0) {
- log_info(LD_REND,"Didn't find rendezvous service (port %d)",
- n_stream->base_.port);
- /* Send back reason DONE because we want to make hidden service port
- * scanning harder thus instead of returning that the exit policy
- * didn't match, which makes it obvious that the port is closed,
- * return DONE and kill the circuit. That way, a user (malicious or
- * not) needs one circuit per bad port unless it matches the policy of
- * the hidden service. */
- relay_send_end_cell_from_edge(rh.stream_id, circ,
- END_STREAM_REASON_DONE,
- layer_hint);
- connection_free(TO_CONN(n_stream));
- tor_free(address);
-
- /* Drop the circuit here since it might be someone deliberately
- * scanning the hidden service ports. Note that this mitigates port
- * scanning by adding more work on the attacker side to successfully
- * scan but does not fully solve it. */
- if (r < -1)
- return END_CIRC_AT_ORIGIN;
- else
- return 0;
- }
- assert_circuit_ok(circ);
- log_debug(LD_REND,"Finished assigning addr/port");
- n_stream->cpath_layer = origin_circ->cpath->prev; /* link it */
-
- /* add it into the linked list of p_streams on this circuit */
- n_stream->next_stream = origin_circ->p_streams;
- n_stream->on_circuit = circ;
- origin_circ->p_streams = n_stream;
- assert_circuit_ok(circ);
-
- origin_circ->rend_data->nr_streams++;
-
- connection_exit_connect(n_stream);
-
- /* For path bias: This circuit was used successfully */
- pathbias_mark_use_success(origin_circ);
-
tor_free(address);
- return 0;
+ /* We handle this circuit and stream in this function for all supported
+ * hidden service version. */
+ return handle_hs_exit_conn(circ, n_stream);
}
tor_strlower(address);
n_stream->base_.address = address;
@@ -3566,8 +3812,12 @@ int
connection_edge_is_rendezvous_stream(const edge_connection_t *conn)
{
tor_assert(conn);
- if (conn->rend_data)
+ /* It should not be possible to set both of these structs */
+ tor_assert_nonfatal(!(conn->rend_data && conn->hs_ident));
+
+ if (conn->rend_data || conn->hs_ident) {
return 1;
+ }
return 0;
}
@@ -3591,7 +3841,7 @@ connection_ap_can_use_exit(const entry_connection_t *conn,
*/
if (conn->chosen_exit_name) {
const node_t *chosen_exit =
- node_get_by_nickname(conn->chosen_exit_name, 1);
+ node_get_by_nickname(conn->chosen_exit_name, 0);
if (!chosen_exit || tor_memneq(chosen_exit->identity,
exit_node->identity, DIGEST_LEN)) {
/* doesn't match */
@@ -3640,10 +3890,12 @@ connection_ap_can_use_exit(const entry_connection_t *conn,
}
/** If address is of the form "y.onion" with a well-formed handle y:
- * Put a NUL after y, lower-case it, and return ONION_HOSTNAME.
+ * Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or
+ * ONION_V3_HOSTNAME depending on the HS version.
*
* If address is of the form "x.y.onion" with a well-formed handle x:
- * Drop "x.", put a NUL after y, lower-case it, and return ONION_HOSTNAME.
+ * Drop "x.", put a NUL after y, lower-case it, and return
+ * ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version.
*
* If address is of the form "y.onion" with a badly-formed handle y:
* Return BAD_HOSTNAME and log a message.
@@ -3659,7 +3911,7 @@ parse_extended_hostname(char *address)
{
char *s;
char *q;
- char query[REND_SERVICE_ID_LEN_BASE32+1];
+ char query[HS_SERVICE_ADDR_LEN_BASE32+1];
s = strrchr(address,'.');
if (!s)
@@ -3679,14 +3931,17 @@ parse_extended_hostname(char *address)
goto failed; /* reject sub-domain, as DNS does */
}
q = (NULL == q) ? address : q + 1;
- if (strlcpy(query, q, REND_SERVICE_ID_LEN_BASE32+1) >=
- REND_SERVICE_ID_LEN_BASE32+1)
+ if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >=
+ HS_SERVICE_ADDR_LEN_BASE32+1)
goto failed;
if (q != address) {
memmove(address, q, strlen(q) + 1 /* also get \0 */);
}
- if (rend_valid_service_id(query)) {
- return ONION_HOSTNAME; /* success */
+ if (rend_valid_v2_service_id(query)) {
+ return ONION_V2_HOSTNAME; /* success */
+ }
+ if (hs_address_is_valid(query)) {
+ return ONION_V3_HOSTNAME;
}
failed:
/* otherwise, return to previous state and return 0 */