diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 43dccf7dbb..373f1c78a8 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -787,6 +787,56 @@ static int new_route_len(double cw, uint8_t purpose, smartlist_t *routers) { return routelen; } +/** Fetch the list of predicted ports, turn it into a smartlist of + * strings, remove the ones that are already handled by an + * existing circuit, and return it. + */ +static smartlist_t * +circuit_get_unhandled_ports(time_t now) { + char *pp = rep_hist_get_predicted_ports(now); + smartlist_t *needed_ports = smartlist_create(); + smartlist_split_string(needed_ports, pp, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + tor_free(pp); + + circuit_remove_handled_ports(needed_ports); + return needed_ports; +} + +/** Return 1 if we already have circuits present or on the way for + * all anticipated ports. Return 0 if we should make more. + */ +int +circuit_all_predicted_ports_handled(time_t now) { + int enough; + smartlist_t *sl = circuit_get_unhandled_ports(now); + enough = (smartlist_len(sl) == 0); + smartlist_free(sl); + return enough; +} + +/** Return 1 if router can handle one or more of the ports in + * needed_ports, else return 0. + */ +static int +router_handles_some_port(routerinfo_t *router, smartlist_t *needed_ports) { + int i; + uint16_t port; + + for (i = 0; i < smartlist_len(needed_ports); ++i) { + port = *(uint16_t *)smartlist_get(needed_ports, i); + tor_assert(port); + if (router_compare_addr_to_addr_policy(0, port, router->exit_policy) != + ADDR_POLICY_REJECTED) + return 1; + } + return 0; +} + +/** How many circuits do we want simultaneously in-progress to handle + * a given stream? + */ +#define MIN_CIRCUITS_HANDLING_STREAM 2 + /** Return a pointer to a suitable router to be the exit node for the * general-purpose circuit we're about to build. * @@ -820,7 +870,7 @@ static routerinfo_t *choose_good_exit_server_general(routerlist_t *dir) if (carray[i]->type == CONN_TYPE_AP && carray[i]->state == AP_CONN_STATE_CIRCUIT_WAIT && !carray[i]->marked_for_close && - !circuit_stream_is_being_handled(carray[i])) + !circuit_stream_is_being_handled(carray[i], 0, MIN_CIRCUITS_HANDLING_STREAM)) ++n_pending_connections; } // log_fn(LOG_DEBUG, "Choosing exit node; %d connections are pending", @@ -873,7 +923,7 @@ static routerinfo_t *choose_good_exit_server_general(routerlist_t *dir) if (carray[j]->type != CONN_TYPE_AP || carray[j]->state != AP_CONN_STATE_CIRCUIT_WAIT || carray[j]->marked_for_close || - circuit_stream_is_being_handled(carray[j])) + circuit_stream_is_being_handled(carray[j], 0, MIN_CIRCUITS_HANDLING_STREAM)) continue; /* Skip everything but APs in CIRCUIT_WAIT */ if (connection_ap_can_use_exit(carray[j], router)) { ++n_supported[i]; @@ -920,18 +970,35 @@ static routerinfo_t *choose_good_exit_server_general(routerlist_t *dir) router = routerlist_sl_choose_by_bandwidth(sl); } else { /* Either there are no pending connections, or no routers even seem to - * possibly support any of them. Choose a router at random. */ + * possibly support any of them. Choose a router at random that satisfies + * at least one predicted exit port. */ + + int try; + smartlist_t *needed_ports = circuit_get_unhandled_ports(time(NULL)); + if (best_support == -1) { log(LOG_WARN, "All routers are down or middleman -- choosing a doomed exit at random."); } - for (i = 0; i < smartlist_len(dir->routers); i++) - if (n_supported[i] != -1) - smartlist_add(sl, smartlist_get(dir->routers, i)); + for (try = 0; try < 2; try++) { + /* try once to pick only from routers that satisfy a needed port, + * then if there are none, pick from any that support exiting. */ + for (i = 0; i < smartlist_len(dir->routers); i++) { + router = smartlist_get(dir->routers, i); + if (n_supported[i] != -1 && + (try || router_handles_some_port(router, needed_ports))) { + log_fn(LOG_DEBUG,"Try %d: '%s' is a possibility.", try, router->nickname); + smartlist_add(sl, router); + } + } - smartlist_subtract(sl,excludedexits); - if (options->StrictExitNodes || smartlist_overlap(sl,preferredexits)) - smartlist_intersect(sl,preferredexits); - router = routerlist_sl_choose_by_bandwidth(sl); + smartlist_subtract(sl,excludedexits); + if (options->StrictExitNodes || smartlist_overlap(sl,preferredexits)) + smartlist_intersect(sl,preferredexits); + router = routerlist_sl_choose_by_bandwidth(sl); + if (router) + break; + } + smartlist_free(needed_ports); } smartlist_free(preferredexits); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 64604679da..e316522d0f 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -249,16 +249,34 @@ void circuit_expire_building(time_t now) { } } -/** How many circuits do we want simultaneously in-progress to handle - * a given stream? +/** Remove any elements in needed_ports that are handled by an + * open or in-progress circuit. */ -#define MIN_CIRCUITS_HANDLING_STREAM 2 +void +circuit_remove_handled_ports(smartlist_t *needed_ports) { + int i; + uint16_t port; + char *portstring; -/** Return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open - * general-purpose circuits will have an acceptable exit node for - * conn. Else return 0. + for (i = 0; i < smartlist_len(needed_ports); ++i) { + portstring = smartlist_get(needed_ports, i); + port = *(uint16_t*)(portstring); + tor_assert(port); + if (circuit_stream_is_being_handled(NULL, port, 2)) { +// log_fn(LOG_DEBUG,"Port %d is already being handled; removing.", port); + smartlist_del(needed_ports, i--); + tor_free(portstring); + } else { + log_fn(LOG_DEBUG,"Port %d is not handled.", port); + } + } +} + +/** Return 1 if at least min general-purpose circuits will have + * an acceptable exit node for conn if conn is defined, else for "*:port". + * Else return 0. */ -int circuit_stream_is_being_handled(connection_t *conn) { +int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min) { circuit_t *circ; routerinfo_t *exitrouter; int num=0; @@ -266,15 +284,19 @@ int circuit_stream_is_being_handled(connection_t *conn) { for (circ=global_circuitlist;circ;circ = circ->next) { if (CIRCUIT_IS_ORIGIN(circ) && - circ->state != CIRCUIT_STATE_OPEN && !circ->marked_for_close && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL && (!circ->timestamp_dirty || circ->timestamp_dirty + get_options()->NewCircuitPeriod < now)) { exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest); - if (exitrouter && connection_ap_can_use_exit(conn, exitrouter)) - if (++num >= MIN_CIRCUITS_HANDLING_STREAM) + if (exitrouter && + ((conn && connection_ap_can_use_exit(conn, exitrouter)) || + (!conn && + router_compare_addr_to_addr_policy(0, port, exitrouter->exit_policy) != + ADDR_POLICY_REJECTED))) { + if (++num >= min) return 1; + } } } return 0; @@ -316,6 +338,7 @@ void circuit_build_needed_circs(time_t now) { } } +#if 0 /** How many simultaneous in-progress general-purpose circuits do we * want to be building at once, if there are no open general-purpose * circuits? @@ -327,6 +350,14 @@ void circuit_build_needed_circs(time_t now) { < CIRCUIT_MIN_BUILDING_GENERAL) { circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL); } +#endif + + /* if we know of a port that's been requested recently and no + * circuit is currently available that can handle it, start one + * for that too. */ + if (!circuit_all_predicted_ports_handled(now)) { + circuit_launch_by_identity(CIRCUIT_PURPOSE_C_GENERAL, NULL); + } /* XXX count idle rendezvous circs and build more */ } @@ -471,12 +502,19 @@ circuit_expire_old_circuits(void) } else if (!circ->timestamp_dirty && CIRCUIT_IS_ORIGIN(circ) && circ->state == CIRCUIT_STATE_OPEN && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) { - /* Also, gather a list of open unused general circuits that we created. - * Because we add elements to the front of global_circuitlist, - * the last elements of unused_open_circs will be the oldest - * ones. - */ - smartlist_add(unused_open_circs, circ); +#define CIRCUIT_UNUSED_CIRC_TIMEOUT 3600 /* an hour */ + if (circ->timestamp_created + CIRCUIT_UNUSED_CIRC_TIMEOUT < now) { + log_fn(LOG_DEBUG,"Closing circuit that has been unused for %d seconds.", + (int)(now - circ->timestamp_created)); + circuit_mark_for_close(circ); + } else { + /* Also, gather a list of open unused general circuits that we created. + * Because we add elements to the front of global_circuitlist, + * the last elements of unused_open_circs will be the oldest + * ones. + */ + smartlist_add(unused_open_circs, circ); + } } } for (i = MAX_UNUSED_OPEN_CIRCUITS; i < smartlist_len(unused_open_circs); ++i) { diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 1ef8242f5d..f25719a98a 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -405,6 +405,7 @@ static int connection_ap_handshake_process_socks(connection_t *conn) { return -1; } conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + rep_hist_note_used_port(socks->port, time(NULL)); /* help predict this next time */ return connection_ap_handshake_attach_circuit(conn); } else { /* it's a hidden-service request */ @@ -1016,7 +1017,7 @@ int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit) } else { addr = client_dns_lookup_entry(conn->socks_request->address); if (router_compare_addr_to_addr_policy(addr, conn->socks_request->port, - exit->exit_policy) < 0) + exit->exit_policy) == ADDR_POLICY_REJECTED) return 0; } return 1; diff --git a/src/or/directory.c b/src/or/directory.c index b58d3d35f7..66c30960ab 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -582,7 +582,7 @@ connection_dir_client_reached_eof(connection_t *conn) if (conn->purpose == DIR_PURPOSE_FETCH_DIR) { /* fetch/process the directory to learn about new routers. */ - log_fn(LOG_INFO,"Received directory (size %d):\n%s", (int)body_len, body); + log_fn(LOG_INFO,"Received directory (size %d)", (int)body_len); if (status_code == 503 || body_len == 0) { log_fn(LOG_INFO,"Empty directory. Ignoring."); tor_free(body); tor_free(headers); @@ -606,7 +606,7 @@ connection_dir_client_reached_eof(connection_t *conn) running_routers_t *rrs; routerlist_t *rl; /* just update our list of running routers, if this list is new info */ - log_fn(LOG_INFO,"Received running-routers list (size %d):\n%s", (int)body_len, body); + log_fn(LOG_INFO,"Received running-routers list (size %d)", (int)body_len); if (status_code != 200) { log_fn(LOG_WARN,"Received http status code %d from dirserver. Failing.", status_code); diff --git a/src/or/or.h b/src/or/or.h index a2fb27a75b..85d7d54dfc 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1048,6 +1048,7 @@ int circuit_init_cpath_crypto(crypt_path_t *cpath, char *key_data, int reverse); int circuit_finish_handshake(circuit_t *circ, char *reply); int circuit_truncated(circuit_t *circ, crypt_path_t *layer); int onionskin_answer(circuit_t *circ, unsigned char *payload, unsigned char *keys); +int circuit_all_predicted_ports_handled(time_t now); void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop); /********************************* circuitlist.c ***********************/ @@ -1082,7 +1083,8 @@ void assert_circuit_ok(const circuit_t *c); /********************************* circuituse.c ************************/ void circuit_expire_building(time_t now); -int circuit_stream_is_being_handled(connection_t *conn); +void circuit_remove_handled_ports(smartlist_t *needed_ports); +int circuit_stream_is_being_handled(connection_t *conn, uint16_t port, int min); void circuit_build_needed_circs(time_t now); void circuit_detach_stream(circuit_t *circ, connection_t *conn); void circuit_about_to_close_connection(connection_t *conn); @@ -1447,6 +1449,8 @@ void rep_hist_note_bytes_written(int num_bytes, time_t when); int rep_hist_bandwidth_assess(void); char *rep_hist_get_bandwidth_lines(void); void rep_history_clean(time_t before); +void rep_hist_note_used_port(uint16_t port, time_t now); +char *rep_hist_get_predicted_ports(time_t now); /********************************* rendclient.c ***************************/ diff --git a/src/or/rephist.c b/src/or/rephist.c index 2f1c976f08..3095ad46ac 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -11,6 +11,7 @@ const char rephist_c_id[] = "$Id$"; #include "or.h" static void bw_arrays_init(void); +static void predicted_ports_init(void); /** History of an OR-\>OR link. */ typedef struct link_history_t { @@ -133,6 +134,7 @@ void rep_hist_init(void) { history_map = strmap_new(); bw_arrays_init(); + predicted_ports_init(); } /** Remember that an attempt to connect to the OR with identity digest @@ -617,3 +619,78 @@ rep_hist_get_bandwidth_lines(void) return buf; } +/** A list of port numbers that have been used recently. */ +static smartlist_t *predicted_ports_list=NULL; +/** The corresponding most recently used time for each port. */ +static smartlist_t *predicted_ports_times=NULL; + +static void add_predicted_port(uint16_t port, time_t now) { + uint16_t *tmp_port = tor_malloc(sizeof(uint16_t)); + time_t *tmp_time = tor_malloc(sizeof(time_t)); + *tmp_port = port; + *tmp_time = now; + smartlist_add(predicted_ports_list, tmp_port); + smartlist_add(predicted_ports_times, tmp_time); +} + +static void predicted_ports_init(void) { + predicted_ports_list = smartlist_create(); + predicted_ports_times = smartlist_create(); + add_predicted_port(80, time(NULL)); /* add one to kickstart us */ +} + +/** Remember that port has been asked for as of time now. + * This is used for predicting what sorts of streams we'll make in the + * future and making circuits to anticipate that. + */ +void rep_hist_note_used_port(uint16_t port, time_t now) { + int i; + uint16_t *tmp_port; + time_t *tmp_time; + + tor_assert(predicted_ports_list); + tor_assert(predicted_ports_times); + + if(!port) /* record nothing */ + return; + + for (i = 0; i < smartlist_len(predicted_ports_list); ++i) { + tmp_port = smartlist_get(predicted_ports_list, i); + tmp_time = smartlist_get(predicted_ports_times, i); + if (*tmp_port == port) { + *tmp_time = now; + return; + } + } + /* it's not there yet; we need to add it */ + add_predicted_port(port, now); +} + +#define PREFERRED_PORTS_RELEVANCE_TIME (6*3600) /* 6 hours */ + +/** Allocate and return a string of space-separated port numbers that + * are likely to be asked for in the near future. + */ +char *rep_hist_get_predicted_ports(time_t now) { + int i; + uint16_t *tmp_port; + time_t *tmp_time; + + tor_assert(predicted_ports_list); + tor_assert(predicted_ports_times); + + /* clean out obsolete entries */ + for (i = 0; i < smartlist_len(predicted_ports_list); ++i) { + tmp_time = smartlist_get(predicted_ports_times, i); + if (*tmp_time + PREFERRED_PORTS_RELEVANCE_TIME < now) { + tmp_port = smartlist_get(predicted_ports_list, i); + smartlist_del(predicted_ports_list, i); + smartlist_del(predicted_ports_times, i); + tor_free(tmp_port); + tor_free(tmp_time); + i--; + } + } + return smartlist_join_strings(predicted_ports_list, " ", 0, NULL); +} + diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 9a364d4898..5abca8c149 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -936,10 +936,11 @@ router_resolve_routerlist(routerlist_t *rl) /** Decide whether a given addr:port is definitely accepted, definitely * rejected, or neither by a given policy. If addr is 0, we - * don't know the IP of the target address. + * don't know the IP of the target address. If port is 0, we + * don't know the port of the target address. * - * Returns -1 for "rejected", 0 for "accepted", 1 for "maybe" (since IP is - * unknown). + * Returns -1 for "rejected", 0 for "accepted", 1 for "maybe" (since IP or + * port is unknown). */ int router_compare_addr_to_addr_policy(uint32_t addr, uint16_t port, addr_policy_t *policy) @@ -948,7 +949,6 @@ int router_compare_addr_to_addr_policy(uint32_t addr, uint16_t port, int maybe_accept = 0; int match = 0; int maybe = 0; - struct in_addr in; addr_policy_t *tmpe; for (tmpe=policy; tmpe; tmpe=tmpe->next) { @@ -956,7 +956,8 @@ int router_compare_addr_to_addr_policy(uint32_t addr, uint16_t port, maybe = 0; if (!addr) { /* Address is unknown. */ - if (port >= tmpe->prt_min && port <= tmpe->prt_max) { + if ((port >= tmpe->prt_min && port <= tmpe->prt_max) || + (!port && tmpe->prt_min<=1 && tmpe->prt_max>=65535)) { /* The port definitely matches. */ if (tmpe->msk == 0) { match = 1; @@ -965,10 +966,6 @@ int router_compare_addr_to_addr_policy(uint32_t addr, uint16_t port, } } else if (!port) { /* The port maybe matches. */ - /* XXX Nick: it looks port 0 only means something special for resolve - * commands, which can currently be handled by any exit node. - * Should we treat those specially elsewhere? - */ maybe = 1; } } else { @@ -989,9 +986,10 @@ int router_compare_addr_to_addr_policy(uint32_t addr, uint16_t port, maybe_accept = 1; } if (match) { - in.s_addr = htonl(addr); - log_fn(LOG_DEBUG,"Address %s:%d matches policy '%s'", - inet_ntoa(in), port, tmpe->string); +// struct in_addr in; +// in.s_addr = htonl(addr); +// log_fn(LOG_DEBUG,"Address %s:%d matches policy '%s'", +// inet_ntoa(in), port, tmpe->string); if (tmpe->policy_type == ADDR_POLICY_ACCEPT) { /* If we already hit a clause that might trigger a 'reject', than we * can't be sure of this certain 'accept'.*/ @@ -1024,7 +1022,7 @@ int router_exit_policy_all_routers_reject(uint32_t addr, uint16_t port) { /** Return true iff router does not permit exit streams. */ int router_exit_policy_rejects_all(routerinfo_t *router) { - return router_compare_addr_to_addr_policy(0, 1, router->exit_policy) + return router_compare_addr_to_addr_policy(0, 0, router->exit_policy) == ADDR_POLICY_REJECTED; }