diff --git a/src/or/circuit.c b/src/or/circuit.c index 707cf7d825..abec47c0a9 100644 --- a/src/or/circuit.c +++ b/src/or/circuit.c @@ -13,7 +13,7 @@ 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); -static void circuit_is_ready(circuit_t *circ); +static void circuit_is_open(circuit_t *circ); static void circuit_failed(circuit_t *circ); static circuit_t *circuit_establish_circuit(uint8_t purpose, const char *exit_nickname); @@ -250,21 +250,30 @@ circuit_t *circuit_get_by_conn(connection_t *conn) { return NULL; } -/* Find the newest circ that conn can use, preferably one which is +/* Dear god this function needs refactoring. */ +/* Find the best circ that conn can use, preferably one which is * dirty. Circ must not be too old. * If !conn, return newest. * * 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_INTRODUCING, 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 circ_purpose is not GENERAL, then conn must be defined. - * If circ_purpose is C_ESTABLISH_REND, then it's also ok - * to return a C_REND_JOINED circ. */ -circuit_t *circuit_get_newest(connection_t *conn, - int must_be_open, uint8_t circ_purpose) { - circuit_t *circ, *newest=NULL, *leastdirty=NULL; +circuit_t *circuit_get_best(connection_t *conn, + int must_be_open, uint8_t purpose) { + circuit_t *circ, *best=NULL; routerinfo_t *exitrouter; + time_t now = time(NULL); + + assert(purpose == CIRCUIT_PURPOSE_C_GENERAL || + purpose == CIRCUIT_PURPOSE_C_INTRODUCING || + purpose == CIRCUIT_PURPOSE_C_REND_JOINED); for (circ=global_circuitlist;circ;circ = circ->next) { if (!circ->cpath) @@ -274,18 +283,23 @@ circuit_t *circuit_get_newest(connection_t *conn, if (circ->marked_for_close) continue; - /* if this isn't our purpose, skip. except, if our purpose is - * establish_rend, keep going if circ is rend_joined. - */ - if (circ->purpose != circ_purpose && - (circ_purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND || - circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED)) - continue; + /* if this circ isn't our purpose, skip. */ + if(purpose == CIRCUIT_PURPOSE_C_REND_JOINED) { + if(must_be_open && purpose != circ->purpose) + continue; + if(circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND && + circ->purpose != CIRCUIT_PURPOSE_C_REND_READY && + circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED) + continue; + } else { + if(purpose != circ->purpose) + continue; + } -#if 0 - if (must_be_clean && circ->timestamp_dirty) - continue; /* ignore dirty circs */ -#endif + if(purpose == CIRCUIT_PURPOSE_C_GENERAL) + if(circ->timestamp_dirty && + circ->timestamp_dirty+options.NewCircuitPeriod < now) + continue; /* too old */ if(conn) { /* decide if this circ is suitable for this conn */ @@ -296,10 +310,12 @@ circuit_t *circuit_get_newest(connection_t *conn, else /* not open */ exitrouter = router_get_by_nickname(circ->build_state->chosen_exit); - if(!exitrouter) + if(!exitrouter) { + log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)"); continue; /* this circuit is screwed and doesn't know it yet */ + } - if(circ_purpose == CIRCUIT_PURPOSE_C_GENERAL) { + if(purpose == CIRCUIT_PURPOSE_C_GENERAL) { if(connection_ap_can_use_exit(conn, exitrouter) == ADDR_POLICY_REJECTED) { /* can't exit from this router */ continue; @@ -312,27 +328,40 @@ circuit_t *circuit_get_newest(connection_t *conn, } } - if(!newest || newest->timestamp_created < circ->timestamp_created) { - newest = circ; - } - if(conn && circ->timestamp_dirty && - (!leastdirty || leastdirty->timestamp_dirty < circ->timestamp_dirty)) { - leastdirty = circ; + /* 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) + best = circ; + 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(best->timestamp_dirty) { + if(circ->timestamp_dirty && + circ->timestamp_dirty > best->timestamp_dirty) + best = circ; + } else { + if(circ->timestamp_dirty || + circ->timestamp_created > best->timestamp_created) + best = circ; + } + break; + case CIRCUIT_PURPOSE_C_INTRODUCING: + /* more recently created is best */ + if(circ->timestamp_created > best->timestamp_created) + best = circ; + break; + case CIRCUIT_PURPOSE_C_REND_JOINED: + /* the closer it is to rend_joined the better it is */ + if(circ->purpose > best->purpose) + best = circ; + break; } } - if(leastdirty && - leastdirty->timestamp_dirty+options.NewCircuitPeriod > time(NULL)) { -/* log_fn(LOG_DEBUG,"Choosing in-use circuit %s:%d:%d.", - leastdirty->n_conn->address, leastdirty->n_port, leastdirty->n_circ_id); */ - return leastdirty; - } - if(newest) { -/* log_fn(LOG_DEBUG,"Choosing circuit %s:%d:%d.", - newest->n_conn->address, newest->n_port, newest->n_circ_id); */ - return newest; - } - return NULL; + return best; } /* Return the first circuit in global_circuitlist after 'start' whose @@ -422,8 +451,9 @@ int circuit_count_building(void) { } #define MIN_CIRCUITS_HANDLING_STREAM 2 -/* return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open circuits - * will have an acceptable exit node for conn. Else return 0. +/* 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; @@ -432,7 +462,7 @@ int circuit_stream_is_being_handled(connection_t *conn) { for(circ=global_circuitlist;circ;circ = circ->next) { if(circ->cpath && circ->state != CIRCUIT_STATE_OPEN && - !circ->marked_for_close) { + !circ->marked_for_close && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) { 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) @@ -1047,24 +1077,20 @@ void circuit_expire_unused_circuits(void) { smartlist_free(unused_open_circs); } -static void circuit_is_ready(circuit_t *circ) { +static void circuit_is_open(circuit_t *circ) { - /* should maybe break this into rend_circuit_is_ready() one day */ 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_C_INTRODUCING: - /* at Alice, connecting to intro point */ - connection_ap_attach_pending(); - break; - case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - /* at Alice, waiting for Bob */ - /* XXXNM make and send the rendezvous cookie, and store it in circ */ - connection_ap_attach_pending(); - break; case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: /* at Bob, waiting for introductions */ rend_service_intro_is_ready(circ); @@ -1278,7 +1304,7 @@ int circuit_send_next_onion_skin(circuit_t *circ) { log_fn(LOG_NOTICE,"Tor has successfully opened a circuit. Looks like it's working."); } circuit_rep_hist_note_result(circ); - circuit_is_ready(circ); /* do other actions as necessary */ + circuit_is_open(circ); /* do other actions as necessary */ return 0; } else if (r<0) { log_fn(LOG_INFO,"Unable to extend circuit path."); diff --git a/src/or/config.c b/src/or/config.c index ad121d3ccc..93adb5437e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -596,10 +596,17 @@ int getconfig(int argc, char **argv, or_options_t *options) { if (options->Nickname == NULL) { log_fn(LOG_WARN,"Nickname required if ORPort is set, but not found."); result = -1; - } else if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) != - strlen(options->Nickname)) { - log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname); - result = -1; + } else { + if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) != + strlen(options->Nickname)) { + log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname); + result = -1; + } + if (strlen(options->Nickname) > MAX_NICKNAME_LEN) { + log_fn(LOG_WARN, "Nickname '%s' has more than %d characters.", + options->Nickname, MAX_NICKNAME_LEN); + result = -1; + } } } @@ -654,6 +661,10 @@ int getconfig(int argc, char **argv, or_options_t *options) { result = -1; } + /* XXX look at the various nicknamelists and make sure they're + * valid and don't have hostnames that are too long. + */ + if (rend_config_services(options) < 0) { result = -1; } diff --git a/src/or/connection.c b/src/or/connection.c index 3f0e5cbb5c..471528a49f 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -866,6 +866,22 @@ connection_t *connection_get_by_type_state_lastwritten(int type, int state) { return best; } +connection_t *connection_get_by_type_rendquery(int type, char *rendquery) { + int i, n; + connection_t *conn; + connection_t **carray; + + get_connection_array(&carray,&n); + for(i=0;itype == type && + !conn->marked_for_close && + !rend_cmp_service_ids(rendquery, conn->rend_query)) + return conn; + } + return NULL; +} + int connection_is_listener(connection_t *conn) { if(conn->type == CONN_TYPE_OR_LISTENER || conn->type == CONN_TYPE_AP_LISTENER || @@ -1026,9 +1042,8 @@ void assert_connection_ok(connection_t *conn, time_t now) } else { assert(!conn->socks_request); } - if(conn->type != CONN_TYPE_DIR && - conn->type != CONN_TYPE_AP) { - assert(!conn->purpose); /* only used for dir and ap types currently */ + if(conn->type != CONN_TYPE_DIR) { + assert(!conn->purpose); /* only used for dir types currently */ } switch(conn->type) diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 04bf1794f0..d726549dbf 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -76,6 +76,7 @@ int connection_edge_process_inbuf(connection_t *conn) { case AP_CONN_STATE_OPEN: case EXIT_CONN_STATE_OPEN: if(conn->package_window <= 0) { + /* XXX this is still getting called rarely :( */ log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window); return 0; } @@ -85,6 +86,7 @@ int connection_edge_process_inbuf(connection_t *conn) { } return 0; case EXIT_CONN_STATE_CONNECTING: + case AP_CONN_STATE_RENDDESC_WAIT: case AP_CONN_STATE_CIRCUIT_WAIT: case AP_CONN_STATE_CONNECT_WAIT: log_fn(LOG_INFO,"data from edge while in '%s' state. Leaving it on buffer.", @@ -235,6 +237,7 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, if(conn && conn->state != AP_CONN_STATE_OPEN && conn->state != EXIT_CONN_STATE_OPEN) { if(rh.command == RELAY_COMMAND_END) { + circuit_log_path(LOG_INFO,circ); 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)); conn->has_sent_end = 1; /* we just got an 'end', don't need to send one */ @@ -335,9 +338,7 @@ int connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, */ addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1)); client_dns_set_entry(conn->socks_request->address, addr); - /* conn->purpose is still set to general */ conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - /* attaching to a dirty circuit is fine */ if(connection_ap_handshake_attach_circuit(conn) >= 0) return 0; /* else, conn will get closed below */ @@ -491,6 +492,7 @@ int connection_edge_finished_flushing(connection_t *conn) { connection_edge_consider_sending_sendme(conn); return 0; case AP_CONN_STATE_SOCKS_WAIT: + case AP_CONN_STATE_RENDDESC_WAIT: case AP_CONN_STATE_CIRCUIT_WAIT: case AP_CONN_STATE_CONNECT_WAIT: connection_stop_writing(conn); @@ -609,7 +611,6 @@ void connection_ap_expire_beginning(void) { conn->has_sent_end = 0; /* move it back into 'pending' state. */ conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - /* conn->purpose is still set to general */ circuit_detach_stream(circ, conn); /* kludge to make us not try this circuit again, yet to allow * current streams on it to survive if they can: make it @@ -642,7 +643,6 @@ void connection_ap_attach_pending(void) if (conn->type != CONN_TYPE_AP || conn->state != AP_CONN_STATE_CIRCUIT_WAIT) continue; - /* attaching to a dirty circuit is fine */ if(connection_ap_handshake_attach_circuit(conn) < 0) { /* -1 means it will never work */ /* Don't send end; there is no 'other side' yet */ @@ -709,48 +709,49 @@ static int connection_ap_handshake_process_socks(connection_t *conn) { if (rend_parse_rendezvous_address(socks->address) < 0) { /* normal request */ conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - conn->purpose = AP_PURPOSE_GENERAL; - /* attaching to a dirty circuit is fine */ return connection_ap_handshake_attach_circuit(conn); } else { /* it's a hidden-service request */ const char *descp; int desc_len; - strcpy(conn->rend_query, socks->address); + strcpy(conn->rend_query, socks->address); /* this strcpy is safe -RD */ log_fn(LOG_INFO,"Got a hidden service request for ID '%s'", conn->rend_query); /* see if we already have it cached */ if (rend_cache_lookup(conn->rend_query, &descp, &desc_len) == 1) { - conn->purpose = AP_PURPOSE_RENDPOINT_WAIT; + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; return connection_ap_handshake_attach_circuit(conn); - //circuit_launch_new(CIRCUIT_PURPOSE_C_ESTABLISH_REND, NULL); } else { - conn->purpose = AP_PURPOSE_RENDDESC_WAIT; - /* initiate a dir hidserv desc lookup */ - directory_initiate_command(router_pick_directory_server(), - DIR_PURPOSE_FETCH_RENDDESC, - conn->rend_query, strlen(conn->rend_query)); + conn->state = AP_CONN_STATE_RENDDESC_WAIT; + if(!connection_get_by_type_rendquery(CONN_TYPE_DIR, conn->rend_query)) { + /* not one already; initiate a dir rend desc lookup */ + directory_initiate_command(router_pick_directory_server(), + DIR_PURPOSE_FETCH_RENDDESC, + conn->rend_query, strlen(conn->rend_query)); + } return 0; } } return 0; } -/* find an open circ that we're happy with: return 1. if there isn't - * one, launch one and return 0. if it will never work, return -1. - * write the found or launched circ into *circp. +/* 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 -get_open_circ_or_launch(connection_t *conn, - uint8_t desired_circuit_purpose, - circuit_t **circp) { +circuit_get_open_circ_or_launch(connection_t *conn, + uint8_t desired_circuit_purpose, + circuit_t **circp) { circuit_t *circ; uint32_t addr; assert(conn); assert(circp); + assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); - circ = circuit_get_newest(conn, 1, desired_circuit_purpose); + circ = circuit_get_best(conn, 1, desired_circuit_purpose); if(circ) { *circp = circ; @@ -760,7 +761,7 @@ get_open_circ_or_launch(connection_t *conn, log_fn(LOG_INFO,"No safe circuit (purpose %d) ready for edge connection; delaying.", desired_circuit_purpose); - if(conn->purpose == AP_PURPOSE_GENERAL) { + if(!*conn->rend_query) { /* 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.", @@ -768,13 +769,33 @@ get_open_circ_or_launch(connection_t *conn, return -1; } } - if(!circuit_get_newest(conn, 0, desired_circuit_purpose)) { - /* is one already on the way? */ - circ = circuit_launch_new(desired_circuit_purpose, NULL); - /* depending on purpose, store stuff into circ */ + + /* 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_INTRODUCING) { + /* need to pick an intro point */ + exitname = rend_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; + } + 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 + new_circ_purpose = desired_circuit_purpose; + + circ = circuit_launch_new(new_circ_purpose, exitname); + if(circ && - (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL || - desired_circuit_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND)) { + (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL)) { /* then write the service_id into circ */ strcpy(circ->rend_query, conn->rend_query); } @@ -791,8 +812,6 @@ get_open_circ_or_launch(connection_t *conn, * right next step, and return 1. */ int connection_ap_handshake_attach_circuit(connection_t *conn) { - circuit_t *circ=NULL; - uint8_t desired_circuit_purpose; int retval; assert(conn); @@ -800,60 +819,74 @@ int connection_ap_handshake_attach_circuit(connection_t *conn) { assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); assert(conn->socks_request); - if(conn->purpose == AP_PURPOSE_RENDDESC_WAIT) - return 0; /* these guys don't attach to circuits directly */ + if(!*conn->rend_query) { /* we're a general conn */ + circuit_t *circ=NULL; - switch(conn->purpose) { - case AP_PURPOSE_GENERAL: - desired_circuit_purpose = CIRCUIT_PURPOSE_C_GENERAL; - break; - case AP_PURPOSE_RENDPOINT_WAIT: - desired_circuit_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND; - break; - case AP_PURPOSE_INTROPOINT_WAIT: - desired_circuit_purpose = CIRCUIT_PURPOSE_C_INTRODUCING; - break; - default: - log_fn(LOG_ERR, "Got unexpected purpose: %d", conn->purpose); - assert(0); /* never reached */ + /* 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. */ + + /* 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); + + /* 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); + conn->next_stream = circ->p_streams; + /* assert_connection_ok(conn, time(NULL)); */ + circ->p_streams = conn; + + assert(circ->cpath && circ->cpath->prev); + assert(circ->cpath->prev->state == CPATH_STATE_OPEN); + conn->cpath_layer = circ->cpath->prev; + + connection_ap_handshake_send_begin(conn, circ); + + return 1; + + } else { /* we're a rendezvous conn */ + circuit_t *rendcirc=NULL, *introcirc=NULL; + + /* first, find 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 */ + + if(retval > 0) { + /* one is already established, attach */ + + log_fn(LOG_WARN,"XXX rend joined circ already here. should reuse."); + return -1; + } + + if(rendcirc && + rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY && + rendcirc->build_state->pending_final_cpath) { + log_fn(LOG_WARN,"XXX pending-join circ already here. should reuse."); + return -1; + } + + /* it's on its way. find an intro circ. */ + retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_INTRODUCING, &introcirc); + if(retval < 0) return -1; /* failed */ + + if(retval > 0) { + log_fn(LOG_INFO,"Intro circ is ready for us"); + if(rendcirc && + rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY) { + /* then we know !pending_final_cpath, from above */ + log_fn(LOG_WARN,"XXX intro and rend are both ready. do the magic."); + return -1; + } + } + log_fn(LOG_INFO,"Intro and rend circs are not both ready. Stalling conn."); + return 0; } - - /* find the circuit that we should use, if there is one. */ - retval = get_open_circ_or_launch(conn, desired_circuit_purpose, &circ); - if(retval < 1) - return retval; - - /* We have found a suitable circuit for our conn. Hurray. */ - - /* 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); - - switch(conn->purpose) { - case AP_PURPOSE_GENERAL: - /* 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); - conn->next_stream = circ->p_streams; - /* assert_connection_ok(conn, time(NULL)); */ - circ->p_streams = conn; - - assert(circ->cpath && circ->cpath->prev); - assert(circ->cpath->prev->state == CPATH_STATE_OPEN); - conn->cpath_layer = circ->cpath->prev; - - connection_ap_handshake_send_begin(conn, circ); - break; - case AP_PURPOSE_RENDPOINT_WAIT: - rend_client_rendcirc_is_ready(conn, circ); - break; - case AP_PURPOSE_INTROPOINT_WAIT: - rend_client_introcirc_is_ready(conn, circ); - break; - } - - return 1; } /* Iterate over the two bytes of stream_id until we get one that is not @@ -961,7 +994,6 @@ int connection_ap_make_bridge(char *address, uint16_t port) { } conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - conn->purpose = AP_PURPOSE_GENERAL; connection_start_reading(conn); /* attaching to a dirty circuit is fine */ diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 152c1c2f3d..a2a67d57bc 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -206,7 +206,7 @@ dirserv_add_descriptor(const char **desc) start = strstr(*desc, "router "); if (!start) { - log(LOG_WARN, "no descriptor found."); + log_fn(LOG_WARN, "no 'router' line found. This is not a descriptor."); return -1; } if ((end = strstr(start+6, "\nrouter "))) { diff --git a/src/or/main.c b/src/or/main.c index d030498091..f20a4ae93a 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -384,7 +384,7 @@ static void run_scheduled_events(time_t now) { /* Build a new test circuit every 5 minutes */ #define TESTING_CIRCUIT_INTERVAL 300 - circ = circuit_get_newest(NULL, 1, CIRCUIT_PURPOSE_C_GENERAL); + circ = circuit_get_best(NULL, 1, CIRCUIT_PURPOSE_C_GENERAL); if(time_to_new_circuit < now) { client_dns_clean(); circuit_expire_unused_circuits(); diff --git a/src/or/or.h b/src/or/or.h index 11563475a1..6f7b79ebaf 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -100,7 +100,7 @@ can be lowered by config file */ #define DEFAULT_BANDWIDTH_OP (1024 * 1000) -#define MAX_NICKNAME_LEN 32 +#define MAX_NICKNAME_LEN 19 #define MAX_DIR_SIZE 500000 #ifdef TOR_PERF @@ -159,18 +159,11 @@ /* the AP state values must be disjoint from the EXIT state values */ #define _AP_CONN_STATE_MIN 5 #define AP_CONN_STATE_SOCKS_WAIT 5 -#define AP_CONN_STATE_CIRCUIT_WAIT 6 -#define AP_CONN_STATE_CONNECT_WAIT 7 -#define AP_CONN_STATE_OPEN 8 -#define _AP_CONN_STATE_MAX 8 - -/* only used if state==CIRCUIT_WAIT */ -#define _AP_PURPOSE_MIN 1 -#define AP_PURPOSE_GENERAL 1 -#define AP_PURPOSE_RENDDESC_WAIT 2 -#define AP_PURPOSE_RENDPOINT_WAIT 3 -#define AP_PURPOSE_INTROPOINT_WAIT 4 -#define _AP_PURPOSE_MAX 4 +#define AP_CONN_STATE_RENDDESC_WAIT 6 +#define AP_CONN_STATE_CIRCUIT_WAIT 7 +#define AP_CONN_STATE_CONNECT_WAIT 8 +#define AP_CONN_STATE_OPEN 9 +#define _AP_CONN_STATE_MAX 9 #define _DIR_CONN_STATE_MIN 1 #define DIR_CONN_STATE_CONNECTING 1 @@ -194,23 +187,42 @@ #define CIRCUIT_STATE_OPEN 3 /* onionskin(s) processed, ready to send/receive cells */ #define _CIRCUIT_PURPOSE_MIN 1 + /* these circuits were initiated elsewhere */ #define CIRCUIT_PURPOSE_OR 1 /* normal circuit, at OR. */ #define CIRCUIT_PURPOSE_INTRO_POINT 2 /* At OR, from Bob, waiting for intro from Alices */ #define CIRCUIT_PURPOSE_REND_POINT_WAITING 3 /* At OR, from Alice, waiting for Bob */ #define CIRCUIT_PURPOSE_REND_ESTABLISHED 4 /* At OR, both circuits have this purpose */ + /* these circuits originate at this node */ + +/* here's how circ client-side purposes work: + * normal circuits are C_GENERAL. + * circuits that are c_introducing are either on their way to + * becoming open, or they are open but haven't been used yet. + * (as soon as they are used, they are destroyed.) + * circuits that are c_establish_rend are either on their way + * to becoming open, or they are open and have sent the + * establish_rendezvous cell but haven't received an ack. + * circuits that are c_rend_ready are open and have received an + * ack, but haven't heard from bob yet. if they have a + * buildstate->pending_final_cpath then they're expecting a + * cell from bob, else they're not. + * circuits that are c_rend_joined are open, have heard from + * bob, and are talking to him. + */ #define CIRCUIT_PURPOSE_C_GENERAL 5 /* normal circuit, with cpath */ #define CIRCUIT_PURPOSE_C_INTRODUCING 6 /* at Alice, connecting to intro point */ -#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 7 /* at Alice, waiting for Bob */ -#define CIRCUIT_PURPOSE_C_REND_JOINED 8 /* at Alice, rendezvous established.*/ +#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 7 /* at Alice, waiting for ack */ +#define CIRCUIT_PURPOSE_C_REND_READY 8 /* at Alice, waiting for Bob */ +#define CIRCUIT_PURPOSE_C_REND_JOINED 9 /* at Alice, rendezvous established */ -#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 9 /* at Bob, waiting for introductions */ -#define CIRCUIT_PURPOSE_S_INTRO 10 /* at Bob, successfully established intro */ -#define CIRCUIT_PURPOSE_S_CONNECT_REND 11 /* at Bob, connecting to rend point */ +#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 10 /* at Bob, waiting for introductions */ +#define CIRCUIT_PURPOSE_S_INTRO 11 /* at Bob, successfully established intro */ +#define CIRCUIT_PURPOSE_S_CONNECT_REND 12 /* at Bob, connecting to rend point */ -#define CIRCUIT_PURPOSE_S_REND_JOINED 12 /* at Bob, rendezvous established.*/ -#define _CIRCUIT_PURPOSE_MAX 11 +#define CIRCUIT_PURPOSE_S_REND_JOINED 13 /* at Bob, rendezvous established.*/ +#define _CIRCUIT_PURPOSE_MAX 13 #define RELAY_COMMAND_BEGIN 1 #define RELAY_COMMAND_DATA 2 @@ -347,7 +359,7 @@ struct connection_t { uint8_t type; uint8_t state; - uint8_t purpose; /* only used for DIR and AP types currently */ + uint8_t purpose; /* only used for DIR types currently */ uint8_t wants_to_read; /* should we start reading again once * the bandwidth throttler allows it? */ @@ -687,8 +699,8 @@ int _circuit_mark_for_close(circuit_t *circ); 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_newest(connection_t *conn, - int must_be_open, uint8_t conn_purpose); +circuit_t *circuit_get_best(connection_t *conn, + int must_be_open, 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); @@ -803,6 +815,7 @@ connection_t *connection_exact_get_by_addr_port(uint32_t addr, uint16_t port); connection_t *connection_get_by_type(int type); connection_t *connection_get_by_type_state(int type, int state); connection_t *connection_get_by_type_state_lastwritten(int type, int state); +connection_t *connection_get_by_type_rendquery(int type, char *rendquery); #define connection_speaks_cells(conn) ((conn)->type == CONN_TYPE_OR) #define connection_has_pending_tls_data(conn) \ @@ -1025,12 +1038,14 @@ void rep_hist_dump_stats(time_t now, int severity); /********************************* rendclient.c ***************************/ -void rend_client_introcirc_is_ready(connection_t *apconn, circuit_t *circ); -void rend_client_rendcirc_is_ready(connection_t *apconn, circuit_t *circ); +void rend_client_introcirc_is_open(circuit_t *circ); +void rend_client_rendcirc_is_open(circuit_t *circ); +int rend_client_rendezvous_acked(circuit_t *circ, const char *request, int request_len); void rend_client_rendezvous(connection_t *apconn, circuit_t *circ); void rend_client_desc_fetched(char *query, int success); int rend_cmp_service_ids(char *one, char *two); +char *rend_get_random_intro(char *query); int rend_parse_rendezvous_address(char *address); int rend_client_send_establish_rendezvous(circuit_t *circ); diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 32b3b82541..daca03b5d0 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -6,10 +6,13 @@ /* send the introduce cell */ void -rend_client_introcirc_is_ready(connection_t *apconn, circuit_t *circ) +rend_client_introcirc_is_open(circuit_t *circ) { + assert(circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); + assert(circ->cpath); - log_fn(LOG_WARN,"introcirc is ready"); + log_fn(LOG_INFO,"introcirc is open"); + connection_ap_attach_pending(); } int @@ -35,24 +38,33 @@ rend_client_send_establish_rendezvous(circuit_t *circ) /* send the rendezvous cell */ void -rend_client_rendcirc_is_ready(connection_t *apconn, circuit_t *circ) +rend_client_rendcirc_is_open(circuit_t *circ) { - circuit_t *introcirc; - - assert(apconn->purpose == AP_PURPOSE_RENDPOINT_WAIT); assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); assert(circ->cpath); - log_fn(LOG_INFO,"rendcirc is ready"); + log_fn(LOG_INFO,"rendcirc is open"); - /* XXX generate a rendezvous cookie, store it in circ */ - /* store rendcirc in apconn */ - - apconn->purpose = AP_PURPOSE_INTROPOINT_WAIT; - if (connection_ap_handshake_attach_circuit(apconn) < 0) { - log_fn(LOG_WARN,"failed to start intro point. Closing conn."); - connection_mark_for_close(apconn,0); + /* generate a rendezvous cookie, store it in circ */ + if (rend_client_send_establish_rendezvous(circ) < 0) { + circuit_mark_for_close(circ); + return; } + + connection_ap_attach_pending(); +} + +int +rend_client_rendezvous_acked(circuit_t *circ, const char *request, int request_len) +{ + /* we just got an ack for our establish-rendezvous. switch purposes. */ + if(circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) { + log_fn(LOG_WARN,"Got a rendezvous ack when we weren't expecting one. Closing circ."); + circuit_mark_for_close(circ); + return -1; + } + circ->purpose = CIRCUIT_PURPOSE_C_REND_READY; + return 0; } /* bob sent us a rendezvous cell, join the circs. */ @@ -80,16 +92,14 @@ void rend_client_desc_fetched(char *query, int success) { for (i = 0; i < n; ++i) { conn = carray[i]; if (conn->type != CONN_TYPE_AP || - conn->state != AP_CONN_STATE_CIRCUIT_WAIT) - continue; - if (conn->purpose != AP_PURPOSE_RENDDESC_WAIT) + conn->state != AP_CONN_STATE_RENDDESC_WAIT) continue; if (rend_cmp_service_ids(conn->rend_query, query)) continue; /* great, this guy was waiting */ if(success) { - log_fn(LOG_INFO,"Rend desc retrieved. Launching rend circ."); - conn->purpose = AP_PURPOSE_RENDPOINT_WAIT; + log_fn(LOG_INFO,"Rend desc retrieved. Launching circuits."); + conn->state = AP_CONN_STATE_CIRCUIT_WAIT; if (connection_ap_handshake_attach_circuit(conn) < 0) { /* it will never work */ log_fn(LOG_WARN,"attaching to a rend circ failed. Closing conn."); @@ -106,6 +116,40 @@ int rend_cmp_service_ids(char *one, char *two) { return strcasecmp(one,two); } +/* return a pointer to a nickname for a random introduction + * point of query. return NULL if error. + */ +char *rend_get_random_intro(char *query) { + const char *descp; + int desc_len; + int i; + smartlist_t *sl; + rend_service_descriptor_t *parsed; + char *choice; + + if(rend_cache_lookup(query, &descp, &desc_len) < 1) { + log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.", query); + return NULL; + } + + parsed = rend_parse_service_descriptor(descp,desc_len); + if (!parsed) { + log_fn(LOG_WARN,"Couldn't parse service descriptor"); + return NULL; + } + + sl = smartlist_create(); + + /* add the intro point nicknames */ + for(i=0;in_intro_points;i++) + smartlist_add(sl,parsed->intro_points[i]); + + choice = smartlist_choose(sl); + smartlist_free(sl); + rend_service_descriptor_free(parsed); + return choice; +} + /* If address is of the form "y.onion" with a well-formed handle y, * then put a '\0' after y, lower-case it, and return 0. * Else return -1 and change nothing. diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 878e83e3bd..0bb2fe295d 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -61,6 +61,8 @@ rend_encode_service_descriptor(rend_service_descriptor_t *desc, return 0; } +/* malloc a service_descriptor_t and return it. + * return NULL if invalid descriptor or error */ rend_service_descriptor_t *rend_parse_service_descriptor( const char *str, int len) { @@ -83,6 +85,10 @@ rend_service_descriptor_t *rend_parse_service_descriptor( cp += 4; if (end-cp < 2) goto truncated; result->n_intro_points = get_uint16(cp); + if(result->n_intro_points < 1) { + log_fn(LOG_WARN,"Service descriptor listed no intro points."); + goto error; + } result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points); cp += 2; for (i=0;in_intro_points;++i) { @@ -282,8 +288,7 @@ void rend_process_relay_cell(circuit_t *circ, int command, int length, r = rend_service_intro_established(circ,payload,length); break; case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED: - /* r = rend_client_rendezvous_established(circ,payload,length); */ - log_fn(LOG_NOTICE, "Ignoring a rendezvous_established cell"); + r = rend_client_rendezvous_acked(circ,payload,length); break; default: assert(0); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 2f41fa1a17..ddd592a02a 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -193,7 +193,7 @@ static routerinfo_t *router_pick_directory_server_impl(void) { void add_nickname_list_to_smartlist(smartlist_t *sl, char *list) { char *start,*end; - char nick[MAX_NICKNAME_LEN]; + char nick[MAX_NICKNAME_LEN+1]; routerinfo_t *router; assert(sl); @@ -918,7 +918,7 @@ routerinfo_t *router_get_entry_from_string(const char *s, } router->address = tor_strdup(tok->args[1]); router->addr = 0; - + if (tok->n_args == 6) { router->or_port = atoi(tok->args[2]); router->socks_port = atoi(tok->args[3]);