static void link_drop(CIRCUIT *circuit) { USER *user; CN *cn; // So we don't try and send anything on this circuit. circuit->u.link->flags = p_nil; circuit->flags = p_nil; // Users connected on the dropped link are no longer connected. for (user = user_hd; user; user = user->next) if (user->circuit == circuit) { circuit->refcnt--; node_dec(user->node); text_tellu(user, rtleave, NULL, o_all); user_tell(user, id_leave); user_leave(user); } // Any node known from the dropped link is no longer known. for (cn = circuit->hnode; cn; cn = cn->next) { node_tell(cn->node, id_unlink); node_dec(cn->node); } // The circuit is no longer used. circuit_free(circuit); }
/** Run unit tests for the onion queues. */ static void test_onion_queues(void *arg) { uint8_t buf1[TAP_ONIONSKIN_CHALLENGE_LEN] = {0}; uint8_t buf2[NTOR_ONIONSKIN_LEN] = {0}; or_circuit_t *circ1 = or_circuit_new(0, NULL); or_circuit_t *circ2 = or_circuit_new(0, NULL); create_cell_t *onionskin = NULL, *create2_ptr; create_cell_t *create1 = tor_malloc_zero(sizeof(create_cell_t)); create_cell_t *create2 = tor_malloc_zero(sizeof(create_cell_t)); (void)arg; create2_ptr = create2; /* remember, but do not free */ create_cell_init(create1, CELL_CREATE, ONION_HANDSHAKE_TYPE_TAP, TAP_ONIONSKIN_CHALLENGE_LEN, buf1); create_cell_init(create2, CELL_CREATE, ONION_HANDSHAKE_TYPE_NTOR, NTOR_ONIONSKIN_LEN, buf2); tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); tt_int_op(0,OP_EQ, onion_pending_add(circ1, create1)); create1 = NULL; tt_int_op(1,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); tt_int_op(0,OP_EQ, onion_pending_add(circ2, create2)); create2 = NULL; tt_int_op(1,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); tt_ptr_op(circ2,OP_EQ, onion_next_task(&onionskin)); tt_int_op(1,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); tt_ptr_op(onionskin, OP_EQ, create2_ptr); clear_pending_onions(); tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_TAP)); tt_int_op(0,OP_EQ, onion_num_pending(ONION_HANDSHAKE_TYPE_NTOR)); done: circuit_free(TO_CIRCUIT(circ1)); circuit_free(TO_CIRCUIT(circ2)); tor_free(create1); tor_free(create2); tor_free(onionskin); }
static void logout(CIRCUIT *circuit) { USER *user; circuit->flags = p_nil; user = circuit->u.user; if (log_rt) tlogp("RT Logout"); user_tell(user, id_leave); text_tellu(user, rtleave, NULL, o_all); cn_dec(circuit, user->node); user_leave(user); circuit_free(circuit); }
/** 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; } } }
/** Release all storage held by circuits. */ void circuit_free_all(void) { circuit_t *next; while (global_circuitlist) { next = global_circuitlist->next; if (! CIRCUIT_IS_ORIGIN(global_circuitlist)) { or_circuit_t *or_circ = TO_OR_CIRCUIT(global_circuitlist); while (or_circ->resolving_streams) { edge_connection_t *next_conn; next_conn = or_circ->resolving_streams->next_stream; connection_free(TO_CONN(or_circ->resolving_streams)); or_circ->resolving_streams = next_conn; } } circuit_free(global_circuitlist); global_circuitlist = next; } if (circuits_pending_or_conns) { smartlist_free(circuits_pending_or_conns); circuits_pending_or_conns = NULL; } HT_CLEAR(orconn_circid_map, &orconn_circid_circuit_map); }
static void test_clist_maps(void *arg) { channel_t *ch1 = new_fake_channel(); channel_t *ch2 = new_fake_channel(); channel_t *ch3 = new_fake_channel(); or_circuit_t *or_c1=NULL, *or_c2=NULL; (void) arg; MOCK(circuitmux_attach_circuit, circuitmux_attach_mock); MOCK(circuitmux_detach_circuit, circuitmux_detach_mock); memset(&cam, 0, sizeof(cam)); memset(&cdm, 0, sizeof(cdm)); tt_assert(ch1); tt_assert(ch2); tt_assert(ch3); ch1->cmux = tor_malloc(1); ch2->cmux = tor_malloc(1); ch3->cmux = tor_malloc(1); or_c1 = or_circuit_new(100, ch2); tt_assert(or_c1); GOT_CMUX_ATTACH(ch2->cmux, or_c1, CELL_DIRECTION_IN); tt_int_op(or_c1->p_circ_id, OP_EQ, 100); tt_ptr_op(or_c1->p_chan, OP_EQ, ch2); or_c2 = or_circuit_new(100, ch1); tt_assert(or_c2); GOT_CMUX_ATTACH(ch1->cmux, or_c2, CELL_DIRECTION_IN); tt_int_op(or_c2->p_circ_id, OP_EQ, 100); tt_ptr_op(or_c2->p_chan, OP_EQ, ch1); circuit_set_n_circid_chan(TO_CIRCUIT(or_c1), 200, ch1); GOT_CMUX_ATTACH(ch1->cmux, or_c1, CELL_DIRECTION_OUT); circuit_set_n_circid_chan(TO_CIRCUIT(or_c2), 200, ch2); GOT_CMUX_ATTACH(ch2->cmux, or_c2, CELL_DIRECTION_OUT); tt_ptr_op(circuit_get_by_circid_channel(200, ch1), OP_EQ, TO_CIRCUIT(or_c1)); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2)); tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1)); /* Try the same thing again, to test the "fast" path. */ tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, TO_CIRCUIT(or_c1)); tt_assert(circuit_id_in_use_on_channel(100, ch2)); tt_assert(! circuit_id_in_use_on_channel(101, ch2)); /* Try changing the circuitid and channel of that circuit. */ circuit_set_p_circid_chan(or_c1, 500, ch3); GOT_CMUX_DETACH(ch2->cmux, TO_CIRCUIT(or_c1)); GOT_CMUX_ATTACH(ch3->cmux, TO_CIRCUIT(or_c1), CELL_DIRECTION_IN); tt_ptr_op(circuit_get_by_circid_channel(100, ch2), OP_EQ, NULL); tt_assert(! circuit_id_in_use_on_channel(100, ch2)); tt_ptr_op(circuit_get_by_circid_channel(500, ch3), OP_EQ, TO_CIRCUIT(or_c1)); /* Now let's see about destroy handling. */ tt_assert(! circuit_id_in_use_on_channel(205, ch2)); tt_assert(circuit_id_in_use_on_channel(200, ch2)); channel_note_destroy_pending(ch2, 200); channel_note_destroy_pending(ch2, 205); channel_note_destroy_pending(ch1, 100); tt_assert(circuit_id_in_use_on_channel(205, ch2)) tt_assert(circuit_id_in_use_on_channel(200, ch2)); tt_assert(circuit_id_in_use_on_channel(100, ch1)); tt_assert(TO_CIRCUIT(or_c2)->n_delete_pending != 0); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, TO_CIRCUIT(or_c2)); tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, TO_CIRCUIT(or_c2)); /* Okay, now free ch2 and make sure that the circuit ID is STILL not * usable, because we haven't declared the destroy to be nonpending */ tt_int_op(cdm.ncalls, OP_EQ, 0); circuit_free(TO_CIRCUIT(or_c2)); or_c2 = NULL; /* prevent free */ tt_int_op(cdm.ncalls, OP_EQ, 2); memset(&cdm, 0, sizeof(cdm)); tt_assert(circuit_id_in_use_on_channel(200, ch2)); tt_assert(circuit_id_in_use_on_channel(100, ch1)); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL); tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL); /* Now say that the destroy is nonpending */ channel_note_destroy_not_pending(ch2, 200); tt_ptr_op(circuit_get_by_circid_channel(200, ch2), OP_EQ, NULL); channel_note_destroy_not_pending(ch1, 100); tt_ptr_op(circuit_get_by_circid_channel(100, ch1), OP_EQ, NULL); tt_assert(! circuit_id_in_use_on_channel(200, ch2)); tt_assert(! circuit_id_in_use_on_channel(100, ch1)); done: if (or_c1) circuit_free(TO_CIRCUIT(or_c1)); if (or_c2) circuit_free(TO_CIRCUIT(or_c2)); if (ch1) tor_free(ch1->cmux); if (ch2) tor_free(ch2->cmux); if (ch3) tor_free(ch3->cmux); tor_free(ch1); tor_free(ch2); tor_free(ch3); UNMOCK(circuitmux_attach_circuit); UNMOCK(circuitmux_detach_circuit); }
static void test_rend_token_maps(void *arg) { or_circuit_t *c1, *c2, *c3, *c4; const uint8_t tok1[REND_TOKEN_LEN] = "The cat can't tell y"; const uint8_t tok2[REND_TOKEN_LEN] = "ou its name, and it "; const uint8_t tok3[REND_TOKEN_LEN] = "doesn't really care."; /* -- Adapted from a quote by Fredrik Lundh. */ (void)arg; (void)tok1; //xxxx c1 = or_circuit_new(0, NULL); c2 = or_circuit_new(0, NULL); c3 = or_circuit_new(0, NULL); c4 = or_circuit_new(0, NULL); /* Make sure we really filled up the tok* variables */ tt_int_op(tok1[REND_TOKEN_LEN-1], OP_EQ, 'y'); tt_int_op(tok2[REND_TOKEN_LEN-1], OP_EQ, ' '); tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.'); /* No maps; nothing there. */ tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); circuit_set_rendezvous_cookie(c1, tok1); circuit_set_intro_point_digest(c2, tok2); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok3)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); /* Without purpose set, we don't get the circuits */ tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; /* Okay, make sure they show up now. */ tt_ptr_op(c1, OP_EQ, circuit_get_rendezvous(tok1)); tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); /* Two items at the same place with the same token. */ c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; circuit_set_rendezvous_cookie(c3, tok2); tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); circuit_free(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ circuit_free(TO_CIRCUIT(c2)); c2 = NULL; tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); /* c3 -- are you still there? */ tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); /* Change its cookie. This never happens in Tor per se, but hey. */ c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; circuit_set_intro_point_digest(c3, tok3); tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); tt_ptr_op(c3, OP_EQ, circuit_get_intro_point(tok3)); /* Now replace c3 with c4. */ c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; circuit_set_intro_point_digest(c4, tok3); tt_ptr_op(c4, OP_EQ, circuit_get_intro_point(tok3)); tt_ptr_op(c3->rendinfo, OP_EQ, NULL); tt_ptr_op(c4->rendinfo, OP_NE, NULL); tt_mem_op(c4->rendinfo, OP_EQ, tok3, REND_TOKEN_LEN); /* Now clear c4's cookie. */ circuit_set_intro_point_digest(c4, NULL); tt_ptr_op(c4->rendinfo, OP_EQ, NULL); tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); done: if (c1) circuit_free(TO_CIRCUIT(c1)); if (c2) circuit_free(TO_CIRCUIT(c2)); if (c3) circuit_free(TO_CIRCUIT(c3)); if (c4) circuit_free(TO_CIRCUIT(c4)); }
/** Run unit tests for buffers.c */ static void test_oom_streambuf(void *arg) { or_options_t *options = get_options_mutable(); circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL; struct timeval tv = { 1389641159, 0 }; uint32_t tvms; int i; smartlist_t *edgeconns = smartlist_new(); (void) arg; MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); /* Far too low for real life. */ options->MaxMemInQueues = 81*packed_cell_mem_cost() + 4096 * 34; options->CellStatistics = 0; tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We don't start out OOM. */ tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); /* Start all circuits with a bit of data queued in cells */ tv.tv_usec = 500*1000; /* go halfway into the second. */ tor_gettimeofday_cache_set(&tv); c1 = dummy_or_circuit_new(10,10); tv.tv_usec = 510*1000; tor_gettimeofday_cache_set(&tv); c2 = dummy_origin_circuit_new(20); tv.tv_usec = 520*1000; tor_gettimeofday_cache_set(&tv); c3 = dummy_or_circuit_new(20,20); tv.tv_usec = 530*1000; tor_gettimeofday_cache_set(&tv); c4 = dummy_or_circuit_new(0,0); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 80); tv.tv_usec = 600*1000; tor_gettimeofday_cache_set(&tv); /* Add some connections to c1...c4. */ for (i = 0; i < 4; ++i) { edge_connection_t *ec; /* link it to a circuit */ tv.tv_usec += 10*1000; tor_gettimeofday_cache_set(&tv); ec = dummy_edge_conn_new(c1, CONN_TYPE_EXIT, 1000, 1000); tt_assert(ec); smartlist_add(edgeconns, ec); tv.tv_usec += 10*1000; tor_gettimeofday_cache_set(&tv); ec = dummy_edge_conn_new(c2, CONN_TYPE_AP, 1000, 1000); tt_assert(ec); smartlist_add(edgeconns, ec); tv.tv_usec += 10*1000; tor_gettimeofday_cache_set(&tv); ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); /* Yes, 4 twice*/ tt_assert(ec); smartlist_add(edgeconns, ec); tv.tv_usec += 10*1000; tor_gettimeofday_cache_set(&tv); ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); smartlist_add(edgeconns, ec); tt_assert(ec); } tv.tv_sec += 1; tv.tv_usec = 0; tvms = (uint32_t) tv_to_msec(&tv); tt_int_op(circuit_max_queued_cell_age(c1, tvms), OP_EQ, 500); tt_int_op(circuit_max_queued_cell_age(c2, tvms), OP_EQ, 490); tt_int_op(circuit_max_queued_cell_age(c3, tvms), OP_EQ, 480); tt_int_op(circuit_max_queued_cell_age(c4, tvms), OP_EQ, 0); tt_int_op(circuit_max_queued_data_age(c1, tvms), OP_EQ, 390); tt_int_op(circuit_max_queued_data_age(c2, tvms), OP_EQ, 380); tt_int_op(circuit_max_queued_data_age(c3, tvms), OP_EQ, 0); tt_int_op(circuit_max_queued_data_age(c4, tvms), OP_EQ, 370); tt_int_op(circuit_max_queued_item_age(c1, tvms), OP_EQ, 500); tt_int_op(circuit_max_queued_item_age(c2, tvms), OP_EQ, 490); tt_int_op(circuit_max_queued_item_age(c3, tvms), OP_EQ, 480); tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 370); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 80); tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*16*2); /* Now give c4 a very old buffer of modest size */ { edge_connection_t *ec; tv.tv_sec -= 1; tv.tv_usec = 0; tor_gettimeofday_cache_set(&tv); ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); tt_assert(ec); smartlist_add(edgeconns, ec); } tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2); tt_int_op(circuit_max_queued_item_age(c4, tvms), OP_EQ, 1000); tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* And run over the limit. */ tv.tv_usec = 800*1000; tor_gettimeofday_cache_set(&tv); c5 = dummy_or_circuit_new(0,5); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 85); tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*17*2); tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */ /* C4 should have died. */ tt_assert(! c1->marked_for_close); tt_assert(! c2->marked_for_close); tt_assert(! c3->marked_for_close); tt_assert(c4->marked_for_close); tt_assert(! c5->marked_for_close); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 85); tt_int_op(buf_get_total_allocation(), OP_EQ, 4096*8*2); done: circuit_free(c1); circuit_free(c2); circuit_free(c3); circuit_free(c4); circuit_free(c5); SMARTLIST_FOREACH(edgeconns, edge_connection_t *, ec, connection_free_(TO_CONN(ec))); smartlist_free(edgeconns); UNMOCK(circuit_mark_for_close_); }
/** Run unit tests for buffers.c */ static void test_oom_circbuf(void *arg) { or_options_t *options = get_options_mutable(); circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL; struct timeval tv = { 1389631048, 0 }; (void) arg; MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_); /* Far too low for real life. */ options->MaxMemInQueues = 256*packed_cell_mem_cost(); options->CellStatistics = 0; tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We don't start out OOM. */ tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); /* Now we're going to fake up some circuits and get them added to the global circuit list. */ tv.tv_usec = 0; tor_gettimeofday_cache_set(&tv); c1 = dummy_origin_circuit_new(30); tv.tv_usec = 10*1000; tor_gettimeofday_cache_set(&tv); c2 = dummy_or_circuit_new(20, 20); tt_int_op(packed_cell_mem_cost(), OP_EQ, sizeof(packed_cell_t)); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 70); tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */ tv.tv_usec = 20*1000; tor_gettimeofday_cache_set(&tv); c3 = dummy_or_circuit_new(100, 85); tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */ tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 255); tv.tv_usec = 30*1000; tor_gettimeofday_cache_set(&tv); /* Adding this cell will trigger our OOM handler. */ c4 = dummy_or_circuit_new(2, 0); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * 257); tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */ tt_assert(c1->marked_for_close); tt_assert(! c2->marked_for_close); tt_assert(! c3->marked_for_close); tt_assert(! c4->marked_for_close); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * (257 - 30)); circuit_free(c1); tv.tv_usec = 0; tor_gettimeofday_cache_set(&tv); /* go back in time */ c1 = dummy_or_circuit_new(90, 0); tv.tv_usec = 40*1000; /* go back to the future */ tor_gettimeofday_cache_set(&tv); tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */ tt_assert(c1->marked_for_close); tt_assert(! c2->marked_for_close); tt_assert(! c3->marked_for_close); tt_assert(! c4->marked_for_close); tt_int_op(cell_queues_get_total_allocation(), OP_EQ, packed_cell_mem_cost() * (257 - 30)); done: circuit_free(c1); circuit_free(c2); circuit_free(c3); circuit_free(c4); UNMOCK(circuit_mark_for_close_); }