mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 23:53:32 +01:00
Logic to implement rendezvous/introduction via unknown servers.
- Add a new extend_info_t datatype to hold information needed to extend a circuit (addr,port,keyid,onion_key). Use it in cpath and build_state. Make appropriate functions take or return it instead of routerinfo_t or keyid. - #if 0 needless check in circuit_get_by_edge_conn; if nobody triggers this error in 0.1.0.10, nobody will trigger it. - Implement new hidden service descriptor format, which contains "extend info" for introduction points, along with protocol version list. - Parse new format. - Generate new format - Cache old and new formats alongside each other. - Directories serve "old" format if asked in old way, "newest available" format if asked in new way. - Use new format to find introduction points if possible; otherwise fall back. Keep nickname lists and extendinfo lists in sync. - Tests for new format. - Implement new "v2" INTRODUCE cell format. - Accept new format - Use new format if we have a versioned service descriptor that says the server accepts the new format. - Add documentation for functions and data types. svn:r4506
This commit is contained in:
parent
0505b31933
commit
ec83652357
@ -21,13 +21,12 @@ extern circuit_t *global_circuitlist;
|
|||||||
|
|
||||||
static int circuit_deliver_create_cell(circuit_t *circ,
|
static int circuit_deliver_create_cell(circuit_t *circ,
|
||||||
uint8_t cell_type, char *payload);
|
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 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,
|
static int onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
|
||||||
cpath_build_state_t *state);
|
cpath_build_state_t *state);
|
||||||
static int count_acceptable_routers(smartlist_t *routers);
|
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,
|
/** Iterate over values of circ_id, starting from conn-\>next_circ_id,
|
||||||
* and with the high bit specified by circ_id_type (see
|
* 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();
|
elements = smartlist_create();
|
||||||
|
|
||||||
if (verbose) {
|
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):",
|
tor_snprintf(buf, sizeof(buf)-1, "%s%s circ (length %d, exit %s):",
|
||||||
circ->build_state->is_internal ? "internal" : "exit",
|
circ->build_state->is_internal ? "internal" : "exit",
|
||||||
circ->build_state->need_uptime ? " (high-uptime)" : "",
|
circ->build_state->need_uptime ? " (high-uptime)" : "",
|
||||||
circ->build_state->desired_path_len,
|
circ->build_state->desired_path_len,
|
||||||
circ->build_state->chosen_exit_name);
|
nickname?nickname:"unnamed");
|
||||||
smartlist_add(elements, tor_strdup(buf));
|
smartlist_add(elements, tor_strdup(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
hop = circ->cpath;
|
hop = circ->cpath;
|
||||||
do {
|
do {
|
||||||
const char *elt;
|
const char *elt;
|
||||||
routerinfo_t *r;
|
|
||||||
if (!hop)
|
if (!hop)
|
||||||
break;
|
break;
|
||||||
if (!verbose && hop->state != CPATH_STATE_OPEN)
|
if (!verbose && hop->state != CPATH_STATE_OPEN)
|
||||||
break;
|
break;
|
||||||
if ((r = router_get_by_digest(hop->identity_digest))) {
|
if (!hop->extend_info)
|
||||||
elt = r->nickname;
|
break;
|
||||||
} else if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
|
elt = hop->extend_info->nickname;
|
||||||
elt = "<rendezvous splice>";
|
|
||||||
} else {
|
|
||||||
buf[0]='$';
|
|
||||||
base16_encode(buf+1,sizeof(buf)-1,hop->identity_digest,DIGEST_LEN);
|
|
||||||
elt = buf;
|
|
||||||
}
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
size_t len = strlen(elt)+2+strlen(states[hop->state])+1;
|
size_t len = strlen(elt)+2+strlen(states[hop->state])+1;
|
||||||
char *v = tor_malloc(len);
|
char *v = tor_malloc(len);
|
||||||
@ -166,7 +159,7 @@ circuit_rep_hist_note_result(circuit_t *circ)
|
|||||||
prev_digest = me->identity_digest;
|
prev_digest = me->identity_digest;
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
router = router_get_by_digest(hop->identity_digest);
|
router = router_get_by_digest(hop->extend_info->identity_digest);
|
||||||
if (router) {
|
if (router) {
|
||||||
if (prev_digest) {
|
if (prev_digest) {
|
||||||
if (hop->state == CPATH_STATE_OPEN)
|
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;
|
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
|
* is defined, then use that as your exit router, else choose a suitable
|
||||||
* exit node.
|
* exit node.
|
||||||
*
|
*
|
||||||
@ -280,14 +273,14 @@ circuit_init(uint8_t purpose, int need_uptime, int need_capacity, int internal)
|
|||||||
* it's not open already.
|
* it's not open already.
|
||||||
*/
|
*/
|
||||||
circuit_t *
|
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)
|
int need_uptime, int need_capacity, int internal)
|
||||||
{
|
{
|
||||||
circuit_t *circ;
|
circuit_t *circ;
|
||||||
|
|
||||||
circ = circuit_init(purpose, need_uptime, need_capacity, internal);
|
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) {
|
onion_populate_cpath(circ) < 0) {
|
||||||
circuit_mark_for_close(circ);
|
circuit_mark_for_close(circ);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -309,26 +302,32 @@ circuit_establish_circuit(uint8_t purpose, routerinfo_t *exit,
|
|||||||
int
|
int
|
||||||
circuit_handle_first_hop(circuit_t *circ)
|
circuit_handle_first_hop(circuit_t *circ)
|
||||||
{
|
{
|
||||||
routerinfo_t *firsthop;
|
crypt_path_t *firsthop;
|
||||||
connection_t *n_conn;
|
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);
|
tor_assert(firsthop);
|
||||||
|
|
||||||
/* now see if we're already connected to the first OR in 'route' */
|
/* now see if we're already connected to the first OR in 'route' */
|
||||||
log_fn(LOG_DEBUG,"Looking for firsthop '%s:%u'",
|
in.s_addr = htonl(firsthop->extend_info->addr);
|
||||||
firsthop->address,firsthop->or_port);
|
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 */
|
/* imprint the circuit with its future n_conn->id */
|
||||||
memcpy(circ->n_conn_id_digest, firsthop->identity_digest, DIGEST_LEN);
|
memcpy(circ->n_conn_id_digest, firsthop->extend_info->identity_digest,
|
||||||
n_conn = connection_get_by_identity_digest(firsthop->identity_digest,
|
DIGEST_LEN);
|
||||||
CONN_TYPE_OR);
|
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 */
|
if (!n_conn || n_conn->state != OR_CONN_STATE_OPEN) { /* not currently connected */
|
||||||
circ->n_addr = firsthop->addr;
|
circ->n_addr = firsthop->extend_info->addr;
|
||||||
circ->n_port = firsthop->or_port;
|
circ->n_port = firsthop->extend_info->port;
|
||||||
|
|
||||||
if (!n_conn) { /* launch the connection */
|
if (!n_conn) { /* launch the connection */
|
||||||
n_conn = connection_or_connect(firsthop->addr, firsthop->or_port,
|
n_conn = connection_or_connect(firsthop->extend_info->addr,
|
||||||
firsthop->identity_digest);
|
firsthop->extend_info->port,
|
||||||
|
firsthop->extend_info->identity_digest);
|
||||||
if (!n_conn) { /* connect failed, forget the whole thing */
|
if (!n_conn) { /* connect failed, forget the whole thing */
|
||||||
log_fn(LOG_INFO,"connect to firsthop failed. Closing.");
|
log_fn(LOG_INFO,"connect to firsthop failed. Closing.");
|
||||||
return -1;
|
return -1;
|
||||||
@ -452,7 +451,6 @@ circuit_send_next_onion_skin(circuit_t *circ)
|
|||||||
{
|
{
|
||||||
crypt_path_t *hop;
|
crypt_path_t *hop;
|
||||||
routerinfo_t *router;
|
routerinfo_t *router;
|
||||||
int r;
|
|
||||||
char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
|
char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
|
||||||
char *onionskin;
|
char *onionskin;
|
||||||
size_t payload_len;
|
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.");
|
log_fn(LOG_DEBUG,"First skin; sending create cell.");
|
||||||
|
|
||||||
router = router_get_by_digest(circ->n_conn->identity_digest);
|
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 */
|
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"))) {
|
!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
|
/* We are an OR, or we are connecting to an old Tor: we should
|
||||||
* send an old slow create cell.
|
* send an old slow create cell.
|
||||||
*/
|
*/
|
||||||
cell_type = CELL_CREATE;
|
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),
|
&(circ->cpath->dh_handshake_state),
|
||||||
payload) < 0) {
|
payload) < 0) {
|
||||||
log_fn(LOG_WARN,"onion_skin_create (first hop) failed.");
|
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->cpath->state == CPATH_STATE_OPEN);
|
||||||
tor_assert(circ->state == CIRCUIT_STATE_BUILDING);
|
tor_assert(circ->state == CIRCUIT_STATE_BUILDING);
|
||||||
log_fn(LOG_DEBUG,"starting to send subsequent skin.");
|
log_fn(LOG_DEBUG,"starting to send subsequent skin.");
|
||||||
r = onion_next_router_in_cpath(circ, &router);
|
hop = onion_next_hop_in_cpath(circ->cpath);
|
||||||
if (r > 0) {
|
if (!hop) {
|
||||||
/* done building the circuit. whew. */
|
/* done building the circuit. whew. */
|
||||||
circ->state = CIRCUIT_STATE_OPEN;
|
circ->state = CIRCUIT_STATE_OPEN;
|
||||||
log_fn(LOG_INFO,"circuit built!");
|
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_rep_hist_note_result(circ);
|
||||||
circuit_has_opened(circ); /* do other actions as necessary */
|
circuit_has_opened(circ); /* do other actions as necessary */
|
||||||
return 0;
|
return 0;
|
||||||
} else if (r < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
hop = onion_next_hop_in_cpath(circ->cpath);
|
|
||||||
|
|
||||||
*(uint32_t*)payload = htonl(hop->addr);
|
*(uint32_t*)payload = htonl(hop->extend_info->addr);
|
||||||
*(uint16_t*)(payload+4) = htons(hop->port);
|
*(uint16_t*)(payload+4) = htons(hop->extend_info->port);
|
||||||
|
|
||||||
onionskin = payload+2+4;
|
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;
|
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.");
|
log_fn(LOG_WARN,"onion_skin_create failed.");
|
||||||
return -1;
|
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
|
* router (or use <b>exit</b> if provided). Store these in the
|
||||||
* cpath. Return 0 if ok, -1 if circuit should be closed. */
|
* cpath. Return 0 if ok, -1 if circuit should be closed. */
|
||||||
static int
|
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;
|
cpath_build_state_t *state = circ->build_state;
|
||||||
|
|
||||||
routerlist_t *rl;
|
routerlist_t *rl;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -1237,16 +1227,17 @@ onion_pick_cpath_exit(circuit_t *circ, routerinfo_t *exit)
|
|||||||
|
|
||||||
if (exit) { /* the circuit-builder pre-requested one */
|
if (exit) { /* the circuit-builder pre-requested one */
|
||||||
log_fn(LOG_INFO,"Using requested exit node '%s'", exit->nickname);
|
log_fn(LOG_INFO,"Using requested exit node '%s'", exit->nickname);
|
||||||
|
exit = extend_info_dup(exit);
|
||||||
} else { /* we have to decide one */
|
} 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);
|
state->need_uptime, state->need_capacity);
|
||||||
if (!exit) {
|
if (!router) {
|
||||||
log_fn(LOG_WARN,"failed to choose an exit server");
|
log_fn(LOG_WARN,"failed to choose an exit server");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
exit = extend_info_from_router(router);
|
||||||
}
|
}
|
||||||
memcpy(state->chosen_exit_digest, exit->identity_digest, DIGEST_LEN);
|
state->chosen_exit = exit;
|
||||||
state->chosen_exit_name = tor_strdup(exit->nickname);
|
|
||||||
return 0;
|
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.
|
* the caller will do this if it wants to.
|
||||||
*/
|
*/
|
||||||
int
|
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_assert(circ && CIRCUIT_IS_ORIGIN(circ));
|
||||||
tor_free(circ->build_state->chosen_exit_name);
|
|
||||||
circ->build_state->chosen_exit_name = tor_strdup(exit->nickname);
|
state = circ->build_state;
|
||||||
memcpy(circ->build_state->chosen_exit_digest, exit->identity_digest, DIGEST_LEN);
|
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;
|
++circ->build_state->desired_path_len;
|
||||||
onion_append_hop(&circ->cpath, exit);
|
onion_append_hop(&circ->cpath, info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Take the open circ originating here, give it a new exit destination
|
/** DOCDOC */
|
||||||
* 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. */
|
|
||||||
int
|
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;
|
circ->state = CIRCUIT_STATE_BUILDING;
|
||||||
if (circuit_send_next_onion_skin(circ)<0) {
|
if (circuit_send_next_onion_skin(circ)<0) {
|
||||||
log_fn(LOG_WARN, "Couldn't extend circuit to new point '%s'.",
|
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);
|
circuit_mark_for_close(circ);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -1350,7 +1343,7 @@ choose_good_middle_server(uint8_t purpose,
|
|||||||
|
|
||||||
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
|
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
|
||||||
excluded = smartlist_create();
|
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);
|
smartlist_add(excluded, r);
|
||||||
routerlist_add_family(excluded, r);
|
routerlist_add_family(excluded, r);
|
||||||
}
|
}
|
||||||
@ -1359,7 +1352,7 @@ choose_good_middle_server(uint8_t purpose,
|
|||||||
routerlist_add_family(excluded, r);
|
routerlist_add_family(excluded, r);
|
||||||
}
|
}
|
||||||
for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
|
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);
|
smartlist_add(excluded, r);
|
||||||
routerlist_add_family(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();
|
smartlist_t *excluded = smartlist_create();
|
||||||
or_options_t *options = get_options();
|
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);
|
smartlist_add(excluded, r);
|
||||||
routerlist_add_family(excluded, r);
|
routerlist_add_family(excluded, r);
|
||||||
}
|
}
|
||||||
@ -1424,27 +1417,6 @@ onion_next_hop_in_cpath(crypt_path_t *cpath)
|
|||||||
return NULL;
|
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>,
|
/** Choose a suitable next hop in the cpath <b>head_ptr</b>,
|
||||||
* based on <b>state</b>. Append the hop info to head_ptr.
|
* 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;
|
int cur_len;
|
||||||
crypt_path_t *cpath;
|
crypt_path_t *cpath;
|
||||||
routerinfo_t *choice;
|
extend_info_t *info = NULL;
|
||||||
smartlist_t *excludednodes;
|
smartlist_t *excludednodes;
|
||||||
|
|
||||||
tor_assert(head_ptr);
|
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);
|
add_nickname_list_to_smartlist(excludednodes,get_options()->ExcludeNodes,0);
|
||||||
|
|
||||||
if (cur_len == state->desired_path_len - 1) { /* Picking last node */
|
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 */
|
} 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 {
|
} 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);
|
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);
|
log_fn(LOG_WARN,"Failed to find node for hop %d of our path. Discarding this circuit.", cur_len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)",
|
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;
|
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
|
* corresponding router <b>choice</b>, and append it to the
|
||||||
* end of the cpath <b>head_ptr</b>. */
|
* end of the cpath <b>head_ptr</b>. */
|
||||||
static int
|
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));
|
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->magic = CRYPT_PATH_MAGIC;
|
||||||
hop->state = CPATH_STATE_CLOSED;
|
hop->state = CPATH_STATE_CLOSED;
|
||||||
|
|
||||||
hop->port = choice->or_port;
|
hop->extend_info = extend_info_dup(choice);
|
||||||
hop->addr = choice->addr;
|
|
||||||
memcpy(hop->identity_digest, choice->identity_digest, DIGEST_LEN);
|
|
||||||
|
|
||||||
hop->package_window = CIRCWINDOW_START;
|
hop->package_window = CIRCWINDOW_START;
|
||||||
hop->deliver_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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ static void circuit_free_cpath_node(crypt_path_t *victim);
|
|||||||
|
|
||||||
/********* END VARIABLES ************/
|
/********* END VARIABLES ************/
|
||||||
|
|
||||||
/** DOCDOC This whole section */
|
/** A map from OR connection and circuit ID to circuit. (Lookup performance is
|
||||||
|
* very important here, since we need to do it every time a cell arrives.) */
|
||||||
struct orconn_circid_circuit_map_t {
|
struct orconn_circid_circuit_map_t {
|
||||||
RB_ENTRY(orconn_circid_circuit_map_t) node;
|
RB_ENTRY(orconn_circid_circuit_map_t) node;
|
||||||
connection_t *or_conn;
|
connection_t *or_conn;
|
||||||
@ -32,7 +33,9 @@ struct orconn_circid_circuit_map_t {
|
|||||||
circuit_t *circuit;
|
circuit_t *circuit;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** DOCDOC */
|
/** helper for RB tree: compare the OR connection and circuit ID for a and b,
|
||||||
|
* and return less than, equal to, or greater than zero appropriately.
|
||||||
|
*/
|
||||||
static INLINE int
|
static INLINE int
|
||||||
compare_orconn_circid_entries(struct orconn_circid_circuit_map_t *a,
|
compare_orconn_circid_entries(struct orconn_circid_circuit_map_t *a,
|
||||||
struct orconn_circid_circuit_map_t *b)
|
struct orconn_circid_circuit_map_t *b)
|
||||||
@ -49,9 +52,15 @@ static RB_HEAD(orconn_circid_tree, orconn_circid_circuit_map_t) orconn_circid_ci
|
|||||||
RB_PROTOTYPE(orconn_circid_tree, orconn_circid_circuit_map_t, node, compare_orconn_circid_entries);
|
RB_PROTOTYPE(orconn_circid_tree, orconn_circid_circuit_map_t, node, compare_orconn_circid_entries);
|
||||||
RB_GENERATE(orconn_circid_tree, orconn_circid_circuit_map_t, node, compare_orconn_circid_entries);
|
RB_GENERATE(orconn_circid_tree, orconn_circid_circuit_map_t, node, compare_orconn_circid_entries);
|
||||||
|
|
||||||
|
/** The most recently returned entyr from circuit_get_by_circid_orconn; used
|
||||||
|
* to improve performance when many cells arrive in a row from the same circuit.
|
||||||
|
*/
|
||||||
|
/* (We tried using splay trees, but round-robin turned out to make them suck.) */
|
||||||
struct orconn_circid_circuit_map_t *_last_circid_orconn_ent = NULL;
|
struct orconn_circid_circuit_map_t *_last_circid_orconn_ent = NULL;
|
||||||
|
|
||||||
/** DOCDOC */
|
/** Set the p_conn or n_conn field of a circuit <b>circ</b>, along with the
|
||||||
|
* corresponding circuit ID, and add the circuit as appropriate to the
|
||||||
|
* (orconn,id)-\>circuit map. */
|
||||||
void
|
void
|
||||||
circuit_set_circid_orconn(circuit_t *circ, uint16_t id,
|
circuit_set_circid_orconn(circuit_t *circ, uint16_t id,
|
||||||
connection_t *conn,
|
connection_t *conn,
|
||||||
@ -154,7 +163,7 @@ circuit_close_all_marked(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** DOCDOC **/
|
/** Return the head of the global linked list of circuits. **/
|
||||||
circuit_t *
|
circuit_t *
|
||||||
_circuit_get_global_list(void)
|
_circuit_get_global_list(void)
|
||||||
{
|
{
|
||||||
@ -227,7 +236,8 @@ circuit_free(circuit_t *circ)
|
|||||||
if (circ->p_digest)
|
if (circ->p_digest)
|
||||||
crypto_free_digest_env(circ->p_digest);
|
crypto_free_digest_env(circ->p_digest);
|
||||||
if (circ->build_state) {
|
if (circ->build_state) {
|
||||||
tor_free(circ->build_state->chosen_exit_name);
|
if (circ->build_state->chosen_exit)
|
||||||
|
extend_info_free(circ->build_state->chosen_exit);
|
||||||
if (circ->build_state->pending_final_cpath)
|
if (circ->build_state->pending_final_cpath)
|
||||||
circuit_free_cpath_node(circ->build_state->pending_final_cpath);
|
circuit_free_cpath_node(circ->build_state->pending_final_cpath);
|
||||||
}
|
}
|
||||||
@ -296,6 +306,9 @@ circuit_free_cpath_node(crypt_path_t *victim)
|
|||||||
crypto_free_digest_env(victim->b_digest);
|
crypto_free_digest_env(victim->b_digest);
|
||||||
if (victim->dh_handshake_state)
|
if (victim->dh_handshake_state)
|
||||||
crypto_dh_free(victim->dh_handshake_state);
|
crypto_dh_free(victim->dh_handshake_state);
|
||||||
|
if (victim->extend_info)
|
||||||
|
extend_info_free(victim->extend_info);
|
||||||
|
|
||||||
victim->magic = 0xDEADBEEFu;
|
victim->magic = 0xDEADBEEFu;
|
||||||
tor_free(victim);
|
tor_free(victim);
|
||||||
}
|
}
|
||||||
@ -367,7 +380,7 @@ circuit_get_by_circid_orconn(uint16_t circ_id, connection_t *conn)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** DOCDOC */
|
/** Return the circuit that a given edge connection is using. */
|
||||||
circuit_t *
|
circuit_t *
|
||||||
circuit_get_by_edge_conn(connection_t *conn)
|
circuit_get_by_edge_conn(connection_t *conn)
|
||||||
{
|
{
|
||||||
@ -385,8 +398,9 @@ circuit_get_by_edge_conn(connection_t *conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
circ = conn->on_circuit;
|
circ = conn->on_circuit;
|
||||||
/* All this stuff here is sanity-checking. */
|
|
||||||
tor_assert(circ->magic == CIRCUIT_MAGIC);
|
tor_assert(circ->magic == CIRCUIT_MAGIC);
|
||||||
|
#if 0
|
||||||
|
/* All this stuff here is sanity-checking. */
|
||||||
for (tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream)
|
for (tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream)
|
||||||
if (tmpconn == conn)
|
if (tmpconn == conn)
|
||||||
return circ;
|
return circ;
|
||||||
@ -398,6 +412,8 @@ circuit_get_by_edge_conn(connection_t *conn)
|
|||||||
return circ;
|
return circ;
|
||||||
|
|
||||||
tor_assert(0);
|
tor_assert(0);
|
||||||
|
#endif
|
||||||
|
return circ;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return a circ such that circ is attached to <b>conn</b>, either as
|
/** Return a circ such that circ is attached to <b>conn</b>, either as
|
||||||
@ -592,10 +608,14 @@ _circuit_mark_for_close(circuit_t *circ, int line, const char *file)
|
|||||||
}
|
}
|
||||||
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
||||||
tor_assert(circ->state == CIRCUIT_STATE_OPEN);
|
tor_assert(circ->state == CIRCUIT_STATE_OPEN);
|
||||||
|
tor_assert(circ->build_state->chosen_exit);
|
||||||
/* treat this like getting a nack from it */
|
/* treat this like getting a nack from it */
|
||||||
log_fn(LOG_INFO,"Failed intro circ %s to %s (awaiting ack). Removing from descriptor.",
|
log_fn(LOG_INFO,"Failed intro circ %s to %s (awaiting ack). Removing from descriptor.",
|
||||||
safe_str(circ->rend_query), safe_str(circ->build_state->chosen_exit_name));
|
safe_str(circ->rend_query),
|
||||||
rend_client_remove_intro_point(circ->build_state->chosen_exit_name, circ->rend_query);
|
safe_str(build_state_get_exit_nickname(circ->build_state)));
|
||||||
|
// XXXX NM
|
||||||
|
rend_client_remove_intro_point(circ->build_state->chosen_exit,
|
||||||
|
circ->rend_query);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (circ->n_conn)
|
if (circ->n_conn)
|
||||||
|
@ -74,12 +74,7 @@ circuit_is_acceptable(circuit_t *circ,
|
|||||||
* circuit, it's the magical extra bob hop. so just check the nickname
|
* circuit, it's the magical extra bob hop. so just check the nickname
|
||||||
* of the one we meant to finish at.
|
* of the one we meant to finish at.
|
||||||
*/
|
*/
|
||||||
exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
|
exitrouter = build_state_get_exit_router(circ->build_state);
|
||||||
|
|
||||||
if (!exitrouter) {
|
|
||||||
log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
|
|
||||||
return 0; /* this circuit is screwed and doesn't know it yet */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!circ->build_state->need_uptime &&
|
if (!circ->build_state->need_uptime &&
|
||||||
smartlist_string_num_isin(get_options()->LongLivedPorts,
|
smartlist_string_num_isin(get_options()->LongLivedPorts,
|
||||||
@ -87,6 +82,11 @@ circuit_is_acceptable(circuit_t *circ,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
|
if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
|
||||||
|
if (!exitrouter) {
|
||||||
|
log_fn(LOG_DEBUG,"Not considering circuit with unknown router.");
|
||||||
|
return 0; /* this circuit is screwed and doesn't know it yet,
|
||||||
|
* or is a rendezvous circuit. */
|
||||||
|
}
|
||||||
if (!connection_ap_can_use_exit(conn, exitrouter)) {
|
if (!connection_ap_can_use_exit(conn, exitrouter)) {
|
||||||
/* can't exit from this router */
|
/* can't exit from this router */
|
||||||
return 0;
|
return 0;
|
||||||
@ -296,7 +296,7 @@ circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min)
|
|||||||
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
|
circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
|
||||||
(!circ->timestamp_dirty ||
|
(!circ->timestamp_dirty ||
|
||||||
circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now)) {
|
circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now)) {
|
||||||
exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
|
exitrouter = build_state_get_exit_router(circ->build_state);
|
||||||
if (exitrouter &&
|
if (exitrouter &&
|
||||||
(!need_uptime || circ->build_state->need_uptime)) {
|
(!need_uptime || circ->build_state->need_uptime)) {
|
||||||
int ok;
|
int ok;
|
||||||
@ -682,7 +682,7 @@ circuit_build_failed(circuit_t *circ)
|
|||||||
/* Don't increment failure count, since Alice may have picked
|
/* Don't increment failure count, since Alice may have picked
|
||||||
* the rendezvous point maliciously */
|
* the rendezvous point maliciously */
|
||||||
log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s (%s hop failed).",
|
log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s (%s hop failed).",
|
||||||
circ->build_state->chosen_exit_name,
|
build_state_get_exit_nickname(circ->build_state),
|
||||||
failed_at_last_hop?"last":"non-last");
|
failed_at_last_hop?"last":"non-last");
|
||||||
rend_service_relaunch_rendezvous(circ);
|
rend_service_relaunch_rendezvous(circ);
|
||||||
break;
|
break;
|
||||||
@ -703,12 +703,34 @@ static int did_circs_fail_last_period = 0;
|
|||||||
* success. */
|
* success. */
|
||||||
#define MAX_CIRCUIT_FAILURES 5
|
#define MAX_CIRCUIT_FAILURES 5
|
||||||
|
|
||||||
/** Launch a new circuit based on our arguments. */
|
/** Launch a new circuit; see circuit_launch_by_extend_info for details on
|
||||||
|
* arguments. */
|
||||||
circuit_t *
|
circuit_t *
|
||||||
circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
|
circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
|
||||||
int need_uptime, int need_capacity, int internal)
|
int need_uptime, int need_capacity, int internal)
|
||||||
{
|
{
|
||||||
circuit_t *circ;
|
circuit_t *circ;
|
||||||
|
extend_info_t *info = NULL;
|
||||||
|
if (exit)
|
||||||
|
info = extend_info_from_router(exit);
|
||||||
|
circ = circuit_launch_by_extend_info(purpose, info, need_uptime, need_capacity,
|
||||||
|
internal);
|
||||||
|
if (info)
|
||||||
|
extend_info_free(info);
|
||||||
|
return circ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Launch a new circuit with purpose <b>purpose</b> and exit node <b>info</b>
|
||||||
|
* (or NULL to select a random exit node). If <b>need_uptime</b> is true,
|
||||||
|
* choose among routers with high uptime. If <b>need_capacity</b> is true,
|
||||||
|
* choose among routers with high bandwidth. If <b>internal</b> is true, the
|
||||||
|
* last hop need not be an exit node. Return the newly allocated circuit on
|
||||||
|
* success, or NULL on failure. */
|
||||||
|
circuit_t *
|
||||||
|
circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *info,
|
||||||
|
int need_uptime, int need_capacity, int internal)
|
||||||
|
{
|
||||||
|
circuit_t *circ;
|
||||||
|
|
||||||
if (!has_fetched_directory) {
|
if (!has_fetched_directory) {
|
||||||
log_fn(LOG_DEBUG,"Haven't fetched directory yet; canceling circuit launch.");
|
log_fn(LOG_DEBUG,"Haven't fetched directory yet; canceling circuit launch.");
|
||||||
@ -721,7 +743,7 @@ circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
|
|||||||
if ((circ = circuit_get_clean_open(CIRCUIT_PURPOSE_C_GENERAL, need_uptime,
|
if ((circ = circuit_get_clean_open(CIRCUIT_PURPOSE_C_GENERAL, need_uptime,
|
||||||
need_capacity, internal))) {
|
need_capacity, internal))) {
|
||||||
log_fn(LOG_INFO,"Cannibalizing circ '%s' for purpose %d",
|
log_fn(LOG_INFO,"Cannibalizing circ '%s' for purpose %d",
|
||||||
circ->build_state->chosen_exit_name, purpose);
|
build_state_get_exit_nickname(circ->build_state), purpose);
|
||||||
circ->purpose = purpose;
|
circ->purpose = purpose;
|
||||||
/* reset the birth date of this circ, else expire_building
|
/* reset the birth date of this circ, else expire_building
|
||||||
* will see it and think it's been trying to build since it
|
* will see it and think it's been trying to build since it
|
||||||
@ -740,8 +762,8 @@ circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
|
|||||||
case CIRCUIT_PURPOSE_C_INTRODUCING:
|
case CIRCUIT_PURPOSE_C_INTRODUCING:
|
||||||
case CIRCUIT_PURPOSE_S_CONNECT_REND:
|
case CIRCUIT_PURPOSE_S_CONNECT_REND:
|
||||||
/* need to add a new hop */
|
/* need to add a new hop */
|
||||||
tor_assert(exit);
|
tor_assert(info);
|
||||||
if (circuit_extend_to_new_exit(circ, exit) < 0)
|
if (circuit_extend_to_new_exit(circ, info) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -762,11 +784,12 @@ circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */
|
/* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */
|
||||||
return circuit_establish_circuit(purpose, exit,
|
return circuit_establish_circuit(purpose, info,
|
||||||
need_uptime, need_capacity, internal);
|
need_uptime, need_capacity, internal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Launch a new circuit and return a pointer to it. Return NULL if you failed. */
|
/** Launch a new circuit; see circuit_launch_by_extend_info for details on
|
||||||
|
* arguments. */
|
||||||
circuit_t *
|
circuit_t *
|
||||||
circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
|
circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
|
||||||
int need_uptime, int need_capacity, int internal)
|
int need_uptime, int need_capacity, int internal)
|
||||||
@ -867,30 +890,22 @@ circuit_get_open_circ_or_launch(connection_t *conn,
|
|||||||
/* is one already on the way? */
|
/* is one already on the way? */
|
||||||
circ = circuit_get_best(conn, 0, desired_circuit_purpose);
|
circ = circuit_get_best(conn, 0, desired_circuit_purpose);
|
||||||
if (!circ) {
|
if (!circ) {
|
||||||
char *exitname=NULL;
|
extend_info_t *extend_info=NULL;
|
||||||
uint8_t new_circ_purpose;
|
uint8_t new_circ_purpose;
|
||||||
int is_internal;
|
int is_internal;
|
||||||
|
|
||||||
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
||||||
/* need to pick an intro point */
|
/* need to pick an intro point */
|
||||||
try_an_intro_point:
|
extend_info = rend_client_get_random_intro(conn->rend_query);
|
||||||
exitname = rend_client_get_random_intro(conn->rend_query);
|
if (!extend_info) {
|
||||||
if (!exitname) {
|
|
||||||
log_fn(LOG_INFO,"No intro points for '%s': refetching service descriptor.",
|
log_fn(LOG_INFO,"No intro points for '%s': refetching service descriptor.",
|
||||||
safe_str(conn->rend_query));
|
safe_str(conn->rend_query));
|
||||||
rend_client_refetch_renddesc(conn->rend_query);
|
rend_client_refetch_renddesc(conn->rend_query);
|
||||||
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
|
conn->state = AP_CONN_STATE_RENDDESC_WAIT;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!router_get_by_nickname(exitname)) {
|
|
||||||
log_fn(LOG_NOTICE,"Advertised intro point '%s' is not recognized for hidserv address '%s'. Skipping over.",
|
|
||||||
exitname, safe_str(conn->rend_query));
|
|
||||||
rend_client_remove_intro_point(exitname, conn->rend_query);
|
|
||||||
tor_free(exitname);
|
|
||||||
goto try_an_intro_point;
|
|
||||||
}
|
|
||||||
log_fn(LOG_INFO,"Chose %s as intro point for %s.",
|
log_fn(LOG_INFO,"Chose %s as intro point for %s.",
|
||||||
exitname, safe_str(conn->rend_query));
|
extend_info->nickname, safe_str(conn->rend_query));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we have specified a particular exit node for our
|
/* If we have specified a particular exit node for our
|
||||||
@ -898,13 +913,13 @@ try_an_intro_point:
|
|||||||
*/
|
*/
|
||||||
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
|
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
|
||||||
if (conn->chosen_exit_name) {
|
if (conn->chosen_exit_name) {
|
||||||
exitname = tor_strdup(conn->chosen_exit_name);
|
routerinfo_t *r;
|
||||||
if (!router_get_by_nickname(exitname)) {
|
if (!(r = router_get_by_nickname(conn->chosen_exit_name))) {
|
||||||
log_fn(LOG_NOTICE,"Requested exit point '%s' is not known. Closing.",
|
log_fn(LOG_NOTICE,"Requested exit point '%s' is not known. Closing.",
|
||||||
exitname);
|
conn->chosen_exit_name);
|
||||||
tor_free(exitname);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
extend_info = extend_info_from_router(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,9 +931,10 @@ try_an_intro_point:
|
|||||||
new_circ_purpose = desired_circuit_purpose;
|
new_circ_purpose = desired_circuit_purpose;
|
||||||
|
|
||||||
is_internal = (new_circ_purpose != CIRCUIT_PURPOSE_C_GENERAL || is_resolve);
|
is_internal = (new_circ_purpose != CIRCUIT_PURPOSE_C_GENERAL || is_resolve);
|
||||||
circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime,
|
circ = circuit_launch_by_extend_info(
|
||||||
1, is_internal);
|
new_circ_purpose, extend_info, need_uptime, 1, is_internal);
|
||||||
tor_free(exitname);
|
if (extend_info)
|
||||||
|
extend_info_free(extend_info);
|
||||||
|
|
||||||
if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
|
if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
|
||||||
/* help predict this next time */
|
/* help predict this next time */
|
||||||
@ -989,18 +1005,18 @@ consider_recording_trackhost(connection_t *conn, circuit_t *circ)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!found_needle)
|
if (!found_needle || !circ->build_state->chosen_exit)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Add this exit/hostname pair to the addressmap. */
|
/* Add this exit/hostname pair to the addressmap. */
|
||||||
len = strlen(conn->socks_request->address) + 1 /* '.' */ +
|
len = strlen(conn->socks_request->address) + 1 /* '.' */ +
|
||||||
strlen(circ->build_state->chosen_exit_name) + 1 /* '.' */ +
|
strlen(circ->build_state->chosen_exit->nickname) + 1 /* '.' */ +
|
||||||
strlen("exit") + 1 /* '\0' */;
|
strlen("exit") + 1 /* '\0' */;
|
||||||
new_address = tor_malloc(len);
|
new_address = tor_malloc(len);
|
||||||
|
|
||||||
tor_snprintf(new_address, len, "%s.%s.exit",
|
tor_snprintf(new_address, len, "%s.%s.exit",
|
||||||
conn->socks_request->address,
|
conn->socks_request->address,
|
||||||
circ->build_state->chosen_exit_name);
|
circ->build_state->chosen_exit->nickname);
|
||||||
|
|
||||||
addressmap_register(conn->socks_request->address, new_address,
|
addressmap_register(conn->socks_request->address, new_address,
|
||||||
time(NULL) + options->TrackHostExitsExpire);
|
time(NULL) + options->TrackHostExitsExpire);
|
||||||
|
@ -1051,7 +1051,7 @@ connection_ap_handshake_process_socks(connection_t *conn)
|
|||||||
log_fn(LOG_INFO,"Got a hidden service request for ID '%s'",
|
log_fn(LOG_INFO,"Got a hidden service request for ID '%s'",
|
||||||
safe_str(conn->rend_query));
|
safe_str(conn->rend_query));
|
||||||
/* see if we already have it cached */
|
/* see if we already have it cached */
|
||||||
r = rend_cache_lookup_entry(conn->rend_query, &entry);
|
r = rend_cache_lookup_entry(conn->rend_query, -1, &entry);
|
||||||
if (r<0) {
|
if (r<0) {
|
||||||
log_fn(LOG_WARN,"Invalid service descriptor %s",
|
log_fn(LOG_WARN,"Invalid service descriptor %s",
|
||||||
safe_str(conn->rend_query));
|
safe_str(conn->rend_query));
|
||||||
|
@ -1402,7 +1402,9 @@ handle_control_extendcircuit(connection_t *conn, uint32_t len,
|
|||||||
/* now circ refers to something that is ready to be extended */
|
/* now circ refers to something that is ready to be extended */
|
||||||
SMARTLIST_FOREACH(routers, routerinfo_t *, r,
|
SMARTLIST_FOREACH(routers, routerinfo_t *, r,
|
||||||
{
|
{
|
||||||
circuit_append_new_exit(circ, r);
|
extend_info_t *info = extend_info_from_router(r);
|
||||||
|
circuit_append_new_exit(circ, info);
|
||||||
|
extend_info_free(info);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* now that we've populated the cpath, start extending */
|
/* now that we've populated the cpath, start extending */
|
||||||
|
@ -435,6 +435,9 @@ directory_send_command(connection_t *conn, const char *platform,
|
|||||||
|
|
||||||
httpcommand = "GET";
|
httpcommand = "GET";
|
||||||
tor_snprintf(url, sizeof(url), "/tor/rendezvous/%s", resource);
|
tor_snprintf(url, sizeof(url), "/tor/rendezvous/%s", resource);
|
||||||
|
/* XXXX011 Once directories understand versioned descriptors, switch to this
|
||||||
|
* URL in order to get the most recent version */
|
||||||
|
// tor_snprintf(url, sizeof(url), "/tor/rendezvous1/%s", resource);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case DIR_PURPOSE_UPLOAD_RENDDESC:
|
case DIR_PURPOSE_UPLOAD_RENDDESC:
|
||||||
@ -1004,10 +1007,13 @@ directory_handle_command_get(connection_t *conn, char *headers,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmpstart(url,"/tor/rendezvous/")) {
|
if (!strcmpstart(url,"/tor/rendezvous/") ||
|
||||||
|
!strcmpstart(url,"/tor/rendezvous1/")) {
|
||||||
/* rendezvous descriptor fetch */
|
/* rendezvous descriptor fetch */
|
||||||
const char *descp;
|
const char *descp;
|
||||||
size_t desc_len;
|
size_t desc_len;
|
||||||
|
int versioned = !strcmpstart(url,"/tor/rendezvous1/");
|
||||||
|
const char *query = url+strlen("/tor/rendezvous/")+(versioned?1:0);
|
||||||
|
|
||||||
if (!authdir_mode(get_options())) {
|
if (!authdir_mode(get_options())) {
|
||||||
/* We don't hand out rend descs. In fact, it could be a security
|
/* We don't hand out rend descs. In fact, it could be a security
|
||||||
@ -1019,7 +1025,7 @@ directory_handle_command_get(connection_t *conn, char *headers,
|
|||||||
tor_free(url);
|
tor_free(url);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
switch (rend_cache_lookup_desc(url+strlen("/tor/rendezvous/"), &descp, &desc_len)) {
|
switch (rend_cache_lookup_desc(query, versioned?-1:0, &descp, &desc_len)) {
|
||||||
case 1: /* valid */
|
case 1: /* valid */
|
||||||
format_rfc1123_time(date, time(NULL));
|
format_rfc1123_time(date, time(NULL));
|
||||||
tor_snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n",
|
tor_snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n",
|
||||||
|
@ -153,6 +153,9 @@ onion_skin_create(crypto_pk_env_t *dest_router_key,
|
|||||||
crypto_dh_env_t *dh = NULL;
|
crypto_dh_env_t *dh = NULL;
|
||||||
int dhbytes, pkbytes;
|
int dhbytes, pkbytes;
|
||||||
|
|
||||||
|
tor_assert(dest_router_key);
|
||||||
|
tor_assert(handshake_state_out);
|
||||||
|
tor_assert(onion_skin_out);
|
||||||
*handshake_state_out = NULL;
|
*handshake_state_out = NULL;
|
||||||
memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);
|
memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);
|
||||||
|
|
||||||
|
73
src/or/or.h
73
src/or/or.h
@ -664,7 +664,8 @@ struct connection_t {
|
|||||||
int done_receiving; /**< For half-open connections; not used currently. */
|
int done_receiving; /**< For half-open connections; not used currently. */
|
||||||
char has_sent_end; /**< For debugging: set once we've set the stream end,
|
char has_sent_end; /**< For debugging: set once we've set the stream end,
|
||||||
and check in circuit_about_to_close_connection(). */
|
and check in circuit_about_to_close_connection(). */
|
||||||
struct circuit_t *on_circuit; /**< DOCDOC */
|
struct circuit_t *on_circuit; /**< The circuit (if any) that this edge
|
||||||
|
* connection is using. */
|
||||||
|
|
||||||
/* Used only by AP connections */
|
/* Used only by AP connections */
|
||||||
socks_request_t *socks_request; /**< SOCKS structure describing request (AP
|
socks_request_t *socks_request; /**< SOCKS structure describing request (AP
|
||||||
@ -766,6 +767,17 @@ typedef struct {
|
|||||||
char *signing_router;
|
char *signing_router;
|
||||||
} routerlist_t;
|
} routerlist_t;
|
||||||
|
|
||||||
|
/** Informetation on router used when extending a circuit. (We don't need a
|
||||||
|
* full routerinfo_t to extend: we only need addr:port:keyid to build an OR
|
||||||
|
* connection, and onion_key to create the onionskin.) */
|
||||||
|
typedef struct extend_info_t {
|
||||||
|
char nickname[MAX_HEX_NICKNAME_LEN+1]; /**< This router's nickname for display*/
|
||||||
|
char identity_digest[DIGEST_LEN]; /**< Hash of this router's identity key */
|
||||||
|
uint32_t addr; /**< IP address in host order */
|
||||||
|
uint16_t port; /**< OR port */
|
||||||
|
crypto_pk_env_t *onion_key; /**< Current onionskin key */
|
||||||
|
} extend_info_t;
|
||||||
|
|
||||||
#define CRYPT_PATH_MAGIC 0x70127012u
|
#define CRYPT_PATH_MAGIC 0x70127012u
|
||||||
|
|
||||||
/** Holds accounting information for a single step in the layered encryption
|
/** Holds accounting information for a single step in the layered encryption
|
||||||
@ -793,12 +805,8 @@ struct crypt_path_t {
|
|||||||
/** Negotiated key material shared with the OR at this step. */
|
/** Negotiated key material shared with the OR at this step. */
|
||||||
char handshake_digest[DIGEST_LEN];/* KH in tor-spec.txt */
|
char handshake_digest[DIGEST_LEN];/* KH in tor-spec.txt */
|
||||||
|
|
||||||
/** IP4 address of the OR at this step. */
|
/** Information to extend to the OR at this step. */
|
||||||
uint32_t addr;
|
extend_info_t *extend_info;
|
||||||
/** Port of the OR at this step. */
|
|
||||||
uint16_t port;
|
|
||||||
/** Identity key digest of the OR at this step. */
|
|
||||||
char identity_digest[DIGEST_LEN];
|
|
||||||
|
|
||||||
/** Is the circuit built to this step? Must be one of:
|
/** Is the circuit built to this step? Must be one of:
|
||||||
* - CPATH_STATE_CLOSED (The circuit has not been extended to this step)
|
* - CPATH_STATE_CLOSED (The circuit has not been extended to this step)
|
||||||
@ -836,10 +844,8 @@ typedef struct crypt_path_t crypt_path_t;
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
/** Intended length of the final circuit. */
|
/** Intended length of the final circuit. */
|
||||||
int desired_path_len;
|
int desired_path_len;
|
||||||
/** Nickname of planned exit node. */
|
/** How to extend to the planned exit node. */
|
||||||
char *chosen_exit_name;
|
extend_info_t *chosen_exit;
|
||||||
/** Identity of planned exit node. */
|
|
||||||
char chosen_exit_digest[DIGEST_LEN];
|
|
||||||
/** Whether every node in the circ must have adequate uptime. */
|
/** Whether every node in the circ must have adequate uptime. */
|
||||||
int need_uptime;
|
int need_uptime;
|
||||||
/** Whether every node in the circ must have adequate capacity. */
|
/** Whether every node in the circ must have adequate capacity. */
|
||||||
@ -1220,7 +1226,7 @@ void circuit_rep_hist_note_result(circuit_t *circ);
|
|||||||
void circuit_dump_by_conn(connection_t *conn, int severity);
|
void circuit_dump_by_conn(connection_t *conn, int severity);
|
||||||
circuit_t *circuit_init(uint8_t purpose, int need_uptime,
|
circuit_t *circuit_init(uint8_t purpose, int need_uptime,
|
||||||
int need_capacity, int internal);
|
int need_capacity, int internal);
|
||||||
circuit_t *circuit_establish_circuit(uint8_t purpose, routerinfo_t *exit,
|
circuit_t *circuit_establish_circuit(uint8_t purpose, extend_info_t *exit,
|
||||||
int need_uptime, int need_capacity, int internal);
|
int need_uptime, int need_capacity, int internal);
|
||||||
int circuit_handle_first_hop(circuit_t *circ);
|
int circuit_handle_first_hop(circuit_t *circ);
|
||||||
void circuit_n_conn_done(connection_t *or_conn, int status);
|
void circuit_n_conn_done(connection_t *or_conn, int status);
|
||||||
@ -1234,9 +1240,14 @@ int onionskin_answer(circuit_t *circ, uint8_t cell_type, char *payload, char *ke
|
|||||||
int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
|
int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
|
||||||
int *need_capacity);
|
int *need_capacity);
|
||||||
|
|
||||||
int circuit_append_new_exit(circuit_t *circ, routerinfo_t *exit);
|
int circuit_append_new_exit(circuit_t *circ, extend_info_t *info);
|
||||||
int circuit_extend_to_new_exit(circuit_t *circ, routerinfo_t *exit);
|
int circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info);
|
||||||
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
|
void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
|
||||||
|
extend_info_t *extend_info_from_router(routerinfo_t *r);
|
||||||
|
extend_info_t *extend_info_dup(extend_info_t *info);
|
||||||
|
void extend_info_free(extend_info_t *info);
|
||||||
|
routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
|
||||||
|
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
|
||||||
|
|
||||||
/********************************* circuitlist.c ***********************/
|
/********************************* circuitlist.c ***********************/
|
||||||
|
|
||||||
@ -1280,6 +1291,9 @@ void circuit_has_opened(circuit_t *circ);
|
|||||||
void circuit_build_failed(circuit_t *circ);
|
void circuit_build_failed(circuit_t *circ);
|
||||||
circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
|
circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
|
||||||
int need_uptime, int need_capacity, int is_internal);
|
int need_uptime, int need_capacity, int is_internal);
|
||||||
|
circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
|
||||||
|
extend_info_t *info,
|
||||||
|
int need_uptime, int need_capacity, int is_internal);
|
||||||
circuit_t *circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
|
circuit_t *circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
|
||||||
int need_uptime, int need_capacity, int is_internal);
|
int need_uptime, int need_capacity, int is_internal);
|
||||||
void circuit_reset_failure_count(int timeout);
|
void circuit_reset_failure_count(int timeout);
|
||||||
@ -1707,22 +1721,34 @@ void rend_client_introcirc_has_opened(circuit_t *circ);
|
|||||||
void rend_client_rendcirc_has_opened(circuit_t *circ);
|
void rend_client_rendcirc_has_opened(circuit_t *circ);
|
||||||
int rend_client_introduction_acked(circuit_t *circ, const char *request, size_t request_len);
|
int rend_client_introduction_acked(circuit_t *circ, const char *request, size_t request_len);
|
||||||
void rend_client_refetch_renddesc(const char *query);
|
void rend_client_refetch_renddesc(const char *query);
|
||||||
int rend_client_remove_intro_point(char *failed_intro, const char *query);
|
int rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query);
|
||||||
int rend_client_rendezvous_acked(circuit_t *circ, const char *request, size_t request_len);
|
int rend_client_rendezvous_acked(circuit_t *circ, const char *request, size_t request_len);
|
||||||
int rend_client_receive_rendezvous(circuit_t *circ, const char *request, size_t request_len);
|
int rend_client_receive_rendezvous(circuit_t *circ, const char *request, size_t request_len);
|
||||||
void rend_client_desc_here(char *query);
|
void rend_client_desc_here(const char *query);
|
||||||
|
|
||||||
char *rend_client_get_random_intro(char *query);
|
extend_info_t *rend_client_get_random_intro(const char *query);
|
||||||
|
|
||||||
int rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc);
|
int rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc);
|
||||||
|
|
||||||
/********************************* rendcommon.c ***************************/
|
/********************************* rendcommon.c ***************************/
|
||||||
|
|
||||||
|
/** Information used to connect to a hidden service. */
|
||||||
typedef struct rend_service_descriptor_t {
|
typedef struct rend_service_descriptor_t {
|
||||||
crypto_pk_env_t *pk;
|
crypto_pk_env_t *pk; /**< This service's public key. */
|
||||||
time_t timestamp;
|
int version; /**< 0 or 1 */
|
||||||
int n_intro_points;
|
time_t timestamp; /**< Time when the descriptor was generated. */
|
||||||
|
uint16_t protocols; /**< Bitmask: which rendezvous protocols are supporeted?
|
||||||
|
* (We allow bits '0', '1', and '2' to be set.) */
|
||||||
|
int n_intro_points; /**< Number of introduction points. */
|
||||||
|
/** Array of n_intro_points elements for this service's introduction points'
|
||||||
|
* nicknames. Elements are removed from this array if introduction attempts
|
||||||
|
* fail. */
|
||||||
char **intro_points;
|
char **intro_points;
|
||||||
|
/** Array of n_intro_points elements for this service's introduction points'
|
||||||
|
* extend_infos, or NULL if this descriptor is V0. Elements are removed
|
||||||
|
* from this array if introduction attempts fail. If this array is present,
|
||||||
|
* its elements correspond to the elements of intro_points. */
|
||||||
|
extend_info_t **intro_point_extend_info;
|
||||||
} rend_service_descriptor_t;
|
} rend_service_descriptor_t;
|
||||||
|
|
||||||
int rend_cmp_service_ids(const char *one, const char *two);
|
int rend_cmp_service_ids(const char *one, const char *two);
|
||||||
@ -1732,6 +1758,7 @@ void rend_process_relay_cell(circuit_t *circ, int command, size_t length,
|
|||||||
|
|
||||||
void rend_service_descriptor_free(rend_service_descriptor_t *desc);
|
void rend_service_descriptor_free(rend_service_descriptor_t *desc);
|
||||||
int rend_encode_service_descriptor(rend_service_descriptor_t *desc,
|
int rend_encode_service_descriptor(rend_service_descriptor_t *desc,
|
||||||
|
int version,
|
||||||
crypto_pk_env_t *key,
|
crypto_pk_env_t *key,
|
||||||
char **str_out,
|
char **str_out,
|
||||||
size_t *len_out);
|
size_t *len_out);
|
||||||
@ -1740,7 +1767,7 @@ int rend_get_service_id(crypto_pk_env_t *pk, char *out);
|
|||||||
|
|
||||||
typedef struct rend_cache_entry_t {
|
typedef struct rend_cache_entry_t {
|
||||||
size_t len; /* Length of desc */
|
size_t len; /* Length of desc */
|
||||||
time_t received; /* When did we get the descriptor? */
|
time_t received; /* When was the descriptor received? */
|
||||||
char *desc; /* Service descriptor */
|
char *desc; /* Service descriptor */
|
||||||
rend_service_descriptor_t *parsed; /* Parsed value of 'desc' */
|
rend_service_descriptor_t *parsed; /* Parsed value of 'desc' */
|
||||||
} rend_cache_entry_t;
|
} rend_cache_entry_t;
|
||||||
@ -1749,8 +1776,8 @@ void rend_cache_init(void);
|
|||||||
void rend_cache_clean(void);
|
void rend_cache_clean(void);
|
||||||
void rend_cache_free_all(void);
|
void rend_cache_free_all(void);
|
||||||
int rend_valid_service_id(const char *query);
|
int rend_valid_service_id(const char *query);
|
||||||
int rend_cache_lookup_desc(const char *query, const char **desc, size_t *desc_len);
|
int rend_cache_lookup_desc(const char *query, int version, const char **desc, size_t *desc_len);
|
||||||
int rend_cache_lookup_entry(const char *query, rend_cache_entry_t **entry_out);
|
int rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **entry_out);
|
||||||
int rend_cache_store(const char *desc, size_t desc_len);
|
int rend_cache_store(const char *desc, size_t desc_len);
|
||||||
|
|
||||||
/********************************* rendservice.c ***************************/
|
/********************************* rendservice.c ***************************/
|
||||||
|
@ -633,11 +633,8 @@ connection_edge_process_end_not_open(
|
|||||||
log_fn(LOG_INFO,"Address '%s' refused due to '%s'. Considering retrying.",
|
log_fn(LOG_INFO,"Address '%s' refused due to '%s'. Considering retrying.",
|
||||||
safe_str(conn->socks_request->address),
|
safe_str(conn->socks_request->address),
|
||||||
connection_edge_end_reason_str(reason));
|
connection_edge_end_reason_str(reason));
|
||||||
exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
|
exitrouter =
|
||||||
if (!exitrouter) {
|
router_get_by_digest(circ->build_state->chosen_exit->identity_digest);
|
||||||
log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
|
|
||||||
return 0; /* this circuit is screwed and doesn't know it yet */
|
|
||||||
}
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case END_STREAM_REASON_EXITPOLICY:
|
case END_STREAM_REASON_EXITPOLICY:
|
||||||
if (rh->length >= 5) {
|
if (rh->length >= 5) {
|
||||||
@ -652,15 +649,15 @@ connection_edge_process_end_not_open(
|
|||||||
conn->chosen_exit_name);
|
conn->chosen_exit_name);
|
||||||
}
|
}
|
||||||
/* check if he *ought* to have allowed it */
|
/* check if he *ought* to have allowed it */
|
||||||
if (rh->length < 5 ||
|
if (exitrouter &&
|
||||||
|
(rh->length < 5 ||
|
||||||
(!tor_inet_aton(conn->socks_request->address, &in) &&
|
(!tor_inet_aton(conn->socks_request->address, &in) &&
|
||||||
!conn->chosen_exit_name)) {
|
!conn->chosen_exit_name))) {
|
||||||
log_fn(LOG_NOTICE,"Exitrouter '%s' seems to be more restrictive than its exit policy. Not using this router as exit for now.", exitrouter->nickname);
|
log_fn(LOG_NOTICE,"Exitrouter '%s' seems to be more restrictive than its exit policy. Not using this router as exit for now.", exitrouter->nickname);
|
||||||
addr_policy_free(exitrouter->exit_policy);
|
addr_policy_free(exitrouter->exit_policy);
|
||||||
exitrouter->exit_policy =
|
exitrouter->exit_policy =
|
||||||
router_parse_addr_policy_from_string("reject *:*");
|
router_parse_addr_policy_from_string("reject *:*");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection_ap_detach_retriable(conn, circ) >= 0)
|
if (connection_ap_detach_retriable(conn, circ) >= 0)
|
||||||
return 0;
|
return 0;
|
||||||
/* else, conn will get closed below */
|
/* else, conn will get closed below */
|
||||||
@ -683,10 +680,11 @@ connection_edge_process_end_not_open(
|
|||||||
break;
|
break;
|
||||||
case END_STREAM_REASON_HIBERNATING:
|
case END_STREAM_REASON_HIBERNATING:
|
||||||
case END_STREAM_REASON_RESOURCELIMIT:
|
case END_STREAM_REASON_RESOURCELIMIT:
|
||||||
|
if (exitrouter) {
|
||||||
addr_policy_free(exitrouter->exit_policy);
|
addr_policy_free(exitrouter->exit_policy);
|
||||||
exitrouter->exit_policy =
|
exitrouter->exit_policy =
|
||||||
router_parse_addr_policy_from_string("reject *:*");
|
router_parse_addr_policy_from_string("reject *:*");
|
||||||
|
}
|
||||||
if (connection_ap_detach_retriable(conn, circ) >= 0)
|
if (connection_ap_detach_retriable(conn, circ) >= 0)
|
||||||
return 0;
|
return 0;
|
||||||
/* else, will close below */
|
/* else, will close below */
|
||||||
|
@ -58,15 +58,16 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
|
|||||||
size_t payload_len;
|
size_t payload_len;
|
||||||
int r;
|
int r;
|
||||||
char payload[RELAY_PAYLOAD_SIZE];
|
char payload[RELAY_PAYLOAD_SIZE];
|
||||||
char tmp[1+(MAX_HEX_NICKNAME_LEN+1)+REND_COOKIE_LEN+DH_KEY_LEN];
|
char tmp[RELAY_PAYLOAD_SIZE];
|
||||||
rend_cache_entry_t *entry;
|
rend_cache_entry_t *entry;
|
||||||
crypt_path_t *cpath;
|
crypt_path_t *cpath;
|
||||||
|
off_t dh_offset;
|
||||||
|
|
||||||
tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
|
tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
|
||||||
tor_assert(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY);
|
tor_assert(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY);
|
||||||
tor_assert(!rend_cmp_service_ids(introcirc->rend_query, rendcirc->rend_query));
|
tor_assert(!rend_cmp_service_ids(introcirc->rend_query, rendcirc->rend_query));
|
||||||
|
|
||||||
if (rend_cache_lookup_entry(introcirc->rend_query, &entry) < 1) {
|
if (rend_cache_lookup_entry(introcirc->rend_query, -1, &entry) < 1) {
|
||||||
log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
|
log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
|
||||||
safe_str(introcirc->rend_query));
|
safe_str(introcirc->rend_query));
|
||||||
goto err;
|
goto err;
|
||||||
@ -95,22 +96,28 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* write the remaining items into tmp */
|
/* write the remaining items into tmp */
|
||||||
#if 0
|
if (entry->parsed->protocols & (1<<2)) {
|
||||||
tmp[0] = 1; /* version 1 of the cell format */
|
/* version 2 format */
|
||||||
|
extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
|
||||||
|
int klen;
|
||||||
|
tmp[0] = 2; /* version 2 of the cell format */
|
||||||
/* nul pads */
|
/* nul pads */
|
||||||
strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1));
|
set_uint32(tmp+1, htonl(extend_info->addr));
|
||||||
memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
|
set_uint16(tmp+5, htons(extend_info->port));
|
||||||
#else
|
memcpy(tmp+7, extend_info->identity_digest, DIGEST_LEN);
|
||||||
strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */
|
klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+7+DIGEST_LEN+2,
|
||||||
|
sizeof(tmp)-(7+DIGEST_LEN+2));
|
||||||
|
set_uint16(tmp+7+DIGEST_LEN, htons(klen));
|
||||||
|
memcpy(tmp+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie, REND_COOKIE_LEN);
|
||||||
|
dh_offset = 7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
|
||||||
|
} else {
|
||||||
|
/* Version 0. */
|
||||||
|
strncpy(tmp, rendcirc->build_state->chosen_exit->nickname, (MAX_NICKNAME_LEN+1)); /* nul pads */
|
||||||
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
|
memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
|
||||||
#endif
|
dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
|
||||||
if (crypto_dh_get_public(cpath->dh_handshake_state,
|
}
|
||||||
#if 0
|
|
||||||
tmp+1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN,
|
|
||||||
#else
|
|
||||||
tmp+MAX_NICKNAME_LEN+1+REND_COOKIE_LEN,
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset,
|
||||||
DH_KEY_LEN)<0) {
|
DH_KEY_LEN)<0) {
|
||||||
log_fn(LOG_WARN, "Couldn't extract g^x");
|
log_fn(LOG_WARN, "Couldn't extract g^x");
|
||||||
goto err;
|
goto err;
|
||||||
@ -119,11 +126,7 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc)
|
|||||||
/*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
|
/*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
|
||||||
* to avoid buffer overflows? */
|
* to avoid buffer overflows? */
|
||||||
r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp,
|
r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp,
|
||||||
#if 0
|
dh_offset+DH_KEY_LEN,
|
||||||
1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
|
|
||||||
#else
|
|
||||||
MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
|
|
||||||
#endif
|
|
||||||
PK_PKCS1_OAEP_PADDING, 0);
|
PK_PKCS1_OAEP_PADDING, 0);
|
||||||
if (r<0) {
|
if (r<0) {
|
||||||
log_fn(LOG_WARN,"hybrid pk encrypt failed.");
|
log_fn(LOG_WARN,"hybrid pk encrypt failed.");
|
||||||
@ -174,7 +177,6 @@ int
|
|||||||
rend_client_introduction_acked(circuit_t *circ,
|
rend_client_introduction_acked(circuit_t *circ,
|
||||||
const char *request, size_t request_len)
|
const char *request, size_t request_len)
|
||||||
{
|
{
|
||||||
char *nickname;
|
|
||||||
circuit_t *rendcirc;
|
circuit_t *rendcirc;
|
||||||
|
|
||||||
if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
|
||||||
@ -184,7 +186,8 @@ rend_client_introduction_acked(circuit_t *circ,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tor_assert(circ->build_state->chosen_exit_name);
|
tor_assert(circ->build_state->chosen_exit);
|
||||||
|
tor_assert(circ->build_state->chosen_exit->nickname);
|
||||||
|
|
||||||
if (request_len == 0) {
|
if (request_len == 0) {
|
||||||
/* It's an ACK; the introduction point relayed our introduction request. */
|
/* It's an ACK; the introduction point relayed our introduction request. */
|
||||||
@ -209,27 +212,26 @@ rend_client_introduction_acked(circuit_t *circ,
|
|||||||
* points. If any remain, extend to a new one and try again.
|
* points. If any remain, extend to a new one and try again.
|
||||||
* If none remain, refetch the service descriptor.
|
* If none remain, refetch the service descriptor.
|
||||||
*/
|
*/
|
||||||
if (rend_client_remove_intro_point(circ->build_state->chosen_exit_name,
|
if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
|
||||||
circ->rend_query) > 0) {
|
circ->rend_query) > 0) {
|
||||||
/* There are introduction points left. re-extend the circuit to
|
/* There are introduction points left. re-extend the circuit to
|
||||||
* another intro point and try again. */
|
* another intro point and try again. */
|
||||||
routerinfo_t *r;
|
extend_info_t *info;
|
||||||
nickname = rend_client_get_random_intro(circ->rend_query);
|
int result;
|
||||||
tor_assert(nickname);
|
info = rend_client_get_random_intro(circ->rend_query);
|
||||||
log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.",
|
if (!info) {
|
||||||
safe_str(circ->rend_query),
|
log_fn(LOG_WARN, "No introduction points left for %s. Closing.",
|
||||||
circ->build_state->chosen_exit_name, nickname);
|
safe_str(circ->rend_query));
|
||||||
if (!(r = router_get_by_nickname(nickname))) {
|
|
||||||
log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.",
|
|
||||||
nickname, safe_str(circ->rend_query));
|
|
||||||
tor_free(nickname);
|
|
||||||
circuit_mark_for_close(circ);
|
circuit_mark_for_close(circ);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)",
|
log_fn(LOG_INFO,"Got nack for %s from %s, extending circ %d to %s.",
|
||||||
nickname, safe_str(circ->rend_query), circ->n_circ_id);
|
safe_str(circ->rend_query),
|
||||||
tor_free(nickname);
|
circ->build_state->chosen_exit->nickname, circ->n_circ_id,
|
||||||
return circuit_extend_to_new_exit(circ, r);
|
info->nickname);
|
||||||
|
result = circuit_extend_to_new_exit(circ, info);
|
||||||
|
extend_info_free(info);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -257,13 +259,13 @@ rend_client_refetch_renddesc(const char *query)
|
|||||||
* unrecognized, 1 if recognized and some intro points remain.
|
* unrecognized, 1 if recognized and some intro points remain.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
rend_client_remove_intro_point(char *failed_intro, const char *query)
|
rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query)
|
||||||
{
|
{
|
||||||
int i, r;
|
int i, r;
|
||||||
rend_cache_entry_t *ent;
|
rend_cache_entry_t *ent;
|
||||||
connection_t *conn;
|
connection_t *conn;
|
||||||
|
|
||||||
r = rend_cache_lookup_entry(query, &ent);
|
r = rend_cache_lookup_entry(query, -1, &ent);
|
||||||
if (r<0) {
|
if (r<0) {
|
||||||
log_fn(LOG_WARN, "Malformed service ID '%s'", safe_str(query));
|
log_fn(LOG_WARN, "Malformed service ID '%s'", safe_str(query));
|
||||||
return -1;
|
return -1;
|
||||||
@ -275,14 +277,33 @@ rend_client_remove_intro_point(char *failed_intro, const char *query)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ent->parsed->intro_point_extend_info) {
|
||||||
for (i=0; i < ent->parsed->n_intro_points; ++i) {
|
for (i=0; i < ent->parsed->n_intro_points; ++i) {
|
||||||
if (!strcasecmp(ent->parsed->intro_points[i], failed_intro)) {
|
if (!memcmp(failed_intro->identity_digest,
|
||||||
|
ent->parsed->intro_point_extend_info[i]->identity_digest,
|
||||||
|
DIGEST_LEN)) {
|
||||||
|
tor_assert(!strcmp(ent->parsed->intro_points[i],
|
||||||
|
ent->parsed->intro_point_extend_info[i]->nickname));
|
||||||
|
tor_free(ent->parsed->intro_points[i]);
|
||||||
|
extend_info_free(ent->parsed->intro_point_extend_info[i]);
|
||||||
|
--ent->parsed->n_intro_points;
|
||||||
|
ent->parsed->intro_points[i] =
|
||||||
|
ent->parsed->intro_points[ent->parsed->n_intro_points];
|
||||||
|
ent->parsed->intro_point_extend_info[i] =
|
||||||
|
ent->parsed->intro_point_extend_info[ent->parsed->n_intro_points];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (i=0; i < ent->parsed->n_intro_points; ++i) {
|
||||||
|
if (!strcasecmp(ent->parsed->intro_points[i], failed_intro->nickname)) {
|
||||||
tor_free(ent->parsed->intro_points[i]);
|
tor_free(ent->parsed->intro_points[i]);
|
||||||
ent->parsed->intro_points[i] =
|
ent->parsed->intro_points[i] =
|
||||||
ent->parsed->intro_points[--ent->parsed->n_intro_points];
|
ent->parsed->intro_points[--ent->parsed->n_intro_points];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!ent->parsed->n_intro_points) {
|
if (!ent->parsed->n_intro_points) {
|
||||||
log_fn(LOG_INFO,"No more intro points remain for %s. Re-fetching descriptor.",
|
log_fn(LOG_INFO,"No more intro points remain for %s. Re-fetching descriptor.",
|
||||||
@ -385,7 +406,7 @@ rend_client_receive_rendezvous(circuit_t *circ, const char *request, size_t requ
|
|||||||
* else fail them.
|
* else fail them.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
rend_client_desc_here(char *query)
|
rend_client_desc_here(const char *query)
|
||||||
{
|
{
|
||||||
connection_t *conn;
|
connection_t *conn;
|
||||||
rend_cache_entry_t *entry;
|
rend_cache_entry_t *entry;
|
||||||
@ -393,7 +414,7 @@ rend_client_desc_here(char *query)
|
|||||||
|
|
||||||
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
|
while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
|
||||||
AP_CONN_STATE_RENDDESC_WAIT, query))) {
|
AP_CONN_STATE_RENDDESC_WAIT, query))) {
|
||||||
if (rend_cache_lookup_entry(conn->rend_query, &entry) == 1 &&
|
if (rend_cache_lookup_entry(conn->rend_query, -1, &entry) == 1 &&
|
||||||
entry->parsed->n_intro_points > 0) {
|
entry->parsed->n_intro_points > 0) {
|
||||||
/* either this fetch worked, or it failed but there was a
|
/* either this fetch worked, or it failed but there was a
|
||||||
* valid entry from before which we should reuse */
|
* valid entry from before which we should reuse */
|
||||||
@ -419,37 +440,42 @@ rend_client_desc_here(char *query)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** strdup a nickname for a random introduction
|
/** Return a newly allocated extend_info_t* for a randomly chosen introduction
|
||||||
* point of query. return NULL if error.
|
* point for the named hidden service. Return NULL if all introduction points
|
||||||
|
* have been tried and failed.
|
||||||
*/
|
*/
|
||||||
char *
|
extend_info_t *
|
||||||
rend_client_get_random_intro(char *query)
|
rend_client_get_random_intro(const char *query)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
smartlist_t *sl;
|
|
||||||
char *choice;
|
|
||||||
char *nickname;
|
|
||||||
rend_cache_entry_t *entry;
|
rend_cache_entry_t *entry;
|
||||||
|
|
||||||
if (rend_cache_lookup_entry(query, &entry) < 1) {
|
if (rend_cache_lookup_entry(query, -1, &entry) < 1) {
|
||||||
log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
|
log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
|
||||||
safe_str(query));
|
safe_str(query));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
sl = smartlist_create();
|
again:
|
||||||
|
if (!entry->parsed->n_intro_points)
|
||||||
/* add the intro point nicknames */
|
|
||||||
for (i=0;i<entry->parsed->n_intro_points;i++)
|
|
||||||
smartlist_add(sl,entry->parsed->intro_points[i]);
|
|
||||||
|
|
||||||
choice = smartlist_choose(sl);
|
|
||||||
if (!choice) {
|
|
||||||
smartlist_free(sl);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
i = crypto_pseudo_rand_int(entry->parsed->n_intro_points);
|
||||||
|
|
||||||
|
if (entry->parsed->intro_point_extend_info) {
|
||||||
|
return extend_info_dup(entry->parsed->intro_point_extend_info[i]);
|
||||||
|
} else {
|
||||||
|
/* add the intro point nicknames */
|
||||||
|
char *choice = entry->parsed->intro_points[i];
|
||||||
|
routerinfo_t *router = router_get_by_nickname(choice);
|
||||||
|
if (!router) {
|
||||||
|
log_fn(LOG_WARN, "Unknown router with nickname %s; trying another.",choice);
|
||||||
|
tor_free(choice);
|
||||||
|
entry->parsed->intro_points[i] =
|
||||||
|
entry->parsed->intro_points[--entry->parsed->n_intro_points];
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
return extend_info_from_router(router);
|
||||||
}
|
}
|
||||||
nickname = tor_strdup(choice);
|
|
||||||
smartlist_free(sl);
|
|
||||||
return nickname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,13 @@ void rend_service_descriptor_free(rend_service_descriptor_t *desc)
|
|||||||
}
|
}
|
||||||
tor_free(desc->intro_points);
|
tor_free(desc->intro_points);
|
||||||
}
|
}
|
||||||
|
if (desc->intro_point_extend_info) {
|
||||||
|
for (i=0; i < desc->n_intro_points; ++i) {
|
||||||
|
if (desc->intro_point_extend_info[i])
|
||||||
|
extend_info_free(desc->intro_point_extend_info[i]);
|
||||||
|
}
|
||||||
|
tor_free(desc->intro_point_extend_info);
|
||||||
|
}
|
||||||
tor_free(desc);
|
tor_free(desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,46 +45,60 @@ void rend_service_descriptor_free(rend_service_descriptor_t *desc)
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
rend_encode_service_descriptor(rend_service_descriptor_t *desc,
|
rend_encode_service_descriptor(rend_service_descriptor_t *desc,
|
||||||
|
int version,
|
||||||
crypto_pk_env_t *key,
|
crypto_pk_env_t *key,
|
||||||
char **str_out, size_t *len_out)
|
char **str_out, size_t *len_out)
|
||||||
{
|
{
|
||||||
char *buf, *cp, *ipoint;
|
char *cp;
|
||||||
|
char *end;
|
||||||
int i;
|
int i;
|
||||||
size_t keylen, asn1len;
|
size_t asn1len;
|
||||||
keylen = crypto_pk_keysize(desc->pk);
|
cp = *str_out = tor_malloc(PK_BYTES*2*(desc->n_intro_points+1)); /*Too long, but ok*/
|
||||||
buf = tor_malloc(keylen*2); /* Too long, but that's okay. */
|
end = cp + PK_BYTES*2*(desc->n_intro_points+1);
|
||||||
i = crypto_pk_asn1_encode(desc->pk, buf, keylen*2);
|
if (version) {
|
||||||
if (i<0) {
|
*(uint8_t*)cp = (uint8_t)0xff;
|
||||||
tor_free(buf);
|
*(uint8_t*)(cp+1) = (uint8_t)version;
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
asn1len = i;
|
|
||||||
*len_out = 2 + asn1len + 4 + 2 + keylen;
|
|
||||||
for (i = 0; i < desc->n_intro_points; ++i) {
|
|
||||||
*len_out += strlen(desc->intro_points[i]) + 1;
|
|
||||||
}
|
|
||||||
cp = *str_out = tor_malloc(*len_out);
|
|
||||||
set_uint16(cp, htons((uint16_t)asn1len));
|
|
||||||
cp += 2;
|
cp += 2;
|
||||||
memcpy(cp, buf, asn1len);
|
}
|
||||||
tor_free(buf);
|
asn1len = crypto_pk_asn1_encode(desc->pk, cp+2, end-(cp+2));
|
||||||
cp += asn1len;
|
set_uint16(cp, htons((uint16_t)asn1len));
|
||||||
|
cp += 2+asn1len;
|
||||||
set_uint32(cp, htonl((uint32_t)desc->timestamp));
|
set_uint32(cp, htonl((uint32_t)desc->timestamp));
|
||||||
cp += 4;
|
cp += 4;
|
||||||
|
if (version == 1) {
|
||||||
|
set_uint16(cp, htons(desc->protocols));
|
||||||
|
cp += 2;
|
||||||
|
}
|
||||||
set_uint16(cp, htons((uint16_t)desc->n_intro_points));
|
set_uint16(cp, htons((uint16_t)desc->n_intro_points));
|
||||||
cp += 2;
|
cp += 2;
|
||||||
|
if (version == 0) {
|
||||||
|
tor_assert(desc->intro_points);
|
||||||
for (i=0; i < desc->n_intro_points; ++i) {
|
for (i=0; i < desc->n_intro_points; ++i) {
|
||||||
ipoint = (char*)desc->intro_points[i];
|
char *ipoint = (char*)desc->intro_points[i];
|
||||||
strlcpy(cp, ipoint, *len_out-(cp-*str_out));
|
strlcpy(cp, ipoint, *len_out-(cp-*str_out));
|
||||||
cp += strlen(ipoint)+1;
|
cp += strlen(ipoint)+1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
tor_assert(desc->intro_point_extend_info);
|
||||||
|
for (i=0; i < desc->n_intro_points; ++i) {
|
||||||
|
extend_info_t *info = desc->intro_point_extend_info[i];
|
||||||
|
int klen;
|
||||||
|
set_uint32(cp, htonl(info->addr));
|
||||||
|
set_uint16(cp+4, htons(info->port));
|
||||||
|
memcpy(cp+6, info->identity_digest, DIGEST_LEN);
|
||||||
|
klen = crypto_pk_asn1_encode(info->onion_key, cp+6+DIGEST_LEN+2,
|
||||||
|
(end-(cp+6+DIGEST_LEN+2)));
|
||||||
|
set_uint16(cp+6+DIGEST_LEN, htons((uint16_t)klen));
|
||||||
|
cp += 6+DIGEST_LEN+2+klen;
|
||||||
|
}
|
||||||
|
}
|
||||||
i = crypto_pk_private_sign_digest(key, cp, *str_out, cp-*str_out);
|
i = crypto_pk_private_sign_digest(key, cp, *str_out, cp-*str_out);
|
||||||
if (i<0) {
|
if (i<0) {
|
||||||
tor_free(*str_out);
|
tor_free(*str_out);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
cp += i;
|
cp += i;
|
||||||
tor_assert(*len_out == (size_t)(cp-*str_out));
|
*len_out = (size_t)(cp-*str_out);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,11 +113,19 @@ rend_parse_service_descriptor(const char *str, size_t len)
|
|||||||
int i;
|
int i;
|
||||||
size_t keylen, asn1len;
|
size_t keylen, asn1len;
|
||||||
const char *end, *cp, *eos;
|
const char *end, *cp, *eos;
|
||||||
|
int version = 0;
|
||||||
|
|
||||||
result = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
result = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
||||||
cp = str;
|
cp = str;
|
||||||
end = str+len;
|
end = str+len;
|
||||||
if (end-cp<2) goto truncated;
|
if (end-cp<2) goto truncated;
|
||||||
|
if (*(uint8_t*)cp == 0xff) {
|
||||||
|
result->version = version = *(uint8_t*)(cp+1);
|
||||||
|
cp += 2;
|
||||||
|
} else {
|
||||||
|
result->version = version = 0;
|
||||||
|
}
|
||||||
|
if (end-cp < 2) goto truncated;
|
||||||
asn1len = ntohs(get_uint16(cp));
|
asn1len = ntohs(get_uint16(cp));
|
||||||
cp += 2;
|
cp += 2;
|
||||||
if ((size_t)(end-cp) < asn1len) goto truncated;
|
if ((size_t)(end-cp) < asn1len) goto truncated;
|
||||||
@ -106,10 +135,19 @@ rend_parse_service_descriptor(const char *str, size_t len)
|
|||||||
if (end-cp < 4) goto truncated;
|
if (end-cp < 4) goto truncated;
|
||||||
result->timestamp = (time_t) ntohl(get_uint32(cp));
|
result->timestamp = (time_t) ntohl(get_uint32(cp));
|
||||||
cp += 4;
|
cp += 4;
|
||||||
|
if (version == 1) {
|
||||||
|
if (end-cp < 2) goto truncated;
|
||||||
|
result->protocols = ntohs(get_uint16(cp));
|
||||||
|
cp += 2;
|
||||||
|
} else {
|
||||||
|
result->protocols = 1;
|
||||||
|
}
|
||||||
if (end-cp < 2) goto truncated;
|
if (end-cp < 2) goto truncated;
|
||||||
result->n_intro_points = ntohs(get_uint16(cp));
|
result->n_intro_points = ntohs(get_uint16(cp));
|
||||||
result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
|
|
||||||
cp += 2;
|
cp += 2;
|
||||||
|
if (version == 0) {
|
||||||
|
result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
|
||||||
for (i=0;i<result->n_intro_points;++i) {
|
for (i=0;i<result->n_intro_points;++i) {
|
||||||
if (end-cp < 2) goto truncated;
|
if (end-cp < 2) goto truncated;
|
||||||
eos = (const char *)memchr(cp,'\0',end-cp);
|
eos = (const char *)memchr(cp,'\0',end-cp);
|
||||||
@ -117,6 +155,32 @@ rend_parse_service_descriptor(const char *str, size_t len)
|
|||||||
result->intro_points[i] = tor_strdup(cp);
|
result->intro_points[i] = tor_strdup(cp);
|
||||||
cp = eos+1;
|
cp = eos+1;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
result->intro_point_extend_info =
|
||||||
|
tor_malloc_zero(sizeof(extend_info_t*)*result->n_intro_points);
|
||||||
|
result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
|
||||||
|
for (i=0;i<result->n_intro_points;++i) {
|
||||||
|
extend_info_t *info = result->intro_point_extend_info[i] =
|
||||||
|
tor_malloc_zero(sizeof(extend_info_t));
|
||||||
|
int klen;
|
||||||
|
if (end-cp < 8+DIGEST_LEN) goto truncated;
|
||||||
|
info->addr = ntohl(get_uint32(cp));
|
||||||
|
info->port = ntohs(get_uint16(cp+4));
|
||||||
|
memcpy(info->identity_digest, cp+6, DIGEST_LEN);
|
||||||
|
info->nickname[0] = '$';
|
||||||
|
base16_encode(info->nickname+1, sizeof(info->nickname)-1,
|
||||||
|
info->identity_digest, DIGEST_LEN);
|
||||||
|
result->intro_points[i] = tor_strdup(info->nickname);
|
||||||
|
klen = ntohs(get_uint16(cp+6+DIGEST_LEN));
|
||||||
|
cp += 8+DIGEST_LEN;
|
||||||
|
if (end-cp < klen) goto truncated;
|
||||||
|
if (!(info->onion_key = crypto_pk_asn1_decode(cp,klen))) {
|
||||||
|
log_fn(LOG_WARN, "error decoding onion key for intro point");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
cp += klen;
|
||||||
|
}
|
||||||
|
}
|
||||||
keylen = crypto_pk_keysize(result->pk);
|
keylen = crypto_pk_keysize(result->pk);
|
||||||
tor_assert(end-cp >= 0);
|
tor_assert(end-cp >= 0);
|
||||||
if ((size_t)(end-cp) < keylen) goto truncated;
|
if ((size_t)(end-cp) < keylen) goto truncated;
|
||||||
@ -227,16 +291,28 @@ rend_valid_service_id(const char *query)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>, set
|
/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>,
|
||||||
* *<b>e</b> to that entry and return 1. Else return 0.
|
* set *<b>e</b> to that entry and return 1. Else return 0. If
|
||||||
|
* <b>version</b> is nonnegative, only return an entry in that descriptor
|
||||||
|
* format version. Otherwise (if <b>version</b> is negative), return the most
|
||||||
|
* recent format we have.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
rend_cache_lookup_entry(const char *query, rend_cache_entry_t **e)
|
rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
|
||||||
{
|
{
|
||||||
|
char key[REND_SERVICE_ID_LEN+2];
|
||||||
tor_assert(rend_cache);
|
tor_assert(rend_cache);
|
||||||
if (!rend_valid_service_id(query))
|
if (!rend_valid_service_id(query))
|
||||||
return -1;
|
return -1;
|
||||||
*e = strmap_get_lc(rend_cache, query);
|
*e = NULL;
|
||||||
|
if (version != 0) {
|
||||||
|
tor_snprintf(key, sizeof(key), "1%s", query);
|
||||||
|
*e = strmap_get_lc(rend_cache, key);
|
||||||
|
}
|
||||||
|
if (!*e && version != 1) {
|
||||||
|
tor_snprintf(key, sizeof(key), "0%s", query);
|
||||||
|
*e = strmap_get_lc(rend_cache, key);
|
||||||
|
}
|
||||||
if (!*e)
|
if (!*e)
|
||||||
return 0;
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
@ -251,11 +327,11 @@ rend_cache_lookup_entry(const char *query, rend_cache_entry_t **e)
|
|||||||
* *desc.
|
* *desc.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
rend_cache_lookup_desc(const char *query, const char **desc, size_t *desc_len)
|
rend_cache_lookup_desc(const char *query, int version, const char **desc, size_t *desc_len)
|
||||||
{
|
{
|
||||||
rend_cache_entry_t *e;
|
rend_cache_entry_t *e;
|
||||||
int r;
|
int r;
|
||||||
r = rend_cache_lookup_entry(query,&e);
|
r = rend_cache_lookup_entry(query,version,&e);
|
||||||
if (r <= 0) return r;
|
if (r <= 0) return r;
|
||||||
*desc = e->desc;
|
*desc = e->desc;
|
||||||
*desc_len = e->len;
|
*desc_len = e->len;
|
||||||
@ -275,6 +351,7 @@ rend_cache_store(const char *desc, size_t desc_len)
|
|||||||
rend_cache_entry_t *e;
|
rend_cache_entry_t *e;
|
||||||
rend_service_descriptor_t *parsed;
|
rend_service_descriptor_t *parsed;
|
||||||
char query[REND_SERVICE_ID_LEN+1];
|
char query[REND_SERVICE_ID_LEN+1];
|
||||||
|
char key[REND_SERVICE_ID_LEN+2];
|
||||||
time_t now;
|
time_t now;
|
||||||
|
|
||||||
tor_assert(rend_cache);
|
tor_assert(rend_cache);
|
||||||
@ -288,6 +365,7 @@ rend_cache_store(const char *desc, size_t desc_len)
|
|||||||
rend_service_descriptor_free(parsed);
|
rend_service_descriptor_free(parsed);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
tor_snprintf(key, sizeof(key), "%c%s", parsed->version?'1':'0', query);
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
if (parsed->timestamp < now-REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
|
if (parsed->timestamp < now-REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
|
||||||
log_fn(LOG_WARN,"Service descriptor %s is too old", safe_str(query));
|
log_fn(LOG_WARN,"Service descriptor %s is too old", safe_str(query));
|
||||||
@ -300,9 +378,9 @@ rend_cache_store(const char *desc, size_t desc_len)
|
|||||||
rend_service_descriptor_free(parsed);
|
rend_service_descriptor_free(parsed);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, query);
|
e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
|
||||||
if (e && e->parsed->timestamp > parsed->timestamp) {
|
if (e && e->parsed->timestamp > parsed->timestamp) {
|
||||||
log_fn(LOG_INFO,"We already have a newer service descriptor %s with the same ID", safe_str(query));
|
log_fn(LOG_INFO,"We already have a newer service descriptor %s with the same ID and version", safe_str(query));
|
||||||
rend_service_descriptor_free(parsed);
|
rend_service_descriptor_free(parsed);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -314,7 +392,7 @@ rend_cache_store(const char *desc, size_t desc_len)
|
|||||||
}
|
}
|
||||||
if (!e) {
|
if (!e) {
|
||||||
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
|
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
|
||||||
strmap_set_lc(rend_cache, query, e);
|
strmap_set_lc(rend_cache, key, e);
|
||||||
} else {
|
} else {
|
||||||
rend_service_descriptor_free(e->parsed);
|
rend_service_descriptor_free(e->parsed);
|
||||||
tor_free(e->desc);
|
tor_free(e->desc);
|
||||||
|
@ -272,9 +272,12 @@ rend_service_update_descriptor(rend_service_t *service)
|
|||||||
d = service->desc = tor_malloc(sizeof(rend_service_descriptor_t));
|
d = service->desc = tor_malloc(sizeof(rend_service_descriptor_t));
|
||||||
d->pk = crypto_pk_dup_key(service->private_key);
|
d->pk = crypto_pk_dup_key(service->private_key);
|
||||||
d->timestamp = time(NULL);
|
d->timestamp = time(NULL);
|
||||||
|
d->version = 1;
|
||||||
n = smartlist_len(service->intro_nodes);
|
n = smartlist_len(service->intro_nodes);
|
||||||
d->n_intro_points = 0;
|
d->n_intro_points = 0;
|
||||||
d->intro_points = tor_malloc(sizeof(char*)*n);
|
d->intro_points = tor_malloc_zero(sizeof(char*)*n);
|
||||||
|
d->intro_point_extend_info = tor_malloc_zero(sizeof(extend_info_t*)*n);
|
||||||
|
d->protocols = (1<<2) | (1<<0); /* We support protocol 2 and protocol 0. */
|
||||||
for (i=0; i < n; ++i) {
|
for (i=0; i < n; ++i) {
|
||||||
router = router_get_by_nickname(smartlist_get(service->intro_nodes, i));
|
router = router_get_by_nickname(smartlist_get(service->intro_nodes, i));
|
||||||
if (!router) {
|
if (!router) {
|
||||||
@ -285,7 +288,10 @@ rend_service_update_descriptor(rend_service_t *service)
|
|||||||
circ = find_intro_circuit(router, service->pk_digest);
|
circ = find_intro_circuit(router, service->pk_digest);
|
||||||
if (circ && circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
|
if (circ && circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
|
||||||
/* We have an entirely established intro circuit. */
|
/* We have an entirely established intro circuit. */
|
||||||
d->intro_points[d->n_intro_points++] = tor_strdup(router->nickname);
|
d->intro_points[d->n_intro_points] = tor_strdup(router->nickname);
|
||||||
|
d->intro_point_extend_info[d->n_intro_points] =
|
||||||
|
extend_info_from_router(router);
|
||||||
|
d->n_intro_points++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,7 +385,8 @@ rend_service_requires_uptime(rend_service_t *service) {
|
|||||||
int
|
int
|
||||||
rend_service_introduce(circuit_t *circuit, const char *request, size_t request_len)
|
rend_service_introduce(circuit_t *circuit, const char *request, size_t request_len)
|
||||||
{
|
{
|
||||||
char *ptr, *rp_nickname, *r_cookie;
|
char *ptr, *r_cookie;
|
||||||
|
extend_info_t *extend_info = NULL;
|
||||||
char buf[RELAY_PAYLOAD_SIZE];
|
char buf[RELAY_PAYLOAD_SIZE];
|
||||||
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
|
char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
|
||||||
rend_service_t *service;
|
rend_service_t *service;
|
||||||
@ -390,8 +397,6 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
|
|||||||
crypt_path_t *cpath = NULL;
|
crypt_path_t *cpath = NULL;
|
||||||
char serviceid[REND_SERVICE_ID_LEN+1];
|
char serviceid[REND_SERVICE_ID_LEN+1];
|
||||||
char hexcookie[9];
|
char hexcookie[9];
|
||||||
int version;
|
|
||||||
size_t nickname_field_len;
|
|
||||||
int circ_needs_uptime;
|
int circ_needs_uptime;
|
||||||
|
|
||||||
base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
|
base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
|
||||||
@ -441,6 +446,34 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
len = r;
|
len = r;
|
||||||
|
if (*buf == 2) {
|
||||||
|
/* Version 2 INTRODUCE2 cell. */
|
||||||
|
int klen;
|
||||||
|
extend_info = tor_malloc_zero(sizeof(extend_info_t));
|
||||||
|
extend_info->addr = ntohl(get_uint32(buf+1));
|
||||||
|
extend_info->port = ntohs(get_uint16(buf+5));
|
||||||
|
memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
|
||||||
|
extend_info->nickname[0] = '$';
|
||||||
|
base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
|
||||||
|
extend_info->identity_digest, DIGEST_LEN);
|
||||||
|
|
||||||
|
klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
|
||||||
|
if (len != 7+DIGEST_LEN+2+klen+20+128) {
|
||||||
|
log_fn(LOG_WARN, "Bad length %u for version 2 INTRODUCE2 cell.", (int)len);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
|
||||||
|
if (!extend_info->onion_key) {
|
||||||
|
log_fn(LOG_WARN, "Error decoding onion key in version 2 INTRODUCE2 cell.");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
ptr = buf+7+DIGEST_LEN+2+klen;
|
||||||
|
len -= 7+DIGEST_LEN+2+klen;
|
||||||
|
} else {
|
||||||
|
char *rp_nickname;
|
||||||
|
size_t nickname_field_len;
|
||||||
|
routerinfo_t *router;
|
||||||
|
int version;
|
||||||
if (*buf == 1) {
|
if (*buf == 1) {
|
||||||
rp_nickname = buf+1;
|
rp_nickname = buf+1;
|
||||||
nickname_field_len = MAX_HEX_NICKNAME_LEN+1;
|
nickname_field_len = MAX_HEX_NICKNAME_LEN+1;
|
||||||
@ -450,7 +483,7 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
|
|||||||
rp_nickname = buf;
|
rp_nickname = buf;
|
||||||
version = 0;
|
version = 0;
|
||||||
}
|
}
|
||||||
/* XXX when 0.0.9.x is obsolete, change this to reject version != 1. */
|
/* XXX when 0.1.0.x is obsolete, change this to reject version != 2. */
|
||||||
ptr=memchr(rp_nickname,0,nickname_field_len);
|
ptr=memchr(rp_nickname,0,nickname_field_len);
|
||||||
if (!ptr || ptr == rp_nickname) {
|
if (!ptr || ptr == rp_nickname) {
|
||||||
log_fn(LOG_WARN, "Couldn't find a null-padded nickname in INTRODUCE2 cell");
|
log_fn(LOG_WARN, "Couldn't find a null-padded nickname in INTRODUCE2 cell");
|
||||||
@ -465,10 +498,21 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
|
|||||||
ptr = rp_nickname+nickname_field_len;
|
ptr = rp_nickname+nickname_field_len;
|
||||||
len -= nickname_field_len;
|
len -= nickname_field_len;
|
||||||
len -= rp_nickname - buf; /* also remove header space used by version, if any */
|
len -= rp_nickname - buf; /* also remove header space used by version, if any */
|
||||||
|
router = router_get_by_nickname(rp_nickname);
|
||||||
|
if (!router) {
|
||||||
|
log_fn(LOG_WARN, "Couldn't found router '%s' named in rendezvous cell.",
|
||||||
|
rp_nickname);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend_info = extend_info_from_router(router);
|
||||||
|
}
|
||||||
|
|
||||||
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
|
if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
|
||||||
log_fn(LOG_WARN, "Bad length %u for INTRODUCE2 cell.", (int)len);
|
log_fn(LOG_WARN, "Bad length %u for INTRODUCE2 cell.", (int)len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
r_cookie = ptr;
|
r_cookie = ptr;
|
||||||
base16_encode(hexcookie,9,r_cookie,4);
|
base16_encode(hexcookie,9,r_cookie,4);
|
||||||
|
|
||||||
@ -492,19 +536,20 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
|
|||||||
/* Launch a circuit to alice's chosen rendezvous point.
|
/* Launch a circuit to alice's chosen rendezvous point.
|
||||||
*/
|
*/
|
||||||
for (i=0;i<MAX_REND_FAILURES;i++) {
|
for (i=0;i<MAX_REND_FAILURES;i++) {
|
||||||
launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname,
|
launched = circuit_launch_by_extend_info(
|
||||||
circ_needs_uptime, 1, 1);
|
CIRCUIT_PURPOSE_S_CONNECT_REND, extend_info, circ_needs_uptime, 1, 1);
|
||||||
|
|
||||||
if (launched)
|
if (launched)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!launched) { /* give up */
|
if (!launched) { /* give up */
|
||||||
log_fn(LOG_WARN,"Giving up launching first hop of circuit to rendezvous point '%s' for service %s",
|
log_fn(LOG_WARN,"Giving up launching first hop of circuit to rendezvous point '%s' for service %s",
|
||||||
rp_nickname, serviceid);
|
extend_info->nickname, serviceid);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
log_fn(LOG_INFO,
|
log_fn(LOG_INFO,
|
||||||
"Accepted intro; launching circuit to '%s' (cookie %s) for service %s",
|
"Accepted intro; launching circuit to '%s' (cookie %s) for service %s",
|
||||||
rp_nickname, hexcookie, serviceid);
|
extend_info->nickname, hexcookie, serviceid);
|
||||||
tor_assert(launched->build_state);
|
tor_assert(launched->build_state);
|
||||||
/* Fill in the circuit's state. */
|
/* Fill in the circuit's state. */
|
||||||
memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
|
memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
|
||||||
@ -522,11 +567,13 @@ rend_service_introduce(circuit_t *circuit, const char *request, size_t request_l
|
|||||||
if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
|
if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
|
||||||
goto err;
|
goto err;
|
||||||
memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
|
memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
|
||||||
|
if (extend_info) extend_info_free(extend_info);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err:
|
||||||
if (dh) crypto_dh_free(dh);
|
if (dh) crypto_dh_free(dh);
|
||||||
if (launched) circuit_mark_for_close(launched);
|
if (launched) circuit_mark_for_close(launched);
|
||||||
|
if (extend_info) extend_info_free(extend_info);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +592,7 @@ rend_service_relaunch_rendezvous(circuit_t *oldcirc)
|
|||||||
oldcirc->build_state->failure_count > MAX_REND_FAILURES ||
|
oldcirc->build_state->failure_count > MAX_REND_FAILURES ||
|
||||||
oldcirc->build_state->expiry_time < time(NULL)) {
|
oldcirc->build_state->expiry_time < time(NULL)) {
|
||||||
log_fn(LOG_INFO,"Attempt to build circuit to %s for rendezvous has failed too many times or expired; giving up.",
|
log_fn(LOG_INFO,"Attempt to build circuit to %s for rendezvous has failed too many times or expired; giving up.",
|
||||||
oldcirc->build_state->chosen_exit_name);
|
oldcirc->build_state->chosen_exit->nickname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -558,13 +605,13 @@ rend_service_relaunch_rendezvous(circuit_t *oldcirc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
log_fn(LOG_INFO,"Reattempting rendezvous circuit to %s",
|
log_fn(LOG_INFO,"Reattempting rendezvous circuit to %s",
|
||||||
oldstate->chosen_exit_name);
|
oldstate->chosen_exit->nickname);
|
||||||
|
|
||||||
newcirc = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND,
|
newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND,
|
||||||
oldstate->chosen_exit_name, 0, 1, 1);
|
oldstate->chosen_exit, 0, 1, 1);
|
||||||
if (!newcirc) {
|
if (!newcirc) {
|
||||||
log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s",
|
log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s",
|
||||||
oldstate->chosen_exit_name);
|
oldstate->chosen_exit->nickname);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
newstate = newcirc->build_state;
|
newstate = newcirc->build_state;
|
||||||
@ -783,8 +830,8 @@ find_intro_circuit(routerinfo_t *router, const char *pk_digest)
|
|||||||
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
||||||
CIRCUIT_PURPOSE_S_INTRO))) {
|
CIRCUIT_PURPOSE_S_INTRO))) {
|
||||||
tor_assert(circ->cpath);
|
tor_assert(circ->cpath);
|
||||||
if (circ->build_state->chosen_exit_name &&
|
if (circ->build_state->chosen_exit->nickname &&
|
||||||
!strcasecmp(circ->build_state->chosen_exit_name, router->nickname)) {
|
!strcasecmp(circ->build_state->chosen_exit->nickname, router->nickname)) {
|
||||||
return circ;
|
return circ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -793,8 +840,8 @@ find_intro_circuit(routerinfo_t *router, const char *pk_digest)
|
|||||||
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
|
||||||
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
|
CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
|
||||||
tor_assert(circ->cpath);
|
tor_assert(circ->cpath);
|
||||||
if (circ->build_state->chosen_exit_name &&
|
if (circ->build_state->chosen_exit->nickname &&
|
||||||
!strcasecmp(circ->build_state->chosen_exit_name, router->nickname)) {
|
!strcasecmp(circ->build_state->chosen_exit->nickname, router->nickname)) {
|
||||||
return circ;
|
return circ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -805,7 +852,7 @@ find_intro_circuit(routerinfo_t *router, const char *pk_digest)
|
|||||||
* and upload it to all the dirservers.
|
* and upload it to all the dirservers.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
upload_service_descriptor(rend_service_t *service)
|
upload_service_descriptor(rend_service_t *service, int version)
|
||||||
{
|
{
|
||||||
char *desc;
|
char *desc;
|
||||||
size_t desc_len;
|
size_t desc_len;
|
||||||
@ -813,6 +860,7 @@ upload_service_descriptor(rend_service_t *service)
|
|||||||
/* Update the descriptor. */
|
/* Update the descriptor. */
|
||||||
rend_service_update_descriptor(service);
|
rend_service_update_descriptor(service);
|
||||||
if (rend_encode_service_descriptor(service->desc,
|
if (rend_encode_service_descriptor(service->desc,
|
||||||
|
version,
|
||||||
service->private_key,
|
service->private_key,
|
||||||
&desc, &desc_len)<0) {
|
&desc, &desc_len)<0) {
|
||||||
log_fn(LOG_WARN, "Couldn't encode service descriptor; not uploading");
|
log_fn(LOG_WARN, "Couldn't encode service descriptor; not uploading");
|
||||||
@ -963,7 +1011,10 @@ rend_consider_services_upload(time_t now)
|
|||||||
/* if it's time, or if the directory servers have a wrong service
|
/* if it's time, or if the directory servers have a wrong service
|
||||||
* descriptor and ours has been stable for 5 seconds, upload a
|
* descriptor and ours has been stable for 5 seconds, upload a
|
||||||
* new one. */
|
* new one. */
|
||||||
upload_service_descriptor(service);
|
upload_service_descriptor(service, 0);
|
||||||
|
/* XXXX011 NM Once directories understand versioned descriptors, enable
|
||||||
|
* this. */
|
||||||
|
// upload_service_descriptor(service, 1);
|
||||||
service->next_upload_time = now + rendpostperiod;
|
service->next_upload_time = now + rendpostperiod;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1400,30 +1400,89 @@ test_rend_fns(void)
|
|||||||
rend_service_descriptor_t *d1, *d2;
|
rend_service_descriptor_t *d1, *d2;
|
||||||
char *encoded;
|
char *encoded;
|
||||||
size_t len;
|
size_t len;
|
||||||
crypto_pk_env_t *pk1;
|
crypto_pk_env_t *pk1, *pk2;
|
||||||
time_t now;
|
time_t now;
|
||||||
pk1 = crypto_new_pk_env();
|
pk1 = crypto_new_pk_env();
|
||||||
|
pk2 = crypto_new_pk_env();
|
||||||
test_assert(!crypto_pk_generate_key(pk1));
|
test_assert(!crypto_pk_generate_key(pk1));
|
||||||
|
test_assert(!crypto_pk_generate_key(pk2));
|
||||||
|
|
||||||
|
/* Test unversioned descriptor */
|
||||||
d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
||||||
d1->pk = pk1;
|
d1->pk = crypto_pk_dup_key(pk1);
|
||||||
now = time(NULL);
|
now = time(NULL);
|
||||||
d1->timestamp = now;
|
d1->timestamp = now;
|
||||||
d1->n_intro_points = 3;
|
d1->n_intro_points = 3;
|
||||||
|
d1->version = 0;
|
||||||
d1->intro_points = tor_malloc(sizeof(char*)*3);
|
d1->intro_points = tor_malloc(sizeof(char*)*3);
|
||||||
d1->intro_points[0] = tor_strdup("tom");
|
d1->intro_points[0] = tor_strdup("tom");
|
||||||
d1->intro_points[1] = tor_strdup("crow");
|
d1->intro_points[1] = tor_strdup("crow");
|
||||||
d1->intro_points[2] = tor_strdup("joel");
|
d1->intro_points[2] = tor_strdup("joel");
|
||||||
test_assert(! rend_encode_service_descriptor(d1, pk1, &encoded, &len));
|
test_assert(! rend_encode_service_descriptor(d1, 0, pk1, &encoded, &len));
|
||||||
d2 = rend_parse_service_descriptor(encoded, len);
|
d2 = rend_parse_service_descriptor(encoded, len);
|
||||||
test_assert(d2);
|
test_assert(d2);
|
||||||
|
|
||||||
test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
|
test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
|
||||||
test_eq(d2->timestamp, now);
|
test_eq(d2->timestamp, now);
|
||||||
|
test_eq(d2->version, 0);
|
||||||
|
test_eq(d2->protocols, 1);
|
||||||
test_eq(d2->n_intro_points, 3);
|
test_eq(d2->n_intro_points, 3);
|
||||||
test_streq(d2->intro_points[0], "tom");
|
test_streq(d2->intro_points[0], "tom");
|
||||||
test_streq(d2->intro_points[1], "crow");
|
test_streq(d2->intro_points[1], "crow");
|
||||||
test_streq(d2->intro_points[2], "joel");
|
test_streq(d2->intro_points[2], "joel");
|
||||||
|
test_eq(NULL, d2->intro_point_extend_info);
|
||||||
|
|
||||||
|
rend_service_descriptor_free(d1);
|
||||||
|
rend_service_descriptor_free(d2);
|
||||||
|
tor_free(encoded);
|
||||||
|
|
||||||
|
/* Test versioned descriptor. */
|
||||||
|
d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
||||||
|
d1->pk = crypto_pk_dup_key(pk1);
|
||||||
|
now = time(NULL);
|
||||||
|
d1->timestamp = now;
|
||||||
|
d1->n_intro_points = 2;
|
||||||
|
d1->version = 1;
|
||||||
|
d1->protocols = 60;
|
||||||
|
d1->intro_points = tor_malloc(sizeof(char*)*2);
|
||||||
|
d1->intro_point_extend_info = tor_malloc(sizeof(extend_info_t*)*2);
|
||||||
|
d1->intro_points[0] = tor_strdup("tom");
|
||||||
|
d1->intro_points[1] = tor_strdup("crow");
|
||||||
|
d1->intro_point_extend_info[0] = tor_malloc_zero(sizeof(extend_info_t));
|
||||||
|
strcpy(d1->intro_point_extend_info[0]->nickname, "tom");
|
||||||
|
d1->intro_point_extend_info[0]->addr = 1234;
|
||||||
|
d1->intro_point_extend_info[0]->port = 4567;
|
||||||
|
d1->intro_point_extend_info[0]->onion_key = crypto_pk_dup_key(pk1);
|
||||||
|
memset(d1->intro_point_extend_info[0]->identity_digest, 'a', DIGEST_LEN);
|
||||||
|
|
||||||
|
d1->intro_point_extend_info[1] = tor_malloc_zero(sizeof(extend_info_t));
|
||||||
|
strcpy(d1->intro_point_extend_info[1]->nickname, "crow");
|
||||||
|
d1->intro_point_extend_info[1]->addr = 6060842;
|
||||||
|
d1->intro_point_extend_info[1]->port = 8000;
|
||||||
|
d1->intro_point_extend_info[1]->onion_key = crypto_pk_dup_key(pk2);
|
||||||
|
memset(d1->intro_point_extend_info[1]->identity_digest, 'b', DIGEST_LEN);
|
||||||
|
|
||||||
|
test_assert(! rend_encode_service_descriptor(d1, 1, pk1, &encoded, &len));
|
||||||
|
d2 = rend_parse_service_descriptor(encoded, len);
|
||||||
|
test_assert(d2);
|
||||||
|
|
||||||
|
test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
|
||||||
|
test_eq(d2->timestamp, now);
|
||||||
|
test_eq(d2->version, 1);
|
||||||
|
test_eq(d2->protocols, 60);
|
||||||
|
test_eq(d2->n_intro_points, 2);
|
||||||
|
test_streq(d2->intro_points[0], d2->intro_point_extend_info[0]->nickname);
|
||||||
|
test_streq(d2->intro_points[1], d2->intro_point_extend_info[1]->nickname);
|
||||||
|
test_eq(d2->intro_point_extend_info[0]->addr, 1234);
|
||||||
|
test_eq(d2->intro_point_extend_info[0]->port, 4567);
|
||||||
|
test_assert(!crypto_pk_cmp_keys(pk1,d2->intro_point_extend_info[0]->onion_key));
|
||||||
|
test_memeq(d2->intro_point_extend_info[0]->identity_digest,
|
||||||
|
d1->intro_point_extend_info[0]->identity_digest, DIGEST_LEN);
|
||||||
|
test_eq(d2->intro_point_extend_info[1]->addr, 6060842);
|
||||||
|
test_eq(d2->intro_point_extend_info[1]->port, 8000);
|
||||||
|
|
||||||
|
test_memeq(d2->intro_point_extend_info[1]->identity_digest,
|
||||||
|
d1->intro_point_extend_info[1]->identity_digest, DIGEST_LEN);
|
||||||
|
|
||||||
test_eq(BAD_HOSTNAME, parse_extended_hostname(address1));
|
test_eq(BAD_HOSTNAME, parse_extended_hostname(address1));
|
||||||
test_eq(ONION_HOSTNAME, parse_extended_hostname(address2));
|
test_eq(ONION_HOSTNAME, parse_extended_hostname(address2));
|
||||||
@ -1432,6 +1491,8 @@ test_rend_fns(void)
|
|||||||
|
|
||||||
rend_service_descriptor_free(d1);
|
rend_service_descriptor_free(d1);
|
||||||
rend_service_descriptor_free(d2);
|
rend_service_descriptor_free(d2);
|
||||||
|
crypto_free_pk_env(pk1);
|
||||||
|
crypto_free_pk_env(pk2);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
Loading…
Reference in New Issue
Block a user