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.c1085
1 files changed, 782 insertions, 303 deletions
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 4763bf59a..63a3213d3 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2011, The Tor Project, Inc. */
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -23,6 +23,7 @@
#include "dirserv.h"
#include "hibernate.h"
#include "main.h"
+#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
#include "relay.h"
@@ -50,27 +51,43 @@
#define SOCKS4_GRANTED 90
#define SOCKS4_REJECT 91
-static int connection_ap_handshake_process_socks(edge_connection_t *conn);
-static int connection_ap_process_natd(edge_connection_t *conn);
+static int connection_ap_handshake_process_socks(entry_connection_t *conn);
+static int connection_ap_process_natd(entry_connection_t *conn);
static int connection_exit_connect_dir(edge_connection_t *exitconn);
static int address_is_in_virtual_range(const char *addr);
-static int consider_plaintext_ports(edge_connection_t *conn, uint16_t port);
+static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port);
static void clear_trackexithost_mappings(const char *exitname);
+static int connection_ap_supports_optimistic_data(const entry_connection_t *);
/** An AP stream has failed/finished. If it hasn't already sent back
* a socks reply, send one now (based on endreason). Also set
* has_sent_end to 1, and mark the conn.
*/
void
-_connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
+_connection_mark_unattached_ap(entry_connection_t *conn, int endreason,
int line, const char *file)
{
- tor_assert(conn->_base.type == CONN_TYPE_AP);
- conn->edge_has_sent_end = 1; /* no circ yet */
-
- if (conn->_base.marked_for_close) {
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ ENTRY_TO_EDGE_CONN(conn)->edge_has_sent_end = 1; /* no circ yet */
+
+ /* If this is a rendezvous stream and it is failing without ever
+ * being attached to a circuit, assume that an attempt to connect to
+ * the destination hidden service has just ended.
+ *
+ * XXX023 This condition doesn't limit to only streams failing
+ * without ever being attached. That sloppiness should be harmless,
+ * 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->onion_address);
+ }
+
+ if (base_conn->marked_for_close) {
/* This call will warn as appropriate. */
- _connection_mark_for_close(TO_CONN(conn), line, file);
+ _connection_mark_for_close(base_conn, line, file);
return;
}
@@ -90,9 +107,9 @@ _connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
conn->socks_request->has_finished = 1;
}
- _connection_mark_for_close(TO_CONN(conn), line, file);
- conn->_base.hold_open_until_flushed = 1;
- conn->end_reason = endreason;
+ _connection_mark_and_flush(base_conn, line, file);
+
+ ENTRY_TO_EDGE_CONN(conn)->end_reason = endreason;
}
/** There was an EOF. Send an end and mark the connection for close.
@@ -100,7 +117,7 @@ _connection_mark_unattached_ap(edge_connection_t *conn, int endreason,
int
connection_edge_reached_eof(edge_connection_t *conn)
{
- if (buf_datalen(conn->_base.inbuf) &&
+ if (connection_get_inbuf_len(TO_CONN(conn)) &&
connection_state_is_open(TO_CONN(conn))) {
/* it still has stuff to process. don't let it die yet. */
return 0;
@@ -110,8 +127,11 @@ connection_edge_reached_eof(edge_connection_t *conn)
/* only mark it if not already marked. it's possible to
* get the 'end' right around when the client hangs up on us. */
connection_edge_end(conn, END_STREAM_REASON_DONE);
- if (conn->socks_request) /* eof, so don't send a socks reply back */
- conn->socks_request->has_finished = 1;
+ if (conn->_base.type == CONN_TYPE_AP) {
+ /* eof, so don't send a socks reply back */
+ if (EDGE_TO_ENTRY_CONN(conn)->socks_request)
+ EDGE_TO_ENTRY_CONN(conn)->socks_request->has_finished = 1;
+ }
connection_mark_for_close(TO_CONN(conn));
}
return 0;
@@ -134,13 +154,13 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
switch (conn->_base.state) {
case AP_CONN_STATE_SOCKS_WAIT:
- if (connection_ap_handshake_process_socks(conn) < 0) {
+ if (connection_ap_handshake_process_socks(EDGE_TO_ENTRY_CONN(conn)) <0) {
/* already marked */
return -1;
}
return 0;
case AP_CONN_STATE_NATD_WAIT:
- if (connection_ap_process_natd(conn) < 0) {
+ if (connection_ap_process_natd(EDGE_TO_ENTRY_CONN(conn)) < 0) {
/* already marked */
return -1;
}
@@ -153,10 +173,26 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
return -1;
}
return 0;
+ case AP_CONN_STATE_CONNECT_WAIT:
+ if (connection_ap_supports_optimistic_data(EDGE_TO_ENTRY_CONN(conn))) {
+ log_info(LD_EDGE,
+ "data from edge while in '%s' state. Sending it anyway. "
+ "package_partial=%d, buflen=%ld",
+ conn_state_to_string(conn->_base.type, conn->_base.state),
+ package_partial,
+ (long)connection_get_inbuf_len(TO_CONN(conn)));
+ if (connection_edge_package_raw_inbuf(conn, package_partial, NULL)<0) {
+ /* (We already sent an end cell if possible) */
+ connection_mark_for_close(TO_CONN(conn));
+ return -1;
+ }
+ return 0;
+ }
+ /* Fall through if the connection is on a circuit without optimistic
+ * data support. */
case EXIT_CONN_STATE_CONNECTING:
case AP_CONN_STATE_RENDDESC_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
- case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_RESOLVE_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
log_info(LD_EDGE,
@@ -181,9 +217,10 @@ connection_edge_destroy(circid_t circ_id, edge_connection_t *conn)
log_info(LD_EDGE,
"CircID %d: At an edge. Marking connection for close.", circ_id);
if (conn->_base.type == CONN_TYPE_AP) {
- connection_mark_unattached_ap(conn, END_STREAM_REASON_DESTROY);
+ entry_connection_t *entry_conn = EDGE_TO_ENTRY_CONN(conn);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_DESTROY);
control_event_stream_bandwidth(conn);
- control_event_stream_status(conn, STREAM_EVENT_CLOSED,
+ control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
END_STREAM_REASON_DESTROY);
conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
} else {
@@ -191,8 +228,7 @@ connection_edge_destroy(circid_t circ_id, edge_connection_t *conn)
conn->edge_has_sent_end = 1;
conn->end_reason = END_STREAM_REASON_DESTROY;
conn->end_reason |= END_STREAM_REASON_FLAG_ALREADY_SENT_CLOSED;
- connection_mark_for_close(TO_CONN(conn));
- conn->_base.hold_open_until_flushed = 1;
+ connection_mark_and_flush(TO_CONN(conn));
}
}
conn->cpath_layer = NULL;
@@ -336,7 +372,6 @@ connection_edge_finished_flushing(edge_connection_t *conn)
switch (conn->_base.state) {
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
- connection_stop_writing(TO_CONN(conn));
connection_edge_consider_sending_sendme(conn);
return 0;
case AP_CONN_STATE_SOCKS_WAIT:
@@ -345,7 +380,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
case AP_CONN_STATE_CIRCUIT_WAIT:
case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
- connection_stop_writing(TO_CONN(conn));
+ case AP_CONN_STATE_RESOLVE_WAIT:
return 0;
default:
log_warn(LD_BUG, "Called in unexpected state %d.",conn->_base.state);
@@ -375,8 +410,9 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
rep_hist_note_exit_stream_opened(conn->port);
conn->state = EXIT_CONN_STATE_OPEN;
- connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
- if (connection_wants_to_flush(conn)) /* in case there are any queued relay
+ IF_HAS_NO_BUFFEREVENT(conn)
+ connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
+ if (connection_get_outbuf_len(conn)) /* in case there are any queued relay
* cells */
connection_start_writing(conn);
/* deliver a 'connected' relay cell back through the circuit. */
@@ -408,13 +444,79 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
return connection_edge_process_inbuf(edge_conn, 1);
}
+/** Common code to connection_(ap|exit)_about_to_close. */
+static void
+connection_edge_about_to_close(edge_connection_t *edge_conn)
+{
+ if (!edge_conn->edge_has_sent_end) {
+ connection_t *conn = TO_CONN(edge_conn);
+ log_warn(LD_BUG, "(Harmless.) Edge connection (marked at %s:%d) "
+ "hasn't sent end yet?",
+ conn->marked_for_close_file, conn->marked_for_close);
+ tor_fragile_assert();
+ }
+}
+
+/** Called when we're about to finally unlink and free an AP (client)
+ * connection: perform necessary accounting and cleanup */
+void
+connection_ap_about_to_close(entry_connection_t *entry_conn)
+{
+ circuit_t *circ;
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
+ connection_t *conn = ENTRY_TO_CONN(entry_conn);
+
+ if (entry_conn->socks_request->has_finished == 0) {
+ /* since conn gets removed right after this function finishes,
+ * there's no point trying to send back a reply at this point. */
+ log_warn(LD_BUG,"Closing stream (marked at %s:%d) without sending"
+ " back a socks reply.",
+ conn->marked_for_close_file, conn->marked_for_close);
+ }
+ if (!edge_conn->end_reason) {
+ log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
+ " set end_reason.",
+ conn->marked_for_close_file, conn->marked_for_close);
+ }
+ if (entry_conn->dns_server_request) {
+ log_warn(LD_BUG,"Closing stream (marked at %s:%d) without having"
+ " replied to DNS request.",
+ conn->marked_for_close_file, conn->marked_for_close);
+ dnsserv_reject_request(entry_conn);
+ }
+ control_event_stream_bandwidth(edge_conn);
+ control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
+ edge_conn->end_reason);
+ circ = circuit_get_by_edge_conn(edge_conn);
+ if (circ)
+ circuit_detach_stream(circ, edge_conn);
+}
+
+/** Called when we're about to finally unlink and free an exit
+ * connection: perform necessary accounting and cleanup */
+void
+connection_exit_about_to_close(edge_connection_t *edge_conn)
+{
+ circuit_t *circ;
+ connection_t *conn = TO_CONN(edge_conn);
+
+ connection_edge_about_to_close(edge_conn);
+
+ circ = circuit_get_by_edge_conn(edge_conn);
+ if (circ)
+ circuit_detach_stream(circ, edge_conn);
+ if (conn->state == EXIT_CONN_STATE_RESOLVING) {
+ connection_dns_remove(edge_conn);
+ }
+}
+
/** Define a schedule for how long to wait between retrying
* application connections. Rather than waiting a fixed amount of
* time between each retry, we wait 10 seconds each for the first
* two tries, and 15 seconds for each retry after
* that. Hopefully this will improve the expected user experience. */
static int
-compute_retry_timeout(edge_connection_t *conn)
+compute_retry_timeout(entry_connection_t *conn)
{
int timeout = get_options()->CircuitStreamTimeout;
if (timeout) /* if our config options override the default, use them */
@@ -437,41 +539,43 @@ void
connection_ap_expire_beginning(void)
{
edge_connection_t *conn;
+ entry_connection_t *entry_conn;
circuit_t *circ;
time_t now = time(NULL);
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int severity;
int cutoff;
int seconds_idle, seconds_since_born;
smartlist_t *conns = get_connection_array();
- SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) {
- if (c->type != CONN_TYPE_AP || c->marked_for_close)
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ if (base_conn->type != CONN_TYPE_AP || base_conn->marked_for_close)
continue;
- conn = TO_EDGE_CONN(c);
+ entry_conn = TO_ENTRY_CONN(base_conn);
+ conn = ENTRY_TO_EDGE_CONN(entry_conn);
/* if it's an internal linked connection, don't yell its status. */
- severity = (tor_addr_is_null(&conn->_base.addr) && !conn->_base.port)
+ severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port)
? LOG_INFO : LOG_NOTICE;
- seconds_idle = (int)( now - conn->_base.timestamp_lastread );
- seconds_since_born = (int)( now - conn->_base.timestamp_created );
+ seconds_idle = (int)( now - base_conn->timestamp_lastread );
+ seconds_since_born = (int)( now - base_conn->timestamp_created );
- if (conn->_base.state == AP_CONN_STATE_OPEN)
+ if (base_conn->state == AP_CONN_STATE_OPEN)
continue;
/* We already consider SocksTimeout in
* connection_ap_handshake_attach_circuit(), but we need to consider
* it here too because controllers that put streams in controller_wait
* state never ask Tor to attach the circuit. */
- if (AP_CONN_STATE_IS_UNATTACHED(conn->_base.state)) {
+ if (AP_CONN_STATE_IS_UNATTACHED(base_conn->state)) {
if (seconds_since_born >= options->SocksTimeout) {
log_fn(severity, LD_APP,
"Tried for %d seconds to get a connection to %s:%d. "
"Giving up. (%s)",
seconds_since_born,
- safe_str_client(conn->socks_request->address),
- conn->socks_request->port,
- conn_state_to_string(CONN_TYPE_AP, conn->_base.state));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
+ safe_str_client(entry_conn->socks_request->address),
+ entry_conn->socks_request->port,
+ conn_state_to_string(CONN_TYPE_AP, base_conn->state));
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
}
continue;
}
@@ -479,14 +583,14 @@ connection_ap_expire_beginning(void)
/* We're in state connect_wait or resolve_wait now -- waiting for a
* reply to our relay cell. See if we want to retry/give up. */
- cutoff = compute_retry_timeout(conn);
+ cutoff = compute_retry_timeout(entry_conn);
if (seconds_idle < cutoff)
continue;
circ = circuit_get_by_edge_conn(conn);
if (!circ) { /* it's vanished? */
log_info(LD_APP,"Conn is waiting (address %s), but lost its circ.",
- safe_str_client(conn->socks_request->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
+ safe_str_client(entry_conn->socks_request->address));
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
continue;
}
if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
@@ -495,9 +599,9 @@ connection_ap_expire_beginning(void)
"Rend stream is %d seconds late. Giving up on address"
" '%s.onion'.",
seconds_idle,
- safe_str_client(conn->socks_request->address));
+ safe_str_client(entry_conn->socks_request->address));
connection_edge_end(conn, END_STREAM_REASON_TIMEOUT);
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TIMEOUT);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
}
continue;
}
@@ -506,9 +610,10 @@ connection_ap_expire_beginning(void)
"We tried for %d seconds to connect to '%s' using exit %s."
" Retrying on a new circuit.",
seconds_idle,
- safe_str_client(conn->socks_request->address),
+ safe_str_client(entry_conn->socks_request->address),
conn->cpath_layer ?
- extend_info_describe(conn->cpath_layer->extend_info): "*unnamed*");
+ extend_info_describe(conn->cpath_layer->extend_info):
+ "*unnamed*");
/* send an end down the circuit */
connection_edge_end(conn, END_STREAM_REASON_TIMEOUT);
/* un-mark it as ending, since we're going to reuse it */
@@ -522,15 +627,16 @@ connection_ap_expire_beginning(void)
circ->timestamp_dirty -= options->MaxCircuitDirtiness;
/* give our stream another 'cutoff' seconds to try */
conn->_base.timestamp_lastread += cutoff;
- if (conn->num_socks_retries < 250) /* avoid overflow */
- conn->num_socks_retries++;
+ if (entry_conn->num_socks_retries < 250) /* avoid overflow */
+ entry_conn->num_socks_retries++;
/* move it back into 'pending' state, and try to attach. */
- if (connection_ap_detach_retriable(conn, TO_ORIGIN_CIRCUIT(circ),
+ if (connection_ap_detach_retriable(entry_conn, TO_ORIGIN_CIRCUIT(circ),
END_STREAM_REASON_TIMEOUT)<0) {
- if (!conn->_base.marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
+ if (!base_conn->marked_for_close)
+ connection_mark_unattached_ap(entry_conn,
+ END_STREAM_REASON_CANT_ATTACH);
}
- } SMARTLIST_FOREACH_END(conn);
+ } SMARTLIST_FOREACH_END(base_conn);
}
/** Tell any AP streams that are waiting for a new circuit to try again,
@@ -539,7 +645,7 @@ connection_ap_expire_beginning(void)
void
connection_ap_attach_pending(void)
{
- edge_connection_t *edge_conn;
+ entry_connection_t *entry_conn;
smartlist_t *conns = get_connection_array();
SMARTLIST_FOREACH(conns, connection_t *, conn,
{
@@ -547,10 +653,10 @@ connection_ap_attach_pending(void)
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (connection_ap_handshake_attach_circuit(edge_conn) < 0) {
- if (!edge_conn->_base.marked_for_close)
- connection_mark_unattached_ap(edge_conn,
+ entry_conn = TO_ENTRY_CONN(conn);
+ if (connection_ap_handshake_attach_circuit(entry_conn) < 0) {
+ if (!conn->marked_for_close)
+ connection_mark_unattached_ap(entry_conn,
END_STREAM_REASON_CANT_ATTACH);
}
});
@@ -565,7 +671,7 @@ void
connection_ap_fail_onehop(const char *failed_digest,
cpath_build_state_t *build_state)
{
- edge_connection_t *edge_conn;
+ entry_connection_t *entry_conn;
char digest[DIGEST_LEN];
smartlist_t *conns = get_connection_array();
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@ -573,27 +679,27 @@ connection_ap_fail_onehop(const char *failed_digest,
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->want_onehop)
+ entry_conn = TO_ENTRY_CONN(conn);
+ if (!entry_conn->want_onehop)
continue;
- if (hexdigest_to_digest(edge_conn->chosen_exit_name, digest) < 0 ||
+ if (hexdigest_to_digest(entry_conn->chosen_exit_name, digest) < 0 ||
tor_memneq(digest, failed_digest, DIGEST_LEN))
continue;
if (tor_digest_is_zero(digest)) {
/* we don't know the digest; have to compare addr:port */
tor_addr_t addr;
if (!build_state || !build_state->chosen_exit ||
- !edge_conn->socks_request || !edge_conn->socks_request->address)
+ !entry_conn->socks_request || !entry_conn->socks_request->address)
continue;
- if (tor_addr_from_str(&addr, edge_conn->socks_request->address)<0 ||
+ if (tor_addr_parse(&addr, entry_conn->socks_request->address)<0 ||
!tor_addr_eq(&build_state->chosen_exit->addr, &addr) ||
- build_state->chosen_exit->port != edge_conn->socks_request->port)
+ build_state->chosen_exit->port != entry_conn->socks_request->port)
continue;
}
log_info(LD_APP, "Closing one-hop stream to '%s/%s' because the OR conn "
- "just failed.", edge_conn->chosen_exit_name,
- edge_conn->socks_request->address);
- connection_mark_unattached_ap(edge_conn, END_STREAM_REASON_TIMEOUT);
+ "just failed.", entry_conn->chosen_exit_name,
+ entry_conn->socks_request->address);
+ connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
} SMARTLIST_FOREACH_END(conn);
}
@@ -604,8 +710,8 @@ connection_ap_fail_onehop(const char *failed_digest,
void
circuit_discard_optional_exit_enclaves(extend_info_t *info)
{
- edge_connection_t *edge_conn;
- routerinfo_t *r1, *r2;
+ entry_connection_t *entry_conn;
+ const node_t *r1, *r2;
smartlist_t *conns = get_connection_array();
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
@@ -613,32 +719,32 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
conn->type != CONN_TYPE_AP ||
conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
continue;
- edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->chosen_exit_optional &&
- !edge_conn->chosen_exit_retries)
+ entry_conn = TO_ENTRY_CONN(conn);
+ if (!entry_conn->chosen_exit_optional &&
+ !entry_conn->chosen_exit_retries)
continue;
- r1 = router_get_by_nickname(edge_conn->chosen_exit_name, 0);
- r2 = router_get_by_digest(info->identity_digest);
+ r1 = node_get_by_nickname(entry_conn->chosen_exit_name, 0);
+ r2 = node_get_by_id(info->identity_digest);
if (!r1 || !r2 || r1 != r2)
continue;
- tor_assert(edge_conn->socks_request);
- if (edge_conn->chosen_exit_optional) {
+ tor_assert(entry_conn->socks_request);
+ if (entry_conn->chosen_exit_optional) {
log_info(LD_APP, "Giving up on enclave exit '%s' for destination %s.",
- safe_str_client(edge_conn->chosen_exit_name),
- escaped_safe_str_client(edge_conn->socks_request->address));
- edge_conn->chosen_exit_optional = 0;
- tor_free(edge_conn->chosen_exit_name); /* clears it */
+ safe_str_client(entry_conn->chosen_exit_name),
+ escaped_safe_str_client(entry_conn->socks_request->address));
+ entry_conn->chosen_exit_optional = 0;
+ tor_free(entry_conn->chosen_exit_name); /* clears it */
/* if this port is dangerous, warn or reject it now that we don't
* think it'll be using an enclave. */
- consider_plaintext_ports(edge_conn, edge_conn->socks_request->port);
+ consider_plaintext_ports(entry_conn, entry_conn->socks_request->port);
}
- if (edge_conn->chosen_exit_retries) {
- if (--edge_conn->chosen_exit_retries == 0) { /* give up! */
- clear_trackexithost_mappings(edge_conn->chosen_exit_name);
- tor_free(edge_conn->chosen_exit_name); /* clears it */
+ if (entry_conn->chosen_exit_retries) {
+ if (--entry_conn->chosen_exit_retries == 0) { /* give up! */
+ clear_trackexithost_mappings(entry_conn->chosen_exit_name);
+ tor_free(entry_conn->chosen_exit_name); /* clears it */
/* if this port is dangerous, warn or reject it now that we don't
* think it'll be using an enclave. */
- consider_plaintext_ports(edge_conn, edge_conn->socks_request->port);
+ consider_plaintext_ports(entry_conn, entry_conn->socks_request->port);
}
}
} SMARTLIST_FOREACH_END(conn);
@@ -652,20 +758,27 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
* Returns -1 on err, 1 on success, 0 on not-yet-sure.
*/
int
-connection_ap_detach_retriable(edge_connection_t *conn, origin_circuit_t *circ,
+connection_ap_detach_retriable(entry_connection_t *conn,
+ origin_circuit_t *circ,
int reason)
{
control_event_stream_status(conn, STREAM_EVENT_FAILED_RETRIABLE, reason);
- conn->_base.timestamp_lastread = time(NULL);
+ ENTRY_TO_CONN(conn)->timestamp_lastread = time(NULL);
+
+ if (conn->pending_optimistic_data) {
+ generic_buffer_set_to_copy(&conn->sending_optimistic_data,
+ conn->pending_optimistic_data);
+ }
+
if (!get_options()->LeaveStreamsUnattached || conn->use_begindir) {
/* If we're attaching streams ourself, or if this connection is
* a tunneled directory connection, then just attach it. */
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
- circuit_detach_stream(TO_CIRCUIT(circ),conn);
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
return connection_ap_handshake_attach_circuit(conn);
} else {
- conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
- circuit_detach_stream(TO_CIRCUIT(circ),conn);
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
+ circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn));
return 0;
}
}
@@ -690,11 +803,18 @@ connection_ap_detach_retriable(edge_connection_t *conn, origin_circuit_t *circ,
* the configuration file, "1" for mappings set from the control
* interface, and other values for DNS and TrackHostExit mappings that can
* expire.)
+ *
+ * A mapping may be 'wildcarded'. If "src_wildcard" is true, then
+ * any address that ends with a . followed by the key for this entry will
+ * get remapped by it. If "dst_wildcard" is also true, then only the
+ * matching suffix of such addresses will get replaced by new_address.
*/
typedef struct {
char *new_address;
time_t expires;
addressmap_entry_source_t source:3;
+ unsigned src_wildcard:1;
+ unsigned dst_wildcard:1;
short num_resolve_failures;
} addressmap_entry_t;
@@ -789,13 +909,10 @@ addressmap_ent_remove(const char *address, addressmap_entry_t *ent)
static void
clear_trackexithost_mappings(const char *exitname)
{
- char *suffix;
- size_t suffix_len;
+ char *suffix = NULL;
if (!addressmap || !exitname)
return;
- suffix_len = strlen(exitname) + 16;
- suffix = tor_malloc(suffix_len);
- tor_snprintf(suffix, suffix_len, ".%s.exit", exitname);
+ tor_asprintf(&suffix, ".%s.exit", exitname);
tor_strlower(suffix);
STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) {
@@ -813,7 +930,7 @@ clear_trackexithost_mappings(const char *exitname)
* host is unknown or no longer allowed, or for which the source address
* is no longer in trackexithosts. */
void
-addressmap_clear_excluded_trackexithosts(or_options_t *options)
+addressmap_clear_excluded_trackexithosts(const or_options_t *options)
{
const routerset_t *allow_nodes = options->ExitNodes;
const routerset_t *exclude_nodes = options->_ExcludeExitNodesUnion;
@@ -829,7 +946,7 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options)
size_t len;
const char *target = ent->new_address, *dot;
char *nodename;
- routerinfo_t *ri; /* XXX023 Use node_t. */
+ const node_t *node;
if (!target) {
/* DNS resolving in progress */
@@ -849,11 +966,11 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options)
dot--;
if (*dot == '.') dot++;
nodename = tor_strndup(dot, len-5-(dot-target));;
- ri = router_get_by_nickname(nodename, 0);
+ node = node_get_by_nickname(nodename, 0);
tor_free(nodename);
- if (!ri ||
- (allow_nodes && !routerset_contains_router(allow_nodes, ri)) ||
- routerset_contains_router(exclude_nodes, ri) ||
+ if (!node ||
+ (allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
+ routerset_contains_node(exclude_nodes, node) ||
!hostname_in_track_host_exits(options, address)) {
/* We don't know this one, or we want to be rid of it. */
addressmap_ent_remove(address, ent);
@@ -867,7 +984,7 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options)
* no longer allowed by AutomapHostsOnResolve, or for which the
* target address is no longer in the virtual network. */
void
-addressmap_clear_invalid_automaps(or_options_t *options)
+addressmap_clear_invalid_automaps(const or_options_t *options)
{
int clear_all = !options->AutomapHostsOnResolve;
const smartlist_t *suffixes = options->AutomapHostsSuffixes;
@@ -941,6 +1058,37 @@ addressmap_free_all(void)
virtaddress_reversemap = NULL;
}
+/** Try to find a match for AddressMap expressions that use
+ * wildcard notation such as '*.c.d *.e.f' (so 'a.c.d' will map to 'a.e.f') or
+ * '*.c.d a.b.c' (so 'a.c.d' will map to a.b.c).
+ * Return the matching entry in AddressMap or NULL if no match is found.
+ * For expressions such as '*.c.d *.e.f', truncate <b>address</b> 'a.c.d'
+ * to 'a' before we return the matching AddressMap entry.
+ *
+ * This function does not handle the case where a pattern of the form "*.c.d"
+ * matches the address c.d -- that's done by the main addressmap_rewrite
+ * function.
+ */
+static addressmap_entry_t *
+addressmap_match_superdomains(char *address)
+{
+ addressmap_entry_t *val;
+ char *cp;
+
+ cp = address;
+ while ((cp = strchr(cp, '.'))) {
+ /* cp now points to a suffix of address that begins with a . */
+ val = strmap_get_lc(addressmap, cp+1);
+ if (val && val->src_wildcard) {
+ if (val->dst_wildcard)
+ *cp = '\0';
+ return val;
+ }
+ ++cp;
+ }
+ return NULL;
+}
+
/** Look at address, and rewrite it until it doesn't want any
* more rewrites; but don't get into an infinite loop.
* Don't write more than maxlen chars into address. Return true if the
@@ -953,25 +1101,49 @@ addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out)
{
addressmap_entry_t *ent;
int rewrites;
- char *cp;
time_t expires = TIME_MAX;
for (rewrites = 0; rewrites < 16; rewrites++) {
+ int exact_match = 0;
+ char *addr_orig = tor_strdup(escaped_safe_str_client(address));
+
ent = strmap_get(addressmap, address);
if (!ent || !ent->new_address) {
+ ent = addressmap_match_superdomains(address);
+ } else {
+ if (ent->src_wildcard && !ent->dst_wildcard &&
+ !strcasecmp(address, ent->new_address)) {
+ /* This is a rule like *.example.com example.com, and we just got
+ * "example.com" */
+ tor_free(addr_orig);
+ if (expires_out)
+ *expires_out = expires;
+ return rewrites > 0;
+ }
+
+ exact_match = 1;
+ }
+
+ if (!ent || !ent->new_address) {
+ tor_free(addr_orig);
if (expires_out)
*expires_out = expires;
return (rewrites > 0); /* done, no rewrite needed */
}
- cp = tor_strdup(escaped_safe_str_client(ent->new_address));
+ if (ent->dst_wildcard && !exact_match) {
+ strlcat(address, ".", maxlen);
+ strlcat(address, ent->new_address, maxlen);
+ } else {
+ strlcpy(address, ent->new_address, maxlen);
+ }
+
log_info(LD_APP, "Addressmap: rewriting %s to %s",
- escaped_safe_str_client(address), cp);
+ addr_orig, escaped_safe_str_client(address));
if (ent->expires > 1 && ent->expires < expires)
expires = ent->expires;
- tor_free(cp);
- strlcpy(address, ent->new_address, maxlen);
+ tor_free(addr_orig);
}
log_warn(LD_CONFIG,
"Loop detected: we've rewritten %s 16 times! Using it as-is.",
@@ -990,11 +1162,10 @@ addressmap_rewrite(char *address, size_t maxlen, time_t *expires_out)
static int
addressmap_rewrite_reverse(char *address, size_t maxlen, time_t *expires_out)
{
- size_t len = maxlen + 16;
- char *s = tor_malloc(len), *cp;
+ char *s, *cp;
addressmap_entry_t *ent;
int r = 0;
- tor_snprintf(s, len, "REVERSE[%s]", address);
+ tor_asprintf(&s, "REVERSE[%s]", address);
ent = strmap_get(addressmap, s);
if (ent) {
cp = tor_strdup(escaped_safe_str_client(ent->new_address));
@@ -1035,17 +1206,34 @@ addressmap_have_mapping(const char *address, int update_expiry)
* <b>new_address</b> should be a newly dup'ed string, which we'll use or
* free as appropriate. We will leave address alone.
*
- * If <b>new_address</b> is NULL, or equal to <b>address</b>, remove
- * any mappings that exist from <b>address</b>.
- */
+ * If <b>wildcard_addr</b> is true, then the mapping will match any address
+ * equal to <b>address</b>, or any address ending with a period followed by
+ * <b>address</b>. If <b>wildcard_addr</b> and <b>wildcard_new_addr</b> are
+ * both true, the mapping will rewrite addresses that end with
+ * ".<b>address</b>" into ones that end with ".<b>new_address</b>."
+ *
+ * If <b>new_address</b> is NULL, or <b>new_address</b> is equal to
+ * <b>address</b> and <b>wildcard_addr</b> is equal to
+ * <b>wildcard_new_addr</b>, remove any mappings that exist from
+ * <b>address</b>.
+ *
+ *
+ * It is an error to set <b>wildcard_new_addr</b> if <b>wildcard_addr</b> is
+ * not set. */
void
addressmap_register(const char *address, char *new_address, time_t expires,
- addressmap_entry_source_t source)
+ addressmap_entry_source_t source,
+ const int wildcard_addr,
+ const int wildcard_new_addr)
{
addressmap_entry_t *ent;
+ if (wildcard_new_addr)
+ tor_assert(wildcard_addr);
+
ent = strmap_get(addressmap, address);
- if (!new_address || !strcasecmp(address,new_address)) {
+ if (!new_address || (!strcasecmp(address,new_address) &&
+ wildcard_addr == wildcard_new_addr)) {
/* Remove the mapping, if any. */
tor_free(new_address);
if (ent) {
@@ -1080,6 +1268,8 @@ addressmap_register(const char *address, char *new_address, time_t expires,
ent->expires = expires==2 ? 1 : expires;
ent->num_resolve_failures = 0;
ent->source = source;
+ ent->src_wildcard = wildcard_addr ? 1 : 0;
+ ent->dst_wildcard = wildcard_new_addr ? 1 : 0;
log_info(LD_CONFIG, "Addressmap: (re)mapped '%s' to '%s'",
safe_str_client(address),
@@ -1164,7 +1354,7 @@ client_dns_set_addressmap_impl(const char *address, const char *name,
"%s", name);
}
addressmap_register(extendedaddress, tor_strdup(extendedval),
- time(NULL) + ttl, ADDRMAPSRC_DNS);
+ time(NULL) + ttl, ADDRMAPSRC_DNS, 0, 0);
}
/** Record the fact that <b>address</b> resolved to <b>val</b>.
@@ -1209,9 +1399,8 @@ client_dns_set_reverse_addressmap(const char *address, const char *v,
const char *exitname,
int ttl)
{
- size_t len = strlen(address) + 16;
- char *s = tor_malloc(len);
- tor_snprintf(s, len, "REVERSE[%s]", address);
+ char *s = NULL;
+ tor_asprintf(&s, "REVERSE[%s]", address);
client_dns_set_addressmap_impl(s, v, exitname, ttl);
tor_free(s);
}
@@ -1314,7 +1503,6 @@ static char *
addressmap_get_virtual_address(int type)
{
char buf[64];
- struct in_addr in;
tor_assert(addressmap);
if (type == RESOLVED_TYPE_HOSTNAME) {
@@ -1328,6 +1516,7 @@ addressmap_get_virtual_address(int type)
} else if (type == RESOLVED_TYPE_IPV4) {
// This is an imperfect estimate of how many addresses are available, but
// that's ok.
+ struct in_addr in;
uint32_t available = 1u << (32-virtual_addr_netmask_bits);
while (available) {
/* Don't hand out any .0 or .255 address. */
@@ -1416,7 +1605,7 @@ addressmap_register_virtual_address(int type, char *new_address)
log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address);
if (vent_needs_to_be_added)
strmap_set(virtaddress_reversemap, new_address, vent);
- addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP);
+ addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP, 0, 0);
#if 0
{
@@ -1495,21 +1684,18 @@ addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
addressmap_ent_remove(key, val);
continue;
} else if (val->new_address) {
- size_t len = strlen(key)+strlen(val->new_address)+ISO_TIME_LEN+5;
- char *line = tor_malloc(len);
if (want_expiry) {
if (val->expires < 3 || val->expires == TIME_MAX)
- tor_snprintf(line, len, "%s %s NEVER", key, val->new_address);
+ smartlist_add_asprintf(sl, "%s %s NEVER", key, val->new_address);
else {
char time[ISO_TIME_LEN+1];
format_iso_time(time, val->expires);
- tor_snprintf(line, len, "%s %s \"%s\"", key, val->new_address,
+ smartlist_add_asprintf(sl, "%s %s \"%s\"", key, val->new_address,
time);
}
} else {
- tor_snprintf(line, len, "%s %s", key, val->new_address);
+ smartlist_add_asprintf(sl, "%s %s", key, val->new_address);
}
- smartlist_add(sl, line);
}
}
iter = strmap_iter_next(addressmap,iter);
@@ -1519,9 +1705,9 @@ addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
/** Check if <b>conn</b> is using a dangerous port. Then warn and/or
* reject depending on our config options. */
static int
-consider_plaintext_ports(edge_connection_t *conn, uint16_t port)
+consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
int reject = smartlist_string_num_isin(options->RejectPlaintextPorts, port);
if (smartlist_string_num_isin(options->WarnPlaintextPorts, port)) {
@@ -1554,14 +1740,14 @@ consider_plaintext_ports(edge_connection_t *conn, uint16_t port)
* documentation for arguments and return value.
*/
int
-connection_ap_rewrite_and_attach_if_allowed(edge_connection_t *conn,
+connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
if (options->LeaveStreamsUnattached) {
- conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT;
return 0;
}
return connection_ap_handshake_rewrite_and_attach(conn, circ, cpath);
@@ -1583,13 +1769,13 @@ connection_ap_rewrite_and_attach_if_allowed(edge_connection_t *conn,
* <b>cpath</b> is NULL.
*/
int
-connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
+connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath)
{
socks_request_t *socks = conn->socks_request;
hostname_type_t addresstype;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
struct in_addr addr_tmp;
/* We set this to true if this is an address we should automatically
* remap to a local address in VirtualAddrNetwork */
@@ -1600,6 +1786,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
address, and we remap it to one because of an entry in the addressmap. */
int remapped_to_exit = 0;
time_t now = time(NULL);
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
tor_strlower(socks->address); /* normalize it */
strlcpy(orig_address, socks->address, sizeof(orig_address));
@@ -1607,6 +1794,9 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
safe_str_client(socks->address),
socks->port);
+ if (! conn->original_dest_address)
+ conn->original_dest_address = tor_strdup(conn->socks_request->address);
+
if (socks->command == SOCKS_COMMAND_RESOLVE &&
!tor_inet_aton(socks->address, &addr_tmp) &&
options->AutomapHostsOnResolve && options->AutomapHostsSuffixes) {
@@ -1652,7 +1842,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
/* Don't let people try to do a reverse lookup on 10.0.0.1. */
tor_addr_t addr;
int ok;
- ok = tor_addr_parse_reverse_lookup_name(
+ ok = tor_addr_parse_PTR_name(
&addr, socks->address, AF_UNSPEC, 1);
if (ok == 1 && tor_addr_is_internal(&addr, 0)) {
connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_ERROR,
@@ -1696,8 +1886,6 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
remapped_to_exit || options->AllowDotExit);
if (addresstype == BAD_HOSTNAME) {
- log_warn(LD_APP, "Invalid onion hostname %s; rejecting",
- 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);
@@ -1712,15 +1900,14 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
/* If StrictNodes is not set, then .exit overrides ExcludeNodes. */
routerset_t *excludeset = options->StrictNodes ?
options->_ExcludeExitNodesUnion : options->ExcludeExitNodes;
- /*XXX023 make this a node_t. */
- routerinfo_t *router;
+ const node_t *node;
tor_assert(!automap);
if (s) {
/* The address was of the form "(stuff).(name).exit */
if (s[1] != '\0') {
conn->chosen_exit_name = tor_strdup(s+1);
- router = router_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 1);
if (remapped_to_exit) /* 5 tries before it expires the addressmap */
conn->chosen_exit_retries = TRACKHOSTEXITS_RETRIES;
*s = 0;
@@ -1735,15 +1922,16 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
}
} else {
/* It looks like they just asked for "foo.exit". */
+
conn->chosen_exit_name = tor_strdup(socks->address);
- router = router_get_by_nickname(conn->chosen_exit_name, 1);
- if (router) {
+ node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ if (node) {
*socks->address = 0;
- strlcpy(socks->address, router->address, sizeof(socks->address));
+ node_get_address_string(node, socks->address, sizeof(socks->address));
}
}
/* Now make sure that the chosen exit exists... */
- if (!router) {
+ if (!node) {
log_warn(LD_APP,
"Unrecognized relay in exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
@@ -1751,7 +1939,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
return -1;
}
/* ...and make sure that it isn't excluded. */
- if (routerset_contains_router(excludeset, router)) {
+ if (routerset_contains_node(excludeset, node)) {
log_warn(LD_APP,
"Excluded relay in exit address '%s.exit'. Refusing.",
safe_str_client(socks->address));
@@ -1775,6 +1963,14 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
return -1;
}
+ if (options->Tor2webMode) {
+ log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname %s "
+ "because tor2web mode is enabled.",
+ safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ }
+
if (socks->command == SOCKS_COMMAND_RESOLVE) {
uint32_t answer;
struct in_addr in;
@@ -1804,20 +2000,35 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
if (options->ClientRejectInternalAddresses &&
!conn->use_begindir && !conn->chosen_exit_name && !circ) {
tor_addr_t addr;
- if (tor_addr_from_str(&addr, socks->address) >= 0 &&
- tor_addr_is_internal(&addr, 0)) {
+ if (tor_addr_hostname_is_local(socks->address) ||
+ (tor_addr_parse(&addr, socks->address) >= 0 &&
+ tor_addr_is_internal(&addr, 0))) {
/* If this is an explicit private address with no chosen exit node,
* then we really don't want to try to connect to it. That's
* probably an error. */
if (conn->is_transparent_ap) {
- log_warn(LD_NET,
- "Rejecting request for anonymous connection to private "
- "address %s on a TransPort or NATDPort. Possible loop "
- "in your NAT rules?", safe_str_client(socks->address));
+#define WARN_INTRVL_LOOP 300
+ static ratelim_t loop_warn_limit = RATELIM_INIT(WARN_INTRVL_LOOP);
+ char *m;
+ if ((m = rate_limit_log(&loop_warn_limit, approx_time()))) {
+ log_warn(LD_NET,
+ "Rejecting request for anonymous connection to private "
+ "address %s on a TransPort or NATDPort. Possible loop "
+ "in your NAT rules?%s", safe_str_client(socks->address),
+ m);
+ tor_free(m);
+ }
} else {
- log_warn(LD_NET,
- "Rejecting SOCKS request for anonymous connection to "
- "private address %s", safe_str_client(socks->address));
+#define WARN_INTRVL_PRIV 300
+ static ratelim_t priv_warn_limit = RATELIM_INIT(WARN_INTRVL_PRIV);
+ char *m;
+ if ((m = rate_limit_log(&priv_warn_limit, approx_time()))) {
+ log_warn(LD_NET,
+ "Rejecting SOCKS request for anonymous connection to "
+ "private address %s.%s",
+ safe_str_client(socks->address),m);
+ tor_free(m);
+ }
}
connection_mark_unattached_ap(conn, END_STREAM_REASON_PRIVATE_ADDR);
return -1;
@@ -1826,17 +2037,16 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
/* see if we can find a suitable enclave exit */
- routerinfo_t *r =
+ const node_t *r =
router_find_exact_exit_enclave(socks->address, socks->port);
if (r) {
log_info(LD_APP,
"Redirecting address %s to exit at enclave router %s",
- safe_str_client(socks->address),
- router_describe(r));
+ safe_str_client(socks->address), node_describe(r));
/* use the hex digest, not nickname, in case there are two
routers with this nickname */
conn->chosen_exit_name =
- tor_strdup(hex_str(r->cache_info.identity_digest, DIGEST_LEN));
+ tor_strdup(hex_str(r->identity, DIGEST_LEN));
conn->chosen_exit_optional = 1;
}
}
@@ -1856,12 +2066,12 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
} else {
tor_fragile_assert();
}
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
if ((circ && connection_ap_handshake_attach_chosen_circuit(
conn, circ, cpath) < 0) ||
(!circ &&
connection_ap_handshake_attach_circuit(conn) < 0)) {
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return -1;
}
@@ -1871,6 +2081,7 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
rend_cache_entry_t *entry;
int r;
rend_service_authorization_t *client_auth;
+ rend_data_t *rend_data;
tor_assert(!automap);
if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
/* if it's a resolve request, fail it right now, rather than
@@ -1892,16 +2103,17 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
return -1;
}
- conn->rend_data = tor_malloc_zero(sizeof(rend_data_t));
- strlcpy(conn->rend_data->onion_address, socks->address,
- sizeof(conn->rend_data->onion_address));
+ ENTRY_TO_EDGE_CONN(conn)->rend_data = rend_data =
+ tor_malloc_zero(sizeof(rend_data_t));
+ strlcpy(rend_data->onion_address, socks->address,
+ sizeof(rend_data->onion_address));
log_info(LD_REND,"Got a hidden service request for ID '%s'",
- safe_str_client(conn->rend_data->onion_address));
+ safe_str_client(rend_data->onion_address));
/* see if we already have it cached */
- r = rend_cache_lookup_entry(conn->rend_data->onion_address, -1, &entry);
+ r = rend_cache_lookup_entry(rend_data->onion_address, -1, &entry);
if (r<0) {
log_warn(LD_BUG,"Invalid service name '%s'",
- safe_str_client(conn->rend_data->onion_address));
+ safe_str_client(rend_data->onion_address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
@@ -1912,24 +2124,24 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
/* Look up if we have client authorization for it. */
client_auth = rend_client_lookup_service_authorization(
- conn->rend_data->onion_address);
+ rend_data->onion_address);
if (client_auth) {
log_info(LD_REND, "Using previously configured client authorization "
"for hidden service request.");
- memcpy(conn->rend_data->descriptor_cookie,
+ memcpy(rend_data->descriptor_cookie,
client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN);
- conn->rend_data->auth_type = client_auth->auth_type;
+ rend_data->auth_type = client_auth->auth_type;
}
if (r==0) {
- conn->_base.state = AP_CONN_STATE_RENDDESC_WAIT;
+ base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
- safe_str_client(conn->rend_data->onion_address));
- rend_client_refetch_v2_renddesc(conn->rend_data);
+ safe_str_client(rend_data->onion_address));
+ rend_client_refetch_v2_renddesc(rend_data);
} else { /* r > 0 */
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great.");
if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return -1;
}
@@ -1951,10 +2163,10 @@ get_pf_socket(void)
#ifdef OPENBSD
/* only works on OpenBSD */
- pf = open("/dev/pf", O_RDONLY);
+ pf = tor_open_cloexec("/dev/pf", O_RDONLY, 0);
#else
/* works on NetBSD and FreeBSD */
- pf = open("/dev/pf", O_RDWR);
+ pf = tor_open_cloexec("/dev/pf", O_RDWR, 0);
#endif
if (pf < 0) {
@@ -1975,7 +2187,7 @@ get_pf_socket(void)
* else return 0.
*/
static int
-connection_ap_get_original_destination(edge_connection_t *conn,
+connection_ap_get_original_destination(entry_connection_t *conn,
socks_request_t *req)
{
#ifdef TRANS_NETFILTER
@@ -1984,9 +2196,9 @@ connection_ap_get_original_destination(edge_connection_t *conn,
socklen_t orig_dst_len = sizeof(orig_dst);
tor_addr_t addr;
- if (getsockopt(conn->_base.s, SOL_IP, SO_ORIGINAL_DST,
+ if (getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST,
(struct sockaddr*)&orig_dst, &orig_dst_len) < 0) {
- int e = tor_socket_errno(conn->_base.s);
+ int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s);
log_warn(LD_NET, "getsockopt() failed: %s", tor_socket_strerror(e));
return -1;
}
@@ -2003,9 +2215,9 @@ connection_ap_get_original_destination(edge_connection_t *conn,
tor_addr_t addr;
int pf = -1;
- if (getsockname(conn->_base.s, (struct sockaddr*)&proxy_addr,
+ if (getsockname(ENTRY_TO_CONN(conn)->s, (struct sockaddr*)&proxy_addr,
&proxy_addr_len) < 0) {
- int e = tor_socket_errno(conn->_base.s);
+ int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s);
log_warn(LD_NET, "getsockname() to determine transocks destination "
"failed: %s", tor_socket_strerror(e));
return -1;
@@ -2017,16 +2229,16 @@ connection_ap_get_original_destination(edge_connection_t *conn,
if (proxy_sa->sa_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)proxy_sa;
pnl.af = AF_INET;
- pnl.saddr.v4.s_addr = tor_addr_to_ipv4n(&conn->_base.addr);
- pnl.sport = htons(conn->_base.port);
+ pnl.saddr.v4.s_addr = tor_addr_to_ipv4n(&ENTRY_TO_CONN(conn)->addr);
+ pnl.sport = htons(ENTRY_TO_CONN(conn)->port);
pnl.daddr.v4.s_addr = sin->sin_addr.s_addr;
pnl.dport = sin->sin_port;
} else if (proxy_sa->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)proxy_sa;
pnl.af = AF_INET6;
- memcpy(&pnl.saddr.v6, tor_addr_to_in6(&conn->_base.addr),
+ memcpy(&pnl.saddr.v6, tor_addr_to_in6(&ENTRY_TO_CONN(conn)->addr),
sizeof(struct in6_addr));
- pnl.sport = htons(conn->_base.port);
+ pnl.sport = htons(ENTRY_TO_CONN(conn)->port);
memcpy(&pnl.daddr.v6, &sin6->sin6_addr, sizeof(struct in6_addr));
pnl.dport = sin6->sin6_port;
} else {
@@ -2077,38 +2289,48 @@ connection_ap_get_original_destination(edge_connection_t *conn,
* else return 0.
*/
static int
-connection_ap_handshake_process_socks(edge_connection_t *conn)
+connection_ap_handshake_process_socks(entry_connection_t *conn)
{
socks_request_t *socks;
int sockshere;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
+ int had_reply = 0;
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
- tor_assert(conn->_base.state == AP_CONN_STATE_SOCKS_WAIT);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ tor_assert(base_conn->state == AP_CONN_STATE_SOCKS_WAIT);
tor_assert(conn->socks_request);
socks = conn->socks_request;
log_debug(LD_APP,"entered.");
- sockshere = fetch_from_buf_socks(conn->_base.inbuf, socks,
- options->TestSocks, options->SafeSocks);
- if (sockshere == 0) {
- if (socks->replylen) {
- connection_write_to_buf(socks->reply, socks->replylen, TO_CONN(conn));
- /* zero it out so we can do another round of negotiation */
- socks->replylen = 0;
- } else {
- log_debug(LD_APP,"socks handshake not all here yet.");
+ IF_HAS_BUFFEREVENT(base_conn, {
+ struct evbuffer *input = bufferevent_get_input(base_conn->bufev);
+ sockshere = fetch_from_evbuffer_socks(input, socks,
+ options->TestSocks, options->SafeSocks);
+ }) ELSE_IF_NO_BUFFEREVENT {
+ sockshere = fetch_from_buf_socks(base_conn->inbuf, socks,
+ options->TestSocks, options->SafeSocks);
+ };
+
+ if (socks->replylen) {
+ had_reply = 1;
+ connection_write_to_buf((const char*)socks->reply, socks->replylen,
+ base_conn);
+ socks->replylen = 0;
+ if (sockshere == -1) {
+ /* An invalid request just got a reply, no additional
+ * one is necessary. */
+ socks->has_finished = 1;
}
+ }
+
+ if (sockshere == 0) {
+ log_debug(LD_APP,"socks handshake not all here yet.");
return 0;
} else if (sockshere == -1) {
- if (socks->replylen) { /* we should send reply back */
- log_debug(LD_APP,"reply is already set for us. Using it.");
- connection_ap_handshake_socks_reply(conn, socks->reply, socks->replylen,
- END_STREAM_REASON_SOCKSPROTOCOL);
-
- } else {
+ if (!had_reply) {
log_warn(LD_APP,"Fetching socks handshake failed. Closing.");
connection_ap_handshake_socks_reply(conn, NULL, 0,
END_STREAM_REASON_SOCKSPROTOCOL);
@@ -2135,12 +2357,11 @@ connection_ap_handshake_process_socks(edge_connection_t *conn)
* for close), else return 0.
*/
int
-connection_ap_process_transparent(edge_connection_t *conn)
+connection_ap_process_transparent(entry_connection_t *conn)
{
socks_request_t *socks;
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
tor_assert(conn->socks_request);
socks = conn->socks_request;
@@ -2176,7 +2397,7 @@ connection_ap_process_transparent(edge_connection_t *conn)
* for close), else return 0.
*/
static int
-connection_ap_process_natd(edge_connection_t *conn)
+connection_ap_process_natd(entry_connection_t *conn)
{
char tmp_buf[36], *tbuf, *daddr;
size_t tlen = 30;
@@ -2184,8 +2405,7 @@ connection_ap_process_natd(edge_connection_t *conn)
socks_request_t *socks;
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
- tor_assert(conn->_base.state == AP_CONN_STATE_NATD_WAIT);
+ tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_NATD_WAIT);
tor_assert(conn->socks_request);
socks = conn->socks_request;
@@ -2193,7 +2413,7 @@ connection_ap_process_natd(edge_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 = fetch_from_buf_line(conn->_base.inbuf, tmp_buf, &tlen);
+ err = connection_fetch_from_buf_line(ENTRY_TO_CONN(conn), tmp_buf, &tlen);
if (err == 0)
return 0;
if (err < 0) {
@@ -2237,7 +2457,7 @@ connection_ap_process_natd(edge_connection_t *conn)
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT;
return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
}
@@ -2267,28 +2487,46 @@ get_unique_stream_id_by_circ(origin_circuit_t *circ)
return test_stream_id;
}
+/** Return true iff <b>conn</b> is linked to a circuit and configured to use
+ * an exit that supports optimistic data. */
+static int
+connection_ap_supports_optimistic_data(const entry_connection_t *conn)
+{
+ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ /* We can only send optimistic data if we're connected to an open
+ general circuit. */
+ if (edge_conn->on_circuit == NULL ||
+ edge_conn->on_circuit->state != CIRCUIT_STATE_OPEN ||
+ edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ return 0;
+
+ return conn->may_use_optimistic_data;
+}
+
/** Write a relay begin cell, using destaddr and destport from ap_conn's
* socks_request field, and send it down circ.
*
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
int
-connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
+connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
{
char payload[CELL_PAYLOAD_SIZE];
int payload_len;
int begin_type;
origin_circuit_t *circ;
- tor_assert(ap_conn->on_circuit);
- circ = TO_ORIGIN_CIRCUIT(ap_conn->on_circuit);
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
+ connection_t *base_conn = TO_CONN(edge_conn);
+ tor_assert(edge_conn->on_circuit);
+ circ = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit);
- tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
- tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(ap_conn->socks_request);
tor_assert(SOCKS_COMMAND_IS_CONNECT(ap_conn->socks_request->command));
- ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
- if (ap_conn->stream_id==0) {
+ edge_conn->stream_id = get_unique_stream_id_by_circ(circ);
+ if (edge_conn->stream_id==0) {
/* XXXX023 Instead of closing this stream, we should make it get
* retried on another circuit. */
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
@@ -2306,26 +2544,44 @@ connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
ap_conn->socks_request->port);
payload_len = (int)strlen(payload)+1;
- log_debug(LD_APP,
- "Sending relay cell to begin stream %d.", ap_conn->stream_id);
+ log_info(LD_APP,
+ "Sending relay cell %d to begin stream %d.",
+ (int)ap_conn->use_begindir,
+ edge_conn->stream_id);
begin_type = ap_conn->use_begindir ?
RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN;
if (begin_type == RELAY_COMMAND_BEGIN) {
+#ifndef NON_ANONYMOUS_MODE_ENABLED
tor_assert(circ->build_state->onehop_tunnel == 0);
+#endif
}
- if (connection_edge_send_command(ap_conn, begin_type,
+ if (connection_edge_send_command(edge_conn, begin_type,
begin_type == RELAY_COMMAND_BEGIN ? payload : NULL,
begin_type == RELAY_COMMAND_BEGIN ? payload_len : 0) < 0)
return -1; /* circuit is closed, don't continue */
- ap_conn->package_window = STREAMWINDOW_START;
- ap_conn->deliver_window = STREAMWINDOW_START;
- ap_conn->_base.state = AP_CONN_STATE_CONNECT_WAIT;
+ edge_conn->package_window = STREAMWINDOW_START;
+ edge_conn->deliver_window = STREAMWINDOW_START;
+ base_conn->state = AP_CONN_STATE_CONNECT_WAIT;
log_info(LD_APP,"Address/port sent, ap socket %d, n_circ_id %d",
- ap_conn->_base.s, circ->_base.n_circ_id);
+ base_conn->s, circ->_base.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_CONNECT, 0);
+
+ /* If there's queued-up data, send it now */
+ if ((connection_get_inbuf_len(base_conn) ||
+ ap_conn->sending_optimistic_data) &&
+ connection_ap_supports_optimistic_data(ap_conn)) {
+ log_info(LD_APP, "Sending up to %ld + %ld bytes of queued-up data",
+ (long)connection_get_inbuf_len(base_conn),
+ ap_conn->sending_optimistic_data ?
+ (long)generic_buffer_len(ap_conn->sending_optimistic_data) : 0);
+ if (connection_edge_package_raw_inbuf(edge_conn, 1, NULL) < 0) {
+ connection_mark_for_close(base_conn);
+ }
+ }
+
return 0;
}
@@ -2335,25 +2591,27 @@ connection_ap_handshake_send_begin(edge_connection_t *ap_conn)
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
int
-connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
+connection_ap_handshake_send_resolve(entry_connection_t *ap_conn)
{
int payload_len, command;
const char *string_addr;
char inaddr_buf[REVERSE_LOOKUP_NAME_BUF_LEN];
origin_circuit_t *circ;
- tor_assert(ap_conn->on_circuit);
- circ = TO_ORIGIN_CIRCUIT(ap_conn->on_circuit);
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
+ connection_t *base_conn = TO_CONN(edge_conn);
+ tor_assert(edge_conn->on_circuit);
+ circ = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit);
- tor_assert(ap_conn->_base.type == CONN_TYPE_AP);
- tor_assert(ap_conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT);
+ tor_assert(base_conn->type == CONN_TYPE_AP);
+ tor_assert(base_conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
tor_assert(ap_conn->socks_request);
tor_assert(circ->_base.purpose == CIRCUIT_PURPOSE_C_GENERAL);
command = ap_conn->socks_request->command;
tor_assert(SOCKS_COMMAND_IS_RESOLVE(command));
- ap_conn->stream_id = get_unique_stream_id_by_circ(circ);
- if (ap_conn->stream_id==0) {
+ edge_conn->stream_id = get_unique_stream_id_by_circ(circ);
+ if (edge_conn->stream_id==0) {
/* XXXX023 Instead of closing this stream, we should make it get
* retried on another circuit. */
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
@@ -2376,7 +2634,7 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
/* We're doing a reverse lookup. The input could be an IP address, or
* could be an .in-addr.arpa or .ip6.arpa address */
- r = tor_addr_parse_reverse_lookup_name(&addr, a, AF_INET, 1);
+ r = tor_addr_parse_PTR_name(&addr, a, AF_INET, 1);
if (r <= 0) {
log_warn(LD_APP, "Rejecting ill-formed reverse lookup of %s",
safe_str_client(a));
@@ -2384,7 +2642,7 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
return -1;
}
- r = tor_addr_to_reverse_lookup_name(inaddr_buf, sizeof(inaddr_buf), &addr);
+ r = tor_addr_to_PTR_name(inaddr_buf, sizeof(inaddr_buf), &addr);
if (r < 0) {
log_warn(LD_BUG, "Couldn't generate reverse lookup hostname of %s",
safe_str_client(a));
@@ -2398,41 +2656,47 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn)
}
log_debug(LD_APP,
- "Sending relay cell to begin stream %d.", ap_conn->stream_id);
+ "Sending relay cell to begin stream %d.", edge_conn->stream_id);
- if (connection_edge_send_command(ap_conn,
+ if (connection_edge_send_command(edge_conn,
RELAY_COMMAND_RESOLVE,
string_addr, payload_len) < 0)
return -1; /* circuit is closed, don't continue */
- tor_free(ap_conn->_base.address); /* Maybe already set by dnsserv. */
- ap_conn->_base.address = tor_strdup("(Tor_internal)");
- ap_conn->_base.state = AP_CONN_STATE_RESOLVE_WAIT;
+ tor_free(base_conn->address); /* Maybe already set by dnsserv. */
+ base_conn->address = tor_strdup("(Tor_internal)");
+ base_conn->state = AP_CONN_STATE_RESOLVE_WAIT;
log_info(LD_APP,"Address sent for resolve, ap socket %d, n_circ_id %d",
- ap_conn->_base.s, circ->_base.n_circ_id);
+ base_conn->s, circ->_base.n_circ_id);
control_event_stream_status(ap_conn, STREAM_EVENT_NEW, 0);
control_event_stream_status(ap_conn, STREAM_EVENT_SENT_RESOLVE, 0);
return 0;
}
-/** Make an AP connection_t, make a new linked connection pair, and attach
- * one side to the conn, connection_add it, initialize it to circuit_wait,
- * and call connection_ap_handshake_attach_circuit(conn) on it.
+/** Make an AP connection_t linked to the connection_t <b>partner</b>. make a
+ * new linked connection pair, and attach one side to the conn, connection_add
+ * it, initialize it to circuit_wait, and call
+ * connection_ap_handshake_attach_circuit(conn) on it.
*
- * Return the other end of the linked connection pair, or -1 if error.
+ * Return the newly created end of the linked connection pair, or -1 if error.
*/
-edge_connection_t *
-connection_ap_make_link(char *address, uint16_t port,
- const char *digest, int use_begindir, int want_onehop)
+entry_connection_t *
+connection_ap_make_link(connection_t *partner,
+ char *address, uint16_t port,
+ const char *digest,
+ int session_group, int isolation_flags,
+ int use_begindir, int want_onehop)
{
- edge_connection_t *conn;
+ entry_connection_t *conn;
+ connection_t *base_conn;
log_info(LD_APP,"Making internal %s tunnel to %s:%d ...",
want_onehop ? "direct" : "anonymized",
safe_str_client(address), port);
- conn = edge_connection_new(CONN_TYPE_AP, AF_INET);
- conn->_base.linked = 1; /* so that we can add it safely below. */
+ conn = entry_connection_new(CONN_TYPE_AP, tor_addr_family(&partner->addr));
+ base_conn = ENTRY_TO_CONN(conn);
+ base_conn->linked = 1; /* so that we can add it safely below. */
/* populate conn->socks_request */
@@ -2453,22 +2717,30 @@ connection_ap_make_link(char *address, uint16_t port,
digest, DIGEST_LEN);
}
- conn->_base.address = tor_strdup("(Tor_internal)");
- tor_addr_make_unspec(&conn->_base.addr);
- conn->_base.port = 0;
+ /* Populate isolation fields. */
+ conn->socks_request->listener_type = CONN_TYPE_DIR_LISTENER;
+ conn->original_dest_address = tor_strdup(address);
+ conn->session_group = session_group;
+ conn->isolation_flags = isolation_flags;
- if (connection_add(TO_CONN(conn)) < 0) { /* no space, forget it */
- connection_free(TO_CONN(conn));
+ base_conn->address = tor_strdup("(Tor_internal)");
+ tor_addr_make_unspec(&base_conn->addr);
+ base_conn->port = 0;
+
+ connection_link_connections(partner, base_conn);
+
+ if (connection_add(base_conn) < 0) { /* no space, forget it */
+ connection_free(base_conn);
return NULL;
}
- conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
/* attaching to a dirty circuit is fine */
if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!conn->_base.marked_for_close)
+ if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return NULL;
}
@@ -2481,7 +2753,7 @@ connection_ap_make_link(char *address, uint16_t port,
* or resolve error. Takes the same arguments as does
* connection_ap_handshake_socks_resolved(). */
static void
-tell_controller_about_resolved_result(edge_connection_t *conn,
+tell_controller_about_resolved_result(entry_connection_t *conn,
int answer_type,
size_t answer_len,
const char *answer,
@@ -2493,13 +2765,11 @@ tell_controller_about_resolved_result(edge_connection_t *conn,
answer_type == RESOLVED_TYPE_HOSTNAME)) {
return; /* we already told the controller. */
} else if (answer_type == RESOLVED_TYPE_IPV4 && answer_len >= 4) {
- struct in_addr in;
- char buf[INET_NTOA_BUF_LEN];
- in.s_addr = get_uint32(answer);
- tor_inet_ntoa(&in, buf, sizeof(buf));
+ char *cp = tor_dup_ip(ntohl(get_uint32(answer)));
control_event_address_mapped(conn->socks_request->address,
- buf, expires, NULL);
- } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len <256) {
+ cp, expires, NULL);
+ tor_free(cp);
+ } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256) {
char *cp = tor_strndup(answer, answer_len);
control_event_address_mapped(conn->socks_request->address,
cp, expires, NULL);
@@ -2522,7 +2792,7 @@ tell_controller_about_resolved_result(edge_connection_t *conn,
/* XXXX023 the use of the ttl and expires fields is nutty. Let's make this
* interface and those that use it less ugly. */
void
-connection_ap_handshake_socks_resolved(edge_connection_t *conn,
+connection_ap_handshake_socks_resolved(entry_connection_t *conn,
int answer_type,
size_t answer_len,
const uint8_t *answer,
@@ -2547,7 +2817,7 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
}
}
- if (conn->is_dns_request) {
+ if (ENTRY_TO_EDGE_CONN(conn)->is_dns_request) {
if (conn->dns_server_request) {
/* We had a request on our DNS port: answer it. */
dnsserv_resolved(conn, answer_type, answer_len, (char*)answer, ttl);
@@ -2627,7 +2897,7 @@ connection_ap_handshake_socks_resolved(edge_connection_t *conn,
* be 0 or REASON_DONE. Send endreason to the controller, if appropriate.
*/
void
-connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
+connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
size_t replylen, int endreason)
{
char buf[256];
@@ -2646,7 +2916,7 @@ connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
return;
}
if (replylen) { /* we already have a reply in mind */
- connection_write_to_buf(reply, replylen, TO_CONN(conn));
+ connection_write_to_buf(reply, replylen, ENTRY_TO_CONN(conn));
conn->socks_request->has_finished = 1;
return;
}
@@ -2654,7 +2924,7 @@ connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
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, TO_CONN(conn));
+ connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, ENTRY_TO_CONN(conn));
} else if (conn->socks_request->socks_version == 5) {
buf[0] = 5; /* version 5 */
buf[1] = (char)status;
@@ -2662,7 +2932,7 @@ connection_ap_handshake_socks_reply(edge_connection_t *conn, char *reply,
buf[3] = 1; /* ipv4 addr */
memset(buf+4,0,6); /* Set external addr/port to 0.
The spec doesn't seem to say what to do here. -RD */
- connection_write_to_buf(buf,10,TO_CONN(conn));
+ connection_write_to_buf(buf,10,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. */
@@ -2696,7 +2966,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
char *address=NULL;
uint16_t port;
or_circuit_t *or_circ = NULL;
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
assert_circuit_ok(circ);
if (!CIRCUIT_IS_ORIGIN(circ))
@@ -2728,9 +2998,9 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
END_STREAM_REASON_TORPROTOCOL, NULL);
return 0;
}
- if (parse_addr_port(LOG_PROTOCOL_WARN,
- (char*)(cell->payload+RELAY_HEADER_SIZE),
- &address,NULL,&port)<0) {
+ if (tor_addr_port_split(LOG_PROTOCOL_WARN,
+ (char*)(cell->payload+RELAY_HEADER_SIZE),
+ &address,&port)<0) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Unable to parse addr:port in relay begin cell. Closing.");
relay_send_end_cell_from_edge(rh.stream_id, circ,
@@ -2979,12 +3249,13 @@ connection_exit_connect(edge_connection_t *edge_conn)
}
conn->state = EXIT_CONN_STATE_OPEN;
- if (connection_wants_to_flush(conn)) {
+ if (connection_get_outbuf_len(conn)) {
/* in case there are any queued data cells */
log_warn(LD_BUG,"newly connected conn had data waiting!");
// connection_start_writing(conn);
}
- connection_watch_events(conn, READ_EVENT);
+ IF_HAS_NO_BUFFEREVENT(conn)
+ connection_watch_events(conn, READ_EVENT);
/* also, deliver a 'connected' cell back through the circuit. */
if (connection_edge_is_rendezvous_stream(edge_conn)) {
@@ -3029,7 +3300,7 @@ connection_exit_connect_dir(edge_connection_t *exitconn)
exitconn->_base.state = EXIT_CONN_STATE_OPEN;
- dirconn = dir_connection_new(AF_INET);
+ dirconn = dir_connection_new(tor_addr_family(&exitconn->_base.addr));
tor_addr_copy(&dirconn->_base.addr, &exitconn->_base.addr);
dirconn->_base.port = 0;
@@ -3094,12 +3365,11 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
* resolved.)
*/
int
-connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
+connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
{
- or_options_t *options = get_options();
+ const or_options_t *options = get_options();
tor_assert(conn);
- tor_assert(conn->_base.type == CONN_TYPE_AP);
tor_assert(conn->socks_request);
tor_assert(exit);
@@ -3107,10 +3377,10 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
* make sure the exit node of the existing circuit matches exactly.
*/
if (conn->chosen_exit_name) {
- routerinfo_t *chosen_exit =
- router_get_by_nickname(conn->chosen_exit_name, 1);
- if (!chosen_exit || tor_memneq(chosen_exit->cache_info.identity_digest,
- exit->cache_info.identity_digest, DIGEST_LEN)) {
+ const node_t *chosen_exit =
+ node_get_by_nickname(conn->chosen_exit_name, 1);
+ if (!chosen_exit || tor_memneq(chosen_exit->identity,
+ exit->identity, DIGEST_LEN)) {
/* doesn't match */
// log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
// conn->chosen_exit_name, exit->nickname);
@@ -3118,15 +3388,20 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
}
}
- if (conn->socks_request->command == SOCKS_COMMAND_CONNECT &&
- !conn->use_begindir) {
+ if (conn->use_begindir) {
+ /* Internal directory fetches do not count as exiting. */
+ return 1;
+ }
+
+ if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) {
struct in_addr in;
- uint32_t addr = 0;
+ tor_addr_t addr, *addrp = NULL;
addr_policy_result_t r;
- if (tor_inet_aton(conn->socks_request->address, &in))
- addr = ntohl(in.s_addr);
- r = compare_addr_to_addr_policy(addr, conn->socks_request->port,
- exit->exit_policy);
+ if (tor_inet_aton(conn->socks_request->address, &in)) {
+ tor_addr_from_in(&addr, &in);
+ addrp = &addr;
+ }
+ r = compare_tor_addr_to_node_policy(addrp, conn->socks_request->port,exit);
if (r == ADDR_POLICY_REJECTED)
return 0; /* We know the address, and the exit policy rejects it. */
if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name)
@@ -3134,17 +3409,11 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
* addresses with this port. Since the user didn't ask for
* this node, err on the side of caution. */
} else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
- /* Can't support reverse lookups without eventdns. */
- if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR &&
- exit->has_old_dnsworkers)
- return 0;
-
/* Don't send DNS requests to non-exit servers by default. */
- if (!conn->chosen_exit_name && policy_is_reject_star(exit->exit_policy))
+ if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit))
return 0;
}
- if (options->_ExcludeExitNodesUnion &&
- routerset_contains_router(options->_ExcludeExitNodesUnion, exit)) {
+ if (routerset_contains_node(options->_ExcludeExitNodesUnion, exit)) {
/* Not a suitable exit. Refuse it. */
return 0;
}
@@ -3155,9 +3424,15 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit)
/** 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.
*
+ * If address is of the form "y.onion" with a badly-formed handle y:
+ * Return BAD_HOSTNAME and log a message.
+ *
* If address is of the form "y.exit" and <b>allowdotexit</b> is true:
* Put a NUL after y and return EXIT_HOSTNAME.
*
+ * If address is of the form "y.exit" and <b>allowdotexit</b> is false:
+ * Return BAD_HOSTNAME and log a message.
+ *
* Otherwise:
* Return NORMAL_HOSTNAME and change nothing.
*/
@@ -3196,6 +3471,210 @@ parse_extended_hostname(char *address, int allowdotexit)
failed:
/* otherwise, return to previous state and return 0 */
*s = '.';
+ log_warn(LD_APP, "Invalid onion hostname %s; rejecting",
+ safe_str_client(address));
return BAD_HOSTNAME;
}
+/** Return true iff the (possibly NULL) <b>alen</b>-byte chunk of memory at
+ * <b>a</b> is equal to the (possibly NULL) <b>blen</b>-byte chunk of memory
+ * at <b>b</b>. */
+static int
+memeq_opt(const char *a, size_t alen, const char *b, size_t blen)
+{
+ if (a == NULL) {
+ return (b == NULL);
+ } else if (b == NULL) {
+ return 0;
+ } else if (alen != blen) {
+ return 0;
+ } else {
+ return tor_memeq(a, b, alen);
+ }
+}
+
+/**
+ * Return true iff none of the isolation flags and fields in <b>conn</b>
+ * should prevent it from being attached to <b>circ</b>.
+ */
+int
+connection_edge_compatible_with_circuit(const entry_connection_t *conn,
+ const origin_circuit_t *circ)
+{
+ const uint8_t iso = conn->isolation_flags;
+ const socks_request_t *sr = conn->socks_request;
+
+ /* If circ has never been used for an isolated connection, we can
+ * totally use it for this one. */
+ if (!circ->isolation_values_set)
+ return 1;
+
+ /* If circ has been used for connections having more than one value
+ * for some field f, it will have the corresponding bit set in
+ * isolation_flags_mixed. If isolation_flags_mixed has any bits
+ * in common with iso, then conn must be isolated from at least
+ * one stream that has been attached to circ. */
+ if ((iso & circ->isolation_flags_mixed) != 0) {
+ /* For at least one field where conn is isolated, the circuit
+ * already has mixed streams. */
+ return 0;
+ }
+
+ if (! conn->original_dest_address) {
+ log_warn(LD_BUG, "Reached connection_edge_compatible_with_circuit without "
+ "having set conn->original_dest_address");
+ ((entry_connection_t*)conn)->original_dest_address =
+ tor_strdup(conn->socks_request->address);
+ }
+
+ if ((iso & ISO_STREAM) &&
+ (circ->associated_isolated_stream_global_id !=
+ ENTRY_TO_CONN(conn)->global_identifier))
+ return 0;
+
+ if ((iso & ISO_DESTPORT) && conn->socks_request->port != circ->dest_port)
+ return 0;
+ if ((iso & ISO_DESTADDR) &&
+ strcasecmp(conn->original_dest_address, circ->dest_address))
+ return 0;
+ if ((iso & ISO_SOCKSAUTH) &&
+ (! memeq_opt(sr->username, sr->usernamelen,
+ circ->socks_username, circ->socks_username_len) ||
+ ! memeq_opt(sr->password, sr->passwordlen,
+ circ->socks_password, circ->socks_password_len)))
+ return 0;
+ if ((iso & ISO_CLIENTPROTO) &&
+ (conn->socks_request->listener_type != circ->client_proto_type ||
+ conn->socks_request->socks_version != circ->client_proto_socksver))
+ return 0;
+ if ((iso & ISO_CLIENTADDR) &&
+ !tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr))
+ return 0;
+ if ((iso & ISO_SESSIONGRP) && conn->session_group != circ->session_group)
+ return 0;
+ if ((iso & ISO_NYM_EPOCH) && conn->nym_epoch != circ->nym_epoch)
+ return 0;
+
+ return 1;
+}
+
+/**
+ * If <b>dry_run</b> is false, update <b>circ</b>'s isolation flags and fields
+ * to reflect having had <b>conn</b> attached to it, and return 0. Otherwise,
+ * if <b>dry_run</b> is true, then make no changes to <b>circ</b>, and return
+ * a bitfield of isolation flags that we would have to set in
+ * isolation_flags_mixed to add <b>conn</b> to <b>circ</b>, or -1 if
+ * <b>circ</b> has had no streams attached to it.
+ */
+int
+connection_edge_update_circuit_isolation(const entry_connection_t *conn,
+ origin_circuit_t *circ,
+ int dry_run)
+{
+ const socks_request_t *sr = conn->socks_request;
+ if (! conn->original_dest_address) {
+ log_warn(LD_BUG, "Reached connection_update_circuit_isolation without "
+ "having set conn->original_dest_address");
+ ((entry_connection_t*)conn)->original_dest_address =
+ tor_strdup(conn->socks_request->address);
+ }
+
+ if (!circ->isolation_values_set) {
+ if (dry_run)
+ return -1;
+ circ->associated_isolated_stream_global_id =
+ ENTRY_TO_CONN(conn)->global_identifier;
+ circ->dest_port = conn->socks_request->port;
+ circ->dest_address = tor_strdup(conn->original_dest_address);
+ circ->client_proto_type = conn->socks_request->listener_type;
+ circ->client_proto_socksver = conn->socks_request->socks_version;
+ tor_addr_copy(&circ->client_addr, &ENTRY_TO_CONN(conn)->addr);
+ circ->session_group = conn->session_group;
+ circ->nym_epoch = conn->nym_epoch;
+ circ->socks_username = sr->username ?
+ tor_memdup(sr->username, sr->usernamelen) : NULL;
+ circ->socks_password = sr->password ?
+ tor_memdup(sr->password, sr->passwordlen) : NULL;
+ circ->socks_username_len = sr->usernamelen;
+ circ->socks_password_len = sr->passwordlen;
+
+ circ->isolation_values_set = 1;
+ return 0;
+ } else {
+ uint8_t mixed = 0;
+ if (conn->socks_request->port != circ->dest_port)
+ mixed |= ISO_DESTPORT;
+ if (strcasecmp(conn->original_dest_address, circ->dest_address))
+ mixed |= ISO_DESTADDR;
+ if (!memeq_opt(sr->username, sr->usernamelen,
+ circ->socks_username, circ->socks_username_len) ||
+ !memeq_opt(sr->password, sr->passwordlen,
+ circ->socks_password, circ->socks_password_len))
+ mixed |= ISO_SOCKSAUTH;
+ if ((conn->socks_request->listener_type != circ->client_proto_type ||
+ conn->socks_request->socks_version != circ->client_proto_socksver))
+ mixed |= ISO_CLIENTPROTO;
+ if (!tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr))
+ mixed |= ISO_CLIENTADDR;
+ if (conn->session_group != circ->session_group)
+ mixed |= ISO_SESSIONGRP;
+ if (conn->nym_epoch != circ->nym_epoch)
+ mixed |= ISO_NYM_EPOCH;
+
+ if (dry_run)
+ return mixed;
+
+ if ((mixed & conn->isolation_flags) != 0) {
+ log_warn(LD_BUG, "Updating a circuit with seemingly incompatible "
+ "isolation flags.");
+ }
+ circ->isolation_flags_mixed |= mixed;
+ return 0;
+ }
+}
+
+/**
+ * Clear the isolation settings on <b>circ</b>.
+ *
+ * This only works on an open circuit that has never had a stream attached to
+ * it, and whose isolation settings are hypothetical. (We set hypothetical
+ * isolation settings on circuits as we're launching them, so that we
+ * know whether they can handle more streams or whether we need to launch
+ * even more circuits. Once the circuit is open, if it turns out that
+ * we no longer have any streams to attach to it, we clear the isolation flags
+ * and data so that other streams can have a chance.)
+ */
+void
+circuit_clear_isolation(origin_circuit_t *circ)
+{
+ if (circ->isolation_any_streams_attached) {
+ log_warn(LD_BUG, "Tried to clear the isolation status of a dirty circuit");
+ return;
+ }
+ if (TO_CIRCUIT(circ)->state != CIRCUIT_STATE_OPEN) {
+ log_warn(LD_BUG, "Tried to clear the isolation status of a non-open "
+ "circuit");
+ return;
+ }
+
+ circ->isolation_values_set = 0;
+ circ->isolation_flags_mixed = 0;
+ circ->associated_isolated_stream_global_id = 0;
+ circ->client_proto_type = 0;
+ circ->client_proto_socksver = 0;
+ circ->dest_port = 0;
+ tor_addr_make_unspec(&circ->client_addr);
+ tor_free(circ->dest_address);
+ circ->session_group = -1;
+ circ->nym_epoch = 0;
+ if (circ->socks_username) {
+ memset(circ->socks_username, 0x11, circ->socks_username_len);
+ tor_free(circ->socks_username);
+ }
+ if (circ->socks_password) {
+ memset(circ->socks_password, 0x05, circ->socks_password_len);
+ tor_free(circ->socks_password);
+ }
+ circ->socks_username_len = circ->socks_password_len = 0;
+}
+