aboutsummaryrefslogtreecommitdiff
path: root/src/or/onion.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/onion.c')
-rw-r--r--src/or/onion.c133
1 files changed, 85 insertions, 48 deletions
diff --git a/src/or/onion.c b/src/or/onion.c
index 924cbece1..f1d8548eb 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -152,11 +152,12 @@ int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *key
return 0;
}
-char **parse_nickname_list(char *list, int *num) {
+#if 0
+static char **parse_nickname_list(char *list, int *num) {
char **out;
char *start,*end;
int i;
-
+
while(isspace(*list)) list++;
i=0, start = list;
@@ -175,11 +176,34 @@ char **parse_nickname_list(char *list, int *num) {
strncpy(out[i],start,end-start);
out[i][end-start] = 0; /* null terminate it */
i++;
- while(*end && isspace(*end)) end++;
+ while(isspace(*end)) end++;
start = end;
}
*num = i;
- return out;
+ return out;
+}
+#endif
+
+static void add_nickname_list_to_smartlist(smartlist_t *sl, char *list) {
+ char *start,*end;
+ char nick[MAX_NICKNAME_LEN];
+ routerinfo_t *router;
+
+ while(isspace(*list) || *list==',') list++;
+
+ start = list;
+ while(*start) {
+ end=start; while(*end && !isspace(*end) && *end != ',') end++;
+ memcpy(nick,start,end-start);
+ nick[end-start] = 0; /* null terminate it */
+ router = router_get_by_nickname(nick);
+ if(router && router->is_running)
+ smartlist_add(sl,router);
+ else
+ log_fn(LOG_WARN,"Nickname list includes '%s' which isn't a known router.",nick);
+ while(isspace(*end) || *end==',') end++;
+ start = end;
+ }
}
static int new_route_len(double cw, routerinfo_t **rarray, int rarray_len) {
@@ -412,6 +436,28 @@ static int count_acceptable_routers(routerinfo_t **rarray, int rarray_len) {
return num;
}
+/* prototypes for smartlist operations from routerlist.h
+ * they're here to prevent precedence issues with the .h files
+ */
+void router_add_running_routers_to_smartlist(smartlist_t *sl);
+
+static void remove_twins_from_smartlist(smartlist_t *sl, routerinfo_t *twin) {
+ int i;
+ routerinfo_t *r;
+
+ if(twin == NULL)
+ return;
+
+/* XXX abstraction violation: this function reaches inside smartlist :( */
+ for(i=0; i < sl->num_used; i++) {
+ r = sl->list[i];
+ if (!crypto_pk_cmp_keys(r->onion_pkey, twin->onion_pkey)) {
+ sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
+ i--; /* so we process the new i'th element */
+ }
+ }
+}
+
int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, routerinfo_t **router_out)
{
int cur_len;
@@ -419,7 +465,7 @@ int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, rout
routerinfo_t *r;
routerinfo_t *choice;
int i;
- int n_failures;
+ smartlist_t *sl;
assert(head_ptr);
assert(router_out);
@@ -437,31 +483,10 @@ int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, rout
state->desired_path_len);
return 1;
}
- log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len,
+ log_fn(LOG_DEBUG, "Path is %d long; we want %d", cur_len,
state->desired_path_len);
- n_failures = 0;
- goto start;
- again:
- log_fn(LOG_DEBUG, "Picked an already-selected router for hop %d; retrying.",
- cur_len);
- ++n_failures;
- if (n_failures == 50) {
- /* XXX hack to prevent infinite loop. Ideally we should build a list
- * of acceptable choices and then choose from it. */
- log_fn(LOG_INFO, "Unable to continue generating circuit path");
- return -1;
- }
- start:
- /* XXX through each of these, don't pick nodes that are down */
- if(cur_len == 0) { /* picking entry node */
- log_fn(LOG_DEBUG, "Contemplating first hop: random choice.");
- choice = router_pick_randomly_from_running();
- if(!choice) {
- log_fn(LOG_WARN,"No routers are running while picking entry node. Failing.");
- return -1;
- }
- } else if (cur_len == state->desired_path_len - 1) { /* Picking last node */
+ if(cur_len == state->desired_path_len - 1) { /* Picking last node */
log_fn(LOG_DEBUG, "Contemplating last hop: choice already made.");
choice = router_get_by_nickname(state->chosen_exit);
if(!choice) {
@@ -469,34 +494,46 @@ int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, rout
state->chosen_exit);
return -1;
}
+ } else if(cur_len == 0) { /* picking first node */
+ /* try the nodes in EntryNodes first */
+ sl = smartlist_create(MAX_ROUTERS_IN_DIR);
+ add_nickname_list_to_smartlist(sl,options.EntryNodes);
+ remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
+ choice = smartlist_choose(sl);
+ smartlist_free(sl);
+ if(!choice) {
+ sl = smartlist_create(MAX_ROUTERS_IN_DIR);
+ router_add_running_routers_to_smartlist(sl);
+ remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
+ choice = smartlist_choose(sl);
+ smartlist_free(sl);
+ }
+ if(!choice) {
+ log_fn(LOG_WARN,"No acceptable routers while picking entry node. Failing.");
+ return -1;
+ }
} else {
log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
- choice = router_pick_randomly_from_running();
+ sl = smartlist_create(MAX_ROUTERS_IN_DIR);
+ router_add_running_routers_to_smartlist(sl);
+ remove_twins_from_smartlist(sl,router_get_by_nickname(state->chosen_exit));
+ for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) {
+ r = router_get_by_addr_port(cpath->addr, cpath->port);
+ assert(r);
+ remove_twins_from_smartlist(sl,r);
+ }
+ choice = smartlist_choose(sl);
+ smartlist_free(sl);
+
if(!choice) {
- log_fn(LOG_WARN,"No routers are running while picking intermediate node. Failing.");
+ log_fn(LOG_WARN,"No acceptable routers while picking intermediate node. Failing.");
return -1;
}
}
- log_fn(LOG_DEBUG,"Contemplating router %s for hop %d (exit is %s)",
- choice->nickname, cur_len, state->chosen_exit);
- if (cur_len != state->desired_path_len-1 &&
- !strcasecmp(choice->nickname, state->chosen_exit)) {
- /* make sure we don't pick the exit for another node in the path */
- goto again;
- }
-
- for (i = 0, cpath = *head_ptr; i < cur_len; ++i, cpath=cpath->next) {
- r = router_get_by_addr_port(cpath->addr, cpath->port);
- assert(r);
- if (!crypto_pk_cmp_keys(r->onion_pkey, choice->onion_pkey))
- goto again; /* same key -- it or a twin is already chosen */
- if (options.ORPort &&
- !(connection_twin_get_by_addr_port(choice->addr, choice->or_port)))
- goto again; /* this node is not connected to us. */
- }
+ log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)",
+ choice->nickname, cur_len, state->chosen_exit);
- /* Okay, so we haven't used 'choice' before. */
hop = (crypt_path_t *)tor_malloc_zero(sizeof(crypt_path_t));
/* link hop into the cpath, at the end. */