static void test_introduce1_suitable_circuit(void *arg) { int ret; or_circuit_t *circ = NULL; (void) arg; /* Valid suitable circuit. */ { circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); ret = circuit_is_suitable_for_introduce1(circ); circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 1); } /* Test if the circuit purpose safeguard works correctly. */ { circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); ret = circuit_is_suitable_for_introduce1(circ); circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } /* Test the non-edge circuit safeguard works correctly. */ { circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); /* Bogus pointer, the check is against NULL on n_chan. */ circ->base_.n_chan = (channel_t *) circ; ret = circuit_is_suitable_for_introduce1(circ); circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } /* Mangle the circuit a bit more so see if our only one INTRODUCE1 cell * limit works correctly. */ { circ = or_circuit_new(0, NULL); circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); circ->already_received_introduce1 = 1; ret = circuit_is_suitable_for_introduce1(circ); circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, 0); } done: ; }
/* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should * fail. */ static void test_establish_intro_wrong_sig(void *arg) { int retval; char circ_nonce[DIGEST_LEN] = {0}; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we attempt to parse it. */ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body); tt_i64_op(cell_len, OP_GT, 0); /* Mutate the last byte (signature)! :) */ cell_body[cell_len - 1]++; /* Receive the cell. Should fail. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, (size_t)cell_len); expect_log_msg_containing("Failed to verify ESTABLISH_INTRO cell."); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: circuit_free_(TO_CIRCUIT(intro_circ)); }
/* Try sending an ESTABLISH_INTRO cell on a circuit that is already an intro * point. Should fail. */ static void test_establish_intro_wrong_purpose(void *arg) { int retval; ssize_t cell_len = 0; char circ_nonce[DIGEST_LEN] = {0}; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void)arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); memcpy(intro_circ->rend_circ_nonce, circ_nonce, DIGEST_LEN); /* Set a bad circuit purpose!! :) */ circuit_change_purpose(TO_CIRCUIT(intro_circ), CIRCUIT_PURPOSE_INTRO_POINT); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we attempt to parse it. */ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body); tt_i64_op(cell_len, OP_GT, 0); /* Receive the cell. Should fail. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); expect_log_msg_containing("Rejecting ESTABLISH_INTRO on non-OR circuit."); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: circuit_free_(TO_CIRCUIT(intro_circ)); }
/** 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 test_halfstream_wrap(void *arg) { origin_circuit_t *circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0); edge_connection_t *edgeconn; entry_connection_t *entryconn; circ->cpath->state = CPATH_STATE_AWAITING_KEYS; circ->cpath->deliver_window = CIRCWINDOW_START; entryconn = fake_entry_conn(circ, 23); edgeconn = ENTRY_TO_EDGE_CONN(entryconn); (void)arg; /* Suppress the WARN message we generate in this test */ setup_full_capture_of_logs(LOG_WARN); MOCK(connection_mark_for_close_internal_, mock_mark_for_close); /* Verify that get_unique_stream_id_by_circ() can wrap uint16_t */ circ->next_stream_id = 65530; halfstream_insert(circ, edgeconn, NULL, 7, 0); tt_int_op(circ->next_stream_id, OP_EQ, 2); tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 7); /* Insert full-1 */ halfstream_insert(circ, edgeconn, NULL, 65534-smartlist_len(circ->half_streams), 0); tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 65534); /* Verify that we can get_unique_stream_id_by_circ() successfully */ edgeconn->stream_id = get_unique_stream_id_by_circ(circ); tt_int_op(edgeconn->stream_id, OP_NE, 0); /* 0 is failure */ /* Insert an opened stream on the circ with that id */ ENTRY_TO_CONN(entryconn)->marked_for_close = 0; ENTRY_TO_CONN(entryconn)->outbuf_flushlen = 0; edgeconn->base_.state = AP_CONN_STATE_CONNECT_WAIT; circ->p_streams = edgeconn; /* Verify that get_unique_stream_id_by_circ() fails */ tt_int_op(get_unique_stream_id_by_circ(circ), OP_EQ, 0); /* 0 is failure */ /* eof the one opened stream. Verify it is now in half-closed */ tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 65534); connection_edge_reached_eof(edgeconn); tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 65535); /* Verify get_unique_stream_id_by_circ() fails due to full half-closed */ circ->p_streams = NULL; tt_int_op(get_unique_stream_id_by_circ(circ), OP_EQ, 0); /* 0 is failure */ done: circuit_free_(TO_CIRCUIT(circ)); connection_free_minimal(ENTRY_TO_CONN(entryconn)); UNMOCK(connection_mark_for_close_internal_); }
static void test_circuit_n_cells(void *arg) { packed_cell_t *pc1=NULL, *pc2=NULL, *pc3=NULL, *pc4=NULL, *pc5=NULL; origin_circuit_t *origin_c=NULL; or_circuit_t *or_c=NULL; (void)arg; pc1 = packed_cell_new(); pc2 = packed_cell_new(); pc3 = packed_cell_new(); pc4 = packed_cell_new(); pc5 = packed_cell_new(); tt_assert(pc1 && pc2 && pc3 && pc4 && pc5); or_c = or_circuit_new(0, NULL); origin_c = origin_circuit_new(); origin_c->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), OP_EQ, 0); cell_queue_append(&or_c->p_chan_cells, pc1); tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), OP_EQ, 1); cell_queue_append(&or_c->base_.n_chan_cells, pc2); cell_queue_append(&or_c->base_.n_chan_cells, pc3); tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(or_c)), OP_EQ, 3); tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), OP_EQ, 0); cell_queue_append(&origin_c->base_.n_chan_cells, pc4); cell_queue_append(&origin_c->base_.n_chan_cells, pc5); tt_int_op(n_cells_in_circ_queues(TO_CIRCUIT(origin_c)), OP_EQ, 2); done: circuit_free_(TO_CIRCUIT(or_c)); circuit_free_(TO_CIRCUIT(origin_c)); }
/* Send a legit ESTABLISH_INTRO cell but with a wrong sig length. Should * fail. */ static void test_establish_intro_wrong_sig_len(void *arg) { int retval; char circ_nonce[DIGEST_LEN] = {0}; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; ssize_t cell_len = 0; size_t bad_sig_len = ED25519_SIG_LEN - 1; trn_cell_establish_intro_t *cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we * attempt to parse it. */ cell_len = new_establish_intro_cell(circ_nonce, &cell); tt_i64_op(cell_len, OP_GT, 0); tt_assert(cell); /* Mangle the signature length. */ trn_cell_establish_intro_set_sig_len(cell, bad_sig_len); trn_cell_establish_intro_setlen_sig(cell, bad_sig_len); /* Encode cell. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_int_op(cell_len, OP_GT, 0); /* Receive the cell. Should fail. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); expect_log_msg_containing("ESTABLISH_INTRO sig len is invalid"); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: trn_cell_establish_intro_free(cell); circuit_free_(TO_CIRCUIT(intro_circ)); }
/* Send an empty ESTABLISH_INTRO cell. Should fail. */ static void test_establish_intro_wrong_keytype(void *arg) { int retval; or_circuit_t *intro_circ = or_circuit_new(0,NULL); char circ_nonce[DIGEST_LEN] = {0}; (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Receive the cell. Should fail. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, (uint8_t *) "", 0); expect_log_msg_containing("Empty ESTABLISH_INTRO cell."); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: circuit_free_(TO_CIRCUIT(intro_circ)); }
/* Test outbound cell. The callstack is: * channel_flush_some_cells() * -> channel_flush_from_first_active_circuit() * -> channel_write_packed_cell() * -> write_packed_cell() * -> chan->write_packed_cell() fct ptr. * * This test goes from a cell in a circuit up to the channel write handler * that should put them on the connection outbuf. */ static void test_channel_outbound_cell(void *arg) { int old_count; channel_t *chan = NULL; packed_cell_t *p_cell = NULL, *p_cell2 = NULL; origin_circuit_t *circ = NULL; cell_queue_t *queue; (void) arg; /* Set the test time to be mocked, since this test assumes that no * time will pass, ewma values will not need to be re-scaled, and so on */ monotime_enable_test_mocking(); monotime_set_mock_time_nsec(U64_LITERAL(1000000000) * 12345); cmux_ewma_set_options(NULL,NULL); /* The channel will be freed so we need to hijack this so the scheduler * doesn't get confused. */ MOCK(scheduler_release_channel, scheduler_release_channel_mock); /* Accept cells to lower layer */ test_chan_accept_cells = 1; /* Setup a valid circuit to queue a cell. */ circ = origin_circuit_new(); tt_assert(circ); /* Circuit needs an origin purpose to be considered origin. */ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; TO_CIRCUIT(circ)->n_circ_id = 42; /* This is the outbound test so use the next channel queue. */ queue = &TO_CIRCUIT(circ)->n_chan_cells; /* Setup packed cell to queue on the circuit. */ p_cell = packed_cell_new(); tt_assert(p_cell); p_cell2 = packed_cell_new(); tt_assert(p_cell2); /* Setup a channel to put the circuit on. */ chan = new_fake_channel(); tt_assert(chan); chan->state = CHANNEL_STATE_OPENING; channel_change_state_open(chan); /* Outbound channel. */ channel_mark_outgoing(chan); /* Try to register it so we can clean it through the channel cleanup * process. */ channel_register(chan); tt_int_op(chan->registered, OP_EQ, 1); /* Set EWMA policy so we can pick it when flushing. */ circuitmux_set_policy(chan->cmux, &ewma_policy); tt_ptr_op(circuitmux_get_policy(chan->cmux), OP_EQ, &ewma_policy); /* Register circuit to the channel circid map which will attach the circuit * to the channel's cmux as well. */ circuit_set_n_circid_chan(TO_CIRCUIT(circ), 42, chan); tt_int_op(channel_num_circuits(chan), OP_EQ, 1); /* Test the cmux state. */ tt_ptr_op(TO_CIRCUIT(circ)->n_mux, OP_EQ, chan->cmux); tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* Flush the channel without any cell on it. */ old_count = test_cells_written; ssize_t flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 0); tt_int_op(test_cells_written, OP_EQ, old_count); tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 0); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 0); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 0); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, 0); /* Queue cell onto the next queue that is the outbound direction. Than * update its cmux so the circuit can be picked when flushing cells. */ cell_queue_append(queue, p_cell); p_cell = NULL; tt_int_op(queue->n, OP_EQ, 1); cell_queue_append(queue, p_cell2); p_cell2 = NULL; tt_int_op(queue->n, OP_EQ, 2); update_circuit_on_cmux(TO_CIRCUIT(circ), CELL_DIRECTION_OUT); tt_int_op(circuitmux_num_active_circuits(chan->cmux), OP_EQ, 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 2); tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* From this point on, we have a queued cell on an active circuit attached * to the channel's cmux. */ /* Flush the first cell. This is going to go down the call stack. */ old_count = test_cells_written; flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 1); tt_int_op(channel_more_to_flush(chan), OP_EQ, 1); /* Circuit should remain active because there is a second cell queued. */ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); /* Should still be attached. */ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 1); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0)); /* Flush second cell. This is going to go down the call stack. */ old_count = test_cells_written; flushed = channel_flush_some_cells(chan, 1); tt_i64_op(flushed, OP_EQ, 1); tt_int_op(test_cells_written, OP_EQ, old_count + 1); tt_int_op(circuitmux_num_cells(chan->cmux), OP_EQ, 0); tt_int_op(channel_more_to_flush(chan), OP_EQ, 0); /* No more cells should make the circuit inactive. */ tt_int_op(circuitmux_is_circuit_active(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 0); /* Should still be attached. */ tt_int_op(circuitmux_is_circuit_attached(chan->cmux, TO_CIRCUIT(circ)), OP_EQ, 1); tt_u64_op(chan->n_cells_xmitted, OP_EQ, 2); tt_u64_op(chan->n_bytes_xmitted, OP_EQ, get_cell_network_size(0) * 2); done: if (circ) { circuit_free_(TO_CIRCUIT(circ)); } tor_free(p_cell); channel_free_all(); UNMOCK(scheduler_release_channel); monotime_disable_test_mocking(); }
static void test_circbw_relay(void *arg) { cell_t cell; relay_header_t rh; tor_addr_t addr; edge_connection_t *edgeconn; entry_connection_t *entryconn1=NULL; origin_circuit_t *circ; int delivered = 0; int overhead = 0; (void)arg; MOCK(connection_mark_unattached_ap_, mock_connection_mark_unattached_ap_); MOCK(connection_start_reading, mock_start_reading); MOCK(connection_mark_for_close_internal_, mock_mark_for_close); MOCK(relay_send_command_from_edge_, mock_send_command); MOCK(circuit_mark_for_close_, mock_mark_circ_for_close); circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0); circ->cpath->state = CPATH_STATE_AWAITING_KEYS; circ->cpath->deliver_window = CIRCWINDOW_START; entryconn1 = fake_entry_conn(circ, 1); edgeconn = ENTRY_TO_EDGE_CONN(entryconn1); /* Stream id 0: Not counted */ PACK_CELL(0, RELAY_COMMAND_END, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Stream id 1: Counted */ PACK_CELL(1, RELAY_COMMAND_END, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_COUNTED_BW(); /* Properly formatted connect cell: counted */ PACK_CELL(1, RELAY_COMMAND_CONNECTED, "Data1234"); tor_addr_parse(&addr, "30.40.50.60"); rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE, &addr, 1024); relay_header_pack((uint8_t*)&cell.payload, &rh); \ connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_COUNTED_BW(); /* Properly formatted resolved cell in correct state: counted */ edgeconn->base_.state = AP_CONN_STATE_RESOLVE_WAIT; entryconn1->socks_request->command = SOCKS_COMMAND_RESOLVE; edgeconn->on_circuit = TO_CIRCUIT(circ); PACK_CELL(1, RELAY_COMMAND_RESOLVED, "\x04\x04\x12\x00\x00\x01\x00\x00\x02\x00"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_COUNTED_BW(); edgeconn->base_.state = AP_CONN_STATE_OPEN; entryconn1->socks_request->has_finished = 1; /* Connected cell after open: not counted */ PACK_CELL(1, RELAY_COMMAND_CONNECTED, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Resolved cell after open: not counted */ PACK_CELL(1, RELAY_COMMAND_RESOLVED, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Drop cell: not counted */ PACK_CELL(1, RELAY_COMMAND_DROP, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Data cell on stream 0: not counted */ PACK_CELL(0, RELAY_COMMAND_DATA, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Data cell on open connection: counted */ ENTRY_TO_CONN(entryconn1)->marked_for_close = 0; PACK_CELL(1, RELAY_COMMAND_DATA, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_COUNTED_BW(); /* Empty Data cell on open connection: not counted */ ENTRY_TO_CONN(entryconn1)->marked_for_close = 0; PACK_CELL(1, RELAY_COMMAND_DATA, ""); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Sendme on valid stream: counted */ edgeconn->package_window -= STREAMWINDOW_INCREMENT; ENTRY_TO_CONN(entryconn1)->outbuf_flushlen = 0; PACK_CELL(1, RELAY_COMMAND_SENDME, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_COUNTED_BW(); /* Sendme on valid stream with full window: not counted */ ENTRY_TO_CONN(entryconn1)->outbuf_flushlen = 0; PACK_CELL(1, RELAY_COMMAND_SENDME, "Data1234"); edgeconn->package_window = STREAMWINDOW_START; connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Sendme on unknown stream: not counted */ ENTRY_TO_CONN(entryconn1)->outbuf_flushlen = 0; PACK_CELL(1, RELAY_COMMAND_SENDME, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Sendme on circuit with full window: not counted */ PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Sendme on circuit with non-full window: counted */ PACK_CELL(0, RELAY_COMMAND_SENDME, "Data1234"); circ->cpath->package_window = 900; connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_COUNTED_BW(); /* Invalid extended cell: not counted */ PACK_CELL(1, RELAY_COMMAND_EXTENDED2, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Invalid extended cell: not counted */ PACK_CELL(1, RELAY_COMMAND_EXTENDED, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Invalid HS cell: not counted */ PACK_CELL(1, RELAY_COMMAND_ESTABLISH_INTRO, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL, circ->cpath); ASSERT_UNCOUNTED_BW(); /* "Valid" HS cell in expected state: counted */ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND; PACK_CELL(1, RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL, circ->cpath); ASSERT_COUNTED_BW(); /* End cell on non-closed connection: counted */ PACK_CELL(1, RELAY_COMMAND_END, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), edgeconn, circ->cpath); ASSERT_COUNTED_BW(); /* End cell on connection that already got one: not counted */ PACK_CELL(1, RELAY_COMMAND_END, "Data1234"); connection_edge_process_relay_cell(&cell, TO_CIRCUIT(circ), NULL, circ->cpath); ASSERT_UNCOUNTED_BW(); /* Simulate closed stream on entryconn, then test: */ if (!subtest_circbw_halfclosed(circ, 2)) goto done; circ->base_.purpose = CIRCUIT_PURPOSE_PATH_BIAS_TESTING; if (!subtest_circbw_halfclosed(circ, 6)) goto done; /* Path bias: truncated */ tt_int_op(circ->base_.marked_for_close, OP_EQ, 0); PACK_CELL(0, RELAY_COMMAND_TRUNCATED, "Data1234"); pathbias_count_valid_cells(TO_CIRCUIT(circ), &cell); tt_int_op(circ->base_.marked_for_close, OP_EQ, 1); done: UNMOCK(connection_start_reading); UNMOCK(connection_mark_unattached_ap_); UNMOCK(connection_mark_for_close_internal_); UNMOCK(relay_send_command_from_edge_); UNMOCK(circuit_mark_for_close_); circuit_free_(TO_CIRCUIT(circ)); connection_free_minimal(ENTRY_TO_CONN(entryconn1)); }
static void subtest_halfstream_insertremove(int num) { origin_circuit_t *circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_C_GENERAL, 0); edge_connection_t *edgeconn; entry_connection_t *entryconn; streamid_t *streams = tor_malloc_zero(num*sizeof(streamid_t)); int i = 0; circ->cpath->state = CPATH_STATE_AWAITING_KEYS; circ->cpath->deliver_window = CIRCWINDOW_START; entryconn = fake_entry_conn(circ, 23); edgeconn = ENTRY_TO_EDGE_CONN(entryconn); /* Explicity test all operations on an absent stream list */ tt_int_op(connection_half_edge_is_valid_data(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, 23), OP_EQ, 0); /* Insert a duplicate element; verify that other elements absent; * ensure removing it once works */ edgeconn->stream_id = 23; connection_half_edge_add(edgeconn, circ); connection_half_edge_add(edgeconn, circ); connection_half_edge_add(edgeconn, circ); /* Verify that other elements absent */ tt_int_op(connection_half_edge_is_valid_data(circ->half_streams, 22), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams, 22), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams, 22), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams, 22), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, 22), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_data(circ->half_streams, 24), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams, 24), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams, 24), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams, 24), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, 24), OP_EQ, 0); /* Verify we only remove it once */ tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, 23), OP_EQ, 1); tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, 23), OP_EQ, 0); halfstream_insert(circ, edgeconn, streams, num, 1); /* Remove half of them */ for (i = 0; i < num/2; i++) { tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, streams[i]), OP_EQ, 1); } /* Verify first half of list is gone */ for (i = 0; i < num/2; i++) { tt_ptr_op(connection_half_edge_find_stream_id(circ->half_streams, streams[i]), OP_EQ, NULL); } /* Verify second half of list is present */ for (; i < num; i++) { tt_ptr_op(connection_half_edge_find_stream_id(circ->half_streams, streams[i]), OP_NE, NULL); } /* Remove other half. Verify list is empty. */ for (i = num/2; i < num; i++) { tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, streams[i]), OP_EQ, 1); } tt_int_op(smartlist_len(circ->half_streams), OP_EQ, 0); /* Explicity test all operations on an empty stream list */ tt_int_op(connection_half_edge_is_valid_data(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_connected(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_sendme(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_resolved(circ->half_streams, 23), OP_EQ, 0); tt_int_op(connection_half_edge_is_valid_end(circ->half_streams, 23), OP_EQ, 0); /* For valgrind, leave some around then free the circ */ halfstream_insert(circ, edgeconn, NULL, 10, 0); done: tor_free(streams); circuit_free_(TO_CIRCUIT(circ)); connection_free_minimal(ENTRY_TO_CONN(entryconn)); }
void test_circuitstats_hoplen(void *arg) { /* Plan: * 0. Test no other opened circs (relaxed timeout) * 1. Check >3 hop circ building w/o timeout * 2. Check >3 hop circs w/ timeouts.. */ struct timeval circ_start_time; origin_circuit_t *threehop = NULL; origin_circuit_t *fourhop = NULL; (void)arg; MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close); circuit_build_times_init(get_circuit_build_times_mutable()); // Let's set a close_ms to 2X the initial timeout, so we can // test relaxed functionality (which uses the close_ms timeout) get_circuit_build_times_mutable()->close_ms *= 2; tor_gettimeofday(&circ_start_time); circ_start_time.tv_sec -= 119; // make us hit "relaxed" cutoff // Test 1: Build a fourhop circuit that should get marked // as relaxed and eventually counted by circuit_expire_building // (but not before) fourhop = subtest_fourhop_circuit(circ_start_time, 0); tt_int_op(fourhop->relaxed_timeout, OP_EQ, 0); tt_int_op(marked_for_close, OP_EQ, 0); circuit_expire_building(); tt_int_op(marked_for_close, OP_EQ, 0); tt_int_op(fourhop->relaxed_timeout, OP_EQ, 1); TO_CIRCUIT(fourhop)->timestamp_began.tv_sec -= 119; circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); tt_int_op(marked_for_close, OP_EQ, 1); circuit_free_(TO_CIRCUIT(fourhop)); circuit_build_times_reset(get_circuit_build_times_mutable()); // Test 2: Add a threehop circuit for non-relaxed timeouts threehop = add_opened_threehop(); /* This circuit should not timeout */ tor_gettimeofday(&circ_start_time); circ_start_time.tv_sec -= 59; fourhop = subtest_fourhop_circuit(circ_start_time, 0); circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_NE, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); circuit_free_((circuit_t *)fourhop); circuit_build_times_reset(get_circuit_build_times_mutable()); /* Test 3: This circuit should now time out and get marked as a * measurement circuit, but still get counted (and counted only once) */ circ_start_time.tv_sec -= 2; fourhop = subtest_fourhop_circuit(circ_start_time, 0); tt_int_op(TO_CIRCUIT(fourhop)->purpose, OP_EQ, CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); circuit_expire_building(); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 1); done: UNMOCK(circuit_mark_for_close_); circuit_free_(TO_CIRCUIT(threehop)); circuit_free_(TO_CIRCUIT(fourhop)); circuit_build_times_free_timeouts(get_circuit_build_times_mutable()); }
static void test_received_introduce1_handling(void *arg) { int ret; uint8_t *request = NULL, buf[128]; trn_cell_introduce1_t *cell = NULL; or_circuit_t *circ = NULL; (void) arg; MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); hs_circuitmap_init(); /* Too small request length. An INTRODUCE1 expect at the very least a * DIGEST_LEN size. */ { memset(buf, 0, sizeof(buf)); circ = helper_create_intro_circuit(); ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1); tt_int_op(ret, OP_EQ, -1); circuit_free_(TO_CIRCUIT(circ)); } /* We have a unit test only for the suitability of a circuit to receive an * INTRODUCE1 cell so from now on we'll only test the handling of a cell. */ /* Bad request. */ { circ = helper_create_intro_circuit(); uint8_t test[2]; /* Too small request. */ memset(test, 0, sizeof(test)); ret = handle_introduce1(circ, test, sizeof(test)); tor_free(circ->p_chan); circuit_free_(TO_CIRCUIT(circ)); tt_int_op(ret, OP_EQ, -1); } /* Valid case. */ { cell = helper_create_introduce1_cell(); ssize_t request_len = trn_cell_introduce1_encoded_len(cell); tt_int_op((int)request_len, OP_GT, 0); request = tor_malloc_zero(request_len); ssize_t encoded_len = trn_cell_introduce1_encode(request, request_len, cell); tt_int_op((int)encoded_len, OP_GT, 0); circ = helper_create_intro_circuit(); or_circuit_t *service_circ = helper_create_intro_circuit(); circuit_change_purpose(TO_CIRCUIT(service_circ), CIRCUIT_PURPOSE_INTRO_POINT); /* Register the circuit in the map for the auth key of the cell. */ ed25519_public_key_t auth_key; const uint8_t *cell_auth_key = trn_cell_introduce1_getconstarray_auth_key(cell); memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); hs_circuitmap_register_intro_circ_v3_relay_side(service_circ, &auth_key); ret = hs_intro_received_introduce1(circ, request, request_len); circuit_free_(TO_CIRCUIT(circ)); circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } /* Valid legacy cell. */ { tor_free(request); trn_cell_introduce1_free(cell); cell = helper_create_introduce1_cell(); uint8_t *legacy_key_id = trn_cell_introduce1_getarray_legacy_key_id(cell); memset(legacy_key_id, 'a', DIGEST_LEN); /* Add an arbitrary amount of data for the payload of a v2 cell. */ size_t request_len = trn_cell_introduce1_encoded_len(cell) + 256; tt_size_op(request_len, OP_GT, 0); request = tor_malloc_zero(request_len + 256); ssize_t encoded_len = trn_cell_introduce1_encode(request, request_len, cell); tt_int_op((int)encoded_len, OP_GT, 0); circ = helper_create_intro_circuit(); or_circuit_t *service_circ = helper_create_intro_circuit(); circuit_change_purpose(TO_CIRCUIT(service_circ), CIRCUIT_PURPOSE_INTRO_POINT); /* Register the circuit in the map for the auth key of the cell. */ uint8_t token[REND_TOKEN_LEN]; memcpy(token, legacy_key_id, sizeof(token)); hs_circuitmap_register_intro_circ_v2_relay_side(service_circ, token); ret = hs_intro_received_introduce1(circ, request, request_len); circuit_free_(TO_CIRCUIT(circ)); circuit_free_(TO_CIRCUIT(service_circ)); tt_int_op(ret, OP_EQ, 0); } done: trn_cell_introduce1_free(cell); tor_free(request); hs_circuitmap_free_all(); UNMOCK(relay_send_command_from_edge_); }
/** Successfully register a v2 intro point and a v3 intro point. Ensure that HS * circuitmap is maintained properly. */ static void test_intro_point_registration(void *arg) { int retval; hs_circuitmap_ht *the_hs_circuitmap = NULL; or_circuit_t *intro_circ = NULL; trn_cell_establish_intro_t *establish_intro_cell = NULL; ed25519_public_key_t auth_key; crypto_pk_t *legacy_auth_key = NULL; or_circuit_t *legacy_intro_circ = NULL; or_circuit_t *returned_intro_circ = NULL; (void) arg; MOCK(hs_intro_send_intro_established_cell, mock_send_intro_established_cell); hs_circuitmap_init(); /* Check that the circuitmap is currently empty */ { the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(0, OP_EQ, HT_SIZE(the_hs_circuitmap)); /* Do a circuitmap query in any case */ returned_intro_circ =hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(returned_intro_circ, OP_EQ, NULL); } /* Create a v3 intro point */ { intro_circ = or_circuit_new(0, NULL); tt_assert(intro_circ); establish_intro_cell = helper_establish_intro_v3(intro_circ); /* Check that the intro point was registered on the HS circuitmap */ the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(1, OP_EQ, HT_SIZE(the_hs_circuitmap)); get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, establish_intro_cell); returned_intro_circ = hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key); tt_ptr_op(intro_circ, OP_EQ, returned_intro_circ); } /* Create a v2 intro point */ { char key_digest[DIGEST_LEN]; legacy_intro_circ = or_circuit_new(1, NULL); tt_assert(legacy_intro_circ); legacy_auth_key = helper_establish_intro_v2(legacy_intro_circ); tt_assert(legacy_auth_key); /* Check that the circuitmap now has two elements */ the_hs_circuitmap = get_hs_circuitmap(); tt_assert(the_hs_circuitmap); tt_int_op(2, OP_EQ, HT_SIZE(the_hs_circuitmap)); /* Check that the new element is our legacy intro circuit. */ retval = crypto_pk_get_digest(legacy_auth_key, key_digest); tt_int_op(retval, OP_EQ, 0); returned_intro_circ = hs_circuitmap_get_intro_circ_v2_relay_side((uint8_t*)key_digest); tt_ptr_op(legacy_intro_circ, OP_EQ, returned_intro_circ); } /* XXX Continue test and try to register a second v3 intro point with the * same auth key. Make sure that old intro circuit gets closed. */ done: crypto_pk_free(legacy_auth_key); circuit_free_(TO_CIRCUIT(intro_circ)); circuit_free_(TO_CIRCUIT(legacy_intro_circ)); trn_cell_establish_intro_free(establish_intro_cell); test_circuitmap_free_all(); UNMOCK(hs_intro_send_intro_established_cell); }
/* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */ static void test_establish_intro_wrong_mac(void *arg) { int retval; char circ_nonce[DIGEST_LEN] = {0}; ssize_t cell_len = 0; uint8_t cell_body[RELAY_PAYLOAD_SIZE]; trn_cell_establish_intro_t *cell = NULL; or_circuit_t *intro_circ = or_circuit_new(0,NULL); (void) arg; /* Get the auth key of the intro point */ crypto_rand(circ_nonce, sizeof(circ_nonce)); helper_prepare_circ_for_intro(intro_circ, circ_nonce); /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we * attempt to parse it. */ cell_len = new_establish_intro_cell(circ_nonce, &cell); tt_i64_op(cell_len, OP_GT, 0); tt_assert(cell); /* Mangle one byte of the MAC. */ uint8_t *handshake_ptr = trn_cell_establish_intro_getarray_handshake_mac(cell); handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++; /* We need to resign the payload with that change. */ { ed25519_signature_t sig; ed25519_keypair_t key_struct; /* New keypair for the signature since we don't have access to the private * key material generated earlier when creating the cell. */ retval = ed25519_keypair_generate(&key_struct, 0); tt_int_op(retval, OP_EQ, 0); uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell); memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN); /* Encode payload so we can sign it. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_i64_op(cell_len, OP_GT, 0); retval = ed25519_sign_prefixed(&sig, cell_body, cell_len - (ED25519_SIG_LEN + sizeof(cell->sig_len)), ESTABLISH_INTRO_SIG_PREFIX, &key_struct); tt_int_op(retval, OP_EQ, 0); /* And write the signature to the cell */ uint8_t *sig_ptr = trn_cell_establish_intro_getarray_sig(cell); memcpy(sig_ptr, sig.sig, cell->sig_len); /* Re-encode with the new signature. */ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body), cell); tt_i64_op(cell_len, OP_GT, 0); } /* Receive the cell. Should fail because our MAC is wrong. */ setup_full_capture_of_logs(LOG_INFO); retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); expect_log_msg_containing("ESTABLISH_INTRO handshake_auth not as expected"); teardown_capture_of_logs(); tt_int_op(retval, OP_EQ, -1); done: trn_cell_establish_intro_free(cell); circuit_free_(TO_CIRCUIT(intro_circ)); }