|
@@ -21,13 +21,12 @@ extern circuit_t *global_circuitlist;
|
|
|
|
|
|
static int circuit_deliver_create_cell(circuit_t *circ,
|
|
|
uint8_t cell_type, char *payload);
|
|
|
-static int onion_pick_cpath_exit(circuit_t *circ, routerinfo_t *exit);
|
|
|
+static int onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit);
|
|
|
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
|
|
|
-static int onion_next_router_in_cpath(circuit_t *circ, routerinfo_t **router);
|
|
|
static int onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
|
|
|
cpath_build_state_t *state);
|
|
|
static int count_acceptable_routers(smartlist_t *routers);
|
|
|
-static int onion_append_hop(crypt_path_t **head_ptr, routerinfo_t *choice);
|
|
|
+static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
|
|
|
|
|
|
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
|
|
|
* and with the high bit specified by circ_id_type (see
|
|
@@ -85,31 +84,25 @@ circuit_list_path(circuit_t *circ, int verbose)
|
|
|
elements = smartlist_create();
|
|
|
|
|
|
if (verbose) {
|
|
|
+ const char *nickname = build_state_get_exit_nickname(circ->build_state);
|
|
|
tor_snprintf(buf, sizeof(buf)-1, "%s%s circ (length %d, exit %s):",
|
|
|
circ->build_state->is_internal ? "internal" : "exit",
|
|
|
circ->build_state->need_uptime ? " (high-uptime)" : "",
|
|
|
circ->build_state->desired_path_len,
|
|
|
- circ->build_state->chosen_exit_name);
|
|
|
+ nickname?nickname:"unnamed");
|
|
|
smartlist_add(elements, tor_strdup(buf));
|
|
|
}
|
|
|
|
|
|
hop = circ->cpath;
|
|
|
do {
|
|
|
const char *elt;
|
|
|
- routerinfo_t *r;
|
|
|
if (!hop)
|
|
|
break;
|
|
|
if (!verbose && hop->state != CPATH_STATE_OPEN)
|
|
|
break;
|
|
|
- if ((r = router_get_by_digest(hop->identity_digest))) {
|
|
|
- elt = r->nickname;
|
|
|
- } else if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
|
|
|
- elt = "<rendezvous splice>";
|
|
|
- } else {
|
|
|
- buf[0]='$';
|
|
|
- base16_encode(buf+1,sizeof(buf)-1,hop->identity_digest,DIGEST_LEN);
|
|
|
- elt = buf;
|
|
|
- }
|
|
|
+ if (!hop->extend_info)
|
|
|
+ break;
|
|
|
+ elt = hop->extend_info->nickname;
|
|
|
if (verbose) {
|
|
|
size_t len = strlen(elt)+2+strlen(states[hop->state])+1;
|
|
|
char *v = tor_malloc(len);
|
|
@@ -166,7 +159,7 @@ circuit_rep_hist_note_result(circuit_t *circ)
|
|
|
prev_digest = me->identity_digest;
|
|
|
}
|
|
|
do {
|
|
|
- router = router_get_by_digest(hop->identity_digest);
|
|
|
+ router = router_get_by_digest(hop->extend_info->identity_digest);
|
|
|
if (router) {
|
|
|
if (prev_digest) {
|
|
|
if (hop->state == CPATH_STATE_OPEN)
|
|
@@ -272,7 +265,7 @@ circuit_init(uint8_t purpose, int need_uptime, int need_capacity, int internal)
|
|
|
return circ;
|
|
|
}
|
|
|
|
|
|
-/** Build a new circuit for <b>purpose</b>. If <b>exit</b>
|
|
|
+/** Build a new circuit for <b>purpose</b>. If <b>info/b>
|
|
|
* is defined, then use that as your exit router, else choose a suitable
|
|
|
* exit node.
|
|
|
*
|
|
@@ -280,14 +273,14 @@ circuit_init(uint8_t purpose, int need_uptime, int need_capacity, int internal)
|
|
|
* it's not open already.
|
|
|
*/
|
|
|
circuit_t *
|
|
|
-circuit_establish_circuit(uint8_t purpose, routerinfo_t *exit,
|
|
|
+circuit_establish_circuit(uint8_t purpose, extend_info_t *info,
|
|
|
int need_uptime, int need_capacity, int internal)
|
|
|
{
|
|
|
circuit_t *circ;
|
|
|
|
|
|
circ = circuit_init(purpose, need_uptime, need_capacity, internal);
|
|
|
|
|
|
- if (onion_pick_cpath_exit(circ, exit) < 0 ||
|
|
|
+ if (onion_pick_cpath_exit(circ, info) < 0 ||
|
|
|
onion_populate_cpath(circ) < 0) {
|
|
|
circuit_mark_for_close(circ);
|
|
|
return NULL;
|
|
@@ -309,26 +302,32 @@ circuit_establish_circuit(uint8_t purpose, routerinfo_t *exit,
|
|
|
int
|
|
|
circuit_handle_first_hop(circuit_t *circ)
|
|
|
{
|
|
|
- routerinfo_t *firsthop;
|
|
|
+ crypt_path_t *firsthop;
|
|
|
connection_t *n_conn;
|
|
|
+ char tmpbuf[INET_NTOA_BUF_LEN+1];
|
|
|
+ struct in_addr in;
|
|
|
|
|
|
- onion_next_router_in_cpath(circ, &firsthop);
|
|
|
+ firsthop = onion_next_hop_in_cpath(circ->cpath);
|
|
|
tor_assert(firsthop);
|
|
|
|
|
|
/* 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);
|
|
|
+ in.s_addr = htonl(firsthop->extend_info->addr);
|
|
|
+ tor_inet_ntoa(&in, tmpbuf, sizeof(tmpbuf));
|
|
|
+ log_fn(LOG_DEBUG,"Looking for firsthop '%s:%u'",tmpbuf,
|
|
|
+ firsthop->extend_info->port);
|
|
|
/* imprint the circuit with its future n_conn->id */
|
|
|
- memcpy(circ->n_conn_id_digest, firsthop->identity_digest, DIGEST_LEN);
|
|
|
- n_conn = connection_get_by_identity_digest(firsthop->identity_digest,
|
|
|
- CONN_TYPE_OR);
|
|
|
+ memcpy(circ->n_conn_id_digest, firsthop->extend_info->identity_digest,
|
|
|
+ DIGEST_LEN);
|
|
|
+ n_conn = connection_get_by_identity_digest(
|
|
|
+ firsthop->extend_info->identity_digest, CONN_TYPE_OR);
|
|
|
if (!n_conn || n_conn->state != OR_CONN_STATE_OPEN) { /* not currently connected */
|
|
|
- circ->n_addr = firsthop->addr;
|
|
|
- circ->n_port = firsthop->or_port;
|
|
|
+ circ->n_addr = firsthop->extend_info->addr;
|
|
|
+ circ->n_port = firsthop->extend_info->port;
|
|
|
|
|
|
if (!n_conn) { /* launch the connection */
|
|
|
- n_conn = connection_or_connect(firsthop->addr, firsthop->or_port,
|
|
|
- firsthop->identity_digest);
|
|
|
+ n_conn = connection_or_connect(firsthop->extend_info->addr,
|
|
|
+ firsthop->extend_info->port,
|
|
|
+ firsthop->extend_info->identity_digest);
|
|
|
if (!n_conn) { /* connect failed, forget the whole thing */
|
|
|
log_fn(LOG_INFO,"connect to firsthop failed. Closing.");
|
|
|
return -1;
|
|
@@ -452,7 +451,6 @@ circuit_send_next_onion_skin(circuit_t *circ)
|
|
|
{
|
|
|
crypt_path_t *hop;
|
|
|
routerinfo_t *router;
|
|
|
- int r;
|
|
|
char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
|
|
|
char *onionskin;
|
|
|
size_t payload_len;
|
|
@@ -465,20 +463,15 @@ circuit_send_next_onion_skin(circuit_t *circ)
|
|
|
log_fn(LOG_DEBUG,"First skin; sending create cell.");
|
|
|
|
|
|
router = router_get_by_digest(circ->n_conn->identity_digest);
|
|
|
- if (!router) {
|
|
|
- log_fn(LOG_WARN,"Couldn't find routerinfo for %s",
|
|
|
- circ->n_conn->nickname);
|
|
|
- return -1;
|
|
|
- }
|
|
|
|
|
|
if (1 || /* Disable this '1' once we believe CREATE_FAST works. XXXX */
|
|
|
- (get_options()->ORPort || !router->platform ||
|
|
|
+ (get_options()->ORPort || !router || !router->platform ||
|
|
|
!tor_version_as_new_as(router->platform, "0.1.0.6-rc"))) {
|
|
|
/* We are an OR, or we are connecting to an old Tor: we should
|
|
|
* send an old slow create cell.
|
|
|
*/
|
|
|
cell_type = CELL_CREATE;
|
|
|
- if (onion_skin_create(router->onion_pkey,
|
|
|
+ if (onion_skin_create(circ->cpath->extend_info->onion_key,
|
|
|
&(circ->cpath->dh_handshake_state),
|
|
|
payload) < 0) {
|
|
|
log_fn(LOG_WARN,"onion_skin_create (first hop) failed.");
|
|
@@ -505,8 +498,8 @@ circuit_send_next_onion_skin(circuit_t *circ)
|
|
|
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_next_router_in_cpath(circ, &router);
|
|
|
- if (r > 0) {
|
|
|
+ hop = onion_next_hop_in_cpath(circ->cpath);
|
|
|
+ if (!hop) {
|
|
|
/* done building the circuit. whew. */
|
|
|
circ->state = CIRCUIT_STATE_OPEN;
|
|
|
log_fn(LOG_INFO,"circuit built!");
|
|
@@ -524,19 +517,17 @@ circuit_send_next_onion_skin(circuit_t *circ)
|
|
|
circuit_rep_hist_note_result(circ);
|
|
|
circuit_has_opened(circ); /* do other actions as necessary */
|
|
|
return 0;
|
|
|
- } else if (r < 0) {
|
|
|
- return -1;
|
|
|
}
|
|
|
- hop = onion_next_hop_in_cpath(circ->cpath);
|
|
|
|
|
|
- *(uint32_t*)payload = htonl(hop->addr);
|
|
|
- *(uint16_t*)(payload+4) = htons(hop->port);
|
|
|
+ *(uint32_t*)payload = htonl(hop->extend_info->addr);
|
|
|
+ *(uint16_t*)(payload+4) = htons(hop->extend_info->port);
|
|
|
|
|
|
onionskin = payload+2+4;
|
|
|
- memcpy(payload+2+4+ONIONSKIN_CHALLENGE_LEN, hop->identity_digest, DIGEST_LEN);
|
|
|
+ memcpy(payload+2+4+ONIONSKIN_CHALLENGE_LEN, hop->extend_info->identity_digest, DIGEST_LEN);
|
|
|
payload_len = 2+4+ONIONSKIN_CHALLENGE_LEN+DIGEST_LEN;
|
|
|
|
|
|
- if (onion_skin_create(router->onion_pkey, &(hop->dh_handshake_state), onionskin) < 0) {
|
|
|
+ if (onion_skin_create(hop->extend_info->onion_key,
|
|
|
+ &(hop->dh_handshake_state), onionskin) < 0) {
|
|
|
log_fn(LOG_WARN,"onion_skin_create failed.");
|
|
|
return -1;
|
|
|
}
|
|
@@ -1218,10 +1209,9 @@ choose_good_exit_server(uint8_t purpose, routerlist_t *dir,
|
|
|
* router (or use <b>exit</b> if provided). Store these in the
|
|
|
* cpath. Return 0 if ok, -1 if circuit should be closed. */
|
|
|
static int
|
|
|
-onion_pick_cpath_exit(circuit_t *circ, routerinfo_t *exit)
|
|
|
+onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit)
|
|
|
{
|
|
|
cpath_build_state_t *state = circ->build_state;
|
|
|
-
|
|
|
routerlist_t *rl;
|
|
|
int r;
|
|
|
|
|
@@ -1237,16 +1227,17 @@ onion_pick_cpath_exit(circuit_t *circ, routerinfo_t *exit)
|
|
|
|
|
|
if (exit) { /* the circuit-builder pre-requested one */
|
|
|
log_fn(LOG_INFO,"Using requested exit node '%s'", exit->nickname);
|
|
|
+ exit = extend_info_dup(exit);
|
|
|
} else { /* we have to decide one */
|
|
|
- exit = choose_good_exit_server(circ->purpose, rl,
|
|
|
+ routerinfo_t *router = choose_good_exit_server(circ->purpose, rl,
|
|
|
state->need_uptime, state->need_capacity);
|
|
|
- if (!exit) {
|
|
|
+ if (!router) {
|
|
|
log_fn(LOG_WARN,"failed to choose an exit server");
|
|
|
return -1;
|
|
|
}
|
|
|
+ exit = extend_info_from_router(router);
|
|
|
}
|
|
|
- memcpy(state->chosen_exit_digest, exit->identity_digest, DIGEST_LEN);
|
|
|
- state->chosen_exit_name = tor_strdup(exit->nickname);
|
|
|
+ state->chosen_exit = exit;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1255,30 +1246,32 @@ onion_pick_cpath_exit(circuit_t *circ, routerinfo_t *exit)
|
|
|
* the caller will do this if it wants to.
|
|
|
*/
|
|
|
int
|
|
|
-circuit_append_new_exit(circuit_t *circ, routerinfo_t *exit)
|
|
|
+circuit_append_new_exit(circuit_t *circ, extend_info_t *info)
|
|
|
{
|
|
|
- tor_assert(exit);
|
|
|
+ cpath_build_state_t *state;
|
|
|
+ tor_assert(info);
|
|
|
tor_assert(circ && CIRCUIT_IS_ORIGIN(circ));
|
|
|
- tor_free(circ->build_state->chosen_exit_name);
|
|
|
- circ->build_state->chosen_exit_name = tor_strdup(exit->nickname);
|
|
|
- memcpy(circ->build_state->chosen_exit_digest, exit->identity_digest, DIGEST_LEN);
|
|
|
+
|
|
|
+ state = circ->build_state;
|
|
|
+ tor_assert(state);
|
|
|
+ if (state->chosen_exit)
|
|
|
+ extend_info_free(state->chosen_exit);
|
|
|
+ state->chosen_exit = extend_info_dup(info);
|
|
|
+
|
|
|
++circ->build_state->desired_path_len;
|
|
|
- onion_append_hop(&circ->cpath, exit);
|
|
|
+ onion_append_hop(&circ->cpath, info);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/** Take the open circ originating here, give it a new exit destination
|
|
|
- * to <b>exit</b>, and get it to send the next extend cell. If you can't
|
|
|
- * send the extend cell, mark the circuit for close and return -1, else
|
|
|
- * return 0. */
|
|
|
+/** DOCDOC */
|
|
|
int
|
|
|
-circuit_extend_to_new_exit(circuit_t *circ, routerinfo_t *exit)
|
|
|
+circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info)
|
|
|
{
|
|
|
- circuit_append_new_exit(circ, exit);
|
|
|
+ circuit_append_new_exit(circ, info);
|
|
|
circ->state = CIRCUIT_STATE_BUILDING;
|
|
|
if (circuit_send_next_onion_skin(circ)<0) {
|
|
|
log_fn(LOG_WARN, "Couldn't extend circuit to new point '%s'.",
|
|
|
- circ->build_state->chosen_exit_name);
|
|
|
+ info->nickname);
|
|
|
circuit_mark_for_close(circ);
|
|
|
return -1;
|
|
|
}
|
|
@@ -1350,7 +1343,7 @@ choose_good_middle_server(uint8_t purpose,
|
|
|
|
|
|
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
|
|
|
excluded = smartlist_create();
|
|
|
- if ((r = router_get_by_digest(state->chosen_exit_digest))) {
|
|
|
+ if ((r = build_state_get_exit_router(state))) {
|
|
|
smartlist_add(excluded, r);
|
|
|
routerlist_add_family(excluded, r);
|
|
|
}
|
|
@@ -1359,7 +1352,7 @@ choose_good_middle_server(uint8_t purpose,
|
|
|
routerlist_add_family(excluded, r);
|
|
|
}
|
|
|
for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
|
|
|
- if ((r = router_get_by_digest(cpath->identity_digest))) {
|
|
|
+ if ((r = router_get_by_digest(cpath->extend_info->identity_digest))) {
|
|
|
smartlist_add(excluded, r);
|
|
|
routerlist_add_family(excluded, r);
|
|
|
}
|
|
@@ -1379,7 +1372,7 @@ choose_good_entry_server(cpath_build_state_t *state)
|
|
|
smartlist_t *excluded = smartlist_create();
|
|
|
or_options_t *options = get_options();
|
|
|
|
|
|
- if ((r = router_get_by_digest(state->chosen_exit_digest))) {
|
|
|
+ if ((r = build_state_get_exit_router(state))) {
|
|
|
smartlist_add(excluded, r);
|
|
|
routerlist_add_family(excluded, r);
|
|
|
}
|
|
@@ -1424,27 +1417,6 @@ onion_next_hop_in_cpath(crypt_path_t *cpath)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-/** Find the router corresponding to the first non-open hop in
|
|
|
- * circ->cpath. Make sure it's state closed. Return 1 if all
|
|
|
- * hops are open (the circuit is complete), 0 if we find a router
|
|
|
- * (and set it to *router), and -1 if we fail to lookup the router.
|
|
|
- */
|
|
|
-static int
|
|
|
-onion_next_router_in_cpath(circuit_t *circ, routerinfo_t **router) {
|
|
|
- routerinfo_t *r;
|
|
|
- crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath);
|
|
|
- if (!hop) /* all hops are open */
|
|
|
- return 1;
|
|
|
- tor_assert(hop->state == CPATH_STATE_CLOSED);
|
|
|
- r = router_get_by_digest(hop->identity_digest);
|
|
|
- if (!r) {
|
|
|
- log_fn(LOG_WARN,"Circuit intended to extend to a hop whose routerinfo we've lost. Cancelling circuit.");
|
|
|
- return -1;
|
|
|
- }
|
|
|
- *router = r;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/** Choose a suitable next hop in the cpath <b>head_ptr</b>,
|
|
|
* based on <b>state</b>. Append the hop info to head_ptr.
|
|
|
*/
|
|
@@ -1454,7 +1426,7 @@ onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
|
|
|
{
|
|
|
int cur_len;
|
|
|
crypt_path_t *cpath;
|
|
|
- routerinfo_t *choice;
|
|
|
+ extend_info_t *info = NULL;
|
|
|
smartlist_t *excludednodes;
|
|
|
|
|
|
tor_assert(head_ptr);
|
|
@@ -1481,23 +1453,29 @@ onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
|
|
|
add_nickname_list_to_smartlist(excludednodes,get_options()->ExcludeNodes,0);
|
|
|
|
|
|
if (cur_len == state->desired_path_len - 1) { /* Picking last node */
|
|
|
- choice = router_get_by_digest(state->chosen_exit_digest);
|
|
|
+ info = extend_info_dup(state->chosen_exit);
|
|
|
} else if (cur_len == 0) { /* picking first node */
|
|
|
- choice = choose_good_entry_server(state);
|
|
|
+ routerinfo_t *r = choose_good_entry_server(state);
|
|
|
+ if (r)
|
|
|
+ info = extend_info_from_router(r);
|
|
|
} else {
|
|
|
- choice = choose_good_middle_server(purpose, state, *head_ptr, cur_len);
|
|
|
+ routerinfo_t *r =
|
|
|
+ choose_good_middle_server(purpose, state, *head_ptr, cur_len);
|
|
|
+ if (r)
|
|
|
+ info = extend_info_from_router(r);
|
|
|
}
|
|
|
|
|
|
smartlist_free(excludednodes);
|
|
|
- if (!choice) {
|
|
|
+ if (!info) {
|
|
|
log_fn(LOG_WARN,"Failed to find node for hop %d of our path. Discarding this circuit.", cur_len);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)",
|
|
|
- choice->nickname, cur_len+1, state->chosen_exit_name);
|
|
|
+ info->nickname, cur_len+1, build_state_get_exit_nickname(state));
|
|
|
|
|
|
- onion_append_hop(head_ptr, choice);
|
|
|
+ onion_append_hop(head_ptr, info);
|
|
|
+ extend_info_free(info);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1505,7 +1483,7 @@ onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
|
|
|
* corresponding router <b>choice</b>, and append it to the
|
|
|
* end of the cpath <b>head_ptr</b>. */
|
|
|
static int
|
|
|
-onion_append_hop(crypt_path_t **head_ptr, routerinfo_t *choice)
|
|
|
+onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
|
|
|
{
|
|
|
crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
|
|
|
|
|
@@ -1515,9 +1493,7 @@ onion_append_hop(crypt_path_t **head_ptr, routerinfo_t *choice)
|
|
|
hop->magic = CRYPT_PATH_MAGIC;
|
|
|
hop->state = CPATH_STATE_CLOSED;
|
|
|
|
|
|
- hop->port = choice->or_port;
|
|
|
- hop->addr = choice->addr;
|
|
|
- memcpy(hop->identity_digest, choice->identity_digest, DIGEST_LEN);
|
|
|
+ hop->extend_info = extend_info_dup(choice);
|
|
|
|
|
|
hop->package_window = CIRCWINDOW_START;
|
|
|
hop->deliver_window = CIRCWINDOW_START;
|
|
@@ -1525,3 +1501,67 @@ onion_append_hop(crypt_path_t **head_ptr, routerinfo_t *choice)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/** Allocate and return a new extend_info_t that can be used to build a
|
|
|
+ * circuit to or through the router <b>r</b>. */
|
|
|
+extend_info_t *
|
|
|
+extend_info_from_router(routerinfo_t *r)
|
|
|
+{
|
|
|
+ extend_info_t *info;
|
|
|
+ tor_assert(r);
|
|
|
+ info = tor_malloc_zero(sizeof(extend_info_t));
|
|
|
+ strlcpy(info->nickname, r->nickname, sizeof(info->nickname));
|
|
|
+ memcpy(info->identity_digest, r->identity_digest, DIGEST_LEN);
|
|
|
+ info->onion_key = crypto_pk_dup_key(r->onion_pkey);
|
|
|
+ info->addr = r->addr;
|
|
|
+ info->port = r->or_port;
|
|
|
+ return info;
|
|
|
+}
|
|
|
+
|
|
|
+/** Release storage held by an extend_info_t struct. */
|
|
|
+void
|
|
|
+extend_info_free(extend_info_t *info)
|
|
|
+{
|
|
|
+ tor_assert(info);
|
|
|
+ crypto_free_pk_env(info->onion_key);
|
|
|
+ tor_free(info);
|
|
|
+}
|
|
|
+
|
|
|
+/** Allocate and return a new extend_info_t with the same contents as
|
|
|
+ * <b>info</b>. */
|
|
|
+extend_info_t *
|
|
|
+extend_info_dup(extend_info_t *info)
|
|
|
+{
|
|
|
+ extend_info_t *newinfo;
|
|
|
+ tor_assert(info);
|
|
|
+ newinfo = tor_malloc(sizeof(extend_info_t));
|
|
|
+ memcpy(newinfo, info, sizeof(extend_info_t));
|
|
|
+ newinfo->onion_key = crypto_pk_dup_key(info->onion_key);
|
|
|
+ return newinfo;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Return the routerinfo_t for the chosen exit router in <b>state</b>. If
|
|
|
+ * there is no chosen exit, or if we don't know the routerinfo_t for the
|
|
|
+ * chosen exit, return NULL.
|
|
|
+ */
|
|
|
+routerinfo_t *
|
|
|
+build_state_get_exit_router(cpath_build_state_t *state)
|
|
|
+{
|
|
|
+ if (!state || !state->chosen_exit)
|
|
|
+ return NULL;
|
|
|
+ return router_get_by_digest(state->chosen_exit->identity_digest);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Return the nickname for the chosen exit router in <b>state</b>. If
|
|
|
+ * there is no chosen exit, or if we don't know the routerinfo_t for the
|
|
|
+ * chosen exit, return NULL.
|
|
|
+ */
|
|
|
+const char *
|
|
|
+build_state_get_exit_nickname(cpath_build_state_t *state)
|
|
|
+{
|
|
|
+ if (!state || !state->chosen_exit)
|
|
|
+ return NULL;
|
|
|
+ return state->chosen_exit->nickname;
|
|
|
+}
|
|
|
+
|