diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/or/Makefile.am | 6 | ||||
-rw-r--r-- | src/or/circuit.c | 1736 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 1099 | ||||
-rw-r--r-- | src/or/circuitlist.c | 500 | ||||
-rw-r--r-- | src/or/circuituse.c | 821 | ||||
-rw-r--r-- | src/or/connection_edge.c | 716 | ||||
-rw-r--r-- | src/or/onion.c | 524 | ||||
-rw-r--r-- | src/or/or.h | 120 | ||||
-rw-r--r-- | src/or/relay.c | 617 | ||||
-rw-r--r-- | src/or/rendclient.c | 4 | ||||
-rw-r--r-- | src/or/rendservice.c | 4 |
11 files changed, 3110 insertions, 3037 deletions
diff --git a/src/or/Makefile.am b/src/or/Makefile.am index 961cd9514..a071bceba 100644 --- a/src/or/Makefile.am +++ b/src/or/Makefile.am @@ -4,7 +4,8 @@ noinst_PROGRAMS = test bin_PROGRAMS = tor -tor_SOURCES = buffers.c circuit.c command.c config.c \ +tor_SOURCES = buffers.c circuitbuild.c circuitlist.c \ + circuituse.c command.c config.c \ connection.c connection_edge.c connection_or.c \ cpuworker.c directory.c dirserv.c dns.c main.c \ onion.c relay.c rendcommon.c rendclient.c rendmid.c \ @@ -13,7 +14,8 @@ tor_SOURCES = buffers.c circuit.c command.c config.c \ tor_LDADD = ../common/libor.a -test_SOURCES = buffers.c circuit.c command.c config.c \ +test_SOURCES = buffers.c circuitbuild.c circuitlist.c \ + circuituse.c command.c config.c \ connection.c connection_edge.c connection_or.c \ cpuworker.c directory.c dirserv.c dns.c main.c \ onion.c relay.c rendcommon.c rendclient.c rendmid.c \ diff --git a/src/or/circuit.c b/src/or/circuit.c deleted file mode 100644 index 26c0501c1..000000000 --- a/src/or/circuit.c +++ /dev/null @@ -1,1736 +0,0 @@ -/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ -/* See LICENSE for licensing information */ -/* $Id$ */ - -/** - * \file circuit.c - * \brief Manage circuits and the global circuit list. - **/ - -#include "or.h" - -extern or_options_t options; /* command-line and config-file options */ - -static int circuit_resume_edge_reading_helper(connection_t *conn, - circuit_t *circ, - crypt_path_t *layer_hint); -static void circuit_free_cpath_node(crypt_path_t *victim); -static uint16_t get_unique_circ_id_by_conn(connection_t *conn, int circ_id_type); -static void circuit_rep_hist_note_result(circuit_t *circ); - -void circuit_expire_old_circuits(void); -static void circuit_is_open(circuit_t *circ); -static void circuit_build_failed(circuit_t *circ); -static circuit_t *circuit_establish_circuit(uint8_t purpose, const char *exit_nickname); -static void circuit_free(circuit_t *circ); -static void circuit_free_cpath(crypt_path_t *cpath); - -/********* START VARIABLES **********/ - -/** A global (within this file) list of all circuits at this hop. */ -static circuit_t *global_circuitlist=NULL; -/** How many entries are in global_circuitlist? */ -static int circuitlist_len=0; -/** Array of strings to make circ-\>state human-readable */ -char *circuit_state_to_string[] = { - "doing handshakes", /* 0 */ - "processing the onion", /* 1 */ - "connecting to firsthop", /* 2 */ - "open" /* 3 */ -}; - -/********* END VARIABLES ************/ - -/** Add <b>circ</b> to the global list of circuits. This is called only from - * within circuit_new. - */ -static void circuit_add(circuit_t *circ) { - if(!global_circuitlist) { /* first one */ - global_circuitlist = circ; - circ->next = NULL; - } else { - circ->next = global_circuitlist; - global_circuitlist = circ; - } - ++circuitlist_len; -} - -/** Detach from the global circuit list, and deallocate, all - * circuits that have been marked for close. - */ -void circuit_close_all_marked() -{ - circuit_t *tmp,*m; - - while (global_circuitlist && global_circuitlist->marked_for_close) { - tmp = global_circuitlist->next; - circuit_free(global_circuitlist); - global_circuitlist = tmp; - } - - tmp = global_circuitlist; - while (tmp && tmp->next) { - if (tmp->next->marked_for_close) { - m = tmp->next->next; - circuit_free(tmp->next); - tmp->next = m; - /* Need to check new tmp->next; don't advance tmp. */ - } else { - /* Advance tmp. */ - tmp = tmp->next; - } - } -} - -/** Allocate space for a new circuit, initializing with <b>p_circ_id</b> - * and <b>p_conn</b>. Add it to the global circuit list. - */ -circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn) { - circuit_t *circ; - - circ = tor_malloc_zero(sizeof(circuit_t)); - circ->magic = CIRCUIT_MAGIC; - - circ->timestamp_created = time(NULL); - - circ->p_circ_id = p_circ_id; - circ->p_conn = p_conn; - - circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; - - /* CircIDs */ - circ->p_circ_id = p_circ_id; - /* circ->n_circ_id remains 0 because we haven't identified the next hop yet */ - - circ->package_window = CIRCWINDOW_START; - circ->deliver_window = CIRCWINDOW_START; - - circ->next_stream_id = crypto_pseudo_rand_int(1<<16); - - circuit_add(circ); - - return circ; -} - -/** Deallocate space associated with circ. - */ -static void circuit_free(circuit_t *circ) { - tor_assert(circ); - tor_assert(circ->magic == CIRCUIT_MAGIC); - if (circ->n_crypto) - crypto_free_cipher_env(circ->n_crypto); - if (circ->p_crypto) - crypto_free_cipher_env(circ->p_crypto); - if (circ->n_digest) - crypto_free_digest_env(circ->n_digest); - if (circ->p_digest) - crypto_free_digest_env(circ->p_digest); - if(circ->build_state) { - tor_free(circ->build_state->chosen_exit); - if (circ->build_state->pending_final_cpath) - circuit_free_cpath_node(circ->build_state->pending_final_cpath); - } - tor_free(circ->build_state); - circuit_free_cpath(circ->cpath); - if (circ->rend_splice) { - circ->rend_splice->rend_splice = NULL; - } - - memset(circ, 0xAA, sizeof(circuit_t)); /* poison memory */ - free(circ); -} - -/** Deallocate space associated with the linked list <b>cpath</b>. */ -static void circuit_free_cpath(crypt_path_t *cpath) { - crypt_path_t *victim, *head=cpath; - - if(!cpath) - return; - - /* it's a doubly linked list, so we have to notice when we've - * gone through it once. */ - while(cpath->next && cpath->next != head) { - victim = cpath; - cpath = victim->next; - circuit_free_cpath_node(victim); - } - - circuit_free_cpath_node(cpath); -} - -/** Deallocate space associated with the cpath node <b>victim</b>. */ -static void circuit_free_cpath_node(crypt_path_t *victim) { - if(victim->f_crypto) - crypto_free_cipher_env(victim->f_crypto); - if(victim->b_crypto) - crypto_free_cipher_env(victim->b_crypto); - if(victim->f_digest) - crypto_free_digest_env(victim->f_digest); - if(victim->b_digest) - crypto_free_digest_env(victim->b_digest); - if(victim->handshake_state) - crypto_dh_free(victim->handshake_state); - free(victim); -} - -/** Iterate over values of circ_id, starting from conn-\>next_circ_id, - * and with the high bit specified by circ_id_type (see - * decide_circ_id_type()), until we get a circ_id that is not in use - * by any other circuit on that conn. - * - * Return it, or 0 if can't get a unique circ_id. - */ -static uint16_t get_unique_circ_id_by_conn(connection_t *conn, int circ_id_type) { - uint16_t test_circ_id; - int attempts=0; - uint16_t high_bit; - - tor_assert(conn && conn->type == CONN_TYPE_OR); - high_bit = (circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0; - do { - /* Sequentially iterate over test_circ_id=1...1<<15-1 until we find a - * circID such that (high_bit|test_circ_id) is not already used. */ - test_circ_id = conn->next_circ_id++; - if (test_circ_id == 0 || test_circ_id >= 1<<15) { - test_circ_id = 1; - conn->next_circ_id = 2; - } - if(++attempts > 1<<15) { - /* Make sure we don't loop forever if all circ_id's are used. This - * matters because it's an external DoS vulnerability. - */ - log_fn(LOG_WARN,"No unused circ IDs. Failing."); - return 0; - } - test_circ_id |= high_bit; - } while(circuit_get_by_circ_id_conn(test_circ_id, conn)); - return test_circ_id; -} - -/** Return a circ such that: - * - circ-\>n_circ_id or circ-\>p_circ_id is equal to <b>circ_id</b>, and - * - circ is attached to <b>conn</b>, either as p_conn, n-conn, or - * in p_streams or n_streams. - * Return NULL if no such circuit exists. - */ -circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn) { - circuit_t *circ; - connection_t *tmpconn; - - for(circ=global_circuitlist;circ;circ = circ->next) { - if (circ->marked_for_close) - continue; - - if(circ->p_circ_id == circ_id) { - if(circ->p_conn == conn) - return circ; - for(tmpconn = circ->p_streams; tmpconn; tmpconn = tmpconn->next_stream) { - if(tmpconn == conn) - return circ; - } - } - if(circ->n_circ_id == circ_id) { - if(circ->n_conn == conn) - return circ; - for(tmpconn = circ->n_streams; tmpconn; tmpconn = tmpconn->next_stream) { - if(tmpconn == conn) - return circ; - } - for(tmpconn = circ->resolving_streams; tmpconn; tmpconn = tmpconn->next_stream) { - if(tmpconn == conn) - return circ; - } - } - } - return NULL; -} - -/** Return a circ such that circ is attached to <b>conn</b>, either as - * p_conn, n-conn, or in p_streams or n_streams. - * - * Return NULL if no such circuit exists. - */ -circuit_t *circuit_get_by_conn(connection_t *conn) { - circuit_t *circ; - connection_t *tmpconn; - - for(circ=global_circuitlist;circ;circ = circ->next) { - if (circ->marked_for_close) - continue; - - if(circ->p_conn == conn) - return circ; - if(circ->n_conn == conn) - return circ; - for(tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) - if(tmpconn == conn) - return circ; - for(tmpconn = circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) - if(tmpconn == conn) - return circ; - for(tmpconn = circ->resolving_streams; tmpconn; tmpconn=tmpconn->next_stream) - if(tmpconn == conn) - return circ; - } - return NULL; -} - -/* Return 1 if <b>circ</b> could be returned by circuit_get_best(). - * Else return 0. - */ -static int circuit_is_acceptable(circuit_t *circ, - connection_t *conn, - int must_be_open, - uint8_t purpose, - time_t now) -{ - routerinfo_t *exitrouter; - - if (!CIRCUIT_IS_ORIGIN(circ)) - return 0; /* this circ doesn't start at us */ - if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn)) - return 0; /* ignore non-open circs */ - if (circ->marked_for_close) - return 0; - - /* if this circ isn't our purpose, skip. */ - if(purpose == CIRCUIT_PURPOSE_C_REND_JOINED && !must_be_open) { - if(circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND && - circ->purpose != CIRCUIT_PURPOSE_C_REND_READY && - circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED && - circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED) - return 0; - } else if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT && !must_be_open) { - if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCING && - circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) - return 0; - } else { - if(purpose != circ->purpose) - return 0; - } - - if(purpose == CIRCUIT_PURPOSE_C_GENERAL) - if(circ->timestamp_dirty && - circ->timestamp_dirty+options.NewCircuitPeriod < now) - return 0; - - if(conn) { - /* decide if this circ is suitable for this conn */ - - /* for rend circs, circ->cpath->prev is not the last router in the - * circuit, it's the magical extra bob hop. so just check the nickname - * of the one we meant to finish at. - */ - exitrouter = router_get_by_nickname(circ->build_state->chosen_exit); - - if(!exitrouter) { - log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)"); - return 0; /* this circuit is screwed and doesn't know it yet */ - } - - if(purpose == CIRCUIT_PURPOSE_C_GENERAL) { - if(connection_ap_can_use_exit(conn, exitrouter) == ADDR_POLICY_REJECTED) { - /* can't exit from this router */ - return 0; - } - } else { /* not general */ - if(rend_cmp_service_ids(conn->rend_query, circ->rend_query) && - (circ->rend_query[0] || purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) { - /* this circ is not for this conn, and it's not suitable - * for cannibalizing either */ - return 0; - } - } - } - return 1; -} - -/* Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for - * <b>purpose</b>, and return 0 otherwise. Used by circuit_get_best. - */ -static int circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose) -{ - switch(purpose) { - case CIRCUIT_PURPOSE_C_GENERAL: - /* if it's used but less dirty it's best; - * else if it's more recently created it's best - */ - if(b->timestamp_dirty) { - if(a->timestamp_dirty && - a->timestamp_dirty > b->timestamp_dirty) - return 1; - } else { - if(a->timestamp_dirty || - a->timestamp_created > b->timestamp_created) - return 1; - } - break; - case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: - /* the closer it is to ack_wait the better it is */ - if(a->purpose > b->purpose) - return 1; - break; - case CIRCUIT_PURPOSE_C_REND_JOINED: - /* the closer it is to rend_joined the better it is */ - if(a->purpose > b->purpose) - return 1; - break; - } - return 0; -} - -/** Find the best circ that conn can use, preferably one which is - * dirty. Circ must not be too old. - * - * Conn must be defined. - * - * If must_be_open, ignore circs not in CIRCUIT_STATE_OPEN. - * - * circ_purpose specifies what sort of circuit we must have. - * It can be C_GENERAL, C_INTRODUCE_ACK_WAIT, or C_REND_JOINED. - * - * If it's REND_JOINED and must_be_open==0, then return the closest - * rendezvous-purposed circuit that you can find. - * - * If it's INTRODUCE_ACK_WAIT and must_be_open==0, then return the - * closest introduce-purposed circuit that you can find. - */ -circuit_t *circuit_get_best(connection_t *conn, - int must_be_open, uint8_t purpose) { - circuit_t *circ, *best=NULL; - time_t now = time(NULL); - - tor_assert(conn); - - tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL || - purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT || - purpose == CIRCUIT_PURPOSE_C_REND_JOINED); - - for (circ=global_circuitlist;circ;circ = circ->next) { - if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,now)) - continue; - - /* now this is an acceptable circ to hand back. but that doesn't - * mean it's the *best* circ to hand back. try to decide. - */ - if(!best || circuit_is_better(circ,best,purpose)) - best = circ; - } - - return best; -} - -/** Return a circ such that: - * - circ-\>rend_query is equal to <b>rend_query</b>, and - * - circ-\>purpose is equal to <b>purpose</b>. - * - * Return NULL if no such circuit exists. - */ -circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose) { - circuit_t *circ; - - for (circ = global_circuitlist; circ; circ = circ->next) { - if (!circ->marked_for_close && - circ->purpose == purpose && - !rend_cmp_service_ids(rend_query, circ->rend_query)) - return circ; - } - return NULL; -} - -/** Return the first circuit in global_circuitlist after <b>start</b> whose - * rend_pk_digest field is <b>digest</b> and whose purpose is <b>purpose</b>. Returns - * NULL if no circuit is found. If <b>start</b> is NULL, begin at the start of - * the list. - */ -circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start, - const char *digest, uint8_t purpose) -{ - circuit_t *circ; - if (start == NULL) - circ = global_circuitlist; - else - circ = start->next; - - for( ; circ; circ = circ->next) { - if (circ->marked_for_close) - continue; - if (circ->purpose != purpose) - continue; - if (!memcmp(circ->rend_pk_digest, digest, DIGEST_LEN)) - return circ; - } - return NULL; -} - -/** Return the circuit waiting for a rendezvous with the provided cookie. - * Return NULL if no such circuit is found. - */ -circuit_t *circuit_get_rendezvous(const char *cookie) -{ - circuit_t *circ; - for (circ = global_circuitlist; circ; circ = circ->next) { - if (! circ->marked_for_close && - circ->purpose == CIRCUIT_PURPOSE_REND_POINT_WAITING && - ! memcmp(circ->rend_cookie, cookie, REND_COOKIE_LEN) ) - return circ; - } - return NULL; -} - -/** Circuits that were born at the end of their second might be expired - * after 30.1 seconds; circuits born at the beginning might be expired - * after closer to 31 seconds. - */ -#define MIN_SECONDS_BEFORE_EXPIRING_CIRC 30 - -/** Close all circuits that start at us, aren't open, and were born - * at least MIN_SECONDS_BEFORE_EXPIRING_CIRC seconds ago. - */ -void circuit_expire_building(time_t now) { - circuit_t *victim, *circ = global_circuitlist; - - while(circ) { - victim = circ; - circ = circ->next; - if(!CIRCUIT_IS_ORIGIN(victim)) - continue; /* didn't originate here */ - if(victim->marked_for_close) - continue; /* don't mess with marked circs */ - if(victim->timestamp_created + MIN_SECONDS_BEFORE_EXPIRING_CIRC > now) - continue; /* it's young still, don't mess with it */ - - /* some debug logs, to help track bugs */ - if(victim->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING && - victim->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { - if(!victim->timestamp_dirty) - log_fn(LOG_DEBUG,"Considering %sopen purp %d to %s (circid %d). (clean).", - victim->state == CIRCUIT_STATE_OPEN ? "" : "non", - victim->purpose, victim->build_state->chosen_exit, - victim->n_circ_id); - else - log_fn(LOG_DEBUG,"Considering %sopen purp %d to %s (circid %d). %d secs since dirty.", - victim->state == CIRCUIT_STATE_OPEN ? "" : "non", - victim->purpose, victim->build_state->chosen_exit, - victim->n_circ_id, - (int)(now - victim->timestamp_dirty)); - } - - /* if circ is !open, or if it's open but purpose is a non-finished - * intro or rend, then mark it for close */ - if(victim->state != CIRCUIT_STATE_OPEN || - victim->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || - victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCING || - victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || - - /* it's a rend_ready circ, but it's already picked a query */ - (victim->purpose == CIRCUIT_PURPOSE_C_REND_READY && - victim->rend_query[0]) || - - /* c_rend_ready circs measure age since timestamp_dirty, - * because that's set when they switch purposes - */ - /* rend and intro circs become dirty each time they - * make an introduction attempt. so timestamp_dirty - * will reflect the time since the last attempt. - */ - ((victim->purpose == CIRCUIT_PURPOSE_C_REND_READY || - victim->purpose == CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED || - victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) && - victim->timestamp_dirty + MIN_SECONDS_BEFORE_EXPIRING_CIRC > now)) { - if(victim->n_conn) - log_fn(LOG_INFO,"Abandoning circ %s:%d:%d (state %d:%s, purpose %d)", - victim->n_conn->address, victim->n_port, victim->n_circ_id, - victim->state, circuit_state_to_string[victim->state], victim->purpose); - else - log_fn(LOG_INFO,"Abandoning circ %d (state %d:%s, purpose %d)", victim->n_circ_id, - victim->state, circuit_state_to_string[victim->state], victim->purpose); - circuit_log_path(LOG_INFO,victim); - circuit_mark_for_close(victim); - } - } -} - -/** Count the number of circs originating here that aren't open, and - * that have the specified <b>purpose</b>. */ -int circuit_count_building(uint8_t purpose) { - circuit_t *circ; - int num=0; - - for(circ=global_circuitlist;circ;circ = circ->next) { - if(CIRCUIT_IS_ORIGIN(circ) && - circ->state != CIRCUIT_STATE_OPEN && - circ->purpose == purpose && - !circ->marked_for_close) - num++; - } - return num; -} - -/** How many circuits do we want simultaneously in-progress to handle - * a given stream? - */ -#define MIN_CIRCUITS_HANDLING_STREAM 2 - -/** Return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open - * general-purpose circuits will have an acceptable exit node for - * conn. Else return 0. - */ -int circuit_stream_is_being_handled(connection_t *conn) { - circuit_t *circ; - routerinfo_t *exitrouter; - int num=0; - time_t now = time(NULL); - - for(circ=global_circuitlist;circ;circ = circ->next) { - if(CIRCUIT_IS_ORIGIN(circ) && circ->state != CIRCUIT_STATE_OPEN && - !circ->marked_for_close && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL && - (!circ->timestamp_dirty || - circ->timestamp_dirty + options.NewCircuitPeriod < now)) { - exitrouter = router_get_by_nickname(circ->build_state->chosen_exit); - if(exitrouter && connection_ap_can_use_exit(conn, exitrouter) != ADDR_POLICY_REJECTED) - if(++num >= MIN_CIRCUITS_HANDLING_STREAM) - return 1; - } - } - return 0; -} - -/** Return the circuit that is open, has specified <b>purpose</b>, - * has a timestamp_dirty value of 0, and was created most recently, - * or NULL if no circuit fits this description. - */ -static circuit_t * -circuit_get_youngest_clean_open(uint8_t purpose) { - circuit_t *circ; - circuit_t *youngest=NULL; - - for(circ=global_circuitlist;circ;circ = circ->next) { - if(CIRCUIT_IS_ORIGIN(circ) && circ->state == CIRCUIT_STATE_OPEN && - !circ->marked_for_close && circ->purpose == purpose && - !circ->timestamp_dirty && - (!youngest || youngest->timestamp_created < circ->timestamp_created)) - youngest = circ; - } - return youngest; -} - -/** Build a new test circuit every 5 minutes */ -#define TESTING_CIRCUIT_INTERVAL 300 - -/** This function is called once a second. Its job is to make sure - * all services we offer have enough circuits available. Some - * services just want enough circuits for current tasks, whereas - * others want a minimum set of idle circuits hanging around. - */ -void circuit_build_needed_circs(time_t now) { - static long time_to_new_circuit = 0; - circuit_t *circ; - - /* launch a new circ for any pending streams that need one */ - connection_ap_attach_pending(); - - /* make sure any hidden services have enough intro points */ - rend_services_introduce(); - - circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL); - - if(time_to_new_circuit < now) { - circuit_reset_failure_count(); - time_to_new_circuit = now + options.NewCircuitPeriod; - if(options.SocksPort) - client_dns_clean(); - circuit_expire_old_circuits(); - - if(options.RunTesting && circ && - circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) { - log_fn(LOG_INFO,"Creating a new testing circuit."); - circuit_launch_new(CIRCUIT_PURPOSE_C_GENERAL, NULL); - } - } - -/** How many simultaneous in-progress general-purpose circuits do we - * want to be building at once, if there are no open general-purpose - * circuits? - */ -#define CIRCUIT_MIN_BUILDING_GENERAL 3 - /* if there's no open circ, and less than 3 are on the way, - * go ahead and try another. */ - if(!circ && circuit_count_building(CIRCUIT_PURPOSE_C_GENERAL) - < CIRCUIT_MIN_BUILDING_GENERAL) { - circuit_launch_new(CIRCUIT_PURPOSE_C_GENERAL, NULL); - } - - /* XXX count idle rendezvous circs and build more */ -} - -/** The circuit <b>circ</b> has received a circuit-level sendme - * (on hop <b>layer_hint</b>, if we're the OP). Go through all the - * attached streams and let them resume reading and packaging, if - * their stream windows allow it. - */ -void circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint) { - - log_fn(LOG_DEBUG,"resuming"); - - /* have to check both n_streams and p_streams, to handle rendezvous */ - if(circuit_resume_edge_reading_helper(circ->n_streams, circ, layer_hint) >= 0) - circuit_resume_edge_reading_helper(circ->p_streams, circ, layer_hint); -} - -/** A helper function for circuit_resume_edge_reading() above. - * The arguments are the same, except that <b>conn</b> is the head - * of a linked list of edge streams that should each be considered. - */ -static int -circuit_resume_edge_reading_helper(connection_t *conn, - circuit_t *circ, - crypt_path_t *layer_hint) { - - for( ; conn; conn=conn->next_stream) { - if((!layer_hint && conn->package_window > 0) || - (layer_hint && conn->package_window > 0 && conn->cpath_layer == layer_hint)) { - connection_start_reading(conn); - /* handle whatever might still be on the inbuf */ - connection_edge_package_raw_inbuf(conn); - - /* If the circuit won't accept any more data, return without looking - * at any more of the streams. Any connections that should be stopped - * have already been stopped by connection_edge_package_raw_inbuf. */ - if(circuit_consider_stop_edge_reading(circ, layer_hint)) - return -1; - } - } - return 0; -} - -/** Check if the package window for <b>circ</b> is empty (at - * hop <b>layer_hint</b> if it's defined). - * - * If yes, tell edge streams to stop reading and return -1. - * Else return 0. - */ -int circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint) { - connection_t *conn = NULL; - - log_fn(LOG_DEBUG,"considering"); - if(!layer_hint && circ->package_window <= 0) { - log_fn(LOG_DEBUG,"yes, not-at-origin. stopped."); - for(conn = circ->n_streams; conn; conn=conn->next_stream) - connection_stop_reading(conn); - return -1; - } else if(layer_hint && layer_hint->package_window <= 0) { - log_fn(LOG_DEBUG,"yes, at-origin. stopped."); - for(conn = circ->n_streams; conn; conn=conn->next_stream) - if(conn->cpath_layer == layer_hint) - connection_stop_reading(conn); - for(conn = circ->p_streams; conn; conn=conn->next_stream) - if(conn->cpath_layer == layer_hint) - connection_stop_reading(conn); - return -1; - } - return 0; -} - -/** Check if the deliver_window for circuit <b>circ</b> (at hop - * <b>layer_hint</b> if it's defined) is low enough that we should - * send a circuit-level sendme back down the circuit. If so, send - * enough sendmes that the window would be overfull if we sent any - * more. - */ -void circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) { -// log_fn(LOG_INFO,"Considering: layer_hint is %s", -// layer_hint ? "defined" : "null"); - while((layer_hint ? layer_hint->deliver_window : circ->deliver_window) < - CIRCWINDOW_START - CIRCWINDOW_INCREMENT) { - log_fn(LOG_DEBUG,"Queueing circuit sendme."); - if(layer_hint) - layer_hint->deliver_window += CIRCWINDOW_INCREMENT; - else - circ->deliver_window += CIRCWINDOW_INCREMENT; - if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_SENDME, - NULL, 0, layer_hint) < 0) { - log_fn(LOG_WARN,"connection_edge_send_command failed. Circuit's closed."); - return; /* the circuit's closed, don't continue */ - } - } -} - -/** Mark <b>circ</b> to be closed next time we call - * circuit_close_all_marked(). Do any cleanup needed: - * - If state is onionskin_pending, remove circ from the onion_pending - * list. - * - If circ isn't open yet, call circuit_build_failed() if we're - * the origin, and in case call circuit_rep_hist_note_result() - * to note stats. - * - If purpose is C_INTRODUCE_ACK_WAIT, remove the intro point we - * just tried from our list of intro points for that service - * descriptor. - * - Send appropriate destroys and edge_destroys for conns and - * streams attached to circ. - * - If circ->rend_splice is set (we are the midpoint of a joined - * rendezvous stream), then mark the other circuit to close as well. - */ -int _circuit_mark_for_close(circuit_t *circ) { - connection_t *conn; - - assert_circuit_ok(circ); - if (circ->marked_for_close) - return -1; - - if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { - onion_pending_remove(circ); - } - /* If the circuit ever became OPEN, we sent it to the reputation history - * module then. If it isn't OPEN, we send it there now to remember which - * links worked and which didn't. - */ - if (circ->state != CIRCUIT_STATE_OPEN) { - if(CIRCUIT_IS_ORIGIN(circ)) - circuit_build_failed(circ); /* take actions if necessary */ - circuit_rep_hist_note_result(circ); - } - if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { - tor_assert(circ->state == CIRCUIT_STATE_OPEN); - /* treat this like getting a nack from it */ - log_fn(LOG_INFO,"Failed intro circ %s to %s (awaiting ack). Removing from descriptor.", - circ->rend_query, circ->build_state->chosen_exit); - rend_client_remove_intro_point(circ->build_state->chosen_exit, circ->rend_query); - } - - if(circ->n_conn) - connection_send_destroy(circ->n_circ_id, circ->n_conn); - for(conn=circ->n_streams; conn; conn=conn->next_stream) - connection_edge_destroy(circ->n_circ_id, conn); - while(circ->resolving_streams) { - conn = circ->resolving_streams; - circ->resolving_streams = conn->next_stream; - log_fn(LOG_INFO,"Freeing resolving-conn."); - connection_free(conn); - } - if(circ->p_conn) - connection_send_destroy(circ->p_circ_id, circ->p_conn); - for(conn=circ->p_streams; conn; conn=conn->next_stream) - connection_edge_destroy(circ->p_circ_id, conn); - - circ->marked_for_close = 1; - - if (circ->rend_splice && !circ->rend_splice->marked_for_close) { - /* do this after marking this circuit, to avoid infinite recursion. */ - circuit_mark_for_close(circ->rend_splice); - circ->rend_splice = NULL; - } - return 0; -} - -/** If the stream <b>conn</b> is a member of any of the linked - * lists of <b>circ</b>, then remove it from the list. - */ -void circuit_detach_stream(circuit_t *circ, connection_t *conn) { - connection_t *prevconn; - - tor_assert(circ && conn); - - if(conn == circ->p_streams) { - circ->p_streams = conn->next_stream; - return; - } - if(conn == circ->n_streams) { - circ->n_streams = conn->next_stream; - return; - } - if(conn == circ->resolving_streams) { - circ->resolving_streams = conn->next_stream; - return; - } - - for(prevconn = circ->p_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if(prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; - return; - } - - for(prevconn = circ->n_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if(prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; - return; - } - - for(prevconn = circ->resolving_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if(prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; - return; - } - - log_fn(LOG_ERR,"edge conn not in circuit's list?"); - tor_assert(0); /* should never get here */ -} - -/** Notify the global circuit list that <b>conn</b> is about to be - * removed and then freed. - * - * If it's an OR conn, then mark-for-close all the circuits that use - * that conn. - * - * If it's an edge conn, then detach it from its circ, so we don't - * try to reference it later. - */ -void circuit_about_to_close_connection(connection_t *conn) { - /* currently, we assume it's too late to flush conn's buf here. - * down the road, maybe we'll consider that eof doesn't mean can't-write - */ - circuit_t *circ; - - switch(conn->type) { - case CONN_TYPE_OR: - /* We must close all the circuits on it. */ - while((circ = circuit_get_by_conn(conn))) { - if(circ->n_conn == conn) /* it's closing in front of us */ - circ->n_conn = NULL; - if(circ->p_conn == conn) /* it's closing behind us */ - circ->p_conn = NULL; - circuit_mark_for_close(circ); - } - return; - case CONN_TYPE_AP: - case CONN_TYPE_EXIT: - - /* It's an edge conn. Need to remove it from the linked list of - * conn's for this circuit. Confirm that 'end' relay command has - * been sent. But don't kill the circuit. - */ - - circ = circuit_get_by_conn(conn); - if(!circ) - return; - - circuit_detach_stream(circ, conn); - - } /* end switch */ -} - -/** Log, at severity <b>severity</b>, the nicknames of each router in - * circ's cpath. Also log the length of the cpath, and the intended - * exit point. - */ -void circuit_log_path(int severity, circuit_t *circ) { - char buf[1024]; - char *s = buf; - struct crypt_path_t *hop; - char *states[] = {"closed", "waiting for keys", "open"}; - routerinfo_t *router; - tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath); - - snprintf(s, sizeof(buf)-1, "circ (length %d, exit %s): ", - circ->build_state->desired_path_len, circ->build_state->chosen_exit); - hop=circ->cpath; - do { - s = buf + strlen(buf); - router = router_get_by_addr_port(hop->addr,hop->port); - if(router) { - snprintf(s, sizeof(buf) - (s - buf), "%s(%s) ", - router->nickname, states[hop->state]); - } else { - if(circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { - snprintf(s, sizeof(buf) - (s - buf), "(rendjoin hop)"); - } else { - snprintf(s, sizeof(buf) - (s - buf), "UNKNOWN "); - } - } - hop=hop->next; - } while(hop!=circ->cpath); - log_fn(severity,"%s",buf); -} - -/** Tell the rep(utation)hist(ory) module about the status of the links - * in circ. Hops that have become OPEN are marked as successfully - * extended; the _first_ hop that isn't open (if any) is marked as - * unable to extend. - */ -static void -circuit_rep_hist_note_result(circuit_t *circ) -{ - struct crypt_path_t *hop; - char *prev_nickname = NULL; - routerinfo_t *router; - hop = circ->cpath; - if(!hop) { - /* XXX - * if !hop, then we're not the beginning of this circuit. - * for now, just forget about it. later, we should remember when - * extends-through-us failed, too. - */ - return; - } - if (options.ORPort) { - prev_nickname = options.Nickname; - } - do { - router = router_get_by_addr_port(hop->addr,hop->port); - if (router) { - if (prev_nickname) { - if (hop->state == CPATH_STATE_OPEN) - rep_hist_note_extend_succeeded(prev_nickname, router->nickname); - else { - rep_hist_note_extend_failed(prev_nickname, router->nickname); - break; - } - } - prev_nickname = router->nickname; - } else { - prev_nickname = NULL; - } - hop=hop->next; - } while (hop!=circ->cpath); -} - -/** A helper function for circuit_dump_by_conn() below. Log a bunch - * of information about circuit <b>circ</b>. - */ -static void -circuit_dump_details(int severity, circuit_t *circ, int poll_index, - char *type, int this_circid, int other_circid) { - struct crypt_path_t *hop; - log(severity,"Conn %d has %s circuit: circID %d (other side %d), state %d (%s), born %d", - poll_index, type, this_circid, other_circid, circ->state, - circuit_state_to_string[circ->state], (int)circ->timestamp_created); - if(CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ - if(circ->state == CIRCUIT_STATE_BUILDING) - log(severity,"Building: desired len %d, planned exit node %s.", - circ->build_state->desired_path_len, circ->build_state->chosen_exit); - for(hop=circ->cpath;hop->next != circ->cpath; hop=hop->next) - log(severity,"hop: state %d, addr 0x%.8x, port %d", hop->state, - (unsigned int)hop->addr, - (int)hop->port); - } -} - -/** Log, at severity <b>severity</b>, information about each circuit - * that is connected to <b>conn</b>. - */ -void circuit_dump_by_conn(connection_t *conn, int severity) { - circuit_t *circ; - connection_t *tmpconn; - - for(circ=global_circuitlist;circ;circ = circ->next) { - if(circ->p_conn == conn) - circuit_dump_details(severity, circ, conn->poll_index, "App-ward", - circ->p_circ_id, circ->n_circ_id); - for(tmpconn=circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { - if(tmpconn == conn) { - circuit_dump_details(severity, circ, conn->poll_index, "App-ward", - circ->p_circ_id, circ->n_circ_id); - } - } - if(circ->n_conn == conn) - circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward", - circ->n_circ_id, circ->p_circ_id); - for(tmpconn=circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { - if(tmpconn == conn) { - circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward", - circ->n_circ_id, circ->p_circ_id); - } - } - } -} - -/** Don't keep more than 10 unused open circuits around. */ -#define MAX_UNUSED_OPEN_CIRCUITS 10 - -/** Find each circuit that has been dirty for too long, and has - * no streams on it: mark it for close. - * - * Also, if there are more than MAX_UNUSED_OPEN_CIRCUITS open and - * unused circuits, then mark the excess circs for close. - */ -void circuit_expire_old_circuits(void) { - circuit_t *circ; - time_t now = time(NULL); - smartlist_t *unused_open_circs; - int i; - - unused_open_circs = smartlist_create(); - - for (circ = global_circuitlist; circ; circ = circ->next) { - if (circ->marked_for_close) - continue; - /* If the circuit has been dirty for too long, and there are no streams - * on it, mark it for close. - */ - if (circ->timestamp_dirty && - circ->timestamp_dirty + options.NewCircuitPeriod < now && - !circ->p_conn && /* we're the origin */ - !circ->p_streams /* nothing attached */ ) { - log_fn(LOG_DEBUG,"Closing n_circ_id %d (dirty %d secs ago, purp %d)",circ->n_circ_id, - (int)(now - circ->timestamp_dirty), circ->purpose); - /* (only general and purpose_c circs can get dirty) */ - tor_assert(!circ->n_streams); - tor_assert(circ->purpose <= CIRCUIT_PURPOSE_C_REND_JOINED); - circuit_mark_for_close(circ); - } else if (!circ->timestamp_dirty && CIRCUIT_IS_ORIGIN(circ) && - circ->state == CIRCUIT_STATE_OPEN && - circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) { - /* Also, gather a list of open unused general circuits that we created. - * Because we add elements to the front of global_circuitlist, - * the last elements of unused_open_circs will be the oldest - * ones. - */ - smartlist_add(unused_open_circs, circ); - } - } - for (i = MAX_UNUSED_OPEN_CIRCUITS; i < smartlist_len(unused_open_circs); ++i) { - circuit_t *circ = smartlist_get(unused_open_circs, i); - log_fn(LOG_DEBUG,"Expiring excess clean circ (n_circ_id %d, purp %d)", - circ->n_circ_id, circ->purpose); - circuit_mark_for_close(circ); - } - smartlist_free(unused_open_circs); -} - -/** The circuit <b>circ</b> has just become open. Take the next - * step: for rendezvous circuits, we pass circ to the appropriate - * function in rendclient or rendservice. For general circuits, we - * call connection_ap_attach_pending, which looks for pending streams - * that could use circ. - */ -static void circuit_is_open(circuit_t *circ) { - - switch(circ->purpose) { - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - rend_client_rendcirc_is_open(circ); - break; - case CIRCUIT_PURPOSE_C_INTRODUCING: - rend_client_introcirc_is_open(circ); - break; - case CIRCUIT_PURPOSE_C_GENERAL: - /* Tell any AP connections that have been waiting for a new - * circuit that one is ready. */ - connection_ap_attach_pending(); - break; - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - /* at Bob, waiting for introductions */ - rend_service_intro_is_ready(circ); - break; - case CIRCUIT_PURPOSE_S_CONNECT_REND: - /* at Bob, connecting to rend point */ - rend_service_rendezvous_is_ready(circ); - break; - default: - log_fn(LOG_ERR,"unhandled purpose %d",circ->purpose); - tor_assert(0); - } -} - -/*~ Called whenever a circuit could not be successfully built. - */ -static void circuit_build_failed(circuit_t *circ) { - - /* we should examine circ and see if it failed because of - * the last hop or an earlier hop. then use this info below. - */ - int failed_at_last_hop = 0; - /* If the last hop isn't open, and the second-to-last is, we failed - * at the last hop. */ - if (circ->cpath && - circ->cpath->prev->state != CPATH_STATE_OPEN && - circ->cpath->prev->prev->state == CPATH_STATE_OPEN) { - failed_at_last_hop = 1; - } - - switch(circ->purpose) { - case CIRCUIT_PURPOSE_C_GENERAL: - if (circ->state != CIRCUIT_STATE_OPEN) { - /* If we never built the circuit, note it as a failure. */ - /* Note that we can't just check circ->cpath here, because if - * circuit-building failed immediately, it won't be set yet. */ - circuit_increment_failure_count(); - } - break; - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - /* at Bob, waiting for introductions */ - if (circ->state != CIRCUIT_STATE_OPEN) { - circuit_increment_failure_count(); - } - /* no need to care here, because bob will rebuild intro - * points periodically. */ - break; - case CIRCUIT_PURPOSE_C_INTRODUCING: - /* at Alice, connecting to intro point */ - /* Don't increment failure count, since Bob may have picked - * the introduction point maliciously */ - /* Alice will pick a new intro point when this one dies, if - * the stream in question still cares. No need to act here. */ - break; - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - /* at Alice, waiting for Bob */ - if (circ->state != CIRCUIT_STATE_OPEN) { - circuit_increment_failure_count(); - } - /* Alice will pick a new rend point when this one dies, if - * the stream in question still cares. No need to act here. */ - break; - case CIRCUIT_PURPOSE_S_CONNECT_REND: - /* at Bob, connecting to rend point */ - /* Don't increment failure count, since Alice may have picked - * the rendezvous point maliciously */ - if (failed_at_last_hop) { - log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s. Sucks to be Alice.", circ->build_state->chosen_exit); - } else { - log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s, because an earlier node failed.", - circ->build_state->chosen_exit); - rend_service_relaunch_rendezvous(circ); - } - break; - default: - /* Other cases are impossible, since this function is only called with - * unbuilt circuits. */ - tor_assert(0); - } -} - -/** Number of consecutive failures so far; should only be touched by - * circuit_launch_new and circuit_*_failure_count. - */ -static int n_circuit_failures = 0; - -/** Don't retry launching a new circuit if we try this many times with no - * success. */ -#define MAX_CIRCUIT_FAILURES 5 - -/** Launch a new circuit and return a pointer to it. Return NULL if you failed. */ -circuit_t *circuit_launch_new(uint8_t purpose, const char *exit_nickname) { - - if (n_circuit_failures > MAX_CIRCUIT_FAILURES) { - /* too many failed circs in a row. don't try. */ -// log_fn(LOG_INFO,"%d failures so far, not trying.",n_circuit_failures); - return NULL; - } - - /* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */ - return circuit_establish_circuit(purpose, exit_nickname); -} - -/** Record another failure at opening a general circuit. When we have - * too many, we'll stop trying for the remainder of this minute. - */ -void circuit_increment_failure_count(void) { - ++n_circuit_failures; - log_fn(LOG_DEBUG,"n_circuit_failures now %d.",n_circuit_failures); -} - -/** Reset the failure count for opening general circuits. This means - * we will try MAX_CIRCUIT_FAILURES times more (if necessary) before - * stopping again. - */ -void circuit_reset_failure_count(void) { - n_circuit_failures = 0; -} - -/** Build a new circuit for <b>purpose</b>. If <b>exit_nickname</b> - * is defined, then use that as your exit router, else choose a suitable - * exit node. - * - * Also launch a connection to the first OR in the chosen path, if - * it's not open already. - */ -static circuit_t *circuit_establish_circuit(uint8_t purpose, - const char *exit_nickname) { - routerinfo_t *firsthop; - connection_t *n_conn; - circuit_t *circ; - - circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */ - circ->state = CIRCUIT_STATE_OR_WAIT; - circ->build_state = onion_new_cpath_build_state(purpose, exit_nickname); - circ->purpose = purpose; - - if (! circ->build_state) { - log_fn(LOG_INFO,"Generating cpath failed."); - circuit_mark_for_close(circ); - return NULL; - } - - onion_extend_cpath(&circ->cpath, circ->build_state, &firsthop); - if(!CIRCUIT_IS_ORIGIN(circ)) { - log_fn(LOG_INFO,"Generating first cpath hop failed."); - circuit_mark_for_close(circ); - return NULL; - } - - /* now see if we're already connected to the first OR in 'route' */ - - log_fn(LOG_DEBUG,"Looking for firsthop '%s:%u'", - firsthop->address,firsthop->or_port); - n_conn = connection_twin_get_by_addr_port(firsthop->addr,firsthop->or_port); - if(!n_conn || n_conn->state != OR_CONN_STATE_OPEN) { /* not currently connected */ - circ->n_addr = firsthop->addr; - circ->n_port = firsthop->or_port; - - if(!n_conn) { /* launch the connection */ - n_conn = connection_or_connect(firsthop); - if(!n_conn) { /* connect failed, forget the whole thing */ - log_fn(LOG_INFO,"connect to firsthop failed. Closing."); - circuit_mark_for_close(circ); - return NULL; - } - } - - log_fn(LOG_DEBUG,"connecting in progress (or finished). Good."); - /* return success. The onion/circuit/etc will be taken care of automatically - * (may already have been) whenever n_conn reaches OR_CONN_STATE_OPEN. - */ - return circ; - } else { /* it (or a twin) is already open. use it. */ - circ->n_addr = n_conn->addr; - circ->n_port = n_conn->port; - circ->n_conn = n_conn; - log_fn(LOG_DEBUG,"Conn open. Delivering first onion skin."); - if(circuit_send_next_onion_skin(circ) < 0) { - log_fn(LOG_INFO,"circuit_send_next_onion_skin failed."); - circuit_mark_for_close(circ); - return NULL; - } - } - return circ; -} - -/** Find circuits that are waiting on <b>or_conn</b> to become open, - * if any, and get them to send their create cells forward. - */ -void circuit_n_conn_open(connection_t *or_conn) { - circuit_t *circ; - - for(circ=global_circuitlist;circ;circ = circ->next) { - if (circ->marked_for_close) - continue; - if(CIRCUIT_IS_ORIGIN(circ) && circ->n_addr == or_conn->addr && circ->n_port == or_conn->port) { - tor_assert(circ->state == CIRCUIT_STATE_OR_WAIT); - log_fn(LOG_DEBUG,"Found circ %d, sending onion skin.", circ->n_circ_id); - circ->n_conn = or_conn; - if(circuit_send_next_onion_skin(circ) < 0) { - log_fn(LOG_INFO,"send_next_onion_skin failed; circuit marked for closing."); - circuit_mark_for_close(circ); - continue; - /* XXX could this be bad, eg if next_onion_skin failed because conn died? */ - } - } - } -} - -extern int has_completed_circuit; - -/** This is the backbone function for building circuits. - * - * If circ's first hop is closed, then we need to build a create - * cell and send it forward. - * - * Otherwise, we need to build a relay extend cell and send it - * forward. - * - * Return -1 if we want to tear down circ, else return 0. - */ -int circuit_send_next_onion_skin(circuit_t *circ) { - cell_t cell; - crypt_path_t *hop; - routerinfo_t *router; - int r; - int circ_id_type; - char payload[2+4+ONIONSKIN_CHALLENGE_LEN]; - - tor_assert(circ && CIRCUIT_IS_ORIGIN(circ)); - - if(circ->cpath->state == CPATH_STATE_CLOSED) { - tor_assert(circ->n_conn && circ->n_conn->type == CONN_TYPE_OR); - - log_fn(LOG_DEBUG,"First skin; sending create cell."); - circ_id_type = decide_circ_id_type(options.Nickname, - circ->n_conn->nickname); - circ->n_circ_id = get_unique_circ_id_by_conn(circ->n_conn, circ_id_type); - - memset(&cell, 0, sizeof(cell_t)); - cell.command = CELL_CREATE; - cell.circ_id = circ->n_circ_id; - - router = router_get_by_nickname(circ->n_conn->nickname); - if (!router) { - log_fn(LOG_WARN,"Couldn't find routerinfo for %s", - circ->n_conn->nickname); - return -1; - } - - if(onion_skin_create(router->onion_pkey, - &(circ->cpath->handshake_state), - cell.payload) < 0) { - log_fn(LOG_WARN,"onion_skin_create (first hop) failed."); - return -1; - } - - connection_or_write_cell_to_buf(&cell, circ->n_conn); - - circ->cpath->state = CPATH_STATE_AWAITING_KEYS; - circ->state = CIRCUIT_STATE_BUILDING; - log_fn(LOG_DEBUG,"first skin; finished sending create cell."); - } else { - tor_assert(circ->cpath->state == CPATH_STATE_OPEN); - tor_assert(circ->state == CIRCUIT_STATE_BUILDING); - log_fn(LOG_DEBUG,"starting to send subsequent skin."); - r = onion_extend_cpath(&circ->cpath, circ->build_state, &router); - if (r==1) { - /* done building the circuit. whew. */ - circ->state = CIRCUIT_STATE_OPEN; - log_fn(LOG_INFO,"circuit built!"); - circuit_reset_failure_count(); - if(!has_completed_circuit) { - has_completed_circuit=1; - log_fn(LOG_NOTICE,"Tor has successfully opened a circuit. Looks like it's working."); - } - circuit_rep_hist_note_result(circ); - circuit_is_open(circ); /* do other actions as necessary */ - return 0; - } else if (r<0) { - log_fn(LOG_INFO,"Unable to extend circuit path."); - return -1; - } - hop = circ->cpath->prev; - - *(uint32_t*)payload = htonl(hop->addr); - *(uint16_t*)(payload+4) = htons(hop->port); - if(onion_skin_create(router->onion_pkey, &(hop->handshake_state), payload+6) < 0) { - log_fn(LOG_WARN,"onion_skin_create failed."); - return -1; - } - - log_fn(LOG_DEBUG,"Sending extend relay cell."); - /* send it to hop->prev, because it will transfer - * it to a create cell and then send to hop */ - if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTEND, - payload, sizeof(payload), hop->prev) < 0) - return 0; /* circuit is closed */ - - hop->state = CPATH_STATE_AWAITING_KEYS; - } - return 0; -} - -/** Take the 'extend' cell, pull out addr/port plus the onion skin. Make - * sure we're connected to the next hop, and pass it the onion skin in - * a create cell. - */ -int circuit_extend(cell_t *cell, circuit_t *circ) { - connection_t *n_conn; - int circ_id_type; - cell_t newcell; - - if(circ->n_conn) { - log_fn(LOG_WARN,"n_conn already set. Bug/attack. Closing."); - return -1; - } - - circ->n_addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); - circ->n_port = ntohs(get_uint16(cell->payload+RELAY_HEADER_SIZE+4)); - - n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); - if(!n_conn || n_conn->type != CONN_TYPE_OR) { - /* I've disabled making connections through OPs, but it's definitely - * possible here. I'm not sure if it would be a bug or a feature. - * - * Note also that this will close circuits where the onion has the same - * router twice in a row in the path. I think that's ok. - */ - struct in_addr in; - in.s_addr = htonl(circ->n_addr); - log_fn(LOG_INFO,"Next router (%s:%d) not connected. Closing.", inet_ntoa(in), circ->n_port); -#if 0 /* if we do truncateds, no need to kill circ */ - connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, - NULL, 0, NULL); - return 0; -#endif - circuit_mark_for_close(circ); - return 0; - } - - circ->n_addr = n_conn->addr; /* these are different if we found a twin instead */ - circ->n_port = n_conn->port; - - circ->n_conn = n_conn; - log_fn(LOG_DEBUG,"n_conn is %s:%u",n_conn->address,n_conn->port); - - circ_id_type = decide_circ_id_type(options.Nickname, n_conn->nickname); - -// log_fn(LOG_DEBUG,"circ_id_type = %u.",circ_id_type); - circ->n_circ_id = get_unique_circ_id_by_conn(circ->n_conn, circ_id_type); - if(!circ->n_circ_id) { - log_fn(LOG_WARN,"failed to get unique circID."); - return -1; - } - log_fn(LOG_DEBUG,"Chosen circID %u.",circ->n_circ_id); - - memset(&newcell, 0, sizeof(cell_t)); - newcell.command = CELL_CREATE; - newcell.circ_id = circ->n_circ_id; - - memcpy(newcell.payload, cell->payload+RELAY_HEADER_SIZE+2+4, - ONIONSKIN_CHALLENGE_LEN); - - connection_or_write_cell_to_buf(&newcell, circ->n_conn); - return 0; -} - -/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in - * key_data. key_data must contain CPATH_KEY_MATERIAL bytes, which are - * used as follows: - * - 20 to initialize f_digest - * - 20 to initialize b_digest - * - 16 to key f_crypto - * - 16 to key b_crypto - * - * (If 'reverse' is true, then f_XX and b_XX are swapped.) - */ -int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data, int reverse) -{ - crypto_digest_env_t *tmp_digest; - crypto_cipher_env_t *tmp_crypto; - - tor_assert(cpath && key_data); - tor_assert(!(cpath->f_crypto || cpath->b_crypto || - cpath->f_digest || cpath->b_digest)); - - log_fn(LOG_DEBUG,"hop init digest forward 0x%.8x, backward 0x%.8x.", - (unsigned int)*(uint32_t*)key_data, (unsigned int)*(uint32_t*)(key_data+20)); - cpath->f_digest = crypto_new_digest_env(); - crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN); - cpath->b_digest = crypto_new_digest_env(); - crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN); - - log_fn(LOG_DEBUG,"hop init cipher forward 0x%.8x, backward 0x%.8x.", - (unsigned int)*(uint32_t*)(key_data+40), (unsigned int)*(uint32_t*)(key_data+40+16)); - if (!(cpath->f_crypto = - crypto_create_init_cipher(key_data+(2*DIGEST_LEN),1))) { - log(LOG_WARN,"forward cipher initialization failed."); - return -1; - } - if (!(cpath->b_crypto = - crypto_create_init_cipher(key_data+(2*DIGEST_LEN)+CIPHER_KEY_LEN,0))) { - log(LOG_WARN,"backward cipher initialization failed."); - return -1; - } - - if (reverse) { - tmp_digest = cpath->f_digest; - cpath->f_digest = cpath->b_digest; - cpath->b_digest = tmp_digest; - tmp_crypto = cpath->f_crypto; - cpath->f_crypto = cpath->b_crypto; - cpath->b_crypto = tmp_crypto; - } - - return 0; -} - -/** A created or extended cell came back to us on the circuit, - * and it included <b>reply</b> (the second DH key, plus KH). - * - * Calculate the appropriate keys and digests, make sure KH is - * correct, and initialize this hop of the cpath. - * - * Return -1 if we want to mark circ for close, else return 0. - */ -int circuit_finish_handshake(circuit_t *circ, char *reply) { - unsigned char keys[CPATH_KEY_MATERIAL_LEN]; - crypt_path_t *hop; - - tor_assert(CIRCUIT_IS_ORIGIN(circ)); - if(circ->cpath->state == CPATH_STATE_AWAITING_KEYS) - hop = circ->cpath; - else { - for(hop=circ->cpath->next; - hop != circ->cpath && hop->state == CPATH_STATE_OPEN; - hop=hop->next) ; - if(hop == circ->cpath) { /* got an extended when we're all done? */ - log_fn(LOG_WARN,"got extended when circ already built? Closing."); - return -1; - } - } - tor_assert(hop->state == CPATH_STATE_AWAITING_KEYS); - - if(onion_skin_client_handshake(hop->handshake_state, reply, keys, - DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) { - log_fn(LOG_WARN,"onion_skin_client_handshake failed."); - return -1; - } - - crypto_dh_free(hop->handshake_state); /* don't need it anymore */ - hop->handshake_state = NULL; - /* Remember hash of g^xy */ - memcpy(hop->handshake_digest, reply+DH_KEY_LEN, DIGEST_LEN); - - if (circuit_init_cpath_crypto(hop, keys, 0)<0) { - return -1; - } - - hop->state = CPATH_STATE_OPEN; - log_fn(LOG_INFO,"finished"); - circuit_log_path(LOG_INFO,circ); - return 0; -} - -/** We received a relay truncated cell on circ. - * - * Since we don't ask for truncates currently, getting a truncated - * means that a connection broke or an extend failed. For now, - * just give up: for circ to close, and return 0. - */ -int circuit_truncated(circuit_t *circ, crypt_path_t *layer) { - crypt_path_t *victim; - connection_t *stream; - - tor_assert(circ && CIRCUIT_IS_ORIGIN(circ)); - tor_assert(layer); - - /* XXX Since we don't ask for truncates currently, getting a truncated - * means that a connection broke or an extend failed. For now, - * just give up. - */ - circuit_mark_for_close(circ); - return 0; - - while(layer->next != circ->cpath) { - /* we need to clear out layer->next */ - victim = layer->next; - log_fn(LOG_DEBUG, "Killing a layer of the cpath."); - - for(stream = circ->p_streams; stream; stream=stream->next_stream) { - if(stream->cpath_layer == victim) { - log_fn(LOG_INFO, "Marking stream %d for close.", stream->stream_id); - /* no need to send 'end' relay cells, - * because the other side's already dead - */ - stream->has_sent_end = 1; - connection_mark_for_close(stream); - } - } - - layer->next = victim->next; - circuit_free_cpath_node(victim); - } - - log_fn(LOG_INFO, "finished"); - return 0; -} - -/** Verify that cpath layer <b>cp</b> has all of its invariants - * correct. Trigger an assert if anything is invalid. - */ -void assert_cpath_layer_ok(const crypt_path_t *cp) -{ - tor_assert(cp->f_crypto); - tor_assert(cp->b_crypto); -// tor_assert(cp->addr); /* these are zero for rendezvous extra-hops */ -// tor_assert(cp->port); - switch(cp->state) - { - case CPATH_STATE_CLOSED: - case CPATH_STATE_OPEN: - tor_assert(!cp->handshake_state); - break; - case CPATH_STATE_AWAITING_KEYS: - tor_assert(cp->handshake_state); - break; - default: - tor_assert(0); - } - tor_assert(cp->package_window >= 0); - tor_assert(cp->deliver_window >= 0); -} - -/** Verify that cpath <b>cp</b> has all of its invariants - * correct. Trigger an assert if anything is invalid. - */ -void assert_cpath_ok(const crypt_path_t *cp) -{ - while(cp->prev) - cp = cp->prev; - - while(cp->next) { - assert_cpath_layer_ok(cp); - /* layers must be in sequence of: "open* awaiting? closed*" */ - if (cp->prev) { - if (cp->prev->state == CPATH_STATE_OPEN) { - tor_assert(cp->state == CPATH_STATE_CLOSED || - cp->state == CPATH_STATE_AWAITING_KEYS); - } else { - tor_assert(cp->state == CPATH_STATE_CLOSED); - } - } - cp = cp->next; - } -} - -/** Verify that circuit <b>c</b> has all of its invariants - * correct. Trigger an assert if anything is invalid. - */ -void assert_circuit_ok(const circuit_t *c) -{ - connection_t *conn; - - tor_assert(c); - tor_assert(c->magic == CIRCUIT_MAGIC); - tor_assert(c->purpose >= _CIRCUIT_PURPOSE_MIN && - c->purpose <= _CIRCUIT_PURPOSE_MAX); - - if (c->n_conn) - tor_assert(c->n_conn->type == CONN_TYPE_OR); - if (c->p_conn) - tor_assert(c->p_conn->type == CONN_TYPE_OR); - for (conn = c->p_streams; conn; conn = conn->next_stream) - tor_assert(conn->type == CONN_TYPE_AP); - for (conn = c->n_streams; conn; conn = conn->next_stream) - tor_assert(conn->type == CONN_TYPE_EXIT); - - tor_assert(c->deliver_window >= 0); - tor_assert(c->package_window >= 0); - if (c->state == CIRCUIT_STATE_OPEN) { - if (c->cpath) { - tor_assert(CIRCUIT_IS_ORIGIN(c)); - tor_assert(!c->n_crypto); - tor_assert(!c->p_crypto); - tor_assert(!c->n_digest); - tor_assert(!c->p_digest); - } else { - tor_assert(!CIRCUIT_IS_ORIGIN(c)); - tor_assert(c->n_crypto); - tor_assert(c->p_crypto); - tor_assert(c->n_digest); - tor_assert(c->p_digest); - } - } - if (c->cpath) { -//XXX assert_cpath_ok(c->cpath); - } - if (c->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED) { - if (!c->marked_for_close) { - tor_assert(c->rend_splice); - tor_assert(c->rend_splice->rend_splice == c); - } - tor_assert(c->rend_splice != c); - } else { - tor_assert(!c->rend_splice); - } -} - -/* - Local Variables: - mode:c - indent-tabs-mode:nil - c-basic-offset:2 - End: -*/ diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c new file mode 100644 index 000000000..5556fd8e4 --- /dev/null +++ b/src/or/circuitbuild.c @@ -0,0 +1,1099 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file circuitbuild.c + * \brief The actual details of building circuits. + **/ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +/********* START VARIABLES **********/ + +/** A global list of all circuits at this hop. */ +extern circuit_t *global_circuitlist; + +/********* END VARIABLES ************/ + +static cpath_build_state_t * +onion_new_cpath_build_state(uint8_t purpose, const char *exit_nickname); +static int +onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t + *state, routerinfo_t **router_out); +static int decide_circ_id_type(char *local_nick, char *remote_nick); +static int count_acceptable_routers(smartlist_t *routers); + +/** Iterate over values of circ_id, starting from conn-\>next_circ_id, + * and with the high bit specified by circ_id_type (see + * decide_circ_id_type()), until we get a circ_id that is not in use + * by any other circuit on that conn. + * + * Return it, or 0 if can't get a unique circ_id. + */ +static uint16_t get_unique_circ_id_by_conn(connection_t *conn, int circ_id_type) { + uint16_t test_circ_id; + int attempts=0; + uint16_t high_bit; + + tor_assert(conn && conn->type == CONN_TYPE_OR); + high_bit = (circ_id_type == CIRC_ID_TYPE_HIGHER) ? 1<<15 : 0; + do { + /* Sequentially iterate over test_circ_id=1...1<<15-1 until we find a + * circID such that (high_bit|test_circ_id) is not already used. */ + test_circ_id = conn->next_circ_id++; + if (test_circ_id == 0 || test_circ_id >= 1<<15) { + test_circ_id = 1; + conn->next_circ_id = 2; + } + if(++attempts > 1<<15) { + /* Make sure we don't loop forever if all circ_id's are used. This + * matters because it's an external DoS vulnerability. + */ + log_fn(LOG_WARN,"No unused circ IDs. Failing."); + return 0; + } + test_circ_id |= high_bit; + } while(circuit_get_by_circ_id_conn(test_circ_id, conn)); + return test_circ_id; +} + +/** Log, at severity <b>severity</b>, the nicknames of each router in + * circ's cpath. Also log the length of the cpath, and the intended + * exit point. + */ +void circuit_log_path(int severity, circuit_t *circ) { + char buf[1024]; + char *s = buf; + struct crypt_path_t *hop; + char *states[] = {"closed", "waiting for keys", "open"}; + routerinfo_t *router; + tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath); + + snprintf(s, sizeof(buf)-1, "circ (length %d, exit %s): ", + circ->build_state->desired_path_len, circ->build_state->chosen_exit); + hop=circ->cpath; + do { + s = buf + strlen(buf); + router = router_get_by_addr_port(hop->addr,hop->port); + if(router) { + snprintf(s, sizeof(buf) - (s - buf), "%s(%s) ", + router->nickname, states[hop->state]); + } else { + if(circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { + snprintf(s, sizeof(buf) - (s - buf), "(rendjoin hop)"); + } else { + snprintf(s, sizeof(buf) - (s - buf), "UNKNOWN "); + } + } + hop=hop->next; + } while(hop!=circ->cpath); + log_fn(severity,"%s",buf); +} + +/** Tell the rep(utation)hist(ory) module about the status of the links + * in circ. Hops that have become OPEN are marked as successfully + * extended; the _first_ hop that isn't open (if any) is marked as + * unable to extend. + */ +void circuit_rep_hist_note_result(circuit_t *circ) { + struct crypt_path_t *hop; + char *prev_nickname = NULL; + routerinfo_t *router; + hop = circ->cpath; + if(!hop) { + /* XXX + * if !hop, then we're not the beginning of this circuit. + * for now, just forget about it. later, we should remember when + * extends-through-us failed, too. + */ + return; + } + if (options.ORPort) { + prev_nickname = options.Nickname; + } + do { + router = router_get_by_addr_port(hop->addr,hop->port); + if (router) { + if (prev_nickname) { + if (hop->state == CPATH_STATE_OPEN) + rep_hist_note_extend_succeeded(prev_nickname, router->nickname); + else { + rep_hist_note_extend_failed(prev_nickname, router->nickname); + break; + } + } + prev_nickname = router->nickname; + } else { + prev_nickname = NULL; + } + hop=hop->next; + } while (hop!=circ->cpath); +} + +/** A helper function for circuit_dump_by_conn() below. Log a bunch + * of information about circuit <b>circ</b>. + */ +static void +circuit_dump_details(int severity, circuit_t *circ, int poll_index, + char *type, int this_circid, int other_circid) { + struct crypt_path_t *hop; + log(severity,"Conn %d has %s circuit: circID %d (other side %d), state %d (%s), born %d", + poll_index, type, this_circid, other_circid, circ->state, + circuit_state_to_string[circ->state], (int)circ->timestamp_created); + if(CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ + if(circ->state == CIRCUIT_STATE_BUILDING) + log(severity,"Building: desired len %d, planned exit node %s.", + circ->build_state->desired_path_len, circ->build_state->chosen_exit); + for(hop=circ->cpath;hop->next != circ->cpath; hop=hop->next) + log(severity,"hop: state %d, addr 0x%.8x, port %d", hop->state, + (unsigned int)hop->addr, + (int)hop->port); + } +} + +/** Log, at severity <b>severity</b>, information about each circuit + * that is connected to <b>conn</b>. + */ +void circuit_dump_by_conn(connection_t *conn, int severity) { + circuit_t *circ; + connection_t *tmpconn; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->p_conn == conn) + circuit_dump_details(severity, circ, conn->poll_index, "App-ward", + circ->p_circ_id, circ->n_circ_id); + for(tmpconn=circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(tmpconn == conn) { + circuit_dump_details(severity, circ, conn->poll_index, "App-ward", + circ->p_circ_id, circ->n_circ_id); + } + } + if(circ->n_conn == conn) + circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward", + circ->n_circ_id, circ->p_circ_id); + for(tmpconn=circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) { + if(tmpconn == conn) { + circuit_dump_details(severity, circ, conn->poll_index, "Exit-ward", + circ->n_circ_id, circ->p_circ_id); + } + } + } +} + +/** Build a new circuit for <b>purpose</b>. If <b>exit_nickname</b> + * is defined, then use that as your exit router, else choose a suitable + * exit node. + * + * Also launch a connection to the first OR in the chosen path, if + * it's not open already. + */ +circuit_t *circuit_establish_circuit(uint8_t purpose, + const char *exit_nickname) { + routerinfo_t *firsthop; + connection_t *n_conn; + circuit_t *circ; + + circ = circuit_new(0, NULL); /* sets circ->p_circ_id and circ->p_conn */ + circ->state = CIRCUIT_STATE_OR_WAIT; + circ->build_state = onion_new_cpath_build_state(purpose, exit_nickname); + circ->purpose = purpose; + + if (! circ->build_state) { + log_fn(LOG_INFO,"Generating cpath failed."); + circuit_mark_for_close(circ); + return NULL; + } + + onion_extend_cpath(&circ->cpath, circ->build_state, &firsthop); + if(!CIRCUIT_IS_ORIGIN(circ)) { + log_fn(LOG_INFO,"Generating first cpath hop failed."); + circuit_mark_for_close(circ); + return NULL; + } + + /* now see if we're already connected to the first OR in 'route' */ + + log_fn(LOG_DEBUG,"Looking for firsthop '%s:%u'", + firsthop->address,firsthop->or_port); + n_conn = connection_twin_get_by_addr_port(firsthop->addr,firsthop->or_port); + if(!n_conn || n_conn->state != OR_CONN_STATE_OPEN) { /* not currently connected */ + circ->n_addr = firsthop->addr; + circ->n_port = firsthop->or_port; + + if(!n_conn) { /* launch the connection */ + n_conn = connection_or_connect(firsthop); + if(!n_conn) { /* connect failed, forget the whole thing */ + log_fn(LOG_INFO,"connect to firsthop failed. Closing."); + circuit_mark_for_close(circ); + return NULL; + } + } + + log_fn(LOG_DEBUG,"connecting in progress (or finished). Good."); + /* return success. The onion/circuit/etc will be taken care of automatically + * (may already have been) whenever n_conn reaches OR_CONN_STATE_OPEN. + */ + return circ; + } else { /* it (or a twin) is already open. use it. */ + circ->n_addr = n_conn->addr; + circ->n_port = n_conn->port; + circ->n_conn = n_conn; + log_fn(LOG_DEBUG,"Conn open. Delivering first onion skin."); + if(circuit_send_next_onion_skin(circ) < 0) { + log_fn(LOG_INFO,"circuit_send_next_onion_skin failed."); + circuit_mark_for_close(circ); + return NULL; + } + } + return circ; +} + +/** Find circuits that are waiting on <b>or_conn</b> to become open, + * if any, and get them to send their create cells forward. + */ +void circuit_n_conn_open(connection_t *or_conn) { + circuit_t *circ; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if (circ->marked_for_close) + continue; + if(CIRCUIT_IS_ORIGIN(circ) && circ->n_addr == or_conn->addr && circ->n_port == or_conn->port) { + tor_assert(circ->state == CIRCUIT_STATE_OR_WAIT); + log_fn(LOG_DEBUG,"Found circ %d, sending onion skin.", circ->n_circ_id); + circ->n_conn = or_conn; + if(circuit_send_next_onion_skin(circ) < 0) { + log_fn(LOG_INFO,"send_next_onion_skin failed; circuit marked for closing."); + circuit_mark_for_close(circ); + continue; + /* XXX could this be bad, eg if next_onion_skin failed because conn died? */ + } + } + } +} + +extern int has_completed_circuit; + +/** This is the backbone function for building circuits. + * + * If circ's first hop is closed, then we need to build a create + * cell and send it forward. + * + * Otherwise, we need to build a relay extend cell and send it + * forward. + * + * Return -1 if we want to tear down circ, else return 0. + */ +int circuit_send_next_onion_skin(circuit_t *circ) { + cell_t cell; + crypt_path_t *hop; + routerinfo_t *router; + int r; + int circ_id_type; + char payload[2+4+ONIONSKIN_CHALLENGE_LEN]; + + tor_assert(circ && CIRCUIT_IS_ORIGIN(circ)); + + if(circ->cpath->state == CPATH_STATE_CLOSED) { + tor_assert(circ->n_conn && circ->n_conn->type == CONN_TYPE_OR); + + log_fn(LOG_DEBUG,"First skin; sending create cell."); + circ_id_type = decide_circ_id_type(options.Nickname, + circ->n_conn->nickname); + circ->n_circ_id = get_unique_circ_id_by_conn(circ->n_conn, circ_id_type); + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_CREATE; + cell.circ_id = circ->n_circ_id; + + router = router_get_by_nickname(circ->n_conn->nickname); + if (!router) { + log_fn(LOG_WARN,"Couldn't find routerinfo for %s", + circ->n_conn->nickname); + return -1; + } + + if(onion_skin_create(router->onion_pkey, + &(circ->cpath->handshake_state), + cell.payload) < 0) { + log_fn(LOG_WARN,"onion_skin_create (first hop) failed."); + return -1; + } + + connection_or_write_cell_to_buf(&cell, circ->n_conn); + + circ->cpath->state = CPATH_STATE_AWAITING_KEYS; + circ->state = CIRCUIT_STATE_BUILDING; + log_fn(LOG_DEBUG,"first skin; finished sending create cell."); + } else { + tor_assert(circ->cpath->state == CPATH_STATE_OPEN); + tor_assert(circ->state == CIRCUIT_STATE_BUILDING); + log_fn(LOG_DEBUG,"starting to send subsequent skin."); + r = onion_extend_cpath(&circ->cpath, circ->build_state, &router); + if (r==1) { + /* done building the circuit. whew. */ + circ->state = CIRCUIT_STATE_OPEN; + log_fn(LOG_INFO,"circuit built!"); + circuit_reset_failure_count(); + if(!has_completed_circuit) { + has_completed_circuit=1; + log_fn(LOG_NOTICE,"Tor has successfully opened a circuit. Looks like it's working."); + } + circuit_rep_hist_note_result(circ); + circuit_has_opened(circ); /* do other actions as necessary */ + return 0; + } else if (r<0) { + log_fn(LOG_INFO,"Unable to extend circuit path."); + return -1; + } + hop = circ->cpath->prev; + + *(uint32_t*)payload = htonl(hop->addr); + *(uint16_t*)(payload+4) = htons(hop->port); + if(onion_skin_create(router->onion_pkey, &(hop->handshake_state), payload+6) < 0) { + log_fn(LOG_WARN,"onion_skin_create failed."); + return -1; + } + + log_fn(LOG_DEBUG,"Sending extend relay cell."); + /* send it to hop->prev, because it will transfer + * it to a create cell and then send to hop */ + if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_EXTEND, + payload, sizeof(payload), hop->prev) < 0) + return 0; /* circuit is closed */ + + hop->state = CPATH_STATE_AWAITING_KEYS; + } + return 0; +} + +/** Take the 'extend' cell, pull out addr/port plus the onion skin. Make + * sure we're connected to the next hop, and pass it the onion skin in + * a create cell. + */ +int circuit_extend(cell_t *cell, circuit_t *circ) { + connection_t *n_conn; + int circ_id_type; + cell_t newcell; + + if(circ->n_conn) { + log_fn(LOG_WARN,"n_conn already set. Bug/attack. Closing."); + return -1; + } + + circ->n_addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); + circ->n_port = ntohs(get_uint16(cell->payload+RELAY_HEADER_SIZE+4)); + + n_conn = connection_twin_get_by_addr_port(circ->n_addr,circ->n_port); + if(!n_conn || n_conn->type != CONN_TYPE_OR) { + /* I've disabled making connections through OPs, but it's definitely + * possible here. I'm not sure if it would be a bug or a feature. + * + * Note also that this will close circuits where the onion has the same + * router twice in a row in the path. I think that's ok. + */ + struct in_addr in; + in.s_addr = htonl(circ->n_addr); + log_fn(LOG_INFO,"Next router (%s:%d) not connected. Closing.", inet_ntoa(in), circ->n_port); +#if 0 /* if we do truncateds, no need to kill circ */ + connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, + NULL, 0, NULL); + return 0; +#endif + circuit_mark_for_close(circ); + return 0; + } + + circ->n_addr = n_conn->addr; /* these are different if we found a twin instead */ + circ->n_port = n_conn->port; + + circ->n_conn = n_conn; + log_fn(LOG_DEBUG,"n_conn is %s:%u",n_conn->address,n_conn->port); + + circ_id_type = decide_circ_id_type(options.Nickname, n_conn->nickname); + +// log_fn(LOG_DEBUG,"circ_id_type = %u.",circ_id_type); + circ->n_circ_id = get_unique_circ_id_by_conn(circ->n_conn, circ_id_type); + if(!circ->n_circ_id) { + log_fn(LOG_WARN,"failed to get unique circID."); + return -1; + } + log_fn(LOG_DEBUG,"Chosen circID %u.",circ->n_circ_id); + + memset(&newcell, 0, sizeof(cell_t)); + newcell.command = CELL_CREATE; + newcell.circ_id = circ->n_circ_id; + + memcpy(newcell.payload, cell->payload+RELAY_HEADER_SIZE+2+4, + ONIONSKIN_CHALLENGE_LEN); + + connection_or_write_cell_to_buf(&newcell, circ->n_conn); + return 0; +} + +/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in + * key_data. key_data must contain CPATH_KEY_MATERIAL bytes, which are + * used as follows: + * - 20 to initialize f_digest + * - 20 to initialize b_digest + * - 16 to key f_crypto + * - 16 to key b_crypto + * + * (If 'reverse' is true, then f_XX and b_XX are swapped.) + */ +int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data, int reverse) +{ + crypto_digest_env_t *tmp_digest; + crypto_cipher_env_t *tmp_crypto; + + tor_assert(cpath && key_data); + tor_assert(!(cpath->f_crypto || cpath->b_crypto || + cpath->f_digest || cpath->b_digest)); + + log_fn(LOG_DEBUG,"hop init digest forward 0x%.8x, backward 0x%.8x.", + (unsigned int)*(uint32_t*)key_data, (unsigned int)*(uint32_t*)(key_data+20)); + cpath->f_digest = crypto_new_digest_env(); + crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN); + cpath->b_digest = crypto_new_digest_env(); + crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN); + + log_fn(LOG_DEBUG,"hop init cipher forward 0x%.8x, backward 0x%.8x.", + (unsigned int)*(uint32_t*)(key_data+40), (unsigned int)*(uint32_t*)(key_data+40+16)); + if (!(cpath->f_crypto = + crypto_create_init_cipher(key_data+(2*DIGEST_LEN),1))) { + log(LOG_WARN,"forward cipher initialization failed."); + return -1; + } + if (!(cpath->b_crypto = + crypto_create_init_cipher(key_data+(2*DIGEST_LEN)+CIPHER_KEY_LEN,0))) { + log(LOG_WARN,"backward cipher initialization failed."); + return -1; + } + + if (reverse) { + tmp_digest = cpath->f_digest; + cpath->f_digest = cpath->b_digest; + cpath->b_digest = tmp_digest; + tmp_crypto = cpath->f_crypto; + cpath->f_crypto = cpath->b_crypto; + cpath->b_crypto = tmp_crypto; + } + + return 0; +} + +/** A created or extended cell came back to us on the circuit, + * and it included <b>reply</b> (the second DH key, plus KH). + * + * Calculate the appropriate keys and digests, make sure KH is + * correct, and initialize this hop of the cpath. + * + * Return -1 if we want to mark circ for close, else return 0. + */ +int circuit_finish_handshake(circuit_t *circ, char *reply) { + unsigned char keys[CPATH_KEY_MATERIAL_LEN]; + crypt_path_t *hop; + + tor_assert(CIRCUIT_IS_ORIGIN(circ)); + if(circ->cpath->state == CPATH_STATE_AWAITING_KEYS) + hop = circ->cpath; + else { + for(hop=circ->cpath->next; + hop != circ->cpath && hop->state == CPATH_STATE_OPEN; + hop=hop->next) ; + if(hop == circ->cpath) { /* got an extended when we're all done? */ + log_fn(LOG_WARN,"got extended when circ already built? Closing."); + return -1; + } + } + tor_assert(hop->state == CPATH_STATE_AWAITING_KEYS); + + if(onion_skin_client_handshake(hop->handshake_state, reply, keys, + DIGEST_LEN*2+CIPHER_KEY_LEN*2) < 0) { + log_fn(LOG_WARN,"onion_skin_client_handshake failed."); + return -1; + } + + crypto_dh_free(hop->handshake_state); /* don't need it anymore */ + hop->handshake_state = NULL; + /* Remember hash of g^xy */ + memcpy(hop->handshake_digest, reply+DH_KEY_LEN, DIGEST_LEN); + + if (circuit_init_cpath_crypto(hop, keys, 0)<0) { + return -1; + } + + hop->state = CPATH_STATE_OPEN; + log_fn(LOG_INFO,"finished"); + circuit_log_path(LOG_INFO,circ); + return 0; +} + +/** We received a relay truncated cell on circ. + * + * Since we don't ask for truncates currently, getting a truncated + * means that a connection broke or an extend failed. For now, + * just give up: for circ to close, and return 0. + */ +int circuit_truncated(circuit_t *circ, crypt_path_t *layer) { + crypt_path_t *victim; + connection_t *stream; + + tor_assert(circ && CIRCUIT_IS_ORIGIN(circ)); + tor_assert(layer); + + /* XXX Since we don't ask for truncates currently, getting a truncated + * means that a connection broke or an extend failed. For now, + * just give up. + */ + circuit_mark_for_close(circ); + return 0; + + while(layer->next != circ->cpath) { + /* we need to clear out layer->next */ + victim = layer->next; + log_fn(LOG_DEBUG, "Killing a layer of the cpath."); + + for(stream = circ->p_streams; stream; stream=stream->next_stream) { + if(stream->cpath_layer == victim) { + log_fn(LOG_INFO, "Marking stream %d for close.", stream->stream_id); + /* no need to send 'end' relay cells, + * because the other side's already dead + */ + stream->has_sent_end = 1; + connection_mark_for_close(stream); + } + } + + layer->next = victim->next; + circuit_free_cpath_node(victim); + } + + log_fn(LOG_INFO, "finished"); + return 0; +} + +/** Decide whether the first bit of the circuit ID will be + * 0 or 1, to avoid conflicts where each side randomly chooses + * the same circuit ID. + * + * Return CIRC_ID_TYPE_LOWER if local_nick is NULL, or if + * local_nick is lexographically smaller than remote_nick. + * Else return CIRC_ID_TYPE_HIGHER. + */ +static int decide_circ_id_type(char *local_nick, char *remote_nick) { + int result; + + tor_assert(remote_nick); + if(!local_nick) + return CIRC_ID_TYPE_LOWER; + result = strcasecmp(local_nick, remote_nick); + tor_assert(result); + if(result < 0) + return CIRC_ID_TYPE_LOWER; + return CIRC_ID_TYPE_HIGHER; +} + +/** Given a response payload and keys, initialize, then send a created + * cell back. + */ +int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys) { + cell_t cell; + crypt_path_t *tmp_cpath; + + tmp_cpath = tor_malloc_zero(sizeof(crypt_path_t)); + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_CREATED; + cell.circ_id = circ->p_circ_id; + + circ->state = CIRCUIT_STATE_OPEN; + + log_fn(LOG_DEBUG,"Entering."); + + memcpy(cell.payload, payload, ONIONSKIN_REPLY_LEN); + + log_fn(LOG_INFO,"init digest forward 0x%.8x, backward 0x%.8x.", + (unsigned int)*(uint32_t*)(keys), (unsigned int)*(uint32_t*)(keys+20)); + if (circuit_init_cpath_crypto(tmp_cpath, keys, 0)<0) { + log_fn(LOG_WARN,"Circuit initialization failed"); + tor_free(tmp_cpath); + return -1; + } + circ->n_digest = tmp_cpath->f_digest; + circ->n_crypto = tmp_cpath->f_crypto; + circ->p_digest = tmp_cpath->b_digest; + circ->p_crypto = tmp_cpath->b_crypto; + tor_free(tmp_cpath); + + memcpy(circ->handshake_digest, cell.payload+DH_KEY_LEN, DIGEST_LEN); + + connection_or_write_cell_to_buf(&cell, circ->p_conn); + log_fn(LOG_DEBUG,"Finished sending 'created' cell."); + + return 0; +} + +extern int has_fetched_directory; /* from main.c */ + +/** Choose a length for a circuit of purpose <b>purpose</b>. + * Default length is 3 + the number of endpoints that would give something + * away. If the routerlist <b>routers</b> doesn't have enough routers + * to handle the desired path length, return as large a path length as + * is feasible, except if it's less than 2, in which case return -1. + */ +static int new_route_len(double cw, uint8_t purpose, smartlist_t *routers) { + int num_acceptable_routers; + int routelen; + + tor_assert((cw >= 0) && (cw < 1) && routers); /* valid parameters */ + +#ifdef TOR_PERF + routelen = 2; +#else + if(purpose == CIRCUIT_PURPOSE_C_GENERAL) + routelen = 3; + else if(purpose == CIRCUIT_PURPOSE_C_INTRODUCING) + routelen = 4; + else if(purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND) + routelen = 3; + else if(purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) + routelen = 3; + else if(purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) + routelen = 4; + else { + log_fn(LOG_WARN,"Unhandled purpose %d", purpose); + return -1; + } +#endif +#if 0 + for(routelen = 3; ; routelen++) { /* 3, increment until coinflip says we're done */ + if (crypto_pseudo_rand_int(255) >= cw*255) /* don't extend */ + break; + } +#endif + log_fn(LOG_DEBUG,"Chosen route length %d (%d routers available).",routelen, + smartlist_len(routers)); + + num_acceptable_routers = count_acceptable_routers(routers); + + if(num_acceptable_routers < 2) { + log_fn(LOG_INFO,"Not enough acceptable routers (%d). Discarding this circuit.", + num_acceptable_routers); + return -1; + } + + if(num_acceptable_routers < routelen) { + log_fn(LOG_INFO,"Not enough routers: cutting routelen from %d to %d.", + routelen, num_acceptable_routers); + routelen = num_acceptable_routers; + } + + return routelen; +} + +/** Return a pointer to a suitable router to be the exit node for the + * general-purpose circuit we're about to build. + * + * Look through the connection array, and choose a router that maximizes + * the number of pending streams that can exit from this router. + * + * Return NULL if we can't find any suitable routers. + */ +static routerinfo_t *choose_good_exit_server_general(routerlist_t *dir) +{ + int *n_supported; + int i, j; + int n_pending_connections = 0; + connection_t **carray; + int n_connections; + int best_support = -1; + int n_best_support=0; + smartlist_t *sl, *preferredexits, *excludedexits; + routerinfo_t *router; + + get_connection_array(&carray, &n_connections); + + /* Count how many connections are waiting for a circuit to be built. + * We use this for log messages now, but in the future we may depend on it. + */ + for (i = 0; i < n_connections; ++i) { + if (carray[i]->type == CONN_TYPE_AP && + carray[i]->state == AP_CONN_STATE_CIRCUIT_WAIT && + !carray[i]->marked_for_close && + !circuit_stream_is_being_handled(carray[i])) + ++n_pending_connections; + } + log_fn(LOG_DEBUG, "Choosing exit node; %d connections are pending", + n_pending_connections); + /* Now we count, for each of the routers in the directory, how many + * of the pending connections could possibly exit from that + * router (n_supported[i]). (We can't be sure about cases where we + * don't know the IP address of the pending connection.) + */ + n_supported = tor_malloc(sizeof(int)*smartlist_len(dir->routers)); + for (i = 0; i < smartlist_len(dir->routers); ++i) { /* iterate over routers */ + router = smartlist_get(dir->routers, i); + if(router_is_me(router)) { + n_supported[i] = -1; + log_fn(LOG_DEBUG,"Skipping node %s -- it's me.", router->nickname); + /* XXX there's probably a reverse predecessor attack here, but + * it's slow. should we take this out? -RD + */ + continue; + } + if(!router->is_running) { + n_supported[i] = -1; + log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- directory says it's not running.", + router->nickname, i); + continue; /* skip routers that are known to be down */ + } + if(router_exit_policy_rejects_all(router)) { + n_supported[i] = -1; + log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.", + router->nickname, i); + continue; /* skip routers that reject all */ + } + n_supported[i] = 0; + for (j = 0; j < n_connections; ++j) { /* iterate over connections */ + if (carray[j]->type != CONN_TYPE_AP || + carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT || + carray[j]->marked_for_close || + circuit_stream_is_being_handled(carray[j])) + continue; /* Skip everything but APs in CIRCUIT_WAIT */ + switch (connection_ap_can_use_exit(carray[j], router)) + { + case ADDR_POLICY_REJECTED: + log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.", + router->nickname, i); + break; /* would be rejected; try next connection */ + case ADDR_POLICY_ACCEPTED: + case ADDR_POLICY_UNKNOWN: + ++n_supported[i]; + log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.", + router->nickname, i, n_supported[i]); + } + } /* End looping over connections. */ + if (n_supported[i] > best_support) { + /* If this router is better than previous ones, remember its index + * and goodness, and start counting how many routers are this good. */ + best_support = n_supported[i]; n_best_support=1; + log_fn(LOG_DEBUG,"%s is new best supported option so far.", + router->nickname); + } else if (n_supported[i] == best_support) { + /* If this router is _as good_ as the best one, just increment the + * count of equally good routers.*/ + ++n_best_support; + } + } + log_fn(LOG_INFO, "Found %d servers that might support %d/%d pending connections.", + n_best_support, best_support, n_pending_connections); + + preferredexits = smartlist_create(); + add_nickname_list_to_smartlist(preferredexits,options.ExitNodes); + + excludedexits = smartlist_create(); + add_nickname_list_to_smartlist(excludedexits,options.ExcludeNodes); + + sl = smartlist_create(); + + /* If any routers definitely support any pending connections, choose one + * at random. */ + if (best_support > 0) { + for (i = 0; i < smartlist_len(dir->routers); i++) + if (n_supported[i] == best_support) + smartlist_add(sl, smartlist_get(dir->routers, i)); + + smartlist_subtract(sl,excludedexits); + if (smartlist_overlap(sl,preferredexits)) + smartlist_intersect(sl,preferredexits); + router = smartlist_choose(sl); + } else { + /* Either there are no pending connections, or no routers even seem to + * possibly support any of them. Choose a router at random. */ + if (best_support == -1) { + log(LOG_WARN, "All routers are down or middleman -- choosing a doomed exit at random."); + } + for(i = 0; i < smartlist_len(dir->routers); i++) + if(n_supported[i] != -1) + smartlist_add(sl, smartlist_get(dir->routers, i)); + + smartlist_subtract(sl,excludedexits); + if (smartlist_overlap(sl,preferredexits)) + smartlist_intersect(sl,preferredexits); + router = smartlist_choose(sl); + } + + smartlist_free(preferredexits); + smartlist_free(excludedexits); + smartlist_free(sl); + tor_free(n_supported); + if(router) { + log_fn(LOG_INFO, "Chose exit server '%s'", router->nickname); + return router; + } + log_fn(LOG_WARN, "No exit routers seem to be running; can't choose an exit."); + return NULL; +} + +/** Return a pointer to a suitable router to be the exit node for the + * circuit of purpose <b>purpose</b> that we're about to build (or NULL + * if no router is suitable). + * + * For general-purpose circuits, pass it off to + * choose_good_exit_server_general() + * + * For client-side rendezvous circuits, choose a random node, weighted + * toward the preferences in 'options'. + */ +static routerinfo_t *choose_good_exit_server(uint8_t purpose, routerlist_t *dir) +{ + routerinfo_t *r; + switch(purpose) { + case CIRCUIT_PURPOSE_C_GENERAL: + return choose_good_exit_server_general(dir); + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + r = router_choose_random_node(dir, options.RendNodes, options.RendExcludeNodes, NULL); + return r; + default: + log_fn(LOG_WARN,"unhandled purpose %d", purpose); + tor_assert(0); + } + return NULL; /* never reached */ +} + +/** Allocate a cpath_build_state_t, populate it based on + * <b>purpose</b> and <b>exit_nickname</b> (if specified), and + * return it. + */ +static cpath_build_state_t * +onion_new_cpath_build_state(uint8_t purpose, const char *exit_nickname) +{ + routerlist_t *rl; + int r; + cpath_build_state_t *info; + routerinfo_t *exit; + + router_get_routerlist(&rl); + r = new_route_len(options.PathlenCoinWeight, purpose, rl->routers); + if (r < 0) + return NULL; + info = tor_malloc_zero(sizeof(cpath_build_state_t)); + info->desired_path_len = r; + if(exit_nickname) { /* the circuit-builder pre-requested one */ + log_fn(LOG_INFO,"Using requested exit node '%s'", exit_nickname); + info->chosen_exit = tor_strdup(exit_nickname); + } else { /* we have to decide one */ + exit = choose_good_exit_server(purpose, rl); + if(!exit) { + log_fn(LOG_WARN,"failed to choose an exit server"); + tor_free(info); + return NULL; + } + info->chosen_exit = tor_strdup(exit->nickname); + } + return info; +} + +/** Return the number of routers in <b>routers</b> that are currently up + * and available for building circuits through. Count sets of twins only + * once. + */ +static int count_acceptable_routers(smartlist_t *routers) { + int i, j, n; + int num=0; + connection_t *conn; + routerinfo_t *r, *r2; + + n = smartlist_len(routers); + for(i=0;i<n;i++) { + r = smartlist_get(routers, i); + log_fn(LOG_DEBUG,"Contemplating whether router %d (%s) is a new option...", + i, r->nickname); + if(r->is_running == 0) { + log_fn(LOG_DEBUG,"Nope, the directory says %d is not running.",i); + goto next_i_loop; + } + if(options.ORPort) { + conn = connection_exact_get_by_addr_port(r->addr, r->or_port); + if(!conn || conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) { + log_fn(LOG_DEBUG,"Nope, %d is not connected.",i); + goto next_i_loop; + } + } + for(j=0;j<i;j++) { + r2 = smartlist_get(routers, j); + if(!crypto_pk_cmp_keys(r->onion_pkey, r2->onion_pkey)) { + /* these guys are twins. so we've already counted him. */ + log_fn(LOG_DEBUG,"Nope, %d is a twin of %d.",i,j); + goto next_i_loop; + } + } + num++; + log_fn(LOG_DEBUG,"I like %d. num_acceptable_routers now %d.",i, num); + next_i_loop: + ; /* C requires an explicit statement after the label */ + } + + return num; +} + +/** Go through smartlist <b>sl</b> of routers, and remove all elements that + * have the same onion key as twin. + */ +static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) { + int i; + routerinfo_t *r; + + if(twin == NULL) + return; + + for(i=0; i < smartlist_len(sl); i++) { + r = smartlist_get(sl,i); + if (!crypto_pk_cmp_keys(r->onion_pkey, twin->onion_pkey)) { + smartlist_del(sl,i--); + } + } +} + +/** Add <b>new_hop</b> to the end of the doubly-linked-list <b>head_ptr</b>. + * + * This function is used to extend cpath by another hop. + */ +void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop) +{ + if (*head_ptr) { + new_hop->next = (*head_ptr); + new_hop->prev = (*head_ptr)->prev; + (*head_ptr)->prev->next = new_hop; + (*head_ptr)->prev = new_hop; + } else { + *head_ptr = new_hop; + new_hop->prev = new_hop->next = new_hop; + } +} + +/** Choose a suitable next hop in the cpath <b>head_ptr</b>, + * based on <b>state</b>. Add the hop info to head_ptr, and return a + * pointer to the chosen router in <b>router_out</b>. + */ +static int +onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t + *state, routerinfo_t **router_out) +{ + int cur_len; + crypt_path_t *cpath, *hop; + routerinfo_t *r; + routerinfo_t *choice; + int i; + smartlist_t *sl, *excludednodes; + + tor_assert(head_ptr); + tor_assert(router_out); + + if (!*head_ptr) { + cur_len = 0; + } else { + cur_len = 1; + for (cpath = *head_ptr; cpath->next != *head_ptr; cpath = cpath->next) { + ++cur_len; + } + } + if (cur_len >= state->desired_path_len) { + log_fn(LOG_DEBUG, "Path is complete: %d steps long", + state->desired_path_len); + return 1; + } + log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len, + state->desired_path_len); + + excludednodes = smartlist_create(); + add_nickname_list_to_smartlist(excludednodes,options.ExcludeNodes); + + if(cur_len == state->desired_path_len - 1) { /* Picking last node */ + log_fn(LOG_DEBUG, "Contemplating last hop: choice already made: %s", + state->chosen_exit); + choice = router_get_by_nickname(state->chosen_exit); + smartlist_free(excludednodes); + if(!choice) { + log_fn(LOG_WARN,"Our chosen exit %s is no longer in the directory? Discarding this circuit.", + state->chosen_exit); + return -1; + } + } else if(cur_len == 0) { /* picking first node */ + /* try the nodes in EntryNodes first */ + sl = smartlist_create(); + add_nickname_list_to_smartlist(sl,options.EntryNodes); + /* XXX one day, consider picking chosen_exit knowing what's in EntryNodes */ + remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); + remove_twins_from_smartlist(sl,router_get_my_routerinfo()); + smartlist_subtract(sl,excludednodes); + choice = smartlist_choose(sl); + smartlist_free(sl); + if(!choice) { + sl = smartlist_create(); + router_add_running_routers_to_smartlist(sl); + remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); + remove_twins_from_smartlist(sl,router_get_my_routerinfo()); + smartlist_subtract(sl,excludednodes); + choice = smartlist_choose(sl); + smartlist_free(sl); + } + smartlist_free(excludednodes); + if(!choice) { + log_fn(LOG_WARN,"No acceptable routers while picking entry node. Discarding this circuit."); + return -1; + } + } else { + log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice."); + sl = smartlist_create(); + router_add_running_routers_to_smartlist(sl); + remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); + remove_twins_from_smartlist(sl,router_get_my_routerinfo()); + for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) { + r = router_get_by_addr_port(cpath->addr, cpath->port); + tor_assert(r); + remove_twins_from_smartlist(sl,r); + } + smartlist_subtract(sl,excludednodes); + choice = smartlist_choose(sl); + smartlist_free(sl); + smartlist_free(excludednodes); + if(!choice) { + log_fn(LOG_WARN,"No acceptable routers while picking intermediate node. Discarding this circuit."); + return -1; + } + } + + log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)", + choice->nickname, cur_len, state->chosen_exit); + + hop = tor_malloc_zero(sizeof(crypt_path_t)); + + /* link hop into the cpath, at the end. */ + onion_append_to_cpath(head_ptr, hop); + + hop->state = CPATH_STATE_CLOSED; + + hop->port = choice->or_port; + hop->addr = choice->addr; + + hop->package_window = CIRCWINDOW_START; + hop->deliver_window = CIRCWINDOW_START; + + log_fn(LOG_DEBUG, "Extended circuit path with %s for hop %d", + choice->nickname, cur_len); + + *router_out = choice; + return 0; +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c new file mode 100644 index 000000000..33066cf74 --- /dev/null +++ b/src/or/circuitlist.c @@ -0,0 +1,500 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file circuitlist.c + * \brief Manage the global circuit list. + **/ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +/********* START VARIABLES **********/ + +/** A global list of all circuits at this hop. */ +circuit_t *global_circuitlist=NULL; + +/** Array of strings to make circ-\>state human-readable */ +char *circuit_state_to_string[] = { + "doing handshakes", /* 0 */ + "processing the onion", /* 1 */ + "connecting to firsthop", /* 2 */ + "open" /* 3 */ +}; + +/********* END VARIABLES ************/ + +static void circuit_free(circuit_t *circ); +static void circuit_free_cpath(crypt_path_t *cpath); + +/** Add <b>circ</b> to the global list of circuits. This is called only from + * within circuit_new. + */ +static void circuit_add(circuit_t *circ) { + if(!global_circuitlist) { /* first one */ + global_circuitlist = circ; + circ->next = NULL; + } else { + circ->next = global_circuitlist; + global_circuitlist = circ; + } +} + +/** Detach from the global circuit list, and deallocate, all + * circuits that have been marked for close. + */ +void circuit_close_all_marked(void) +{ + circuit_t *tmp,*m; + + while (global_circuitlist && global_circuitlist->marked_for_close) { + tmp = global_circuitlist->next; + circuit_free(global_circuitlist); + global_circuitlist = tmp; + } + + tmp = global_circuitlist; + while (tmp && tmp->next) { + if (tmp->next->marked_for_close) { + m = tmp->next->next; + circuit_free(tmp->next); + tmp->next = m; + /* Need to check new tmp->next; don't advance tmp. */ + } else { + /* Advance tmp. */ + tmp = tmp->next; + } + } +} + +/** Allocate space for a new circuit, initializing with <b>p_circ_id</b> + * and <b>p_conn</b>. Add it to the global circuit list. + */ +circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn) { + circuit_t *circ; + + circ = tor_malloc_zero(sizeof(circuit_t)); + circ->magic = CIRCUIT_MAGIC; + + circ->timestamp_created = time(NULL); + + circ->p_circ_id = p_circ_id; + circ->p_conn = p_conn; + + circ->state = CIRCUIT_STATE_ONIONSKIN_PENDING; + + /* CircIDs */ + circ->p_circ_id = p_circ_id; + /* circ->n_circ_id remains 0 because we haven't identified the next hop yet */ + + circ->package_window = CIRCWINDOW_START; + circ->deliver_window = CIRCWINDOW_START; + + circ->next_stream_id = crypto_pseudo_rand_int(1<<16); + + circuit_add(circ); + + return circ; +} + +/** Deallocate space associated with circ. + */ +static void circuit_free(circuit_t *circ) { + tor_assert(circ); + tor_assert(circ->magic == CIRCUIT_MAGIC); + if (circ->n_crypto) + crypto_free_cipher_env(circ->n_crypto); + if (circ->p_crypto) + crypto_free_cipher_env(circ->p_crypto); + if (circ->n_digest) + crypto_free_digest_env(circ->n_digest); + if (circ->p_digest) + crypto_free_digest_env(circ->p_digest); + if(circ->build_state) { + tor_free(circ->build_state->chosen_exit); + if (circ->build_state->pending_final_cpath) + circuit_free_cpath_node(circ->build_state->pending_final_cpath); + } + tor_free(circ->build_state); + circuit_free_cpath(circ->cpath); + if (circ->rend_splice) { + circ->rend_splice->rend_splice = NULL; + } + + memset(circ, 0xAA, sizeof(circuit_t)); /* poison memory */ + free(circ); +} + +/** Deallocate space associated with the linked list <b>cpath</b>. */ +static void circuit_free_cpath(crypt_path_t *cpath) { + crypt_path_t *victim, *head=cpath; + + if(!cpath) + return; + + /* it's a doubly linked list, so we have to notice when we've + * gone through it once. */ + while(cpath->next && cpath->next != head) { + victim = cpath; + cpath = victim->next; + circuit_free_cpath_node(victim); + } + + circuit_free_cpath_node(cpath); +} + +/** Deallocate space associated with the cpath node <b>victim</b>. */ +/* XXX rewrite so the call from circuitbuild isn't necessary */ +void circuit_free_cpath_node(crypt_path_t *victim) { + if(victim->f_crypto) + crypto_free_cipher_env(victim->f_crypto); + if(victim->b_crypto) + crypto_free_cipher_env(victim->b_crypto); + if(victim->f_digest) + crypto_free_digest_env(victim->f_digest); + if(victim->b_digest) + crypto_free_digest_env(victim->b_digest); + if(victim->handshake_state) + crypto_dh_free(victim->handshake_state); + free(victim); +} + +/** Return a circ such that: + * - circ-\>n_circ_id or circ-\>p_circ_id is equal to <b>circ_id</b>, and + * - circ is attached to <b>conn</b>, either as p_conn, n-conn, or + * in p_streams or n_streams. + * Return NULL if no such circuit exists. + */ +circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn) { + circuit_t *circ; + connection_t *tmpconn; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if (circ->marked_for_close) + continue; + + if(circ->p_circ_id == circ_id) { + if(circ->p_conn == conn) + return circ; + for(tmpconn = circ->p_streams; tmpconn; tmpconn = tmpconn->next_stream) { + if(tmpconn == conn) + return circ; + } + } + if(circ->n_circ_id == circ_id) { + if(circ->n_conn == conn) + return circ; + for(tmpconn = circ->n_streams; tmpconn; tmpconn = tmpconn->next_stream) { + if(tmpconn == conn) + return circ; + } + for(tmpconn = circ->resolving_streams; tmpconn; tmpconn = tmpconn->next_stream) { + if(tmpconn == conn) + return circ; + } + } + } + return NULL; +} + +/** Return a circ such that circ is attached to <b>conn</b>, either as + * p_conn, n-conn, or in p_streams or n_streams. + * + * Return NULL if no such circuit exists. + */ +circuit_t *circuit_get_by_conn(connection_t *conn) { + circuit_t *circ; + connection_t *tmpconn; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if (circ->marked_for_close) + continue; + + if(circ->p_conn == conn) + return circ; + if(circ->n_conn == conn) + return circ; + for(tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream) + if(tmpconn == conn) + return circ; + for(tmpconn = circ->n_streams; tmpconn; tmpconn=tmpconn->next_stream) + if(tmpconn == conn) + return circ; + for(tmpconn = circ->resolving_streams; tmpconn; tmpconn=tmpconn->next_stream) + if(tmpconn == conn) + return circ; + } + return NULL; +} + +/** Return a circ such that: + * - circ-\>rend_query is equal to <b>rend_query</b>, and + * - circ-\>purpose is equal to <b>purpose</b>. + * + * Return NULL if no such circuit exists. + */ +circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose) { + circuit_t *circ; + + for (circ = global_circuitlist; circ; circ = circ->next) { + if (!circ->marked_for_close && + circ->purpose == purpose && + !rend_cmp_service_ids(rend_query, circ->rend_query)) + return circ; + } + return NULL; +} + +/** Return the first circuit in global_circuitlist after <b>start</b> whose + * rend_pk_digest field is <b>digest</b> and whose purpose is <b>purpose</b>. Returns + * NULL if no circuit is found. If <b>start</b> is NULL, begin at the start of + * the list. + */ +circuit_t * +circuit_get_next_by_pk_and_purpose(circuit_t *start, + const char *digest, uint8_t purpose) +{ + circuit_t *circ; + if (start == NULL) + circ = global_circuitlist; + else + circ = start->next; + + for( ; circ; circ = circ->next) { + if (circ->marked_for_close) + continue; + if (circ->purpose != purpose) + continue; + if (!memcmp(circ->rend_pk_digest, digest, DIGEST_LEN)) + return circ; + } + return NULL; +} + +/** Return the circuit waiting for a rendezvous with the provided cookie. + * Return NULL if no such circuit is found. + */ +circuit_t *circuit_get_rendezvous(const char *cookie) +{ + circuit_t *circ; + for (circ = global_circuitlist; circ; circ = circ->next) { + if (! circ->marked_for_close && + circ->purpose == CIRCUIT_PURPOSE_REND_POINT_WAITING && + ! memcmp(circ->rend_cookie, cookie, REND_COOKIE_LEN) ) + return circ; + } + return NULL; +} + +/** Count the number of circs originating here that aren't open, and + * that have the specified <b>purpose</b>. */ +int circuit_count_building(uint8_t purpose) { + circuit_t *circ; + int num=0; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(CIRCUIT_IS_ORIGIN(circ) && + circ->state != CIRCUIT_STATE_OPEN && + circ->purpose == purpose && + !circ->marked_for_close) + num++; + } + return num; +} + +/** Return the circuit that is open, has specified <b>purpose</b>, + * has a timestamp_dirty value of 0, and was created most recently, + * or NULL if no circuit fits this description. + */ +circuit_t * +circuit_get_youngest_clean_open(uint8_t purpose) { + circuit_t *circ; + circuit_t *youngest=NULL; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(CIRCUIT_IS_ORIGIN(circ) && circ->state == CIRCUIT_STATE_OPEN && + !circ->marked_for_close && circ->purpose == purpose && + !circ->timestamp_dirty && + (!youngest || youngest->timestamp_created < circ->timestamp_created)) + youngest = circ; + } + return youngest; +} + +/** Mark <b>circ</b> to be closed next time we call + * circuit_close_all_marked(). Do any cleanup needed: + * - If state is onionskin_pending, remove circ from the onion_pending + * list. + * - If circ isn't open yet, call circuit_build_failed() if we're + * the origin, and in case call circuit_rep_hist_note_result() + * to note stats. + * - If purpose is C_INTRODUCE_ACK_WAIT, remove the intro point we + * just tried from our list of intro points for that service + * descriptor. + * - Send appropriate destroys and edge_destroys for conns and + * streams attached to circ. + * - If circ->rend_splice is set (we are the midpoint of a joined + * rendezvous stream), then mark the other circuit to close as well. + */ +int _circuit_mark_for_close(circuit_t *circ) { + connection_t *conn; + + assert_circuit_ok(circ); + if (circ->marked_for_close) + return -1; + + if(circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { + onion_pending_remove(circ); + } + /* If the circuit ever became OPEN, we sent it to the reputation history + * module then. If it isn't OPEN, we send it there now to remember which + * links worked and which didn't. + */ + if (circ->state != CIRCUIT_STATE_OPEN) { + if(CIRCUIT_IS_ORIGIN(circ)) + circuit_build_failed(circ); /* take actions if necessary */ + circuit_rep_hist_note_result(circ); + } + if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + tor_assert(circ->state == CIRCUIT_STATE_OPEN); + /* treat this like getting a nack from it */ + log_fn(LOG_INFO,"Failed intro circ %s to %s (awaiting ack). Removing from descriptor.", + circ->rend_query, circ->build_state->chosen_exit); + rend_client_remove_intro_point(circ->build_state->chosen_exit, circ->rend_query); + } + + if(circ->n_conn) + connection_send_destroy(circ->n_circ_id, circ->n_conn); + for(conn=circ->n_streams; conn; conn=conn->next_stream) + connection_edge_destroy(circ->n_circ_id, conn); + while(circ->resolving_streams) { + conn = circ->resolving_streams; + circ->resolving_streams = conn->next_stream; + log_fn(LOG_INFO,"Freeing resolving-conn."); + connection_free(conn); + } + if(circ->p_conn) + connection_send_destroy(circ->p_circ_id, circ->p_conn); + for(conn=circ->p_streams; conn; conn=conn->next_stream) + connection_edge_destroy(circ->p_circ_id, conn); + + circ->marked_for_close = 1; + + if (circ->rend_splice && !circ->rend_splice->marked_for_close) { + /* do this after marking this circuit, to avoid infinite recursion. */ + circuit_mark_for_close(circ->rend_splice); + circ->rend_splice = NULL; + } + return 0; +} + +/** Verify that cpath layer <b>cp</b> has all of its invariants + * correct. Trigger an assert if anything is invalid. + */ +void assert_cpath_layer_ok(const crypt_path_t *cp) +{ + tor_assert(cp->f_crypto); + tor_assert(cp->b_crypto); +// tor_assert(cp->addr); /* these are zero for rendezvous extra-hops */ +// tor_assert(cp->port); + switch(cp->state) + { + case CPATH_STATE_CLOSED: + case CPATH_STATE_OPEN: + tor_assert(!cp->handshake_state); + break; + case CPATH_STATE_AWAITING_KEYS: + tor_assert(cp->handshake_state); + break; + default: + tor_assert(0); + } + tor_assert(cp->package_window >= 0); + tor_assert(cp->deliver_window >= 0); +} + +/** Verify that cpath <b>cp</b> has all of its invariants + * correct. Trigger an assert if anything is invalid. + */ +static void + assert_cpath_ok(const crypt_path_t *cp) +{ + while(cp->prev) + cp = cp->prev; + + while(cp->next) { + assert_cpath_layer_ok(cp); + /* layers must be in sequence of: "open* awaiting? closed*" */ + if (cp->prev) { + if (cp->prev->state == CPATH_STATE_OPEN) { + tor_assert(cp->state == CPATH_STATE_CLOSED || + cp->state == CPATH_STATE_AWAITING_KEYS); + } else { + tor_assert(cp->state == CPATH_STATE_CLOSED); + } + } + cp = cp->next; + } +} + +/** Verify that circuit <b>c</b> has all of its invariants + * correct. Trigger an assert if anything is invalid. + */ +void assert_circuit_ok(const circuit_t *c) +{ + connection_t *conn; + + tor_assert(c); + tor_assert(c->magic == CIRCUIT_MAGIC); + tor_assert(c->purpose >= _CIRCUIT_PURPOSE_MIN && + c->purpose <= _CIRCUIT_PURPOSE_MAX); + + if (c->n_conn) + tor_assert(c->n_conn->type == CONN_TYPE_OR); + if (c->p_conn) + tor_assert(c->p_conn->type == CONN_TYPE_OR); + for (conn = c->p_streams; conn; conn = conn->next_stream) + tor_assert(conn->type == CONN_TYPE_AP); + for (conn = c->n_streams; conn; conn = conn->next_stream) + tor_assert(conn->type == CONN_TYPE_EXIT); + + tor_assert(c->deliver_window >= 0); + tor_assert(c->package_window >= 0); + if (c->state == CIRCUIT_STATE_OPEN) { + if (c->cpath) { + tor_assert(CIRCUIT_IS_ORIGIN(c)); + tor_assert(!c->n_crypto); + tor_assert(!c->p_crypto); + tor_assert(!c->n_digest); + tor_assert(!c->p_digest); + } else { + tor_assert(!CIRCUIT_IS_ORIGIN(c)); + tor_assert(c->n_crypto); + tor_assert(c->p_crypto); + tor_assert(c->n_digest); + tor_assert(c->p_digest); + } + } + if (c->cpath) { + assert_cpath_ok(c->cpath); + } + if (c->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED) { + if (!c->marked_for_close) { + tor_assert(c->rend_splice); + tor_assert(c->rend_splice->rend_splice == c); + } + tor_assert(c->rend_splice != c); + } else { + tor_assert(!c->rend_splice); + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/src/or/circuituse.c b/src/or/circuituse.c new file mode 100644 index 000000000..1374f0a50 --- /dev/null +++ b/src/or/circuituse.c @@ -0,0 +1,821 @@ +/* Copyright 2001 Matej Pfajfar, 2001-2004 Roger Dingledine. */ +/* See LICENSE for licensing information */ +/* $Id$ */ + +/** + * \file circuituse.c + * \brief Launch the right sort of circuits, attach streams to them. + **/ + +#include "or.h" + +extern or_options_t options; /* command-line and config-file options */ + +/********* START VARIABLES **********/ + +extern circuit_t *global_circuitlist; /* from circuitlist.c */ + +/********* END VARIABLES ************/ + +static void circuit_expire_old_circuits(void); +static void circuit_increment_failure_count(void); + +/* Return 1 if <b>circ</b> could be returned by circuit_get_best(). + * Else return 0. + */ +static int circuit_is_acceptable(circuit_t *circ, + connection_t *conn, + int must_be_open, + uint8_t purpose, + time_t now) +{ + routerinfo_t *exitrouter; + + if (!CIRCUIT_IS_ORIGIN(circ)) + return 0; /* this circ doesn't start at us */ + if (must_be_open && (circ->state != CIRCUIT_STATE_OPEN || !circ->n_conn)) + return 0; /* ignore non-open circs */ + if (circ->marked_for_close) + return 0; + + /* if this circ isn't our purpose, skip. */ + if(purpose == CIRCUIT_PURPOSE_C_REND_JOINED && !must_be_open) { + if(circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND && + circ->purpose != CIRCUIT_PURPOSE_C_REND_READY && + circ->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED && + circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED) + return 0; + } else if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT && !must_be_open) { + if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCING && + circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) + return 0; + } else { + if(purpose != circ->purpose) + return 0; + } + + if(purpose == CIRCUIT_PURPOSE_C_GENERAL) + if(circ->timestamp_dirty && + circ->timestamp_dirty+options.NewCircuitPeriod < now) + return 0; + + if(conn) { + /* decide if this circ is suitable for this conn */ + + /* for rend circs, circ->cpath->prev is not the last router in the + * circuit, it's the magical extra bob hop. so just check the nickname + * of the one we meant to finish at. + */ + exitrouter = router_get_by_nickname(circ->build_state->chosen_exit); + + if(!exitrouter) { + log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)"); + return 0; /* this circuit is screwed and doesn't know it yet */ + } + + if(purpose == CIRCUIT_PURPOSE_C_GENERAL) { + if(connection_ap_can_use_exit(conn, exitrouter) == ADDR_POLICY_REJECTED) { + /* can't exit from this router */ + return 0; + } + } else { /* not general */ + if(rend_cmp_service_ids(conn->rend_query, circ->rend_query) && + (circ->rend_query[0] || purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) { + /* this circ is not for this conn, and it's not suitable + * for cannibalizing either */ + return 0; + } + } + } + return 1; +} + +/* Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for + * <b>purpose</b>, and return 0 otherwise. Used by circuit_get_best. + */ +static int circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose) +{ + switch(purpose) { + case CIRCUIT_PURPOSE_C_GENERAL: + /* if it's used but less dirty it's best; + * else if it's more recently created it's best + */ + if(b->timestamp_dirty) { + if(a->timestamp_dirty && + a->timestamp_dirty > b->timestamp_dirty) + return 1; + } else { + if(a->timestamp_dirty || + a->timestamp_created > b->timestamp_created) + return 1; + } + break; + case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: + /* the closer it is to ack_wait the better it is */ + if(a->purpose > b->purpose) + return 1; + break; + case CIRCUIT_PURPOSE_C_REND_JOINED: + /* the closer it is to rend_joined the better it is */ + if(a->purpose > b->purpose) + return 1; + break; + } + return 0; +} + +/** Find the best circ that conn can use, preferably one which is + * dirty. Circ must not be too old. + * + * Conn must be defined. + * + * If must_be_open, ignore circs not in CIRCUIT_STATE_OPEN. + * + * circ_purpose specifies what sort of circuit we must have. + * It can be C_GENERAL, C_INTRODUCE_ACK_WAIT, or C_REND_JOINED. + * + * If it's REND_JOINED and must_be_open==0, then return the closest + * rendezvous-purposed circuit that you can find. + * + * If it's INTRODUCE_ACK_WAIT and must_be_open==0, then return the + * closest introduce-purposed circuit that you can find. + */ +static circuit_t * +circuit_get_best(connection_t *conn, int must_be_open, uint8_t purpose) +{ + circuit_t *circ, *best=NULL; + time_t now = time(NULL); + + tor_assert(conn); + + tor_assert(purpose == CIRCUIT_PURPOSE_C_GENERAL || + purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT || + purpose == CIRCUIT_PURPOSE_C_REND_JOINED); + + for (circ=global_circuitlist;circ;circ = circ->next) { + if (!circuit_is_acceptable(circ,conn,must_be_open,purpose,now)) + continue; + + /* now this is an acceptable circ to hand back. but that doesn't + * mean it's the *best* circ to hand back. try to decide. + */ + if(!best || circuit_is_better(circ,best,purpose)) + best = circ; + } + + return best; +} + +/** Circuits that were born at the end of their second might be expired + * after 30.1 seconds; circuits born at the beginning might be expired + * after closer to 31 seconds. + */ +#define MIN_SECONDS_BEFORE_EXPIRING_CIRC 30 + +/** Close all circuits that start at us, aren't open, and were born + * at least MIN_SECONDS_BEFORE_EXPIRING_CIRC seconds ago. + */ +void circuit_expire_building(time_t now) { + circuit_t *victim, *circ = global_circuitlist; + + while(circ) { + victim = circ; + circ = circ->next; + if(!CIRCUIT_IS_ORIGIN(victim)) + continue; /* didn't originate here */ + if(victim->marked_for_close) + continue; /* don't mess with marked circs */ + if(victim->timestamp_created + MIN_SECONDS_BEFORE_EXPIRING_CIRC > now) + continue; /* it's young still, don't mess with it */ + + /* some debug logs, to help track bugs */ + if(victim->purpose >= CIRCUIT_PURPOSE_C_INTRODUCING && + victim->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { + if(!victim->timestamp_dirty) + log_fn(LOG_DEBUG,"Considering %sopen purp %d to %s (circid %d). (clean).", + victim->state == CIRCUIT_STATE_OPEN ? "" : "non", + victim->purpose, victim->build_state->chosen_exit, + victim->n_circ_id); + else + log_fn(LOG_DEBUG,"Considering %sopen purp %d to %s (circid %d). %d secs since dirty.", + victim->state == CIRCUIT_STATE_OPEN ? "" : "non", + victim->purpose, victim->build_state->chosen_exit, + victim->n_circ_id, + (int)(now - victim->timestamp_dirty)); + } + + /* if circ is !open, or if it's open but purpose is a non-finished + * intro or rend, then mark it for close */ + if(victim->state != CIRCUIT_STATE_OPEN || + victim->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || + victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCING || + victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || + + /* it's a rend_ready circ, but it's already picked a query */ + (victim->purpose == CIRCUIT_PURPOSE_C_REND_READY && + victim->rend_query[0]) || + + /* c_rend_ready circs measure age since timestamp_dirty, + * because that's set when they switch purposes + */ + /* rend and intro circs become dirty each time they + * make an introduction attempt. so timestamp_dirty + * will reflect the time since the last attempt. + */ + ((victim->purpose == CIRCUIT_PURPOSE_C_REND_READY || + victim->purpose == CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED || + victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) && + victim->timestamp_dirty + MIN_SECONDS_BEFORE_EXPIRING_CIRC > now)) { + if(victim->n_conn) + log_fn(LOG_INFO,"Abandoning circ %s:%d:%d (state %d:%s, purpose %d)", + victim->n_conn->address, victim->n_port, victim->n_circ_id, + victim->state, circuit_state_to_string[victim->state], victim->purpose); + else + log_fn(LOG_INFO,"Abandoning circ %d (state %d:%s, purpose %d)", victim->n_circ_id, + victim->state, circuit_state_to_string[victim->state], victim->purpose); + circuit_log_path(LOG_INFO,victim); + circuit_mark_for_close(victim); + } + } +} + +/** How many circuits do we want simultaneously in-progress to handle + * a given stream? + */ +#define MIN_CIRCUITS_HANDLING_STREAM 2 + +/** Return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open + * general-purpose circuits will have an acceptable exit node for + * conn. Else return 0. + */ +int circuit_stream_is_being_handled(connection_t *conn) { + circuit_t *circ; + routerinfo_t *exitrouter; + int num=0; + time_t now = time(NULL); + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(CIRCUIT_IS_ORIGIN(circ) && circ->state != CIRCUIT_STATE_OPEN && + !circ->marked_for_close && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL && + (!circ->timestamp_dirty || + circ->timestamp_dirty + options.NewCircuitPeriod < now)) { + exitrouter = router_get_by_nickname(circ->build_state->chosen_exit); + if(exitrouter && connection_ap_can_use_exit(conn, exitrouter) != ADDR_POLICY_REJECTED) + if(++num >= MIN_CIRCUITS_HANDLING_STREAM) + return 1; + } + } + return 0; +} + +/** Build a new test circuit every 5 minutes */ +#define TESTING_CIRCUIT_INTERVAL 300 + +/** This function is called once a second. Its job is to make sure + * all services we offer have enough circuits available. Some + * services just want enough circuits for current tasks, whereas + * others want a minimum set of idle circuits hanging around. + */ +void circuit_build_needed_circs(time_t now) { + static long time_to_new_circuit = 0; + circuit_t *circ; + + /* launch a new circ for any pending streams that need one */ + connection_ap_attach_pending(); + + /* make sure any hidden services have enough intro points */ + rend_services_introduce(); + + circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL); + + if(time_to_new_circuit < now) { + circuit_reset_failure_count(); + time_to_new_circuit = now + options.NewCircuitPeriod; + if(options.SocksPort) + client_dns_clean(); + circuit_expire_old_circuits(); + + if(options.RunTesting && circ && + circ->timestamp_created + TESTING_CIRCUIT_INTERVAL < now) { + log_fn(LOG_INFO,"Creating a new testing circuit."); + circuit_launch_new(CIRCUIT_PURPOSE_C_GENERAL, NULL); + } + } + +/** How many simultaneous in-progress general-purpose circuits do we + * want to be building at once, if there are no open general-purpose + * circuits? + */ +#define CIRCUIT_MIN_BUILDING_GENERAL 3 + /* if there's no open circ, and less than 3 are on the way, + * go ahead and try another. */ + if(!circ && circuit_count_building(CIRCUIT_PURPOSE_C_GENERAL) + < CIRCUIT_MIN_BUILDING_GENERAL) { + circuit_launch_new(CIRCUIT_PURPOSE_C_GENERAL, NULL); + } + + /* XXX count idle rendezvous circs and build more */ +} + +/** If the stream <b>conn</b> is a member of any of the linked + * lists of <b>circ</b>, then remove it from the list. + */ +void circuit_detach_stream(circuit_t *circ, connection_t *conn) { + connection_t *prevconn; + + tor_assert(circ && conn); + + if(conn == circ->p_streams) { + circ->p_streams = conn->next_stream; + return; + } + if(conn == circ->n_streams) { + circ->n_streams = conn->next_stream; + return; + } + if(conn == circ->resolving_streams) { + circ->resolving_streams = conn->next_stream; + return; + } + + for(prevconn = circ->p_streams; + prevconn && prevconn->next_stream && prevconn->next_stream != conn; + prevconn = prevconn->next_stream) + ; + if(prevconn && prevconn->next_stream) { + prevconn->next_stream = conn->next_stream; + return; + } + + for(prevconn = circ->n_streams; + prevconn && prevconn->next_stream && prevconn->next_stream != conn; + prevconn = prevconn->next_stream) + ; + if(prevconn && prevconn->next_stream) { + prevconn->next_stream = conn->next_stream; + return; + } + + for(prevconn = circ->resolving_streams; + prevconn && prevconn->next_stream && prevconn->next_stream != conn; + prevconn = prevconn->next_stream) + ; + if(prevconn && prevconn->next_stream) { + prevconn->next_stream = conn->next_stream; + return; + } + + log_fn(LOG_ERR,"edge conn not in circuit's list?"); + tor_assert(0); /* should never get here */ +} + +/** Notify the global circuit list that <b>conn</b> is about to be + * removed and then freed. + * + * If it's an OR conn, then mark-for-close all the circuits that use + * that conn. + * + * If it's an edge conn, then detach it from its circ, so we don't + * try to reference it later. + */ +void circuit_about_to_close_connection(connection_t *conn) { + /* currently, we assume it's too late to flush conn's buf here. + * down the road, maybe we'll consider that eof doesn't mean can't-write + */ + circuit_t *circ; + + switch(conn->type) { + case CONN_TYPE_OR: + /* We must close all the circuits on it. */ + while((circ = circuit_get_by_conn(conn))) { + if(circ->n_conn == conn) /* it's closing in front of us */ + circ->n_conn = NULL; + if(circ->p_conn == conn) /* it's closing behind us */ + circ->p_conn = NULL; + circuit_mark_for_close(circ); + } + return; + case CONN_TYPE_AP: + case CONN_TYPE_EXIT: + + /* It's an edge conn. Need to remove it from the linked list of + * conn's for this circuit. Confirm that 'end' relay command has + * been sent. But don't kill the circuit. + */ + + circ = circuit_get_by_conn(conn); + if(!circ) + return; + + circuit_detach_stream(circ, conn); + + } /* end switch */ +} + +/** Don't keep more than 10 unused open circuits around. */ +#define MAX_UNUSED_OPEN_CIRCUITS 10 + +/** Find each circuit that has been dirty for too long, and has + * no streams on it: mark it for close. + * + * Also, if there are more than MAX_UNUSED_OPEN_CIRCUITS open and + * unused circuits, then mark the excess circs for close. + */ +static void +circuit_expire_old_circuits(void) +{ + circuit_t *circ; + time_t now = time(NULL); + smartlist_t *unused_open_circs; + int i; + + unused_open_circs = smartlist_create(); + + for (circ = global_circuitlist; circ; circ = circ->next) { + if (circ->marked_for_close) + continue; + /* If the circuit has been dirty for too long, and there are no streams + * on it, mark it for close. + */ + if (circ->timestamp_dirty && + circ->timestamp_dirty + options.NewCircuitPeriod < now && + !circ->p_conn && /* we're the origin */ + !circ->p_streams /* nothing attached */ ) { + log_fn(LOG_DEBUG,"Closing n_circ_id %d (dirty %d secs ago, purp %d)",circ->n_circ_id, + (int)(now - circ->timestamp_dirty), circ->purpose); + /* (only general and purpose_c circs can get dirty) */ + tor_assert(!circ->n_streams); + tor_assert(circ->purpose <= CIRCUIT_PURPOSE_C_REND_JOINED); + circuit_mark_for_close(circ); + } else if (!circ->timestamp_dirty && CIRCUIT_IS_ORIGIN(circ) && + circ->state == CIRCUIT_STATE_OPEN && + circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) { + /* Also, gather a list of open unused general circuits that we created. + * Because we add elements to the front of global_circuitlist, + * the last elements of unused_open_circs will be the oldest + * ones. + */ + smartlist_add(unused_open_circs, circ); + } + } + for (i = MAX_UNUSED_OPEN_CIRCUITS; i < smartlist_len(unused_open_circs); ++i) { + circuit_t *circ = smartlist_get(unused_open_circs, i); + log_fn(LOG_DEBUG,"Expiring excess clean circ (n_circ_id %d, purp %d)", + circ->n_circ_id, circ->purpose); + circuit_mark_for_close(circ); + } + smartlist_free(unused_open_circs); +} + +/** The circuit <b>circ</b> has just become open. Take the next + * step: for rendezvous circuits, we pass circ to the appropriate + * function in rendclient or rendservice. For general circuits, we + * call connection_ap_attach_pending, which looks for pending streams + * that could use circ. + */ +void circuit_has_opened(circuit_t *circ) { + + switch(circ->purpose) { + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + rend_client_rendcirc_has_opened(circ); + break; + case CIRCUIT_PURPOSE_C_INTRODUCING: + rend_client_introcirc_has_opened(circ); + break; + case CIRCUIT_PURPOSE_C_GENERAL: + /* Tell any AP connections that have been waiting for a new + * circuit that one is ready. */ + connection_ap_attach_pending(); + break; + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: + /* at Bob, waiting for introductions */ + rend_service_intro_has_opened(circ); + break; + case CIRCUIT_PURPOSE_S_CONNECT_REND: + /* at Bob, connecting to rend point */ + rend_service_rendezvous_has_opened(circ); + break; + default: + log_fn(LOG_ERR,"unhandled purpose %d",circ->purpose); + tor_assert(0); + } +} + +/*~ Called whenever a circuit could not be successfully built. + */ +void circuit_build_failed(circuit_t *circ) { + + /* we should examine circ and see if it failed because of + * the last hop or an earlier hop. then use this info below. + */ + int failed_at_last_hop = 0; + /* If the last hop isn't open, and the second-to-last is, we failed + * at the last hop. */ + if (circ->cpath && + circ->cpath->prev->state != CPATH_STATE_OPEN && + circ->cpath->prev->prev->state == CPATH_STATE_OPEN) { + failed_at_last_hop = 1; + } + + switch(circ->purpose) { + case CIRCUIT_PURPOSE_C_GENERAL: + if (circ->state != CIRCUIT_STATE_OPEN) { + /* If we never built the circuit, note it as a failure. */ + /* Note that we can't just check circ->cpath here, because if + * circuit-building failed immediately, it won't be set yet. */ + circuit_increment_failure_count(); + } + break; + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: + /* at Bob, waiting for introductions */ + if (circ->state != CIRCUIT_STATE_OPEN) { + circuit_increment_failure_count(); + } + /* no need to care here, because bob will rebuild intro + * points periodically. */ + break; + case CIRCUIT_PURPOSE_C_INTRODUCING: + /* at Alice, connecting to intro point */ + /* Don't increment failure count, since Bob may have picked + * the introduction point maliciously */ + /* Alice will pick a new intro point when this one dies, if + * the stream in question still cares. No need to act here. */ + break; + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + /* at Alice, waiting for Bob */ + if (circ->state != CIRCUIT_STATE_OPEN) { + circuit_increment_failure_count(); + } + /* Alice will pick a new rend point when this one dies, if + * the stream in question still cares. No need to act here. */ + break; + case CIRCUIT_PURPOSE_S_CONNECT_REND: + /* at Bob, connecting to rend point */ + /* Don't increment failure count, since Alice may have picked + * the rendezvous point maliciously */ + if (failed_at_last_hop) { + log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s. Sucks to be Alice.", circ->build_state->chosen_exit); + } else { + log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s, because an earlier node failed.", + circ->build_state->chosen_exit); + rend_service_relaunch_rendezvous(circ); + } + break; + default: + /* Other cases are impossible, since this function is only called with + * unbuilt circuits. */ + tor_assert(0); + } +} + +/** Number of consecutive failures so far; should only be touched by + * circuit_launch_new and circuit_*_failure_count. + */ +static int n_circuit_failures = 0; + +/** Don't retry launching a new circuit if we try this many times with no + * success. */ +#define MAX_CIRCUIT_FAILURES 5 + +/** Launch a new circuit and return a pointer to it. Return NULL if you failed. */ +circuit_t *circuit_launch_new(uint8_t purpose, const char *exit_nickname) { + + if (n_circuit_failures > MAX_CIRCUIT_FAILURES) { + /* too many failed circs in a row. don't try. */ +// log_fn(LOG_INFO,"%d failures so far, not trying.",n_circuit_failures); + return NULL; + } + + /* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */ + return circuit_establish_circuit(purpose, exit_nickname); +} + +/** Record another failure at opening a general circuit. When we have + * too many, we'll stop trying for the remainder of this minute. + */ +static void circuit_increment_failure_count(void) { + ++n_circuit_failures; + log_fn(LOG_DEBUG,"n_circuit_failures now %d.",n_circuit_failures); +} + +/** Reset the failure count for opening general circuits. This means + * we will try MAX_CIRCUIT_FAILURES times more (if necessary) before + * stopping again. + */ +void circuit_reset_failure_count(void) { + n_circuit_failures = 0; +} + +/** Find an open circ that we're happy with: return 1. If there isn't + * one, and there isn't one on the way, launch one and return 0. If it + * will never work, return -1. + * + * Write the found or in-progress or launched circ into *circp. + */ +static int +circuit_get_open_circ_or_launch(connection_t *conn, + uint8_t desired_circuit_purpose, + circuit_t **circp) { + circuit_t *circ; + uint32_t addr; + + tor_assert(conn); + tor_assert(circp); + tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + + circ = circuit_get_best(conn, 1, desired_circuit_purpose); + + if(circ) { + *circp = circ; + return 1; /* we're happy */ + } + + if(!connection_edge_is_rendezvous_stream(conn)) { /* general purpose circ */ + addr = client_dns_lookup_entry(conn->socks_request->address); + if(router_exit_policy_all_routers_reject(addr, conn->socks_request->port)) { + log_fn(LOG_WARN,"No Tor server exists that allows exit to %s:%d. Rejecting.", + conn->socks_request->address, conn->socks_request->port); + return -1; + } + } + + /* is one already on the way? */ + circ = circuit_get_best(conn, 0, desired_circuit_purpose); + if(!circ) { + char *exitname=NULL; + uint8_t new_circ_purpose; + + if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { + /* need to pick an intro point */ + exitname = rend_client_get_random_intro(conn->rend_query); + if(!exitname) { + log_fn(LOG_WARN,"Couldn't get an intro point for '%s'. Closing conn.", + conn->rend_query); + return -1; + } + if(!router_get_by_nickname(exitname)) { + log_fn(LOG_WARN,"Advertised intro point '%s' is not known. Closing.", exitname); + return -1; + } + /* XXX if we failed, then refetch the descriptor */ + log_fn(LOG_INFO,"Chose %s as intro point for %s.", exitname, conn->rend_query); + } + + if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED) + new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND; + else if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) + new_circ_purpose = CIRCUIT_PURPOSE_C_INTRODUCING; + else + new_circ_purpose = desired_circuit_purpose; + + circ = circuit_launch_new(new_circ_purpose, exitname); + tor_free(exitname); + + if(circ && + (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL)) { + /* then write the service_id into circ */ + strcpy(circ->rend_query, conn->rend_query); + } + } + if(!circ) + log_fn(LOG_INFO,"No safe circuit (purpose %d) ready for edge connection; delaying.", + desired_circuit_purpose); + *circp = circ; + return 0; +} + +/** Attach the AP stream <b>apconn</b> to circ's linked list of + * p_streams. Also set apconn's cpath_layer to the last hop in + * circ's cpath. + */ +static void link_apconn_to_circ(connection_t *apconn, circuit_t *circ) { + /* add it into the linked list of streams on this circuit */ + log_fn(LOG_DEBUG,"attaching new conn to circ. n_circ_id %d.", circ->n_circ_id); + apconn->next_stream = circ->p_streams; + /* assert_connection_ok(conn, time(NULL)); */ + circ->p_streams = apconn; + + tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath && circ->cpath->prev); + tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN); + apconn->cpath_layer = circ->cpath->prev; +} + +/** Try to find a safe live circuit for CONN_TYPE_AP connection conn. If + * we don't find one: if conn cannot be handled by any known nodes, + * warn and return -1 (conn needs to die); + * else launch new circuit (if necessary) and return 0. + * Otherwise, associate conn with a safe live circuit, do the + * right next step, and return 1. + */ +int connection_ap_handshake_attach_circuit(connection_t *conn) { + int retval; + int conn_age; + + tor_assert(conn); + tor_assert(conn->type == CONN_TYPE_AP); + tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + tor_assert(conn->socks_request); + + conn_age = time(NULL) - conn->timestamp_created; + if(conn_age > 60) { + /* XXX make this cleaner than '60' */ + log_fn(LOG_WARN,"Giving up on unattached conn (%d sec old).", conn_age); + return -1; + } + + if(!connection_edge_is_rendezvous_stream(conn)) { /* we're a general conn */ + circuit_t *circ=NULL; + + /* find the circuit that we should use, if there is one. */ + retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_GENERAL, &circ); + if(retval < 1) + return retval; + + /* We have found a suitable circuit for our conn. Hurray. */ + + log_fn(LOG_DEBUG,"Attaching apconn to general circ %d (stream %d sec old).", + circ->n_circ_id, conn_age); + /* here, print the circ's path. so people can figure out which circs are sucking. */ + circuit_log_path(LOG_INFO,circ); + + if(!circ->timestamp_dirty) + circ->timestamp_dirty = time(NULL); + + link_apconn_to_circ(conn, circ); + connection_ap_handshake_send_begin(conn, circ); + + return 1; + + } else { /* we're a rendezvous conn */ + circuit_t *rendcirc=NULL, *introcirc=NULL; + + tor_assert(!conn->cpath_layer); + + /* start by finding a rendezvous circuit for us */ + + retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_REND_JOINED, &rendcirc); + if(retval < 0) return -1; /* failed */ + tor_assert(rendcirc); + + if(retval > 0) { + /* one is already established, attach */ + log_fn(LOG_INFO,"rend joined circ %d already here. attaching. (stream %d sec old)", + rendcirc->n_circ_id, conn_age); + link_apconn_to_circ(conn, rendcirc); + if(connection_ap_handshake_send_begin(conn, rendcirc) < 0) + return 0; /* already marked, let them fade away */ + return 1; + } + + if(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { + log_fn(LOG_INFO,"pending-join circ %d already here, with intro ack. Stalling. (stream %d sec old)", rendcirc->n_circ_id, conn_age); + return 0; + } + + /* it's on its way. find an intro circ. */ + retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT, &introcirc); + if(retval < 0) return -1; /* failed */ + tor_assert(introcirc); + + if(retval > 0) { + /* one has already sent the intro. keep waiting. */ + log_fn(LOG_INFO,"Intro circ %d present and awaiting ack (rend %d). Stalling. (stream %d sec old)", + introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); + return 0; + } + + /* now both rendcirc and introcirc are defined, and neither is finished */ + + if(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY) { + log_fn(LOG_INFO,"ready rend circ %d already here (no intro-ack yet on intro %d). (stream %d sec old)", + rendcirc->n_circ_id, introcirc->n_circ_id, conn_age); + /* look around for any new intro circs that should introduce */ + + tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); + if(introcirc->state == CIRCUIT_STATE_OPEN) { + log_fn(LOG_INFO,"found open intro circ %d (rend %d); sending introduction. (stream %d sec old)", + introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); + /* XXX here we should cannibalize the rend circ if it's a zero service id */ + if(rend_client_send_introduction(introcirc, rendcirc) < 0) { + return -1; + } + rendcirc->timestamp_dirty = time(NULL); + introcirc->timestamp_dirty = time(NULL); + assert_circuit_ok(rendcirc); + assert_circuit_ok(introcirc); + return 0; + } + } + + log_fn(LOG_INFO,"Intro (%d) and rend (%d) circs are not both ready. Stalling conn. (%d sec old)", introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); + return 0; + } +} + +/* + Local Variables: + mode:c + indent-tabs-mode:nil + c-basic-offset:2 + End: +*/ diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index b875e8b20..c20b0cc2e 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -4,7 +4,7 @@ /** * \file connection_edge.c - * \brief Handle edge streams and relay cells. + * \brief Handle edge streams. **/ #include "or.h" @@ -15,38 +15,6 @@ extern char *conn_state_to_string[][_CONN_TYPE_MAX+1]; /* from connection.c */ static int connection_ap_handshake_process_socks(connection_t *conn); -static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); -static void connection_edge_consider_sending_sendme(connection_t *conn); - -static uint32_t client_dns_lookup_entry(const char *address); -static void client_dns_set_entry(const char *address, uint32_t val); -static int client_dns_incr_failures(const char *address); - -/** Pack the relay_header_t host-order structure <b>src</b> into - * network-order in the buffer <b>dest</b>. See tor-spec.txt for details - * about the wire format. - */ -void relay_header_pack(char *dest, const relay_header_t *src) { - *(uint8_t*)(dest) = src->command; - - set_uint16(dest+1, htons(src->recognized)); - set_uint16(dest+3, htons(src->stream_id)); - memcpy(dest+5, src->integrity, 4); - set_uint16(dest+9, htons(src->length)); -} - -/** Unpack the network-order buffer <b>src</b> into a host-order - * relay_header_t structure <b>dest</b>. - */ -void relay_header_unpack(relay_header_t *dest, const char *src) { - dest->command = *(uint8_t*)(src); - - dest->recognized = ntohs(get_uint16(src+1)); - dest->stream_id = ntohs(get_uint16(src+3)); - memcpy(dest->integrity, src+5, 4); - dest->length = ntohs(get_uint16(src+9)); -} - /** Handle new bytes on conn->inbuf, or notification of eof. * * If there was an EOF, then send an end and mark the connection @@ -142,33 +110,6 @@ int connection_edge_destroy(uint16_t circ_id, connection_t *conn) { return 0; } -/** Translate the <b>payload</b> of length <b>length</b>, which - * came from a relay 'end' cell, into a static const string describing - * why the stream is closing. - */ -static const char * -connection_edge_end_reason(char *payload, uint16_t length) { - if(length < 1) { - log_fn(LOG_WARN,"End cell arrived with length 0. Should be at least 1."); - return "MALFORMED"; - } - if(*payload < _MIN_END_STREAM_REASON || *payload > _MAX_END_STREAM_REASON) { - log_fn(LOG_WARN,"Reason for ending (%d) not recognized.",*payload); - return "MALFORMED"; - } - switch(*payload) { - case END_STREAM_REASON_MISC: return "misc error"; - case END_STREAM_REASON_RESOLVEFAILED: return "resolve failed"; - case END_STREAM_REASON_CONNECTFAILED: return "connect failed"; - case END_STREAM_REASON_EXITPOLICY: return "exit policy failed"; - case END_STREAM_REASON_DESTROY: return "destroyed"; - case END_STREAM_REASON_DONE: return "closed normally"; - case END_STREAM_REASON_TIMEOUT: return "gave up (timeout)"; - } - tor_assert(0); - return ""; -} - /** Send a relay end cell from stream <b>conn</b> to conn's circuit, * with a destination of cpath_layer. (If cpath_layer is NULL, the * destination is the circuit's origin.) Mark the relay end cell as @@ -210,338 +151,6 @@ connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer) return 0; } -/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and - * send it onto the open circuit <b>circ</b>. <b>fromconn</b> is the stream - * that's sending the relay cell, or NULL if it's a control cell. - * <b>cpath_layer</b> is NULL for OR->OP cells, or the destination hop - * for OP->OR cells. - * - * If you can't send the cell, mark the circuit for close and - * return -1. Else return 0. - */ -int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, - int relay_command, const char *payload, - int payload_len, crypt_path_t *cpath_layer) { - cell_t cell; - relay_header_t rh; - int cell_direction; - - if(!circ) { - log_fn(LOG_WARN,"no circ. Closing conn."); - tor_assert(fromconn); - fromconn->has_sent_end = 1; /* no circ to send to */ - connection_mark_for_close(fromconn); - return -1; - } - - memset(&cell, 0, sizeof(cell_t)); - cell.command = CELL_RELAY; - if(cpath_layer) { - cell.circ_id = circ->n_circ_id; - cell_direction = CELL_DIRECTION_OUT; - } else { - cell.circ_id = circ->p_circ_id; - cell_direction = CELL_DIRECTION_IN; - } - - memset(&rh, 0, sizeof(rh)); - rh.command = relay_command; - if(fromconn) - rh.stream_id = fromconn->stream_id; /* else it's 0 */ - rh.length = payload_len; - relay_header_pack(cell.payload, &rh); - if(payload_len) - memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len); - - log_fn(LOG_DEBUG,"delivering %d cell %s.", relay_command, - cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward"); - - if(circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) { - log_fn(LOG_WARN,"circuit_package_relay_cell failed. Closing."); - circuit_mark_for_close(circ); - return -1; - } - return 0; -} - -/** How many times will I retry a stream that fails due to DNS - * resolve failure? - */ -#define MAX_RESOLVE_FAILURES 3 - -/** An incoming relay cell has arrived from circuit <b>circ</b> to - * stream <b>conn</b>. - * - * The arguments here are the same as in - * connection_edge_process_relay_cell() below; this function is called - * from there when <b>conn</b> is defined and not in an open state. - */ -static int -connection_edge_process_relay_cell_not_open( - relay_header_t *rh, cell_t *cell, circuit_t *circ, - connection_t *conn, crypt_path_t *layer_hint) { - uint32_t addr; - int reason; - - if(rh->command == RELAY_COMMAND_END) { - reason = *(cell->payload+RELAY_HEADER_SIZE); - /* We have to check this here, since we aren't connected yet. */ - if (rh->length >= 5 && reason == END_STREAM_REASON_EXITPOLICY) { - log_fn(LOG_INFO,"Address %s refused due to exit policy. Retrying.", - conn->socks_request->address); - addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1)); - client_dns_set_entry(conn->socks_request->address, addr); - conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - circuit_detach_stream(circ,conn); - if(connection_ap_handshake_attach_circuit(conn) >= 0) - return 0; - log_fn(LOG_INFO,"Giving up on retrying (from exitpolicy); conn can't be handled."); - /* else, conn will get closed below */ - } else if (rh->length && reason == END_STREAM_REASON_RESOLVEFAILED) { - if (client_dns_incr_failures(conn->socks_request->address) - < MAX_RESOLVE_FAILURES) { - /* We haven't retried too many times; reattach the connection. */ - conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - circuit_detach_stream(circ,conn); - if(connection_ap_handshake_attach_circuit(conn) >= 0) - return 0; - /* else, conn will get closed below */ - log_fn(LOG_INFO,"Giving up on retrying (from resolvefailed); conn can't be handled."); - } else { - log_fn(LOG_WARN,"Have tried resolving address %s at %d different places. Giving up.", - conn->socks_request->address, MAX_RESOLVE_FAILURES); - } - } - log_fn(LOG_INFO,"Edge got end (%s) before we're connected. Marking for close.", - connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh->length)); - if(CIRCUIT_IS_ORIGIN(circ)) - circuit_log_path(LOG_INFO,circ); - conn->has_sent_end = 1; /* we just got an 'end', don't need to send one */ - connection_mark_for_close(conn); - return 0; - } - - if(conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_CONNECTED) { - if(conn->state != AP_CONN_STATE_CONNECT_WAIT) { - log_fn(LOG_WARN,"Got 'connected' while not in state connect_wait. Dropping."); - return 0; - } -// log_fn(LOG_INFO,"Connected! Notifying application."); - conn->state = AP_CONN_STATE_OPEN; - if (rh->length >= 4) { - addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); - client_dns_set_entry(conn->socks_request->address, addr); - } - log_fn(LOG_INFO,"'connected' received after %d seconds.", - (int)(time(NULL) - conn->timestamp_lastread)); - circuit_log_path(LOG_INFO,circ); - connection_ap_handshake_socks_reply(conn, NULL, 0, 1); - conn->socks_request->has_finished = 1; - /* handle anything that might have queued */ - if (connection_edge_package_raw_inbuf(conn) < 0) { - connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); - connection_mark_for_close(conn); - return 0; - } - return 0; - } - - log_fn(LOG_WARN,"Got an unexpected relay command %d, in state %d (%s). Closing.", - rh->command, conn->state, conn_state_to_string[conn->type][conn->state]); - connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); - connection_mark_for_close(conn); - return -1; -} - -/** An incoming relay cell has arrived on circuit <b>circ</b>. If - * <b>conn</b> is NULL this is a control cell, else <b>cell</b> is - * destined for <b>conn</b>. - * - * If <b>layer_hint</b> is defined, then we're the origin of the - * circuit, and it specifies the hop that packaged <b>cell</b>. - * - * Return -1 if you want to tear down the circuit, else 0. - */ -int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, - connection_t *conn, - crypt_path_t *layer_hint) { - static int num_seen=0; - relay_header_t rh; - - tor_assert(cell && circ); - - relay_header_unpack(&rh, cell->payload); -// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id); - num_seen++; - log_fn(LOG_DEBUG,"Now seen %d relay cells here.", num_seen); - - /* either conn is NULL, in which case we've got a control cell, or else - * conn points to the recognized stream. */ - - if(conn && - conn->state != AP_CONN_STATE_OPEN && - conn->state != EXIT_CONN_STATE_OPEN) { - return connection_edge_process_relay_cell_not_open( - &rh, cell, circ, conn, layer_hint); - } - - switch(rh.command) { - case RELAY_COMMAND_DROP: - log_fn(LOG_INFO,"Got a relay-level padding cell. Dropping."); - return 0; - case RELAY_COMMAND_BEGIN: - if (layer_hint && - circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { - log_fn(LOG_WARN,"relay begin request unsupported at AP. Dropping."); - return 0; - } - if(conn) { - log_fn(LOG_WARN,"begin cell for known stream. Dropping."); - return 0; - } - connection_exit_begin_conn(cell, circ); - return 0; - case RELAY_COMMAND_DATA: - ++stats_n_data_cells_received; - if((layer_hint && --layer_hint->deliver_window < 0) || - (!layer_hint && --circ->deliver_window < 0)) { - log_fn(LOG_WARN,"(relay data) circ deliver_window below 0. Killing."); - connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); - connection_mark_for_close(conn); - return -1; - } - log_fn(LOG_DEBUG,"circ deliver_window now %d.", layer_hint ? - layer_hint->deliver_window : circ->deliver_window); - - circuit_consider_sending_sendme(circ, layer_hint); - - if(!conn) { - log_fn(LOG_INFO,"data cell dropped, unknown stream."); - return 0; - } - - if(--conn->deliver_window < 0) { /* is it below 0 after decrement? */ - log_fn(LOG_WARN,"(relay data) conn deliver_window below 0. Killing."); - return -1; /* somebody's breaking protocol. kill the whole circuit. */ - } - - stats_n_data_bytes_received += rh.length; - connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE, - rh.length, conn); - connection_edge_consider_sending_sendme(conn); - return 0; - case RELAY_COMMAND_END: - if(!conn) { - log_fn(LOG_INFO,"end cell (%s) dropped, unknown stream.", - connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length)); - return 0; - } -/* XXX add to this log_fn the exit node's nickname? */ - log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.", - connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length), - conn->stream_id); - -#ifdef HALF_OPEN - conn->done_sending = 1; - shutdown(conn->s, 1); /* XXX check return; refactor NM */ - if (conn->done_receiving) { - /* We just *got* an end; no reason to send one. */ - conn->has_sent_end = 1; - connection_mark_for_close(conn); - conn->hold_open_until_flushed = 1; - } -#else - /* We just *got* an end; no reason to send one. */ - conn->has_sent_end = 1; - connection_mark_for_close(conn); - conn->hold_open_until_flushed = 1; -#endif - return 0; - case RELAY_COMMAND_EXTEND: - if(conn) { - log_fn(LOG_WARN,"'extend' for non-zero stream. Dropping."); - return 0; - } - return circuit_extend(cell, circ); - case RELAY_COMMAND_EXTENDED: - if(!layer_hint) { - log_fn(LOG_WARN,"'extended' unsupported at non-origin. Dropping."); - return 0; - } - log_fn(LOG_DEBUG,"Got an extended cell! Yay."); - if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) { - log_fn(LOG_WARN,"circuit_finish_handshake failed."); - return -1; - } - if (circuit_send_next_onion_skin(circ)<0) { - log_fn(LOG_INFO,"circuit_send_next_onion_skin() failed."); - return -1; - } - return 0; - case RELAY_COMMAND_TRUNCATE: - if(layer_hint) { - log_fn(LOG_WARN,"'truncate' unsupported at origin. Dropping."); - return 0; - } - if(circ->n_conn) { - connection_send_destroy(circ->n_circ_id, circ->n_conn); - circ->n_conn = NULL; - } - log_fn(LOG_DEBUG, "Processed 'truncate', replying."); - connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, - NULL, 0, NULL); - return 0; - case RELAY_COMMAND_TRUNCATED: - if(!layer_hint) { - log_fn(LOG_WARN,"'truncated' unsupported at non-origin. Dropping."); - return 0; - } - circuit_truncated(circ, layer_hint); - return 0; - case RELAY_COMMAND_CONNECTED: - if(conn) { - log_fn(LOG_WARN,"'connected' unsupported while open. Closing circ."); - return -1; - } - log_fn(LOG_INFO,"'connected' received, no conn attached anymore. Ignoring."); - return 0; - case RELAY_COMMAND_SENDME: - if(!conn) { - if(layer_hint) { - layer_hint->package_window += CIRCWINDOW_INCREMENT; - log_fn(LOG_DEBUG,"circ-level sendme at origin, packagewindow %d.", - layer_hint->package_window); - circuit_resume_edge_reading(circ, layer_hint); - } else { - circ->package_window += CIRCWINDOW_INCREMENT; - log_fn(LOG_DEBUG,"circ-level sendme at non-origin, packagewindow %d.", - circ->package_window); - circuit_resume_edge_reading(circ, layer_hint); - } - return 0; - } - conn->package_window += STREAMWINDOW_INCREMENT; - log_fn(LOG_DEBUG,"stream-level sendme, packagewindow now %d.", conn->package_window); - connection_start_reading(conn); - connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */ - return 0; - case RELAY_COMMAND_ESTABLISH_INTRO: - case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: - case RELAY_COMMAND_INTRODUCE1: - case RELAY_COMMAND_INTRODUCE2: - case RELAY_COMMAND_INTRODUCE_ACK: - case RELAY_COMMAND_RENDEZVOUS1: - case RELAY_COMMAND_RENDEZVOUS2: - case RELAY_COMMAND_INTRO_ESTABLISHED: - case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED: - rend_process_relay_cell(circ, rh.command, rh.length, - cell->payload+RELAY_HEADER_SIZE); - return 0; - } - log_fn(LOG_WARN,"unknown relay command %d.",rh.command); - return -1; -} - /** Connection <b>conn</b> has finished writing and has no bytes left on * its outbuf. * @@ -609,84 +218,6 @@ int connection_edge_finished_connecting(connection_t *conn) return connection_edge_process_inbuf(conn); /* in case the server has written anything */ } -uint64_t stats_n_data_cells_packaged = 0; -uint64_t stats_n_data_bytes_packaged = 0; -uint64_t stats_n_data_cells_received = 0; -uint64_t stats_n_data_bytes_received = 0; - -/** While conn->inbuf has an entire relay payload of bytes on it, - * and the appropriate package windows aren't empty, grab a cell - * and send it down the circuit. - * - * Return -1 if conn should be marked for close, else return 0. - */ -int connection_edge_package_raw_inbuf(connection_t *conn) { - int amount_to_process, length; - char payload[CELL_PAYLOAD_SIZE]; - circuit_t *circ; - - tor_assert(conn); - tor_assert(!connection_speaks_cells(conn)); - -repeat_connection_edge_package_raw_inbuf: - - circ = circuit_get_by_conn(conn); - if(!circ) { - log_fn(LOG_INFO,"conn has no circuits! Closing."); - return -1; - } - - if(circuit_consider_stop_edge_reading(circ, conn->cpath_layer)) - return 0; - - if(conn->package_window <= 0) { - log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window); - connection_stop_reading(conn); - return 0; - } - - amount_to_process = buf_datalen(conn->inbuf); - - if(!amount_to_process) - return 0; - - if(amount_to_process > RELAY_PAYLOAD_SIZE) { - length = RELAY_PAYLOAD_SIZE; - } else { - length = amount_to_process; - } - stats_n_data_bytes_packaged += length; - stats_n_data_cells_packaged += 1; - - connection_fetch_from_buf(payload, length, conn); - - log_fn(LOG_DEBUG,"(%d) Packaging %d bytes (%d waiting).", conn->s, length, - (int)buf_datalen(conn->inbuf)); - - if(connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA, - payload, length, conn->cpath_layer) < 0) - return 0; /* circuit is closed, don't continue */ - - if(!conn->cpath_layer) { /* non-rendezvous exit */ - tor_assert(circ->package_window > 0); - circ->package_window--; - } else { /* we're an AP, or an exit on a rendezvous circ */ - tor_assert(conn->cpath_layer->package_window > 0); - conn->cpath_layer->package_window--; - } - - if(--conn->package_window <= 0) { /* is it 0 after decrement? */ - connection_stop_reading(conn); - log_fn(LOG_DEBUG,"conn->package_window reached 0."); - circuit_consider_stop_edge_reading(circ, conn->cpath_layer); - return 0; /* don't process the inbuf any more */ - } - log_fn(LOG_DEBUG,"conn->package_window is now %d",conn->package_window); - - /* handle more if there's more, or return 0 if there isn't */ - goto repeat_connection_edge_package_raw_inbuf; -} - /** How many times do we retry a general-purpose stream (detach it from * one circuit and try another, after we wait a while with no 'connected' * cell) before giving up? @@ -795,37 +326,6 @@ void connection_ap_attach_pending(void) } } -/** Called when we've just received a relay data cell, or when - * we've just finished flushing all bytes to stream <b>conn</b>. - * - * If conn->outbuf is not too full, and our deliver window is - * low, send back a suitable number of stream-level sendme cells. - */ -static void connection_edge_consider_sending_sendme(connection_t *conn) { - circuit_t *circ; - - if(connection_outbuf_too_full(conn)) - return; - - circ = circuit_get_by_conn(conn); - if(!circ) { - /* this can legitimately happen if the destroy has already - * arrived and torn down the circuit */ - log_fn(LOG_INFO,"No circuit associated with conn. Skipping."); - return; - } - - while(conn->deliver_window < STREAMWINDOW_START - STREAMWINDOW_INCREMENT) { - log_fn(LOG_DEBUG,"Outbuf %d, Queueing stream sendme.", conn->outbuf_flushlen); - conn->deliver_window += STREAMWINDOW_INCREMENT; - if(connection_edge_send_command(conn, circ, RELAY_COMMAND_SENDME, - NULL, 0, conn->cpath_layer) < 0) { - log_fn(LOG_WARN,"connection_edge_send_command failed. Returning."); - return; /* the circuit's closed, don't continue */ - } - } -} - /** connection_edge_process_inbuf() found a conn in state * socks_wait. See if conn->inbuf has the right bytes to proceed with * the socks handshake. @@ -908,212 +408,6 @@ static int connection_ap_handshake_process_socks(connection_t *conn) { return 0; } -/** Find an open circ that we're happy with: return 1. If there isn't - * one, and there isn't one on the way, launch one and return 0. If it - * will never work, return -1. - * - * Write the found or in-progress or launched circ into *circp. - */ -static int -circuit_get_open_circ_or_launch(connection_t *conn, - uint8_t desired_circuit_purpose, - circuit_t **circp) { - circuit_t *circ; - uint32_t addr; - - tor_assert(conn); - tor_assert(circp); - tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); - - circ = circuit_get_best(conn, 1, desired_circuit_purpose); - - if(circ) { - *circp = circ; - return 1; /* we're happy */ - } - - if(!connection_edge_is_rendezvous_stream(conn)) { /* general purpose circ */ - addr = client_dns_lookup_entry(conn->socks_request->address); - if(router_exit_policy_all_routers_reject(addr, conn->socks_request->port)) { - log_fn(LOG_WARN,"No Tor server exists that allows exit to %s:%d. Rejecting.", - conn->socks_request->address, conn->socks_request->port); - return -1; - } - } - - /* is one already on the way? */ - circ = circuit_get_best(conn, 0, desired_circuit_purpose); - if(!circ) { - char *exitname=NULL; - uint8_t new_circ_purpose; - - if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { - /* need to pick an intro point */ - exitname = rend_client_get_random_intro(conn->rend_query); - if(!exitname) { - log_fn(LOG_WARN,"Couldn't get an intro point for '%s'. Closing conn.", - conn->rend_query); - return -1; - } - if(!router_get_by_nickname(exitname)) { - log_fn(LOG_WARN,"Advertised intro point '%s' is not known. Closing.", exitname); - return -1; - } - /* XXX if we failed, then refetch the descriptor */ - log_fn(LOG_INFO,"Chose %s as intro point for %s.", exitname, conn->rend_query); - } - - if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED) - new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND; - else if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) - new_circ_purpose = CIRCUIT_PURPOSE_C_INTRODUCING; - else - new_circ_purpose = desired_circuit_purpose; - - circ = circuit_launch_new(new_circ_purpose, exitname); - tor_free(exitname); - - if(circ && - (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL)) { - /* then write the service_id into circ */ - strcpy(circ->rend_query, conn->rend_query); - } - } - if(!circ) - log_fn(LOG_INFO,"No safe circuit (purpose %d) ready for edge connection; delaying.", - desired_circuit_purpose); - *circp = circ; - return 0; -} - -/** Attach the AP stream <b>apconn</b> to circ's linked list of - * p_streams. Also set apconn's cpath_layer to the last hop in - * circ's cpath. - */ -void link_apconn_to_circ(connection_t *apconn, circuit_t *circ) { - /* add it into the linked list of streams on this circuit */ - log_fn(LOG_DEBUG,"attaching new conn to circ. n_circ_id %d.", circ->n_circ_id); - apconn->next_stream = circ->p_streams; - /* assert_connection_ok(conn, time(NULL)); */ - circ->p_streams = apconn; - - tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath && circ->cpath->prev); - tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN); - apconn->cpath_layer = circ->cpath->prev; -} - -/** Try to find a safe live circuit for CONN_TYPE_AP connection conn. If - * we don't find one: if conn cannot be handled by any known nodes, - * warn and return -1 (conn needs to die); - * else launch new circuit (if necessary) and return 0. - * Otherwise, associate conn with a safe live circuit, do the - * right next step, and return 1. - */ -int connection_ap_handshake_attach_circuit(connection_t *conn) { - int retval; - int conn_age; - - tor_assert(conn); - tor_assert(conn->type == CONN_TYPE_AP); - tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); - tor_assert(conn->socks_request); - - conn_age = time(NULL) - conn->timestamp_created; - if(conn_age > 60) { - /* XXX make this cleaner than '60' */ - log_fn(LOG_WARN,"Giving up on unattached conn (%d sec old).", conn_age); - return -1; - } - - if(!connection_edge_is_rendezvous_stream(conn)) { /* we're a general conn */ - circuit_t *circ=NULL; - - /* find the circuit that we should use, if there is one. */ - retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_GENERAL, &circ); - if(retval < 1) - return retval; - - /* We have found a suitable circuit for our conn. Hurray. */ - - log_fn(LOG_DEBUG,"Attaching apconn to general circ %d (stream %d sec old).", - circ->n_circ_id, conn_age); - /* here, print the circ's path. so people can figure out which circs are sucking. */ - circuit_log_path(LOG_INFO,circ); - - if(!circ->timestamp_dirty) - circ->timestamp_dirty = time(NULL); - - link_apconn_to_circ(conn, circ); - connection_ap_handshake_send_begin(conn, circ); - - return 1; - - } else { /* we're a rendezvous conn */ - circuit_t *rendcirc=NULL, *introcirc=NULL; - - tor_assert(!conn->cpath_layer); - - /* start by finding a rendezvous circuit for us */ - - retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_REND_JOINED, &rendcirc); - if(retval < 0) return -1; /* failed */ - tor_assert(rendcirc); - - if(retval > 0) { - /* one is already established, attach */ - log_fn(LOG_INFO,"rend joined circ %d already here. attaching. (stream %d sec old)", - rendcirc->n_circ_id, conn_age); - link_apconn_to_circ(conn, rendcirc); - if(connection_ap_handshake_send_begin(conn, rendcirc) < 0) - return 0; /* already marked, let them fade away */ - return 1; - } - - if(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) { - log_fn(LOG_INFO,"pending-join circ %d already here, with intro ack. Stalling. (stream %d sec old)", rendcirc->n_circ_id, conn_age); - return 0; - } - - /* it's on its way. find an intro circ. */ - retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT, &introcirc); - if(retval < 0) return -1; /* failed */ - tor_assert(introcirc); - - if(retval > 0) { - /* one has already sent the intro. keep waiting. */ - log_fn(LOG_INFO,"Intro circ %d present and awaiting ack (rend %d). Stalling. (stream %d sec old)", - introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); - return 0; - } - - /* now both rendcirc and introcirc are defined, and neither is finished */ - - if(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY) { - log_fn(LOG_INFO,"ready rend circ %d already here (no intro-ack yet on intro %d). (stream %d sec old)", - rendcirc->n_circ_id, introcirc->n_circ_id, conn_age); - /* look around for any new intro circs that should introduce */ - - tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); - if(introcirc->state == CIRCUIT_STATE_OPEN) { - log_fn(LOG_INFO,"found open intro circ %d (rend %d); sending introduction. (stream %d sec old)", - introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); - /* XXX here we should cannibalize the rend circ if it's a zero service id */ - if(rend_client_send_introduction(introcirc, rendcirc) < 0) { - return -1; - } - rendcirc->timestamp_dirty = time(NULL); - introcirc->timestamp_dirty = time(NULL); - assert_circuit_ok(rendcirc); - assert_circuit_ok(introcirc); - return 0; - } - } - - log_fn(LOG_INFO,"Intro (%d) and rend (%d) circs are not both ready. Stalling conn. (%d sec old)", introcirc->n_circ_id, rendcirc->n_circ_id, conn_age); - return 0; - } -} - /** 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. */ @@ -1303,7 +597,7 @@ void connection_ap_handshake_socks_reply(connection_t *conn, char *reply, * * Return -1 if we want to tear down <b>circ</b>. Else return 0. */ -static int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { +int connection_exit_begin_conn(cell_t *cell, circuit_t *circ) { connection_t *n_stream; relay_header_t rh; char *colon; @@ -1537,7 +831,7 @@ _get_or_create_ent(const char *address) /** Return the IP associated with <b>address</b>, if we know it * and it's still fresh enough. Otherwise return 0. */ -static uint32_t client_dns_lookup_entry(const char *address) +uint32_t client_dns_lookup_entry(const char *address) { struct client_dns_entry *ent; struct in_addr in; @@ -1574,7 +868,7 @@ static uint32_t client_dns_lookup_entry(const char *address) * Increment the number of resolve failures we have on record * for it, and then return that number. */ -static int client_dns_incr_failures(const char *address) +int client_dns_incr_failures(const char *address) { struct client_dns_entry *ent; ent = _get_or_create_ent(address); @@ -1589,7 +883,7 @@ static int client_dns_incr_failures(const char *address) * so we can more correctly choose a router that will allow <b>address</b> * to exit from him. */ -static void client_dns_set_entry(const char *address, uint32_t val) +void client_dns_set_entry(const char *address, uint32_t val) { struct client_dns_entry *ent; struct in_addr in; diff --git a/src/or/onion.c b/src/or/onion.c index adaab58c4..8cf007354 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -4,42 +4,14 @@ /** * \file onion.c - * \brief Functions to handle onion parsing/creation and handle cpaths. + * \brief Functions to queue create cells, and handle onionskin + * parsing and creation. **/ #include "or.h" -/* prototypes for smartlist operations from routerlist.h - * They're here to prevent precedence issues with the .h files - */ -void router_add_running_routers_to_smartlist(smartlist_t *sl); -void add_nickname_list_to_smartlist(smartlist_t *sl, char *list); - extern or_options_t options; /**< command-line and config-file options */ -static int count_acceptable_routers(smartlist_t *routers); - -/** Decide whether the first bit of the circuit ID will be - * 0 or 1, to avoid conflicts where each side randomly chooses - * the same circuit ID. - * - * Return CIRC_ID_TYPE_LOWER if local_nick is NULL, or if - * local_nick is lexographically smaller than remote_nick. - * Else return CIRC_ID_TYPE_HIGHER. - */ -int decide_circ_id_type(char *local_nick, char *remote_nick) { - int result; - - tor_assert(remote_nick); - if(!local_nick) - return CIRC_ID_TYPE_LOWER; - result = strcasecmp(local_nick, remote_nick); - tor_assert(result); - if(result < 0) - return CIRC_ID_TYPE_LOWER; - return CIRC_ID_TYPE_HIGHER; -} - struct onion_queue_t { circuit_t *circ; struct onion_queue_t *next; @@ -140,498 +112,6 @@ void onion_pending_remove(circuit_t *circ) { free(victim); } -/** Given a response payload and keys, initialize, then send a created - * cell back. - */ -int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys) { - cell_t cell; - crypt_path_t *tmp_cpath; - - tmp_cpath = tor_malloc_zero(sizeof(crypt_path_t)); - - memset(&cell, 0, sizeof(cell_t)); - cell.command = CELL_CREATED; - cell.circ_id = circ->p_circ_id; - - circ->state = CIRCUIT_STATE_OPEN; - - log_fn(LOG_DEBUG,"Entering."); - - memcpy(cell.payload, payload, ONIONSKIN_REPLY_LEN); - - log_fn(LOG_INFO,"init digest forward 0x%.8x, backward 0x%.8x.", - (unsigned int)*(uint32_t*)(keys), (unsigned int)*(uint32_t*)(keys+20)); - if (circuit_init_cpath_crypto(tmp_cpath, keys, 0)<0) { - log_fn(LOG_WARN,"Circuit initialization failed"); - tor_free(tmp_cpath); - return -1; - } - circ->n_digest = tmp_cpath->f_digest; - circ->n_crypto = tmp_cpath->f_crypto; - circ->p_digest = tmp_cpath->b_digest; - circ->p_crypto = tmp_cpath->b_crypto; - tor_free(tmp_cpath); - - memcpy(circ->handshake_digest, cell.payload+DH_KEY_LEN, DIGEST_LEN); - - connection_or_write_cell_to_buf(&cell, circ->p_conn); - log_fn(LOG_DEBUG,"Finished sending 'created' cell."); - - return 0; -} - -extern int has_fetched_directory; /**< from main.c */ - -/** Choose a length for a circuit of purpose <b>purpose</b>. - * Default length is 3 + the number of endpoints that would give something - * away. If the routerlist <b>routers</b> doesn't have enough routers - * to handle the desired path length, return as large a path length as - * is feasible, except if it's less than 2, in which case return -1. - */ -static int new_route_len(double cw, uint8_t purpose, smartlist_t *routers) { - int num_acceptable_routers; - int routelen; - - tor_assert((cw >= 0) && (cw < 1) && routers); /* valid parameters */ - -#ifdef TOR_PERF - routelen = 2; -#else - if(purpose == CIRCUIT_PURPOSE_C_GENERAL) - routelen = 3; - else if(purpose == CIRCUIT_PURPOSE_C_INTRODUCING) - routelen = 4; - else if(purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND) - routelen = 3; - else if(purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) - routelen = 3; - else if(purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) - routelen = 4; - else { - log_fn(LOG_WARN,"Unhandled purpose %d", purpose); - return -1; - } -#endif -#if 0 - for(routelen = 3; ; routelen++) { /* 3, increment until coinflip says we're done */ - if (crypto_pseudo_rand_int(255) >= cw*255) /* don't extend */ - break; - } -#endif - log_fn(LOG_DEBUG,"Chosen route length %d (%d routers available).",routelen, - smartlist_len(routers)); - - num_acceptable_routers = count_acceptable_routers(routers); - - if(num_acceptable_routers < 2) { - log_fn(LOG_INFO,"Not enough acceptable routers (%d). Discarding this circuit.", - num_acceptable_routers); - return -1; - } - - if(num_acceptable_routers < routelen) { - log_fn(LOG_INFO,"Not enough routers: cutting routelen from %d to %d.", - routelen, num_acceptable_routers); - routelen = num_acceptable_routers; - } - - return routelen; -} - -/** Return a pointer to a suitable router to be the exit node for the - * general-purpose circuit we're about to build. - * - * Look through the connection array, and choose a router that maximizes - * the number of pending streams that can exit from this router. - * - * Return NULL if we can't find any suitable routers. - */ -static routerinfo_t *choose_good_exit_server_general(routerlist_t *dir) -{ - int *n_supported; - int i, j; - int n_pending_connections = 0; - connection_t **carray; - int n_connections; - int best_support = -1; - int n_best_support=0; - smartlist_t *sl, *preferredexits, *excludedexits; - routerinfo_t *router; - - get_connection_array(&carray, &n_connections); - - /* Count how many connections are waiting for a circuit to be built. - * We use this for log messages now, but in the future we may depend on it. - */ - for (i = 0; i < n_connections; ++i) { - if (carray[i]->type == CONN_TYPE_AP && - carray[i]->state == AP_CONN_STATE_CIRCUIT_WAIT && - !carray[i]->marked_for_close && - !circuit_stream_is_being_handled(carray[i])) - ++n_pending_connections; - } - log_fn(LOG_DEBUG, "Choosing exit node; %d connections are pending", - n_pending_connections); - /* Now we count, for each of the routers in the directory, how many - * of the pending connections could possibly exit from that - * router (n_supported[i]). (We can't be sure about cases where we - * don't know the IP address of the pending connection.) - */ - n_supported = tor_malloc(sizeof(int)*smartlist_len(dir->routers)); - for (i = 0; i < smartlist_len(dir->routers); ++i) { /* iterate over routers */ - router = smartlist_get(dir->routers, i); - if(router_is_me(router)) { - n_supported[i] = -1; - log_fn(LOG_DEBUG,"Skipping node %s -- it's me.", router->nickname); - /* XXX there's probably a reverse predecessor attack here, but - * it's slow. should we take this out? -RD - */ - continue; - } - if(!router->is_running) { - n_supported[i] = -1; - log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- directory says it's not running.", - router->nickname, i); - continue; /* skip routers that are known to be down */ - } - if(router_exit_policy_rejects_all(router)) { - n_supported[i] = -1; - log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.", - router->nickname, i); - continue; /* skip routers that reject all */ - } - n_supported[i] = 0; - for (j = 0; j < n_connections; ++j) { /* iterate over connections */ - if (carray[j]->type != CONN_TYPE_AP || - carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT || - carray[j]->marked_for_close || - circuit_stream_is_being_handled(carray[j])) - continue; /* Skip everything but APs in CIRCUIT_WAIT */ - switch (connection_ap_can_use_exit(carray[j], router)) - { - case ADDR_POLICY_REJECTED: - log_fn(LOG_DEBUG,"%s (index %d) would reject this stream.", - router->nickname, i); - break; /* would be rejected; try next connection */ - case ADDR_POLICY_ACCEPTED: - case ADDR_POLICY_UNKNOWN: - ++n_supported[i]; - log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.", - router->nickname, i, n_supported[i]); - } - } /* End looping over connections. */ - if (n_supported[i] > best_support) { - /* If this router is better than previous ones, remember its index - * and goodness, and start counting how many routers are this good. */ - best_support = n_supported[i]; n_best_support=1; - log_fn(LOG_DEBUG,"%s is new best supported option so far.", - router->nickname); - } else if (n_supported[i] == best_support) { - /* If this router is _as good_ as the best one, just increment the - * count of equally good routers.*/ - ++n_best_support; - } - } - log_fn(LOG_INFO, "Found %d servers that might support %d/%d pending connections.", - n_best_support, best_support, n_pending_connections); - - preferredexits = smartlist_create(); - add_nickname_list_to_smartlist(preferredexits,options.ExitNodes); - - excludedexits = smartlist_create(); - add_nickname_list_to_smartlist(excludedexits,options.ExcludeNodes); - - sl = smartlist_create(); - - /* If any routers definitely support any pending connections, choose one - * at random. */ - if (best_support > 0) { - for (i = 0; i < smartlist_len(dir->routers); i++) - if (n_supported[i] == best_support) - smartlist_add(sl, smartlist_get(dir->routers, i)); - - smartlist_subtract(sl,excludedexits); - if (smartlist_overlap(sl,preferredexits)) - smartlist_intersect(sl,preferredexits); - router = smartlist_choose(sl); - } else { - /* Either there are no pending connections, or no routers even seem to - * possibly support any of them. Choose a router at random. */ - if (best_support == -1) { - log(LOG_WARN, "All routers are down or middleman -- choosing a doomed exit at random."); - } - for(i = 0; i < smartlist_len(dir->routers); i++) - if(n_supported[i] != -1) - smartlist_add(sl, smartlist_get(dir->routers, i)); - - smartlist_subtract(sl,excludedexits); - if (smartlist_overlap(sl,preferredexits)) - smartlist_intersect(sl,preferredexits); - router = smartlist_choose(sl); - } - - smartlist_free(preferredexits); - smartlist_free(excludedexits); - smartlist_free(sl); - tor_free(n_supported); - if(router) { - log_fn(LOG_INFO, "Chose exit server '%s'", router->nickname); - return router; - } - log_fn(LOG_WARN, "No exit routers seem to be running; can't choose an exit."); - return NULL; -} - -/** Return a pointer to a suitable router to be the exit node for the - * circuit of purpose <b>purpose</b> that we're about to build (or NULL - * if no router is suitable). - * - * For general-purpose circuits, pass it off to - * choose_good_exit_server_general() - * - * For client-side rendezvous circuits, choose a random node, weighted - * toward the preferences in 'options'. - */ -static routerinfo_t *choose_good_exit_server(uint8_t purpose, routerlist_t *dir) -{ - routerinfo_t *r; - switch(purpose) { - case CIRCUIT_PURPOSE_C_GENERAL: - return choose_good_exit_server_general(dir); - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - r = router_choose_random_node(dir, options.RendNodes, options.RendExcludeNodes, NULL); - return r; - default: - log_fn(LOG_WARN,"unhandled purpose %d", purpose); - tor_assert(0); - } - return NULL; /* never reached */ -} - -/** Allocate a cpath_build_state_t, populate it based on - * <b>purpose</b> and <b>exit_nickname</b> (if specified), and - * return it. - */ -cpath_build_state_t *onion_new_cpath_build_state(uint8_t purpose, - const char *exit_nickname) { - routerlist_t *rl; - int r; - cpath_build_state_t *info; - routerinfo_t *exit; - - router_get_routerlist(&rl); - r = new_route_len(options.PathlenCoinWeight, purpose, rl->routers); - if (r < 0) - return NULL; - info = tor_malloc_zero(sizeof(cpath_build_state_t)); - info->desired_path_len = r; - if(exit_nickname) { /* the circuit-builder pre-requested one */ - log_fn(LOG_INFO,"Using requested exit node '%s'", exit_nickname); - info->chosen_exit = tor_strdup(exit_nickname); - } else { /* we have to decide one */ - exit = choose_good_exit_server(purpose, rl); - if(!exit) { - log_fn(LOG_WARN,"failed to choose an exit server"); - tor_free(info); - return NULL; - } - info->chosen_exit = tor_strdup(exit->nickname); - } - return info; -} - -/** Return the number of routers in <b>routers</b> that are currently up - * and available for building circuits through. Count sets of twins only - * once. - */ -static int count_acceptable_routers(smartlist_t *routers) { - int i, j, n; - int num=0; - connection_t *conn; - routerinfo_t *r, *r2; - - n = smartlist_len(routers); - for(i=0;i<n;i++) { - r = smartlist_get(routers, i); - log_fn(LOG_DEBUG,"Contemplating whether router %d (%s) is a new option...", - i, r->nickname); - if(r->is_running == 0) { - log_fn(LOG_DEBUG,"Nope, the directory says %d is not running.",i); - goto next_i_loop; - } - if(options.ORPort) { - conn = connection_exact_get_by_addr_port(r->addr, r->or_port); - if(!conn || conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN) { - log_fn(LOG_DEBUG,"Nope, %d is not connected.",i); - goto next_i_loop; - } - } - for(j=0;j<i;j++) { - r2 = smartlist_get(routers, j); - if(!crypto_pk_cmp_keys(r->onion_pkey, r2->onion_pkey)) { - /* these guys are twins. so we've already counted him. */ - log_fn(LOG_DEBUG,"Nope, %d is a twin of %d.",i,j); - goto next_i_loop; - } - } - num++; - log_fn(LOG_DEBUG,"I like %d. num_acceptable_routers now %d.",i, num); - next_i_loop: - ; /* C requires an explicit statement after the label */ - } - - return num; -} - -/** Go through smartlist <b>sl</b> of routers, and remove all elements that - * have the same onion key as twin. - */ -static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) { - int i; - routerinfo_t *r; - - if(twin == NULL) - return; - - for(i=0; i < smartlist_len(sl); i++) { - r = smartlist_get(sl,i); - if (!crypto_pk_cmp_keys(r->onion_pkey, twin->onion_pkey)) { - smartlist_del(sl,i--); - } - } -} - -/** Add <b>new_hop</b> to the end of the doubly-linked-list <b>head_ptr</b>. - * - * This function is used to extend cpath by another hop. - */ -void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop) -{ - if (*head_ptr) { - new_hop->next = (*head_ptr); - new_hop->prev = (*head_ptr)->prev; - (*head_ptr)->prev->next = new_hop; - (*head_ptr)->prev = new_hop; - } else { - *head_ptr = new_hop; - new_hop->prev = new_hop->next = new_hop; - } -} - -/** Choose a suitable next hop in the cpath <b>head_ptr</b>, - * based on <b>state</b>. Add the hop info to head_ptr, and return a - * pointer to the chosen router in <b>router_out</b>. - */ -int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t - *state, routerinfo_t **router_out) -{ - int cur_len; - crypt_path_t *cpath, *hop; - routerinfo_t *r; - routerinfo_t *choice; - int i; - smartlist_t *sl, *excludednodes; - - tor_assert(head_ptr); - tor_assert(router_out); - - if (!*head_ptr) { - cur_len = 0; - } else { - cur_len = 1; - for (cpath = *head_ptr; cpath->next != *head_ptr; cpath = cpath->next) { - ++cur_len; - } - } - if (cur_len >= state->desired_path_len) { - log_fn(LOG_DEBUG, "Path is complete: %d steps long", - state->desired_path_len); - return 1; - } - log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len, - state->desired_path_len); - - excludednodes = smartlist_create(); - add_nickname_list_to_smartlist(excludednodes,options.ExcludeNodes); - - if(cur_len == state->desired_path_len - 1) { /* Picking last node */ - log_fn(LOG_DEBUG, "Contemplating last hop: choice already made: %s", - state->chosen_exit); - choice = router_get_by_nickname(state->chosen_exit); - smartlist_free(excludednodes); - if(!choice) { - log_fn(LOG_WARN,"Our chosen exit %s is no longer in the directory? Discarding this circuit.", - state->chosen_exit); - return -1; - } - } else if(cur_len == 0) { /* picking first node */ - /* try the nodes in EntryNodes first */ - sl = smartlist_create(); - add_nickname_list_to_smartlist(sl,options.EntryNodes); - /* XXX one day, consider picking chosen_exit knowing what's in EntryNodes */ - remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); - remove_twins_from_smartlist(sl,router_get_my_routerinfo()); - smartlist_subtract(sl,excludednodes); - choice = smartlist_choose(sl); - smartlist_free(sl); - if(!choice) { - sl = smartlist_create(); - router_add_running_routers_to_smartlist(sl); - remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); - remove_twins_from_smartlist(sl,router_get_my_routerinfo()); - smartlist_subtract(sl,excludednodes); - choice = smartlist_choose(sl); - smartlist_free(sl); - } - smartlist_free(excludednodes); - if(!choice) { - log_fn(LOG_WARN,"No acceptable routers while picking entry node. Discarding this circuit."); - return -1; - } - } else { - log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice."); - sl = smartlist_create(); - router_add_running_routers_to_smartlist(sl); - remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit)); - remove_twins_from_smartlist(sl,router_get_my_routerinfo()); - for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) { - r = router_get_by_addr_port(cpath->addr, cpath->port); - tor_assert(r); - remove_twins_from_smartlist(sl,r); - } - smartlist_subtract(sl,excludednodes); - choice = smartlist_choose(sl); - smartlist_free(sl); - smartlist_free(excludednodes); - if(!choice) { - log_fn(LOG_WARN,"No acceptable routers while picking intermediate node. Discarding this circuit."); - return -1; - } - } - - log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)", - choice->nickname, cur_len, state->chosen_exit); - - hop = tor_malloc_zero(sizeof(crypt_path_t)); - - /* link hop into the cpath, at the end. */ - onion_append_to_cpath(head_ptr, hop); - - hop->state = CPATH_STATE_CLOSED; - - hop->port = choice->or_port; - hop->addr = choice->addr; - - hop->package_window = CIRCWINDOW_START; - hop->deliver_window = CIRCWINDOW_START; - - log_fn(LOG_DEBUG, "Extended circuit path with %s for hop %d", - choice->nickname, cur_len); - - *router_out = choice; - return 0; -} - /*----------------------------------------------------------------------*/ /** Given a router's 128 byte public key, diff --git a/src/or/or.h b/src/or/or.h index 212815c9c..a8eeca897 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -614,6 +614,8 @@ struct crypt_path_t { * at this step? */ }; +#define CPATH_KEY_MATERIAL_LEN (20*2+16*2) + #define DH_KEY_LEN DH_BYTES #define ONIONSKIN_CHALLENGE_LEN (PKCS1_OAEP_PADDING_OVERHEAD+\ CIPHER_KEY_LEN+\ @@ -859,11 +861,36 @@ int fetch_from_buf_socks(buf_t *buf, socks_request_t *req); void assert_buf_ok(buf_t *buf); -/********************************* circuit.c ***************************/ +/********************************* circuitbuild.c **********************/ + +void circuit_log_path(int severity, circuit_t *circ); +void circuit_rep_hist_note_result(circuit_t *circ); +void circuit_dump_by_conn(connection_t *conn, int severity); +circuit_t *circuit_establish_circuit(uint8_t purpose, + const char *exit_nickname); +void circuit_n_conn_open(connection_t *or_conn); +int circuit_send_next_onion_skin(circuit_t *circ); +int circuit_extend(cell_t *cell, circuit_t *circ); +int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data, int reverse); +int circuit_finish_handshake(circuit_t *circ, char *reply); +int circuit_truncated(circuit_t *circ, crypt_path_t *layer); +int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys); +void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); + +/********************************* circuitlist.c ***********************/ extern char *circuit_state_to_string[]; -circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn); void circuit_close_all_marked(void); +circuit_t *circuit_new(uint16_t p_circ_id, connection_t *p_conn); +void circuit_free_cpath_node(crypt_path_t *victim); +circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn); +circuit_t *circuit_get_by_conn(connection_t *conn); +circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose); +circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *start, + const char *digest, uint8_t purpose); +circuit_t *circuit_get_rendezvous(const char *cookie); +int circuit_count_building(uint8_t purpose); +circuit_t *circuit_get_youngest_clean_open(uint8_t purpose); int _circuit_mark_for_close(circuit_t *circ); #define circuit_mark_for_close(c) \ @@ -877,44 +904,26 @@ int _circuit_mark_for_close(circuit_t *circ); } \ } while (0) +void assert_cpath_layer_ok(const crypt_path_t *cp); +void assert_circuit_ok(const circuit_t *c); -circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn); -circuit_t *circuit_get_by_conn(connection_t *conn); -circuit_t *circuit_get_best(connection_t *conn, - int must_be_open, uint8_t purpose); -circuit_t *circuit_get_by_rend_query_and_purpose(const char *rend_query, uint8_t purpose); -circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *circuit, - const char *servid, uint8_t purpose); -circuit_t *circuit_get_rendezvous(const char *cookie); +/********************************* circuituse.c ************************/ void circuit_expire_building(time_t now); -int circuit_count_building(uint8_t purpose); int circuit_stream_is_being_handled(connection_t *conn); void circuit_build_needed_circs(time_t now); - -void circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint); -int circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint); -void circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint); - void circuit_detach_stream(circuit_t *circ, connection_t *conn); void circuit_about_to_close_connection(connection_t *conn); - -void circuit_log_path(int severity, circuit_t *circ); -void circuit_dump_by_conn(connection_t *conn, int severity); - -void circuit_expire_unused_circuits(void); +void circuit_has_opened(circuit_t *circ); +void circuit_build_failed(circuit_t *circ); circuit_t *circuit_launch_new(uint8_t purpose, const char *exit_nickname); -void circuit_increment_failure_count(void); void circuit_reset_failure_count(void); -void circuit_n_conn_open(connection_t *or_conn); -int circuit_send_next_onion_skin(circuit_t *circ); -int circuit_extend(cell_t *cell, circuit_t *circ); -#define CPATH_KEY_MATERIAL_LEN (20*2+16*2) +int connection_ap_handshake_attach_circuit(connection_t *conn); + int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data,int reverse); int circuit_finish_handshake(circuit_t *circ, char *reply); int circuit_truncated(circuit_t *circ, crypt_path_t *layer); -void assert_cpath_ok(const crypt_path_t *c); void assert_cpath_layer_ok(const crypt_path_t *c); void assert_circuit_ok(const circuit_t *c); @@ -945,6 +954,7 @@ int getconfig(int argc, char **argv, or_options_t *options); "Unknown" : conn_type_to_string[(t)]) extern char *conn_type_to_string[]; +extern char *conn_state_to_string[][_CONN_TYPE_MAX+1]; connection_t *connection_new(int type); void connection_free(connection_t *conn); @@ -1006,23 +1016,12 @@ void assert_connection_ok(connection_t *conn, time_t now); /********************************* connection_edge.c ***************************/ -void relay_header_pack(char *dest, const relay_header_t *src); -void relay_header_unpack(relay_header_t *dest, const char *src); int connection_edge_process_inbuf(connection_t *conn); int connection_edge_destroy(uint16_t circ_id, connection_t *conn); int connection_edge_end(connection_t *conn, char reason, crypt_path_t *cpath_layer); -int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, - int relay_command, const char *payload, - int payload_len, crypt_path_t *cpath_layer); -int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, - connection_t *conn, - crypt_path_t *layer_hint); int connection_edge_finished_flushing(connection_t *conn); int connection_edge_finished_connecting(connection_t *conn); -int connection_edge_package_raw_inbuf(connection_t *conn); - -int connection_ap_handshake_attach_circuit(connection_t *conn); int connection_ap_handshake_send_begin(connection_t *ap_conn, circuit_t *circ); int connection_ap_make_bridge(char *address, uint16_t port); @@ -1030,18 +1029,17 @@ int connection_ap_make_bridge(char *address, uint16_t port); void connection_ap_handshake_socks_reply(connection_t *conn, char *reply, int replylen, char success); +int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); void connection_exit_connect(connection_t *conn); int connection_edge_is_rendezvous_stream(connection_t *conn); int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit); void connection_ap_expire_beginning(void); void connection_ap_attach_pending(void); -extern uint64_t stats_n_data_cells_packaged; -extern uint64_t stats_n_data_bytes_packaged; -extern uint64_t stats_n_data_cells_received; -extern uint64_t stats_n_data_bytes_received; - void client_dns_init(void); +uint32_t client_dns_lookup_entry(const char *address); +int client_dns_incr_failures(const char *address); +void client_dns_set_entry(const char *address, uint32_t val); void client_dns_clean(void); /********************************* connection_or.c ***************************/ @@ -1077,6 +1075,7 @@ int connection_dir_finished_flushing(connection_t *conn); int connection_dir_finished_connecting(connection_t *conn); /********************************* dirserv.c ***************************/ + int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk); int dirserv_parse_fingerprint_file(const char *fname); int dirserv_router_fingerprint_is_known(const routerinfo_t *router); @@ -1123,19 +1122,10 @@ int main(int argc, char *argv[]); /********************************* onion.c ***************************/ -int decide_circ_id_type(char *local_nick, char *remote_nick); - int onion_pending_add(circuit_t *circ); circuit_t *onion_next_task(void); void onion_pending_remove(circuit_t *circ); -int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys); - - -void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); -int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, - routerinfo_t **router_out); - int onion_skin_create(crypto_pk_env_t *router_key, crypto_dh_env_t **handshake_state_out, char *onion_skin_out); @@ -1152,9 +1142,6 @@ int onion_skin_client_handshake(crypto_dh_env_t *handshake_state, char *key_out, int key_out_len); -cpath_build_state_t *onion_new_cpath_build_state(uint8_t purpose, - const char *exit_nickname); - /********************************* relay.c ***************************/ extern unsigned long stats_n_relay_cells_relayed; @@ -1165,6 +1152,19 @@ int circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, int circuit_package_relay_cell(cell_t *cell, circuit_t *circ, int cell_direction, crypt_path_t *layer_hint); +void relay_header_pack(char *dest, const relay_header_t *src); +void relay_header_unpack(relay_header_t *dest, const char *src); +int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, + int relay_command, const char *payload, + int payload_len, crypt_path_t *cpath_layer); +int connection_edge_package_raw_inbuf(connection_t *conn); +void connection_edge_consider_sending_sendme(connection_t *conn); + +extern uint64_t stats_n_data_cells_packaged; +extern uint64_t stats_n_data_bytes_packaged; +extern uint64_t stats_n_data_cells_received; +extern uint64_t stats_n_data_bytes_received; + /********************************* rephist.c ***************************/ void rep_hist_init(void); @@ -1179,8 +1179,8 @@ void rep_hist_dump_stats(time_t now, int severity); /********************************* rendclient.c ***************************/ -void rend_client_introcirc_is_open(circuit_t *circ); -void rend_client_rendcirc_is_open(circuit_t *circ); +void rend_client_introcirc_has_opened(circuit_t *circ); +void rend_client_rendcirc_has_opened(circuit_t *circ); int rend_client_introduction_acked(circuit_t *circ, const char *request, int request_len); void rend_client_refetch_renddesc(const char *query); int rend_client_remove_intro_point(char *failed_intro, const char *query); @@ -1237,9 +1237,9 @@ void rend_services_init(void); void rend_services_introduce(void); void rend_services_upload(int force); -void rend_service_intro_is_ready(circuit_t *circuit); +void rend_service_intro_has_opened(circuit_t *circuit); int rend_service_intro_established(circuit_t *circuit, const char *request, int request_len); -void rend_service_rendezvous_is_ready(circuit_t *circuit); +void rend_service_rendezvous_has_opened(circuit_t *circuit); int rend_service_introduce(circuit_t *circuit, const char *request, int request_len); void rend_service_relaunch_rendezvous(circuit_t *oldcirc); int rend_service_set_connection_addr_port(connection_t *conn, circuit_t *circ); @@ -1277,6 +1277,8 @@ int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router, routerinfo_t *router_pick_directory_server(void); struct smartlist_t; +void add_nickname_list_to_smartlist(struct smartlist_t *sl, const char *list); +void router_add_running_routers_to_smartlist(struct smartlist_t *sl); routerinfo_t *router_choose_random_node(routerlist_t *dir, char *preferred, char *excluded, struct smartlist_t *excludedsmartlist); diff --git a/src/or/relay.c b/src/or/relay.c index 9f54d2ed1..444b94010 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -4,15 +4,33 @@ /** * \file relay.c - * \brief Handle relay cell encryption/decryption. + * \brief Handle relay cell encryption/decryption, plus packaging and + * receiving from circuits. **/ #include "or.h" static int relay_crypt(circuit_t *circ, cell_t *cell, int cell_direction, crypt_path_t **layer_hint, char *recognized); -static connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction) -; +static connection_t *relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction); + +static int +connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, + connection_t *conn, + crypt_path_t *layer_hint); +static void +circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint); +static void +circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint); +static int +circuit_resume_edge_reading_helper(connection_t *conn, + circuit_t *circ, + crypt_path_t *layer_hint); +static int +circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint); +void connection_edge_consider_sending_sendme(connection_t *conn); + + /** Stats: how many relay cells have originated at this hop, or have * been relayed onward (not recognized at this hop)? @@ -348,3 +366,596 @@ relay_lookup_conn(circuit_t *circ, cell_t *cell, int cell_direction) return NULL; /* probably a begin relay cell */ } +/** Pack the relay_header_t host-order structure <b>src</b> into + * network-order in the buffer <b>dest</b>. See tor-spec.txt for details + * about the wire format. + */ +void relay_header_pack(char *dest, const relay_header_t *src) { + *(uint8_t*)(dest) = src->command; + + set_uint16(dest+1, htons(src->recognized)); + set_uint16(dest+3, htons(src->stream_id)); + memcpy(dest+5, src->integrity, 4); + set_uint16(dest+9, htons(src->length)); +} + +/** Unpack the network-order buffer <b>src</b> into a host-order + * relay_header_t structure <b>dest</b>. + */ +void relay_header_unpack(relay_header_t *dest, const char *src) { + dest->command = *(uint8_t*)(src); + + dest->recognized = ntohs(get_uint16(src+1)); + dest->stream_id = ntohs(get_uint16(src+3)); + memcpy(dest->integrity, src+5, 4); + dest->length = ntohs(get_uint16(src+9)); +} + +/** Make a relay cell out of <b>relay_command</b> and <b>payload</b>, and + * send it onto the open circuit <b>circ</b>. <b>fromconn</b> is the stream + * that's sending the relay cell, or NULL if it's a control cell. + * <b>cpath_layer</b> is NULL for OR->OP cells, or the destination hop + * for OP->OR cells. + * + * If you can't send the cell, mark the circuit for close and + * return -1. Else return 0. + */ +int connection_edge_send_command(connection_t *fromconn, circuit_t *circ, + int relay_command, const char *payload, + int payload_len, crypt_path_t *cpath_layer) { + cell_t cell; + relay_header_t rh; + int cell_direction; + + if(!circ) { + log_fn(LOG_WARN,"no circ. Closing conn."); + tor_assert(fromconn); + fromconn->has_sent_end = 1; /* no circ to send to */ + connection_mark_for_close(fromconn); + return -1; + } + + memset(&cell, 0, sizeof(cell_t)); + cell.command = CELL_RELAY; + if(cpath_layer) { + cell.circ_id = circ->n_circ_id; + cell_direction = CELL_DIRECTION_OUT; + } else { + cell.circ_id = circ->p_circ_id; + cell_direction = CELL_DIRECTION_IN; + } + + memset(&rh, 0, sizeof(rh)); + rh.command = relay_command; + if(fromconn) + rh.stream_id = fromconn->stream_id; /* else it's 0 */ + rh.length = payload_len; + relay_header_pack(cell.payload, &rh); + if(payload_len) + memcpy(cell.payload+RELAY_HEADER_SIZE, payload, payload_len); + + log_fn(LOG_DEBUG,"delivering %d cell %s.", relay_command, + cell_direction == CELL_DIRECTION_OUT ? "forward" : "backward"); + + if(circuit_package_relay_cell(&cell, circ, cell_direction, cpath_layer) < 0) { + log_fn(LOG_WARN,"circuit_package_relay_cell failed. Closing."); + circuit_mark_for_close(circ); + return -1; + } + return 0; +} + +/** Translate the <b>payload</b> of length <b>length</b>, which + * came from a relay 'end' cell, into a static const string describing + * why the stream is closing. + */ +static const char * +connection_edge_end_reason(char *payload, uint16_t length) { + if(length < 1) { + log_fn(LOG_WARN,"End cell arrived with length 0. Should be at least 1."); + return "MALFORMED"; + } + if(*payload < _MIN_END_STREAM_REASON || *payload > _MAX_END_STREAM_REASON) { + log_fn(LOG_WARN,"Reason for ending (%d) not recognized.",*payload); + return "MALFORMED"; + } + switch(*payload) { + case END_STREAM_REASON_MISC: return "misc error"; + case END_STREAM_REASON_RESOLVEFAILED: return "resolve failed"; + case END_STREAM_REASON_CONNECTFAILED: return "connect failed"; + case END_STREAM_REASON_EXITPOLICY: return "exit policy failed"; + case END_STREAM_REASON_DESTROY: return "destroyed"; + case END_STREAM_REASON_DONE: return "closed normally"; + case END_STREAM_REASON_TIMEOUT: return "gave up (timeout)"; + } + tor_assert(0); + return ""; +} + +/** How many times will I retry a stream that fails due to DNS + * resolve failure? + */ +#define MAX_RESOLVE_FAILURES 3 + +/** An incoming relay cell has arrived from circuit <b>circ</b> to + * stream <b>conn</b>. + * + * The arguments here are the same as in + * connection_edge_process_relay_cell() below; this function is called + * from there when <b>conn</b> is defined and not in an open state. + */ +static int +connection_edge_process_relay_cell_not_open( + relay_header_t *rh, cell_t *cell, circuit_t *circ, + connection_t *conn, crypt_path_t *layer_hint) { + uint32_t addr; + int reason; + + if(rh->command == RELAY_COMMAND_END) { + reason = *(cell->payload+RELAY_HEADER_SIZE); + /* We have to check this here, since we aren't connected yet. */ + if (rh->length >= 5 && reason == END_STREAM_REASON_EXITPOLICY) { + log_fn(LOG_INFO,"Address %s refused due to exit policy. Retrying.", + conn->socks_request->address); + addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1)); + client_dns_set_entry(conn->socks_request->address, addr); + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + circuit_detach_stream(circ,conn); + if(connection_ap_handshake_attach_circuit(conn) >= 0) + return 0; + log_fn(LOG_INFO,"Giving up on retrying (from exitpolicy); conn can't be handled."); + /* else, conn will get closed below */ + } else if (rh->length && reason == END_STREAM_REASON_RESOLVEFAILED) { + if (client_dns_incr_failures(conn->socks_request->address) + < MAX_RESOLVE_FAILURES) { + /* We haven't retried too many times; reattach the connection. */ + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + circuit_detach_stream(circ,conn); + if(connection_ap_handshake_attach_circuit(conn) >= 0) + return 0; + /* else, conn will get closed below */ + log_fn(LOG_INFO,"Giving up on retrying (from resolvefailed); conn can't be handled."); + } else { + log_fn(LOG_WARN,"Have tried resolving address %s at %d different places. Giving up.", + conn->socks_request->address, MAX_RESOLVE_FAILURES); + } + } + log_fn(LOG_INFO,"Edge got end (%s) before we're connected. Marking for close.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh->length)); + if(CIRCUIT_IS_ORIGIN(circ)) + circuit_log_path(LOG_INFO,circ); + conn->has_sent_end = 1; /* we just got an 'end', don't need to send one */ + connection_mark_for_close(conn); + return 0; + } + + if(conn->type == CONN_TYPE_AP && rh->command == RELAY_COMMAND_CONNECTED) { + if(conn->state != AP_CONN_STATE_CONNECT_WAIT) { + log_fn(LOG_WARN,"Got 'connected' while not in state connect_wait. Dropping."); + return 0; + } +// log_fn(LOG_INFO,"Connected! Notifying application."); + conn->state = AP_CONN_STATE_OPEN; + if (rh->length >= 4) { + addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE)); + client_dns_set_entry(conn->socks_request->address, addr); + } + log_fn(LOG_INFO,"'connected' received after %d seconds.", + (int)(time(NULL) - conn->timestamp_lastread)); + circuit_log_path(LOG_INFO,circ); + connection_ap_handshake_socks_reply(conn, NULL, 0, 1); + conn->socks_request->has_finished = 1; + /* handle anything that might have queued */ + if (connection_edge_package_raw_inbuf(conn) < 0) { + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return 0; + } + return 0; + } + + log_fn(LOG_WARN,"Got an unexpected relay command %d, in state %d (%s). Closing.", + rh->command, conn->state, conn_state_to_string[conn->type][conn->state]); + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return -1; +} + +/** An incoming relay cell has arrived on circuit <b>circ</b>. If + * <b>conn</b> is NULL this is a control cell, else <b>cell</b> is + * destined for <b>conn</b>. + * + * If <b>layer_hint</b> is defined, then we're the origin of the + * circuit, and it specifies the hop that packaged <b>cell</b>. + * + * Return -1 if you want to tear down the circuit, else 0. + */ +static int +connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, + connection_t *conn, + crypt_path_t *layer_hint) +{ + static int num_seen=0; + relay_header_t rh; + + tor_assert(cell && circ); + + relay_header_unpack(&rh, cell->payload); +// log_fn(LOG_DEBUG,"command %d stream %d", rh.command, rh.stream_id); + num_seen++; + log_fn(LOG_DEBUG,"Now seen %d relay cells here.", num_seen); + + /* either conn is NULL, in which case we've got a control cell, or else + * conn points to the recognized stream. */ + + if(conn && + conn->state != AP_CONN_STATE_OPEN && + conn->state != EXIT_CONN_STATE_OPEN) { + return connection_edge_process_relay_cell_not_open( + &rh, cell, circ, conn, layer_hint); + } + + switch(rh.command) { + case RELAY_COMMAND_DROP: + log_fn(LOG_INFO,"Got a relay-level padding cell. Dropping."); + return 0; + case RELAY_COMMAND_BEGIN: + if (layer_hint && + circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED) { + log_fn(LOG_WARN,"relay begin request unsupported at AP. Dropping."); + return 0; + } + if(conn) { + log_fn(LOG_WARN,"begin cell for known stream. Dropping."); + return 0; + } + connection_exit_begin_conn(cell, circ); + return 0; + case RELAY_COMMAND_DATA: + ++stats_n_data_cells_received; + if((layer_hint && --layer_hint->deliver_window < 0) || + (!layer_hint && --circ->deliver_window < 0)) { + log_fn(LOG_WARN,"(relay data) circ deliver_window below 0. Killing."); + connection_edge_end(conn, END_STREAM_REASON_MISC, conn->cpath_layer); + connection_mark_for_close(conn); + return -1; + } + log_fn(LOG_DEBUG,"circ deliver_window now %d.", layer_hint ? + layer_hint->deliver_window : circ->deliver_window); + + circuit_consider_sending_sendme(circ, layer_hint); + + if(!conn) { + log_fn(LOG_INFO,"data cell dropped, unknown stream."); + return 0; + } + + if(--conn->deliver_window < 0) { /* is it below 0 after decrement? */ + log_fn(LOG_WARN,"(relay data) conn deliver_window below 0. Killing."); + return -1; /* somebody's breaking protocol. kill the whole circuit. */ + } + + stats_n_data_bytes_received += rh.length; + connection_write_to_buf(cell->payload + RELAY_HEADER_SIZE, + rh.length, conn); + connection_edge_consider_sending_sendme(conn); + return 0; + case RELAY_COMMAND_END: + if(!conn) { + log_fn(LOG_INFO,"end cell (%s) dropped, unknown stream.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length)); + return 0; + } +/* XXX add to this log_fn the exit node's nickname? */ + log_fn(LOG_INFO,"end cell (%s) for stream %d. Removing stream.", + connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length), + conn->stream_id); + +#ifdef HALF_OPEN + conn->done_sending = 1; + shutdown(conn->s, 1); /* XXX check return; refactor NM */ + if (conn->done_receiving) { + /* We just *got* an end; no reason to send one. */ + conn->has_sent_end = 1; + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; + } +#else + /* We just *got* an end; no reason to send one. */ + conn->has_sent_end = 1; + connection_mark_for_close(conn); + conn->hold_open_until_flushed = 1; +#endif + return 0; + case RELAY_COMMAND_EXTEND: + if(conn) { + log_fn(LOG_WARN,"'extend' for non-zero stream. Dropping."); + return 0; + } + return circuit_extend(cell, circ); + case RELAY_COMMAND_EXTENDED: + if(!layer_hint) { + log_fn(LOG_WARN,"'extended' unsupported at non-origin. Dropping."); + return 0; + } + log_fn(LOG_DEBUG,"Got an extended cell! Yay."); + if(circuit_finish_handshake(circ, cell->payload+RELAY_HEADER_SIZE) < 0) { + log_fn(LOG_WARN,"circuit_finish_handshake failed."); + return -1; + } + if (circuit_send_next_onion_skin(circ)<0) { + log_fn(LOG_INFO,"circuit_send_next_onion_skin() failed."); + return -1; + } + return 0; + case RELAY_COMMAND_TRUNCATE: + if(layer_hint) { + log_fn(LOG_WARN,"'truncate' unsupported at origin. Dropping."); + return 0; + } + if(circ->n_conn) { + connection_send_destroy(circ->n_circ_id, circ->n_conn); + circ->n_conn = NULL; + } + log_fn(LOG_DEBUG, "Processed 'truncate', replying."); + connection_edge_send_command(NULL, circ, RELAY_COMMAND_TRUNCATED, + NULL, 0, NULL); + return 0; + case RELAY_COMMAND_TRUNCATED: + if(!layer_hint) { + log_fn(LOG_WARN,"'truncated' unsupported at non-origin. Dropping."); + return 0; + } + circuit_truncated(circ, layer_hint); + return 0; + case RELAY_COMMAND_CONNECTED: + if(conn) { + log_fn(LOG_WARN,"'connected' unsupported while open. Closing circ."); + return -1; + } + log_fn(LOG_INFO,"'connected' received, no conn attached anymore. Ignoring."); + return 0; + case RELAY_COMMAND_SENDME: + if(!conn) { + if(layer_hint) { + layer_hint->package_window += CIRCWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"circ-level sendme at origin, packagewindow %d.", + layer_hint->package_window); + circuit_resume_edge_reading(circ, layer_hint); + } else { + circ->package_window += CIRCWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"circ-level sendme at non-origin, packagewindow %d.", + circ->package_window); + circuit_resume_edge_reading(circ, layer_hint); + } + return 0; + } + conn->package_window += STREAMWINDOW_INCREMENT; + log_fn(LOG_DEBUG,"stream-level sendme, packagewindow now %d.", conn->package_window); + connection_start_reading(conn); + connection_edge_package_raw_inbuf(conn); /* handle whatever might still be on the inbuf */ + return 0; + case RELAY_COMMAND_ESTABLISH_INTRO: + case RELAY_COMMAND_ESTABLISH_RENDEZVOUS: + case RELAY_COMMAND_INTRODUCE1: + case RELAY_COMMAND_INTRODUCE2: + case RELAY_COMMAND_INTRODUCE_ACK: + case RELAY_COMMAND_RENDEZVOUS1: + case RELAY_COMMAND_RENDEZVOUS2: + case RELAY_COMMAND_INTRO_ESTABLISHED: + case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED: + rend_process_relay_cell(circ, rh.command, rh.length, + cell->payload+RELAY_HEADER_SIZE); + return 0; + } + log_fn(LOG_WARN,"unknown relay command %d.",rh.command); + return -1; +} + +uint64_t stats_n_data_cells_packaged = 0; +uint64_t stats_n_data_bytes_packaged = 0; +uint64_t stats_n_data_cells_received = 0; +uint64_t stats_n_data_bytes_received = 0; + +/** While conn->inbuf has an entire relay payload of bytes on it, + * and the appropriate package windows aren't empty, grab a cell + * and send it down the circuit. + * + * Return -1 if conn should be marked for close, else return 0. + */ +int connection_edge_package_raw_inbuf(connection_t *conn) { + int amount_to_process, length; + char payload[CELL_PAYLOAD_SIZE]; + circuit_t *circ; + + tor_assert(conn); + tor_assert(!connection_speaks_cells(conn)); + +repeat_connection_edge_package_raw_inbuf: + + circ = circuit_get_by_conn(conn); + if(!circ) { + log_fn(LOG_INFO,"conn has no circuits! Closing."); + return -1; + } + + if(circuit_consider_stop_edge_reading(circ, conn->cpath_layer)) + return 0; + + if(conn->package_window <= 0) { + log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window); + connection_stop_reading(conn); + return 0; + } + + amount_to_process = buf_datalen(conn->inbuf); + + if(!amount_to_process) + return 0; + + if(amount_to_process > RELAY_PAYLOAD_SIZE) { + length = RELAY_PAYLOAD_SIZE; + } else { + length = amount_to_process; + } + stats_n_data_bytes_packaged += length; + stats_n_data_cells_packaged += 1; + + connection_fetch_from_buf(payload, length, conn); + + log_fn(LOG_DEBUG,"(%d) Packaging %d bytes (%d waiting).", conn->s, length, + (int)buf_datalen(conn->inbuf)); + + if(connection_edge_send_command(conn, circ, RELAY_COMMAND_DATA, + payload, length, conn->cpath_layer) < 0) + return 0; /* circuit is closed, don't continue */ + + if(!conn->cpath_layer) { /* non-rendezvous exit */ + tor_assert(circ->package_window > 0); + circ->package_window--; + } else { /* we're an AP, or an exit on a rendezvous circ */ + tor_assert(conn->cpath_layer->package_window > 0); + conn->cpath_layer->package_window--; + } + + if(--conn->package_window <= 0) { /* is it 0 after decrement? */ + connection_stop_reading(conn); + log_fn(LOG_DEBUG,"conn->package_window reached 0."); + circuit_consider_stop_edge_reading(circ, conn->cpath_layer); + return 0; /* don't process the inbuf any more */ + } + log_fn(LOG_DEBUG,"conn->package_window is now %d",conn->package_window); + + /* handle more if there's more, or return 0 if there isn't */ + goto repeat_connection_edge_package_raw_inbuf; +} + +/** Called when we've just received a relay data cell, or when + * we've just finished flushing all bytes to stream <b>conn</b>. + * + * If conn->outbuf is not too full, and our deliver window is + * low, send back a suitable number of stream-level sendme cells. + */ +void connection_edge_consider_sending_sendme(connection_t *conn) { + circuit_t *circ; + + if(connection_outbuf_too_full(conn)) + return; + + circ = circuit_get_by_conn(conn); + if(!circ) { + /* this can legitimately happen if the destroy has already + * arrived and torn down the circuit */ + log_fn(LOG_INFO,"No circuit associated with conn. Skipping."); + return; + } + + while(conn->deliver_window < STREAMWINDOW_START - STREAMWINDOW_INCREMENT) { + log_fn(LOG_DEBUG,"Outbuf %d, Queueing stream sendme.", conn->outbuf_flushlen); + conn->deliver_window += STREAMWINDOW_INCREMENT; + if(connection_edge_send_command(conn, circ, RELAY_COMMAND_SENDME, + NULL, 0, conn->cpath_layer) < 0) { + log_fn(LOG_WARN,"connection_edge_send_command failed. Returning."); + return; /* the circuit's closed, don't continue */ + } + } +} + +/** The circuit <b>circ</b> has received a circuit-level sendme + * (on hop <b>layer_hint</b>, if we're the OP). Go through all the + * attached streams and let them resume reading and packaging, if + * their stream windows allow it. + */ +static void +circuit_resume_edge_reading(circuit_t *circ, crypt_path_t *layer_hint) +{ + + log_fn(LOG_DEBUG,"resuming"); + + /* have to check both n_streams and p_streams, to handle rendezvous */ + if(circuit_resume_edge_reading_helper(circ->n_streams, circ, layer_hint) >= 0) + circuit_resume_edge_reading_helper(circ->p_streams, circ, layer_hint); +} + +/** A helper function for circuit_resume_edge_reading() above. + * The arguments are the same, except that <b>conn</b> is the head + * of a linked list of edge streams that should each be considered. + */ +static int +circuit_resume_edge_reading_helper(connection_t *conn, + circuit_t *circ, + crypt_path_t *layer_hint) { + + for( ; conn; conn=conn->next_stream) { + if((!layer_hint && conn->package_window > 0) || + (layer_hint && conn->package_window > 0 && conn->cpath_layer == layer_hint)) { + connection_start_reading(conn); + /* handle whatever might still be on the inbuf */ + connection_edge_package_raw_inbuf(conn); + + /* If the circuit won't accept any more data, return without looking + * at any more of the streams. Any connections that should be stopped + * have already been stopped by connection_edge_package_raw_inbuf. */ + if(circuit_consider_stop_edge_reading(circ, layer_hint)) + return -1; + } + } + return 0; +} + +/** Check if the package window for <b>circ</b> is empty (at + * hop <b>layer_hint</b> if it's defined). + * + * If yes, tell edge streams to stop reading and return -1. + * Else return 0. + */ +static int +circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint) +{ + connection_t *conn = NULL; + + log_fn(LOG_DEBUG,"considering"); + if(!layer_hint && circ->package_window <= 0) { + log_fn(LOG_DEBUG,"yes, not-at-origin. stopped."); + for(conn = circ->n_streams; conn; conn=conn->next_stream) + connection_stop_reading(conn); + return -1; + } else if(layer_hint && layer_hint->package_window <= 0) { + log_fn(LOG_DEBUG,"yes, at-origin. stopped."); + for(conn = circ->n_streams; conn; conn=conn->next_stream) + if(conn->cpath_layer == layer_hint) + connection_stop_reading(conn); + for(conn = circ->p_streams; conn; conn=conn->next_stream) + if(conn->cpath_layer == layer_hint) + connection_stop_reading(conn); + return -1; + } + return 0; +} + +/** Check if the deliver_window for circuit <b>circ</b> (at hop + * <b>layer_hint</b> if it's defined) is low enough that we should + * send a circuit-level sendme back down the circuit. If so, send + * enough sendmes that the window would be overfull if we sent any + * more. + */ +static void +circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) +{ +// log_fn(LOG_INFO,"Considering: layer_hint is %s", +// layer_hint ? "defined" : "null"); + while((layer_hint ? layer_hint->deliver_window : circ->deliver_window) < + CIRCWINDOW_START - CIRCWINDOW_INCREMENT) { + log_fn(LOG_DEBUG,"Queueing circuit sendme."); + if(layer_hint) + layer_hint->deliver_window += CIRCWINDOW_INCREMENT; + else + circ->deliver_window += CIRCWINDOW_INCREMENT; + if(connection_edge_send_command(NULL, circ, RELAY_COMMAND_SENDME, + NULL, 0, layer_hint) < 0) { + log_fn(LOG_WARN,"connection_edge_send_command failed. Circuit's closed."); + return; /* the circuit's closed, don't continue */ + } + } +} + diff --git a/src/or/rendclient.c b/src/or/rendclient.c index af27dc799..081384995 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -12,7 +12,7 @@ /** Called when we've established a circuit to an introduction point: * send the introduction request. */ void -rend_client_introcirc_is_open(circuit_t *circ) +rend_client_introcirc_has_opened(circuit_t *circ) { tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); tor_assert(CIRCUIT_IS_ORIGIN(circ) && circ->cpath); @@ -133,7 +133,7 @@ err: /** Called when a rendezvous circuit is open; sends a establish * rendezvous circuit as appropriate. */ void -rend_client_rendcirc_is_open(circuit_t *circ) +rend_client_rendcirc_has_opened(circuit_t *circ) { tor_assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); tor_assert(CIRCUIT_IS_ORIGIN(circ)); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index a42b576aa..4bf6016f8 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -530,7 +530,7 @@ rend_service_launch_establish_intro(rend_service_t *service, const char *nicknam * sends a RELAY_ESTABLISH_INTRO cell. */ void -rend_service_intro_is_ready(circuit_t *circuit) +rend_service_intro_has_opened(circuit_t *circuit) { rend_service_t *service; int len, r; @@ -618,7 +618,7 @@ rend_service_intro_established(circuit_t *circuit, const char *request, int requ * RELAY_COMMAND_RENDEZVOUS1 cell. */ void -rend_service_rendezvous_is_ready(circuit_t *circuit) +rend_service_rendezvous_has_opened(circuit_t *circuit) { rend_service_t *service; char buf[RELAY_PAYLOAD_SIZE]; |