diff options
Diffstat (limited to 'src/or/rendclient.c')
-rw-r--r-- | src/or/rendclient.c | 162 |
1 files changed, 94 insertions, 68 deletions
diff --git a/src/or/rendclient.c b/src/or/rendclient.c index bb64095ee4..e31179355f 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -58,15 +58,16 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) size_t payload_len; int r; 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; crypt_path_t *cpath; + off_t dh_offset; tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING); tor_assert(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY); 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.", safe_str(introcirc->rend_query)); goto err; @@ -95,22 +96,28 @@ rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc) } /* write the remaining items into tmp */ -#if 0 - tmp[0] = 1; /* version 1 of the cell format */ - /* nul pads */ - strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1)); - memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN); -#else - strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */ - memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN); -#endif - 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 (entry->parsed->protocols & (1<<2)) { + /* 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 */ + set_uint32(tmp+1, htonl(extend_info->addr)); + set_uint16(tmp+5, htons(extend_info->port)); + memcpy(tmp+7, extend_info->identity_digest, DIGEST_LEN); + 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); + dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN; + } + if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset, DH_KEY_LEN)<0) { log_fn(LOG_WARN, "Couldn't extract g^x"); 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, * to avoid buffer overflows? */ r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp, -#if 0 - 1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN, -#else - MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN, -#endif + dh_offset+DH_KEY_LEN, PK_PKCS1_OAEP_PADDING, 0); if (r<0) { log_fn(LOG_WARN,"hybrid pk encrypt failed."); @@ -174,7 +177,6 @@ int rend_client_introduction_acked(circuit_t *circ, const char *request, size_t request_len) { - char *nickname; circuit_t *rendcirc; if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { @@ -184,7 +186,8 @@ rend_client_introduction_acked(circuit_t *circ, 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) { /* 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. * 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) { /* There are introduction points left. re-extend the circuit to * another intro point and try again. */ - routerinfo_t *r; - nickname = rend_client_get_random_intro(circ->rend_query); - tor_assert(nickname); - log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.", - safe_str(circ->rend_query), - circ->build_state->chosen_exit_name, nickname); - 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); + extend_info_t *info; + int result; + info = rend_client_get_random_intro(circ->rend_query); + if (!info) { + log_fn(LOG_WARN, "No introduction points left for %s. Closing.", + safe_str(circ->rend_query)); circuit_mark_for_close(circ); return -1; } - log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)", - nickname, safe_str(circ->rend_query), circ->n_circ_id); - tor_free(nickname); - return circuit_extend_to_new_exit(circ, r); + log_fn(LOG_INFO,"Got nack for %s from %s, extending circ %d to %s.", + safe_str(circ->rend_query), + circ->build_state->chosen_exit->nickname, circ->n_circ_id, + info->nickname); + result = circuit_extend_to_new_exit(circ, info); + extend_info_free(info); + return result; } } return 0; @@ -257,13 +259,13 @@ rend_client_refetch_renddesc(const char *query) * unrecognized, 1 if recognized and some intro points remain. */ 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; rend_cache_entry_t *ent; connection_t *conn; - r = rend_cache_lookup_entry(query, &ent); + r = rend_cache_lookup_entry(query, -1, &ent); if (r<0) { log_fn(LOG_WARN, "Malformed service ID '%s'", safe_str(query)); return -1; @@ -275,12 +277,31 @@ rend_client_remove_intro_point(char *failed_intro, const char *query) return 0; } - for (i=0; i < ent->parsed->n_intro_points; ++i) { - if (!strcasecmp(ent->parsed->intro_points[i], failed_intro)) { - tor_free(ent->parsed->intro_points[i]); - ent->parsed->intro_points[i] = - ent->parsed->intro_points[--ent->parsed->n_intro_points]; - break; + if (ent->parsed->intro_point_extend_info) { + for (i=0; i < ent->parsed->n_intro_points; ++i) { + 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]); + ent->parsed->intro_points[i] = + ent->parsed->intro_points[--ent->parsed->n_intro_points]; + break; + } } } @@ -385,7 +406,7 @@ rend_client_receive_rendezvous(circuit_t *circ, const char *request, size_t requ * else fail them. */ void -rend_client_desc_here(char *query) +rend_client_desc_here(const char *query) { connection_t *conn; 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, 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) { /* either this fetch worked, or it failed but there was a * 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 - * point of query. return NULL if error. +/** Return a newly allocated extend_info_t* for a randomly chosen introduction + * point for the named hidden service. Return NULL if all introduction points + * have been tried and failed. */ -char * -rend_client_get_random_intro(char *query) +extend_info_t * +rend_client_get_random_intro(const char *query) { int i; - smartlist_t *sl; - char *choice; - char *nickname; 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.", safe_str(query)); return NULL; } - sl = smartlist_create(); + again: + if (!entry->parsed->n_intro_points) + return NULL; - /* add the intro point nicknames */ - for (i=0;i<entry->parsed->n_intro_points;i++) - smartlist_add(sl,entry->parsed->intro_points[i]); + i = crypto_pseudo_rand_int(entry->parsed->n_intro_points); - choice = smartlist_choose(sl); - if (!choice) { - smartlist_free(sl); - return NULL; + 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; } |