diff options
Diffstat (limited to 'src/or')
44 files changed, 1273 insertions, 634 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index 2a88382501..db926955b4 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -666,12 +666,12 @@ read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls, * (because of EOF), set *<b>reached_eof</b> to 1 and return 0. Return -1 on * error; else return the number of bytes read. */ -/* XXXX021 indicate "read blocked" somehow? */ +/* XXXX023 indicate "read blocked" somehow? */ int read_to_buf(int s, size_t at_most, buf_t *buf, int *reached_eof, int *socket_error) { - /* XXXX021 It's stupid to overload the return values for these functions: + /* XXXX023 It's stupid to overload the return values for these functions: * "error status" and "number of bytes read" are not mutually exclusive. */ int r = 0; @@ -856,7 +856,7 @@ flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk, int flush_buf(int s, buf_t *buf, size_t sz, size_t *buf_flushlen) { - /* XXXX021 It's stupid to overload the return values for these functions: + /* XXXX023 It's stupid to overload the return values for these functions: * "error status" and "number of bytes flushed" are not mutually exclusive. */ int r; @@ -1336,6 +1336,10 @@ log_unsafe_socks_warning(int socks_protocol, const char *address, socks_protocol, address, (int)port); } +/** Do not attempt to parse socks messages longer than this. This value is + * actually significantly higher than the longest possible socks message. */ +#define MAX_SOCKS_MESSAGE_LEN 512 + /** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one * of the forms * - socks4: "socksheader username\\0" @@ -1377,7 +1381,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req, if (buf->datalen < 2) /* version and another byte */ return 0; - buf_pullup(buf, 128, 0); + buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0); tor_assert(buf->head && buf->head->datalen >= 2); socksver = *buf->head->data; @@ -1666,7 +1670,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason) if (buf->datalen < 2) return 0; - buf_pullup(buf, 128, 0); + buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0); tor_assert(buf->head && buf->head->datalen >= 2); data = (unsigned char *) buf->head->data; diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index b1743847c8..2d4d5c032a 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -42,11 +42,12 @@ /********* START VARIABLES **********/ /** Global list of circuit build times */ -// FIXME: Add this as a member for entry_guard_t instead of global? +// XXXX023: Add this as a member for entry_guard_t instead of global? // Then we could do per-guard statistics, as guards are likely to // vary in their own latency. The downside of this is that guards // can change frequently, so we'd be building a lot more circuits // most likely. +/* XXXX023 Make this static; add accessor functions. */ circuit_build_times_t circ_times; /** A global list of all circuits at this hop. */ @@ -99,6 +100,15 @@ static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); static void entry_guards_changed(void); +/** + * This function decides if CBT learning should be disabled. It returns + * true if one or more of the following four conditions are met: + * + * 1. If the cbtdisabled consensus parameter is set. + * 2. If the torrc option LearnCircuitBuildTimeout is false. + * 3. If we are a directory authority + * 4. If we fail to write circuit build time history to our state file. + */ static int circuit_build_times_disabled(void) { @@ -106,7 +116,7 @@ circuit_build_times_disabled(void) return 0; } else { int consensus_disabled = networkstatus_get_param(NULL, "cbtdisabled", - 0); + 0, 0, 1); int config_disabled = !get_options()->LearnCircuitBuildTimeout; int dirauth_disabled = get_options()->AuthoritativeDir; int state_disabled = (get_or_state()->LastWritten == -1); @@ -125,27 +135,54 @@ circuit_build_times_disabled(void) } } +/** + * Retrieve and bounds-check the cbtmaxtimeouts consensus paramter. + * + * Effect: When this many timeouts happen in the last 'cbtrecentcount' + * circuit attempts, the client should discard all of its history and + * begin learning a fresh timeout value. + */ static int32_t circuit_build_times_max_timeouts(void) { - int32_t num = networkstatus_get_param(NULL, "cbtmaxtimeouts", - CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT); - return num; + return networkstatus_get_param(NULL, "cbtmaxtimeouts", + CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT, + CBT_MIN_MAX_RECENT_TIMEOUT_COUNT, + CBT_MAX_MAX_RECENT_TIMEOUT_COUNT); } +/** + * Retrieve and bounds-check the cbtnummodes consensus paramter. + * + * Effect: This value governs how many modes to use in the weighted + * average calculation of Pareto parameter Xm. A value of 3 introduces + * some bias (2-5% of CDF) under ideal conditions, but allows for better + * performance in the event that a client chooses guard nodes of radically + * different performance characteristics. + */ static int32_t circuit_build_times_default_num_xm_modes(void) { int32_t num = networkstatus_get_param(NULL, "cbtnummodes", - CBT_DEFAULT_NUM_XM_MODES); + CBT_DEFAULT_NUM_XM_MODES, + CBT_MIN_NUM_XM_MODES, + CBT_MAX_NUM_XM_MODES); return num; } +/** + * Retrieve and bounds-check the cbtmincircs consensus paramter. + * + * Effect: This is the minimum number of circuits to build before + * computing a timeout. + */ static int32_t circuit_build_times_min_circs_to_observe(void) { int32_t num = networkstatus_get_param(NULL, "cbtmincircs", - CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE); + CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE, + CBT_MIN_MIN_CIRCUITS_TO_OBSERVE, + CBT_MAX_MIN_CIRCUITS_TO_OBSERVE); return num; } @@ -157,53 +194,126 @@ circuit_build_times_enough_to_compute(circuit_build_times_t *cbt) return cbt->total_build_times >= circuit_build_times_min_circs_to_observe(); } +/** + * Retrieve and bounds-check the cbtquantile consensus paramter. + * + * Effect: This is the position on the quantile curve to use to set the + * timeout value. It is a percent (10-99). + */ double circuit_build_times_quantile_cutoff(void) { int32_t num = networkstatus_get_param(NULL, "cbtquantile", - CBT_DEFAULT_QUANTILE_CUTOFF); + CBT_DEFAULT_QUANTILE_CUTOFF, + CBT_MIN_QUANTILE_CUTOFF, + CBT_MAX_QUANTILE_CUTOFF); return num/100.0; } +int +circuit_build_times_get_bw_scale(networkstatus_t *ns) +{ + return networkstatus_get_param(ns, "bwweightscale", + BW_WEIGHT_SCALE, + BW_MIN_WEIGHT_SCALE, + BW_MAX_WEIGHT_SCALE); +} + +/** + * Retrieve and bounds-check the cbtclosequantile consensus paramter. + * + * Effect: This is the position on the quantile curve to use to set the + * timeout value to use to actually close circuits. It is a percent + * (0-99). + */ static double circuit_build_times_close_quantile(void) { - int32_t num = networkstatus_get_param(NULL, "cbtclosequantile", - CBT_DEFAULT_CLOSE_QUANTILE); - - return num/100.0; + int32_t param; + /* Cast is safe - circuit_build_times_quantile_cutoff() is capped */ + int32_t min = (int)tor_lround(100*circuit_build_times_quantile_cutoff()); + param = networkstatus_get_param(NULL, "cbtclosequantile", + CBT_DEFAULT_CLOSE_QUANTILE, + CBT_MIN_CLOSE_QUANTILE, + CBT_MAX_CLOSE_QUANTILE); + if (param < min) { + log_warn(LD_DIR, "Consensus parameter cbtclosequantile is " + "too small, raising to %d", min); + param = min; + } + return param / 100.0; } +/** + * Retrieve and bounds-check the cbttestfreq consensus paramter. + * + * Effect: Describes how often in seconds to build a test circuit to + * gather timeout values. Only applies if less than 'cbtmincircs' + * have been recorded. + */ static int32_t circuit_build_times_test_frequency(void) { int32_t num = networkstatus_get_param(NULL, "cbttestfreq", - CBT_DEFAULT_TEST_FREQUENCY); + CBT_DEFAULT_TEST_FREQUENCY, + CBT_MIN_TEST_FREQUENCY, + CBT_MAX_TEST_FREQUENCY); return num; } +/** + * Retrieve and bounds-check the cbtmintimeout consensus paramter. + * + * Effect: This is the minimum allowed timeout value in milliseconds. + * The minimum is to prevent rounding to 0 (we only check once + * per second). + */ static int32_t circuit_build_times_min_timeout(void) { int32_t num = networkstatus_get_param(NULL, "cbtmintimeout", - CBT_DEFAULT_TIMEOUT_MIN_VALUE); + CBT_DEFAULT_TIMEOUT_MIN_VALUE, + CBT_MIN_TIMEOUT_MIN_VALUE, + CBT_MAX_TIMEOUT_MIN_VALUE); return num; } +/** + * Retrieve and bounds-check the cbtinitialtimeout consensus paramter. + * + * Effect: This is the timeout value to use before computing a timeout, + * in milliseconds. + */ int32_t circuit_build_times_initial_timeout(void) { - int32_t num = networkstatus_get_param(NULL, "cbtinitialtimeout", - CBT_DEFAULT_TIMEOUT_INITIAL_VALUE); - return num; + int32_t min = circuit_build_times_min_timeout(); + int32_t param = networkstatus_get_param(NULL, "cbtinitialtimeout", + CBT_DEFAULT_TIMEOUT_INITIAL_VALUE, + CBT_MIN_TIMEOUT_INITIAL_VALUE, + CBT_MAX_TIMEOUT_INITIAL_VALUE); + if (param < min) { + log_warn(LD_DIR, "Consensus parameter cbtinitialtimeout is too small, " + "raising to %d", min); + param = min; + } + return param; } +/** + * Retrieve and bounds-check the cbtrecentcount consensus paramter. + * + * Effect: This is the number of circuit build times to keep track of + * for deciding if we hit cbtmaxtimeouts and need to reset our state + * and learn a new timeout. + */ static int32_t -circuit_build_times_recent_circuit_count(void) +circuit_build_times_recent_circuit_count(networkstatus_t *ns) { - int32_t num = networkstatus_get_param(NULL, "cbtrecentcount", - CBT_DEFAULT_RECENT_CIRCUITS); - return num; + return networkstatus_get_param(ns, "cbtrecentcount", + CBT_DEFAULT_RECENT_CIRCUITS, + CBT_MIN_RECENT_CIRCUITS, + CBT_MAX_RECENT_CIRCUITS); } /** @@ -216,13 +326,13 @@ void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, networkstatus_t *ns) { - int32_t num = networkstatus_get_param(ns, "cbtrecentcount", - CBT_DEFAULT_RECENT_CIRCUITS); + int32_t num = circuit_build_times_recent_circuit_count(ns); if (num > 0 && num != cbt->liveness.num_recent_circs) { int8_t *recent_circs; - log_notice(LD_CIRC, "Changing recent timeout size from %d to %d", - cbt->liveness.num_recent_circs, num); + log_notice(LD_CIRC, "The Tor Directory Consensus has changed how many " + "circuits we must track to detect network failures from %d " + "to %d.", cbt->liveness.num_recent_circs, num); tor_assert(cbt->liveness.timeouts_after_firsthop); @@ -307,7 +417,8 @@ void circuit_build_times_init(circuit_build_times_t *cbt) { memset(cbt, 0, sizeof(*cbt)); - cbt->liveness.num_recent_circs = circuit_build_times_recent_circuit_count(); + cbt->liveness.num_recent_circs = + circuit_build_times_recent_circuit_count(NULL); cbt->liveness.timeouts_after_firsthop = tor_malloc_zero(sizeof(int8_t)* cbt->liveness.num_recent_circs); cbt->close_ms = cbt->timeout_ms = circuit_build_times_get_initial_timeout(); @@ -347,7 +458,7 @@ circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n) * Add a new build time value <b>time</b> to the set of build times. Time * units are milliseconds. * - * circuit_build_times <b>cbt</a> is a circular array, so loop around when + * circuit_build_times <b>cbt</b> is a circular array, so loop around when * array is full. */ int @@ -545,17 +656,19 @@ circuit_build_times_update_state(circuit_build_times_t *cbt, /** * Shuffle the build times array. * - * Stolen from http://en.wikipedia.org/wiki/Fisher\u2013Yates_shuffle + * Adapted from http://en.wikipedia.org/wiki/Fisher-Yates_shuffle */ static void circuit_build_times_shuffle_and_store_array(circuit_build_times_t *cbt, build_time_t *raw_times, - int num_times) + uint32_t num_times) { - int n = num_times; + uint32_t n = num_times; if (num_times > CBT_NCIRCUITS_TO_OBSERVE) { - log_notice(LD_CIRC, "Decreasing circuit_build_times size from %d to %d", - num_times, CBT_NCIRCUITS_TO_OBSERVE); + log_notice(LD_CIRC, "The number of circuit times that this Tor version " + "uses to calculate build times is less than the number stored " + "in your state file. Decreasing the circuit time history from " + "%d to %d.", num_times, CBT_NCIRCUITS_TO_OBSERVE); } /* This code can only be run on a compact array */ @@ -1036,7 +1149,7 @@ circuit_build_times_network_close(circuit_build_times_t *cbt, if (cbt->liveness.nonlive_timeouts == 1) { log_notice(LD_CIRC, "Tor has not observed any network activity for the past %d " - "seconds. Disabling circuit build timeout code.", + "seconds. Disabling circuit build timeout recording.", (int)(now - cbt->liveness.network_last_live)); } else { log_info(LD_CIRC, @@ -1120,7 +1233,7 @@ circuit_build_times_network_check_changed(circuit_build_times_t *cbt) control_event_buildtimeout_set(cbt, BUILDTIMEOUT_SET_EVENT_RESET); log_notice(LD_CIRC, - "Network connection speed appears to have changed. Resetting " + "Your network connection speed appears to have changed. Resetting " "timeout to %lds after %d timeouts and %d buildtimes.", tor_lround(cbt->timeout_ms/1000), timeout_count, total_build_times); @@ -1258,7 +1371,7 @@ circuit_build_times_set_timeout_worker(circuit_build_times_t *cbt) } if (max_time < INT32_MAX/2 && cbt->close_ms > 2*max_time) { - log_notice(LD_CIRC, + log_info(LD_CIRC, "Circuit build measurement period of %dms is more than twice " "the maximum build time we have ever observed. Capping it to " "%dms.", (int)cbt->close_ms, 2*max_time); @@ -1929,7 +2042,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) struct timeval end; long timediff; tor_gettimeofday(&end); - timediff = tv_mdiff(&circ->_base.highres_created, &end); + timediff = tv_mdiff(&circ->_base.timestamp_created, &end); /* * If the circuit build time is much greater than we would have cut @@ -3702,7 +3815,7 @@ entry_guards_compute_status(or_options_t *options, time_t now) * If <b>mark_relay_status</b>, also call router_set_status() on this * relay. * - * XXX022 change succeeded and mark_relay_status into 'int flags'. + * XXX023 change succeeded and mark_relay_status into 'int flags'. */ int entry_guard_register_connect_status(const char *digest, int succeeded, @@ -3860,7 +3973,7 @@ entry_guards_prepend_from_config(or_options_t *options) /* Split entry guards into those on the list and those not. */ - /* XXXX022 Now that we allow countries and IP ranges in EntryNodes, this is + /* XXXX023 Now that we allow countries and IP ranges in EntryNodes, this is * potentially an enormous list. For now, we disable such values for * EntryNodes in options_validate(); really, this wants a better solution. * Perhaps we should do this calculation once whenever the list of routers @@ -3951,7 +4064,6 @@ choose_random_entry(cpath_build_state_t *state) int preferred_min, consider_exit_family = 0; if (chosen_exit) { - smartlist_add(exit_family, chosen_exit); routerlist_add_family(exit_family, chosen_exit); consider_exit_family = 1; } @@ -3974,6 +4086,8 @@ choose_random_entry(cpath_build_state_t *state) r = entry_is_live(entry, need_uptime, need_capacity, 0, &msg); if (!r) continue; /* down, no point */ + if (r == chosen_exit) + continue; /* don't pick the same node for entry and exit */ if (consider_exit_family && smartlist_isin(exit_family, r)) continue; /* avoid relays that are family members of our exit */ if (options->EntryNodes && @@ -4191,7 +4305,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) } entry_guards = new_entry_guards; entry_guards_dirty = 0; - /* XXX022 hand new_entry_guards to this func, and move it up a + /* XXX023 hand new_entry_guards to this func, and move it up a * few lines, so we don't have to re-dirty it */ if (remove_obsolete_entry_guards(now)) entry_guards_dirty = 1; @@ -4558,6 +4672,29 @@ fetch_bridge_descriptors(or_options_t *options, time_t now) SMARTLIST_FOREACH_END(bridge); } +/** If our <b>bridge</b> is configured to be a different address than + * the bridge gives in its routerinfo <b>ri</b>, rewrite the routerinfo + * we received to use the address we meant to use. Now we handle + * multihomed bridges better. + */ +static void +rewrite_routerinfo_address_for_bridge(bridge_info_t *bridge, routerinfo_t *ri) +{ + tor_addr_t addr; + tor_addr_from_ipv4h(&addr, ri->addr); + + if (!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && + bridge->port == ri->or_port) + return; /* they match, so no need to do anything */ + + ri->addr = tor_addr_to_ipv4h(&bridge->addr); + tor_free(ri->address); + ri->address = tor_dup_ip(ri->addr); + ri->or_port = bridge->port; + log_info(LD_DIR, "Adjusted bridge '%s' to match configured address %s:%d.", + ri->nickname, ri->address, ri->or_port); +} + /** We just learned a descriptor for a bridge. See if that * digest is in our entry guard list, and add it if not. */ void @@ -4576,6 +4713,8 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) if (!from_cache) download_status_reset(&bridge->fetch_status); + rewrite_routerinfo_address_for_bridge(bridge, ri); + add_an_entry_guard(ri, 1); log_notice(LD_DIR, "new bridge descriptor '%s' (%s)", ri->nickname, from_cache ? "cached" : "fresh"); @@ -4615,7 +4754,8 @@ any_pending_bridge_descriptor_fetches(void) conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC && TO_DIR_CONN(conn)->router_purpose == ROUTER_PURPOSE_BRIDGE && !conn->marked_for_close && - conn->linked && !conn->linked_conn->marked_for_close) { + conn->linked && + conn->linked_conn && !conn->linked_conn->marked_for_close) { log_debug(LD_DIR, "found one: %s", conn->address); return 1; } diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 74bbd4f22a..af24931878 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -121,5 +121,7 @@ void circuit_build_times_network_is_live(circuit_build_times_t *cbt); int circuit_build_times_network_check_live(circuit_build_times_t *cbt); void circuit_build_times_network_circ_success(circuit_build_times_t *cbt); +int circuit_build_times_get_bw_scale(networkstatus_t *ns); + #endif diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 58ff27e5e1..d11b457944 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -384,7 +384,9 @@ circuit_purpose_to_controller_string(uint8_t purpose) int32_t circuit_initial_package_window(void) { - int32_t num = networkstatus_get_param(NULL, "circwindow", CIRCWINDOW_START); + int32_t num = networkstatus_get_param(NULL, "circwindow", CIRCWINDOW_START, + CIRCWINDOW_START_MIN, + CIRCWINDOW_START_MAX); /* If the consensus tells us a negative number, we'd assert. */ if (num < 0) num = CIRCWINDOW_START; @@ -396,8 +398,7 @@ circuit_initial_package_window(void) static void init_circuit_base(circuit_t *circ) { - circ->timestamp_created = time(NULL); - tor_gettimeofday(&circ->highres_created); + tor_gettimeofday(&circ->timestamp_created); circ->package_window = circuit_initial_package_window(); circ->deliver_window = CIRCWINDOW_START; @@ -607,9 +608,10 @@ circuit_dump_details(int severity, circuit_t *circ, int conn_array_index, const char *type, int this_circid, int other_circid) { log(severity, LD_CIRC, "Conn %d has %s circuit: circID %d (other side %d), " - "state %d (%s), born %d:", + "state %d (%s), born %ld:", conn_array_index, type, this_circid, other_circid, circ->state, - circuit_state_to_string(circ->state), (int)circ->timestamp_created); + circuit_state_to_string(circ->state), + (long)circ->timestamp_created.tv_sec); if (CIRCUIT_IS_ORIGIN(circ)) { /* circ starts at this node */ circuit_log_path(severity, LD_CIRC, TO_ORIGIN_CIRCUIT(circ)); } @@ -1021,6 +1023,7 @@ circuit_mark_all_unused_circs(void) * This is useful for letting the user change pseudonyms, so new * streams will not be linkable to old streams. */ +/* XXX023 this is a bad name for what this function does */ void circuit_expire_all_dirty_circs(void) { @@ -1031,6 +1034,8 @@ circuit_expire_all_dirty_circs(void) if (CIRCUIT_IS_ORIGIN(circ) && !circ->marked_for_close && circ->timestamp_dirty) + /* XXXX023 This is a screwed-up way to say "This is too dirty + * for new circuits. */ circ->timestamp_dirty -= options->MaxCircuitDirtiness; } } diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 8d9d115863..cdf49e3983 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -31,7 +31,7 @@ extern circuit_t *global_circuitlist; /* from circuitlist.c */ /********* END VARIABLES ************/ -static void circuit_expire_old_circuits_clientside(time_t now); +static void circuit_expire_old_circuits_clientside(void); static void circuit_increment_failure_count(void); /** Return 1 if <b>circ</b> could be returned by circuit_get_best(). @@ -162,7 +162,7 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose) return 1; } else { if (a->timestamp_dirty || - a->timestamp_created > b->timestamp_created) + timercmp(&a->timestamp_created, &b->timestamp_created, >)) return 1; if (CIRCUIT_IS_ORIGIN(b) && TO_ORIGIN_CIRCUIT(b)->build_state->is_internal) @@ -204,7 +204,7 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose, int need_uptime, int need_internal) { circuit_t *circ, *best=NULL; - time_t now = time(NULL); + struct timeval now; int intro_going_on_but_too_old = 0; tor_assert(conn); @@ -213,17 +213,16 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose, purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT || purpose == CIRCUIT_PURPOSE_C_REND_JOINED); + tor_gettimeofday(&now); + for (circ=global_circuitlist;circ;circ = circ->next) { if (!circuit_is_acceptable(circ,conn,must_be_open,purpose, - need_uptime,need_internal,now)) + need_uptime,need_internal,now.tv_sec)) continue; -/* XXX022 make this 15 be a function of circuit finishing times we've - * seen lately, a la Fallon Chen's GSoC work -RD */ -#define REND_PARALLEL_INTRO_DELAY 15 if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT && - !must_be_open && circ->state != CIRCUIT_STATE_OPEN && - circ->timestamp_created + REND_PARALLEL_INTRO_DELAY < now) { + !must_be_open && circ->state != CIRCUIT_STATE_OPEN && + tv_mdiff(&now, &circ->timestamp_created) > circ_times.timeout_ms) { intro_going_on_but_too_old = 1; continue; } @@ -275,22 +274,38 @@ circuit_conforms_to_options(const origin_circuit_t *circ, * at least CircuitBuildTimeout seconds ago. */ void -circuit_expire_building(time_t now) +circuit_expire_building(void) { circuit_t *victim, *next_circ = global_circuitlist; /* circ_times.timeout_ms and circ_times.close_ms are from * circuit_build_times_get_initial_timeout() if we haven't computed * custom timeouts yet */ - time_t general_cutoff = now - tor_lround(circ_times.timeout_ms/1000); - time_t begindir_cutoff = now - tor_lround(circ_times.timeout_ms/2000); - time_t fourhop_cutoff = now - tor_lround(4*circ_times.timeout_ms/3000); - time_t cannibalize_cutoff = now - tor_lround(circ_times.timeout_ms/2000); - time_t close_cutoff = now - tor_lround(circ_times.close_ms/1000); - time_t introcirc_cutoff = begindir_cutoff; + struct timeval general_cutoff, begindir_cutoff, fourhop_cutoff, + cannibalize_cutoff, close_cutoff, extremely_old_cutoff; + struct timeval now; + struct timeval introcirc_cutoff; cpath_build_state_t *build_state; + tor_gettimeofday(&now); +#define SET_CUTOFF(target, msec) do { \ + long ms = tor_lround(msec); \ + struct timeval diff; \ + diff.tv_sec = ms / 1000; \ + diff.tv_usec = (int)((ms % 1000) * 1000); \ + timersub(&now, &diff, &target); \ + } while (0) + + SET_CUTOFF(general_cutoff, circ_times.timeout_ms); + SET_CUTOFF(begindir_cutoff, circ_times.timeout_ms / 2.0); + SET_CUTOFF(fourhop_cutoff, circ_times.timeout_ms * (4/3.0)); + SET_CUTOFF(cannibalize_cutoff, circ_times.timeout_ms / 2.0); + SET_CUTOFF(close_cutoff, circ_times.close_ms); + SET_CUTOFF(extremely_old_cutoff, circ_times.close_ms*2 + 1000); + + introcirc_cutoff = begindir_cutoff; + while (next_circ) { - time_t cutoff; + struct timeval cutoff; victim = next_circ; next_circ = next_circ->next; if (!CIRCUIT_IS_ORIGIN(victim) || /* didn't originate here */ @@ -312,7 +327,7 @@ circuit_expire_building(time_t now) else cutoff = general_cutoff; - if (victim->timestamp_created > cutoff) + if (timercmp(&victim->timestamp_created, &cutoff, >)) continue; /* it's still young, leave it alone */ #if 0 @@ -358,7 +373,7 @@ circuit_expire_building(time_t now) * because that's set when they switch purposes */ if (TO_ORIGIN_CIRCUIT(victim)->rend_data || - victim->timestamp_dirty > cutoff) + victim->timestamp_dirty > cutoff.tv_sec) continue; break; case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: @@ -367,7 +382,7 @@ circuit_expire_building(time_t now) * make an introduction attempt. so timestamp_dirty * will reflect the time since the last attempt. */ - if (victim->timestamp_dirty > cutoff) + if (victim->timestamp_dirty > cutoff.tv_sec) continue; break; } @@ -407,15 +422,15 @@ circuit_expire_building(time_t now) * it off at, we probably had a suspend event along this codepath, * and we should discard the value. */ - if (now - victim->timestamp_created > 2*circ_times.close_ms/1000+1) { + if (timercmp(&victim->timestamp_created, &extremely_old_cutoff, <)) { log_notice(LD_CIRC, "Extremely large value for circuit build timeout: %lds. " "Assuming clock jump. Purpose %d", - (long)(now - victim->timestamp_created), + (long)(now.tv_sec - victim->timestamp_created.tv_sec), victim->purpose); } else if (circuit_build_times_count_close(&circ_times, first_hop_succeeded, - victim->timestamp_created)) { + victim->timestamp_created.tv_sec)) { circuit_build_times_set_timeout(&circ_times); } } @@ -636,7 +651,7 @@ circuit_build_needed_circs(time_t now) time_to_new_circuit = now + options->NewCircuitPeriod; if (proxy_mode(get_options())) addressmap_clean(now); - circuit_expire_old_circuits_clientside(now); + circuit_expire_old_circuits_clientside(); #if 0 /* disable for now, until predict-and-launch-new can cull leftovers */ circ = circuit_get_youngest_clean_open(CIRCUIT_PURPOSE_C_GENERAL); @@ -725,17 +740,20 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn) * for too long and has no streams on it: mark it for close. */ static void -circuit_expire_old_circuits_clientside(time_t now) +circuit_expire_old_circuits_clientside(void) { circuit_t *circ; - time_t cutoff; + struct timeval cutoff, now; + + tor_gettimeofday(&now); + cutoff = now; if (circuit_build_times_needs_circuits(&circ_times)) { /* Circuits should be shorter lived if we need more of them * for learning a good build timeout */ - cutoff = now - IDLE_TIMEOUT_WHILE_LEARNING; + cutoff.tv_sec -= IDLE_TIMEOUT_WHILE_LEARNING; } else { - cutoff = now - get_options()->CircuitIdleTimeout; + cutoff.tv_sec -= get_options()->CircuitIdleTimeout; } for (circ = global_circuitlist; circ; circ = circ->next) { @@ -745,15 +763,15 @@ circuit_expire_old_circuits_clientside(time_t now) * on it, mark it for close. */ if (circ->timestamp_dirty && - circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now && + circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now.tv_sec && !TO_ORIGIN_CIRCUIT(circ)->p_streams /* nothing attached */ ) { - log_debug(LD_CIRC, "Closing n_circ_id %d (dirty %d secs ago, " + log_debug(LD_CIRC, "Closing n_circ_id %d (dirty %ld sec ago, " "purpose %d)", - circ->n_circ_id, (int)(now - circ->timestamp_dirty), + circ->n_circ_id, (long)(now.tv_sec - circ->timestamp_dirty), circ->purpose); circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); } else if (!circ->timestamp_dirty && circ->state == CIRCUIT_STATE_OPEN) { - if (circ->timestamp_created < cutoff) { + if (timercmp(&circ->timestamp_created, &cutoff, <)) { if (circ->purpose == CIRCUIT_PURPOSE_C_GENERAL || circ->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT || circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || @@ -762,8 +780,8 @@ circuit_expire_old_circuits_clientside(time_t now) circ->purpose <= CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) || circ->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND) { log_debug(LD_CIRC, - "Closing circuit that has been unused for %ld seconds.", - (long)(now - circ->timestamp_created)); + "Closing circuit that has been unused for %ld msec.", + tv_mdiff(&circ->timestamp_created, &now)); circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); } else if (!TO_ORIGIN_CIRCUIT(circ)->is_ancient) { /* Server-side rend joined circuits can end up really old, because @@ -775,9 +793,9 @@ circuit_expire_old_circuits_clientside(time_t now) circ->purpose != CIRCUIT_PURPOSE_S_INTRO) { log_notice(LD_CIRC, "Ancient non-dirty circuit %d is still around after " - "%ld seconds. Purpose: %d", + "%ld milliseconds. Purpose: %d", TO_ORIGIN_CIRCUIT(circ)->global_identifier, - (long)(now - circ->timestamp_created), + tv_mdiff(&circ->timestamp_created, &now), circ->purpose); /* FFFF implement a new circuit_purpose_to_string() so we don't * just print out a number for circ->purpose */ @@ -1123,7 +1141,7 @@ circuit_launch_by_extend_info(uint8_t purpose, /* reset the birth date of this circ, else expire_building * will see it and think it's been trying to build since it * began. */ - circ->_base.timestamp_created = time(NULL); + tor_gettimeofday(&circ->_base.timestamp_created); switch (purpose) { case CIRCUIT_PURPOSE_C_ESTABLISH_REND: case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: @@ -1266,7 +1284,8 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn, return -1; } } else { - /* XXXX022 Duplicates checks in connection_ap_handshake_attach_circuit */ + /* XXXX023 Duplicates checks in connection_ap_handshake_attach_circuit: + * refactor into a single function? */ routerinfo_t *router = router_get_by_nickname(conn->chosen_exit_name, 1); int opt = conn->chosen_exit_optional; if (router && !connection_ap_can_use_exit(conn, router, 0)) { @@ -1605,7 +1624,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn) /* find the circuit that we should use, if there is one. */ retval = circuit_get_open_circ_or_launch( conn, CIRCUIT_PURPOSE_C_GENERAL, &circ); - if (retval < 1) // XXX021 if we totally fail, this still returns 0 -RD + if (retval < 1) // XXX022 if we totally fail, this still returns 0 -RD return retval; log_debug(LD_APP|LD_CIRC, diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 1a604b415f..a121099aca 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -12,7 +12,7 @@ #ifndef _TOR_CIRCUITUSE_H #define _TOR_CIRCUITUSE_H -void circuit_expire_building(time_t now); +void circuit_expire_building(void); void circuit_remove_handled_ports(smartlist_t *needed_ports); int circuit_stream_is_being_handled(edge_connection_t *conn, uint16_t port, int min); diff --git a/src/or/config.c b/src/or/config.c index 17d776e71e..9675c73c99 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -195,6 +195,7 @@ static config_var_t _option_vars[] = { V(CircuitStreamTimeout, INTERVAL, "0"), V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/ V(ClientDNSRejectInternalAddresses, BOOL,"1"), + V(ClientRejectInternalAddresses, BOOL, "1"), V(ClientOnly, BOOL, "0"), V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), @@ -287,6 +288,7 @@ static config_var_t _option_vars[] = { OBSOLETE("IgnoreVersion"), V(KeepalivePeriod, INTERVAL, "5 minutes"), VAR("Log", LINELIST, Logs, NULL), + V(LogMessageDomains, BOOL, "0"), OBSOLETE("LinkPadding"), OBSOLETE("LogLevel"), OBSOLETE("LogFile"), @@ -391,6 +393,7 @@ static config_var_t _option_vars[] = { VAR("__HashedControlSessionPassword", LINELIST, HashedControlSessionPassword, NULL), V(MinUptimeHidServDirectoryV2, INTERVAL, "24 hours"), + V(_UsingTestNetworkDefaults, BOOL, "0"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; @@ -405,6 +408,7 @@ static config_var_t testing_tor_network_defaults[] = { V(AuthDirMaxServersPerAddr, UINT, "0"), V(AuthDirMaxServersPerAuthAddr,UINT, "0"), V(ClientDNSRejectInternalAddresses, BOOL,"0"), + V(ClientRejectInternalAddresses, BOOL, "0"), V(ExitPolicyRejectPrivate, BOOL, "0"), V(V3AuthVotingInterval, INTERVAL, "5 minutes"), V(V3AuthVoteDelay, INTERVAL, "20 seconds"), @@ -415,6 +419,7 @@ static config_var_t testing_tor_network_defaults[] = { V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"), V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"), V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"), + V(_UsingTestNetworkDefaults, BOOL, "1"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } }; #undef VAR @@ -443,15 +448,19 @@ static config_var_t _state_vars[] = { V(BWHistoryReadEnds, ISOTIME, NULL), V(BWHistoryReadInterval, UINT, "900"), V(BWHistoryReadValues, CSV, ""), + V(BWHistoryReadMaxima, CSV, ""), V(BWHistoryWriteEnds, ISOTIME, NULL), V(BWHistoryWriteInterval, UINT, "900"), V(BWHistoryWriteValues, CSV, ""), + V(BWHistoryWriteMaxima, CSV, ""), V(BWHistoryDirReadEnds, ISOTIME, NULL), V(BWHistoryDirReadInterval, UINT, "900"), V(BWHistoryDirReadValues, CSV, ""), + V(BWHistoryDirReadMaxima, CSV, ""), V(BWHistoryDirWriteEnds, ISOTIME, NULL), V(BWHistoryDirWriteInterval, UINT, "900"), V(BWHistoryDirWriteValues, CSV, ""), + V(BWHistoryDirWriteMaxima, CSV, ""), V(TorVersion, STRING, NULL), @@ -1250,7 +1259,6 @@ options_act(or_options_t *old_options) /* Check for transitions that need action. */ if (old_options) { - if ((options->UseEntryGuards && !old_options->UseEntryGuards) || (options->ExcludeNodes && !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes)) || @@ -1298,11 +1306,12 @@ options_act(or_options_t *old_options) if (options_transition_affects_workers(old_options, options)) { log_info(LD_GENERAL, "Worker-related options changed. Rotating workers."); + + if (init_keys() < 0) { + log_warn(LD_BUG,"Error initializing keys; exiting"); + return -1; + } if (server_mode(options) && !server_mode(old_options)) { - if (init_keys() < 0) { - log_warn(LD_BUG,"Error initializing keys; exiting"); - return -1; - } ip_address_changed(0); if (can_complete_circuit || !any_predicted_circuits(time(NULL))) inform_testing_reachability(); @@ -1329,7 +1338,7 @@ options_act(or_options_t *old_options) || !geoip_is_loaded())) { /* XXXX Don't use this "<default>" junk; make our filename options * understand prefixes somehow. -NM */ - /* XXXX021 Reload GeoIPFile on SIGHUP. -NM */ + /* XXXX023 Reload GeoIPFile on SIGHUP. -NM */ char *actual_fname = tor_strdup(options->GeoIPFile); #ifdef WIN32 if (!strcmp(actual_fname, "<default>")) { @@ -2474,7 +2483,7 @@ is_local_addr(const tor_addr_t *addr) if (get_options()->EnforceDistinctSubnets == 0) return 0; if (tor_addr_family(addr) == AF_INET) { - /*XXXX022 IP6 what corresponds to an /24? */ + /*XXXX023 IP6 what corresponds to an /24? */ uint32_t ip = tor_addr_to_ipv4h(addr); /* It's possible that this next check will hit before the first time @@ -2491,54 +2500,6 @@ is_local_addr(const tor_addr_t *addr) return 0; } -/** Called when we don't have a nickname set. Try to guess a good nickname - * based on the hostname, and return it in a newly allocated string. If we - * can't, return NULL and let the caller warn if it wants to. */ -static char * -get_default_nickname(void) -{ - static const char * const bad_default_nicknames[] = { - "localhost", - NULL, - }; - char localhostname[256]; - char *cp, *out, *outp; - int i; - - if (gethostname(localhostname, sizeof(localhostname)) < 0) - return NULL; - - /* Put it in lowercase; stop at the first dot. */ - if ((cp = strchr(localhostname, '.'))) - *cp = '\0'; - tor_strlower(localhostname); - - /* Strip invalid characters. */ - cp = localhostname; - out = outp = tor_malloc(strlen(localhostname) + 1); - while (*cp) { - if (strchr(LEGAL_NICKNAME_CHARACTERS, *cp)) - *outp++ = *cp++; - else - cp++; - } - *outp = '\0'; - - /* Enforce length. */ - if (strlen(out) > MAX_NICKNAME_LEN) - out[MAX_NICKNAME_LEN]='\0'; - - /* Check for dumb names. */ - for (i = 0; bad_default_nicknames[i]; ++i) { - if (!strcmp(out, bad_default_nicknames[i])) { - tor_free(out); - return NULL; - } - } - - return out; -} - /** Release storage held by <b>options</b>. */ static void config_free(config_format_t *fmt, void *options) @@ -2839,7 +2800,9 @@ compute_publishserverdescriptor(or_options_t *options) else if (!strcasecmp(string, "bridge")) *auth |= BRIDGE_AUTHORITY; else if (!strcasecmp(string, "hidserv")) - *auth |= HIDSERV_AUTHORITY; + log_warn(LD_CONFIG, + "PublishServerDescriptor hidserv is invalid. See " + "PublishHidServDescriptors."); else if (!strcasecmp(string, "") || !strcmp(string, "0")) /* no authority */; else @@ -2965,14 +2928,7 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->Nickname == NULL) { if (server_mode(options)) { - if (!(options->Nickname = get_default_nickname())) { - log_notice(LD_CONFIG, "Couldn't pick a nickname based on " - "our hostname; using %s instead.", UNNAMED_ROUTER_NICKNAME); options->Nickname = tor_strdup(UNNAMED_ROUTER_NICKNAME); - } else { - log_notice(LD_CONFIG, "Choosing default nickname '%s'", - options->Nickname); - } } } else { if (!is_legal_nickname(options->Nickname)) { @@ -3343,6 +3299,11 @@ options_validate(or_options_t *old_options, or_options_t *options, "PerConnBWBurst", msg) < 0) return -1; + if (options->RelayBandwidthRate && !options->RelayBandwidthBurst) + options->RelayBandwidthBurst = options->RelayBandwidthRate; + if (options->RelayBandwidthBurst && !options->RelayBandwidthRate) + options->RelayBandwidthRate = options->RelayBandwidthBurst; + if (server_mode(options)) { if (options->BandwidthRate < ROUTER_REQUIRED_MIN_BANDWIDTH) { tor_asprintf(msg, @@ -3371,9 +3332,6 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->RelayBandwidthRate && !options->RelayBandwidthBurst) - options->RelayBandwidthBurst = options->RelayBandwidthRate; - if (options->RelayBandwidthRate > options->RelayBandwidthBurst) REJECT("RelayBandwidthBurst must be at least equal " "to RelayBandwidthRate."); @@ -3639,12 +3597,12 @@ options_validate(or_options_t *old_options, or_options_t *options, "ignore you."); } - /*XXXX022 checking for defaults manually like this is a bit fragile.*/ + /*XXXX023 checking for defaults manually like this is a bit fragile.*/ /* Keep changes to hard-coded values synchronous to man page and default * values table. */ if (options->TestingV3AuthInitialVotingInterval != 30*60 && - !options->TestingTorNetwork) { + !options->TestingTorNetwork && !options->_UsingTestNetworkDefaults) { REJECT("TestingV3AuthInitialVotingInterval may only be changed in testing " "Tor networks!"); } else if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL) { @@ -3655,7 +3613,8 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->TestingV3AuthInitialVoteDelay != 5*60 && - !options->TestingTorNetwork) { + !options->TestingTorNetwork && !options->_UsingTestNetworkDefaults) { + REJECT("TestingV3AuthInitialVoteDelay may only be changed in testing " "Tor networks!"); } else if (options->TestingV3AuthInitialVoteDelay < MIN_VOTE_SECONDS) { @@ -3663,7 +3622,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->TestingV3AuthInitialDistDelay != 5*60 && - !options->TestingTorNetwork) { + !options->TestingTorNetwork && !options->_UsingTestNetworkDefaults) { REJECT("TestingV3AuthInitialDistDelay may only be changed in testing " "Tor networks!"); } else if (options->TestingV3AuthInitialDistDelay < MIN_DIST_SECONDS) { @@ -3678,7 +3637,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->TestingAuthDirTimeToLearnReachability != 30*60 && - !options->TestingTorNetwork) { + !options->TestingTorNetwork && !options->_UsingTestNetworkDefaults) { REJECT("TestingAuthDirTimeToLearnReachability may only be changed in " "testing Tor networks!"); } else if (options->TestingAuthDirTimeToLearnReachability < 0) { @@ -3688,7 +3647,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->TestingEstimatedDescriptorPropagationTime != 10*60 && - !options->TestingTorNetwork) { + !options->TestingTorNetwork && !options->_UsingTestNetworkDefaults) { REJECT("TestingEstimatedDescriptorPropagationTime may only be changed in " "testing Tor networks!"); } else if (options->TestingEstimatedDescriptorPropagationTime < 0) { @@ -3810,7 +3769,9 @@ options_transition_affects_workers(or_options_t *old_options, new_options->ServerDNSSearchDomains || old_options->SafeLogging != new_options->SafeLogging || old_options->ClientOnly != new_options->ClientOnly || - !config_lines_eq(old_options->Logs, new_options->Logs)) + public_server_mode(old_options) != public_server_mode(new_options) || + !config_lines_eq(old_options->Logs, new_options->Logs) || + old_options->LogMessageDomains != new_options->LogMessageDomains) return 1; /* Check whether log options match. */ @@ -4366,11 +4327,13 @@ options_init_logs(or_options_t *options, int validate_only) if (smartlist_len(elts) == 2 && !strcasecmp(smartlist_get(elts,0), "file")) { if (!validate_only) { - if (add_file_log(severity, smartlist_get(elts, 1)) < 0) { + char *fname = expand_filename(smartlist_get(elts, 1)); + if (add_file_log(severity, fname) < 0) { log_warn(LD_CONFIG, "Couldn't open file for 'Log %s': %s", opt->value, strerror(errno)); ok = 0; } + tor_free(fname); } goto cleanup; } @@ -4386,6 +4349,9 @@ options_init_logs(or_options_t *options, int validate_only) } smartlist_free(elts); + if (ok && !validate_only) + logs_set_domain_logging(options->LogMessageDomains); + return ok?0:-1; } @@ -4751,10 +4717,10 @@ options_save_current(void) } /** Mapping from a unit name to a multiplier for converting that unit into a - * base unit. */ + * base unit. Used by config_parse_unit. */ struct unit_table_t { - const char *unit; - uint64_t multiplier; + const char *unit; /**< The name of the unit */ + uint64_t multiplier; /**< How many of the base unit appear in this unit */ }; /** Table to map the names of memory units to the number of bytes they @@ -5145,6 +5111,9 @@ or_state_load(void) return r; } +/** If writing the state to disk fails, try again after this many seconds. */ +#define STATE_WRITE_RETRY_INTERVAL 3600 + /** Write the persistent state to disk. Return 0 for success, <0 on failure. */ int or_state_save(time_t now) @@ -5179,10 +5148,14 @@ or_state_save(time_t now) tor_free(state); fname = get_datadir_fname("state"); if (write_str_to_file(fname, contents, 0)<0) { - log_warn(LD_FS, "Unable to write state to file \"%s\"", fname); + log_warn(LD_FS, "Unable to write state to file \"%s\"; " + "will try again later", fname); global_state->LastWritten = -1; tor_free(fname); tor_free(contents); + /* Try again after STATE_WRITE_RETRY_INTERVAL (or sooner, if the state + * changes sooner). */ + global_state->next_write = now + STATE_WRITE_RETRY_INTERVAL; return -1; } diff --git a/src/or/connection.c b/src/or/connection.c index 8a21d81c58..6e7bbd5bad 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -51,7 +51,7 @@ static int connection_finished_flushing(connection_t *conn); static int connection_flushed_some(connection_t *conn); static int connection_finished_connecting(connection_t *conn); static int connection_reached_eof(connection_t *conn); -static int connection_read_to_buf(connection_t *conn, int *max_to_read, +static int connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, int *socket_error); static int connection_process_inbuf(connection_t *conn, int package_partial); static void client_check_address_changed(int sock); @@ -1178,7 +1178,8 @@ connection_handle_listener_read(connection_t *conn, int new_type) } if (connection_init_accepted_conn(newconn, conn->type) < 0) { - connection_mark_for_close(newconn); + if (! newconn->marked_for_close) + connection_mark_for_close(newconn); return 0; } return 0; @@ -1204,9 +1205,11 @@ connection_init_accepted_conn(connection_t *conn, uint8_t listener_type) conn->state = AP_CONN_STATE_SOCKS_WAIT; break; case CONN_TYPE_AP_TRANS_LISTENER: + TO_EDGE_CONN(conn)->is_transparent_ap = 1; conn->state = AP_CONN_STATE_CIRCUIT_WAIT; return connection_ap_process_transparent(TO_EDGE_CONN(conn)); case CONN_TYPE_AP_NATD_LISTENER: + TO_EDGE_CONN(conn)->is_transparent_ap = 1; conn->state = AP_CONN_STATE_NATD_WAIT; break; } @@ -2335,7 +2338,7 @@ connection_bucket_should_increase(int bucket, or_connection_t *conn) static int connection_handle_read_impl(connection_t *conn) { - int max_to_read=-1, try_to_read; + ssize_t max_to_read=-1, try_to_read; size_t before, n_read = 0; int socket_error = 0; @@ -2453,7 +2456,8 @@ connection_handle_read(connection_t *conn) * Return -1 if we want to break conn, else return 0. */ static int -connection_read_to_buf(connection_t *conn, int *max_to_read, int *socket_error) +connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, + int *socket_error) { int result; ssize_t at_most = *max_to_read; @@ -2571,15 +2575,19 @@ connection_read_to_buf(connection_t *conn, int *max_to_read, int *socket_error) n_read = (size_t) result; } - if (n_read > 0) { /* change *max_to_read */ - /*XXXX021 check for overflow*/ - *max_to_read = (int)(at_most - n_read); - } + if (n_read > 0) { + /* change *max_to_read */ + *max_to_read = at_most - n_read; - if (conn->type == CONN_TYPE_AP) { - edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - /*XXXX021 check for overflow*/ - edge_conn->n_read += (int)n_read; + /* Update edge_conn->n_read */ + if (conn->type == CONN_TYPE_AP) { + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Check for overflow: */ + if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_read > n_read)) + edge_conn->n_read += (int)n_read; + else + edge_conn->n_read = UINT32_MAX; + } } connection_buckets_decrement(conn, approx_time(), n_read, n_written); @@ -2778,10 +2786,13 @@ connection_handle_write_impl(connection_t *conn, int force) n_written = (size_t) result; } - if (conn->type == CONN_TYPE_AP) { + if (n_written && conn->type == CONN_TYPE_AP) { edge_connection_t *edge_conn = TO_EDGE_CONN(conn); - /*XXXX021 check for overflow.*/ - edge_conn->n_written += (int)n_written; + /* Check for overflow: */ + if (PREDICT_LIKELY(UINT32_MAX - edge_conn->n_written > n_written)) + edge_conn->n_written += (int)n_written; + else + edge_conn->n_written = UINT32_MAX; } connection_buckets_decrement(conn, approx_time(), n_read, n_written); @@ -3242,6 +3253,8 @@ connection_flushed_some(connection_t *conn) r = connection_dirserv_flushed_some(TO_DIR_CONN(conn)); } else if (conn->type == CONN_TYPE_OR) { r = connection_or_flushed_some(TO_OR_CONN(conn)); + } else if (CONN_IS_EDGE(conn)) { + r = connection_edge_flushed_some(TO_EDGE_CONN(conn)); } conn->in_flushed_some = 0; return r; diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 73ed9fb5c3..72e2c8a409 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -301,6 +301,23 @@ connection_edge_end_errno(edge_connection_t *conn) return connection_edge_end(conn, reason); } +/** We just wrote some data to <b>conn</b>; act appropriately. + * + * (That is, if it's open, consider sending a stream-level sendme cell if we + * have just flushed enough.) + */ +int +connection_edge_flushed_some(edge_connection_t *conn) +{ + switch (conn->_base.state) { + case AP_CONN_STATE_OPEN: + case EXIT_CONN_STATE_OPEN: + connection_edge_consider_sending_sendme(conn); + break; + } + return 0; +} + /** Connection <b>conn</b> has finished writing and has no bytes left on * its outbuf. * @@ -500,6 +517,7 @@ connection_ap_expire_beginning(void) /* kludge to make us not try this circuit again, yet to allow * current streams on it to survive if they can: make it * unattractive to use for new streams */ + /* XXXX023 this is a kludgy way to do this. */ tor_assert(circ->timestamp_dirty); circ->timestamp_dirty -= options->MaxCircuitDirtiness; /* give our stream another 'cutoff' seconds to try */ @@ -540,7 +558,7 @@ connection_ap_attach_pending(void) /** Tell any AP streams that are waiting for a one-hop tunnel to * <b>failed_digest</b> that they are going to fail. */ -/* XXX022 We should get rid of this function, and instead attach +/* XXX023 We should get rid of this function, and instead attach * one-hop streams to circ->p_streams so they get marked in * circuit_mark_for_close like normal p_streams. */ void @@ -1659,6 +1677,28 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn, connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); return -1; } + if (options->ClientRejectInternalAddresses && + !conn->use_begindir && !conn->chosen_exit_name && !circ) { + tor_addr_t addr; + if (tor_addr_from_str(&addr, socks->address) >= 0 && + tor_addr_is_internal(&addr, 0)) { + /* If this is an explicit private address with no chosen exit node, + * then we really don't want to try to connect to it. That's + * probably an error. */ + if (conn->is_transparent_ap) { + log_warn(LD_NET, + "Rejecting request for anonymous connection to private " + "address %s on a TransPort or NATDPort. Possible loop " + "in your NAT rules?", safe_str_client(socks->address)); + } else { + log_warn(LD_NET, + "Rejecting SOCKS request for anonymous connection to " + "private address %s", safe_str_client(socks->address)); + } + connection_mark_unattached_ap(conn, END_STREAM_REASON_PRIVATE_ADDR); + return -1; + } + } if (!conn->use_begindir && !conn->chosen_exit_name && !circ) { /* see if we can find a suitable enclave exit */ @@ -2124,8 +2164,14 @@ connection_ap_handshake_send_begin(edge_connection_t *ap_conn) ap_conn->stream_id = get_unique_stream_id_by_circ(circ); if (ap_conn->stream_id==0) { + /* XXXX023 Instead of closing this stream, we should make it get + * retried on another circuit. */ connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); + + /* Mark this circuit "unusable for new streams". */ + /* XXXX023 this is a kludgy way to do this. */ + tor_assert(circ->_base.timestamp_dirty); + circ->_base.timestamp_dirty -= get_options()->MaxCircuitDirtiness; return -1; } @@ -2183,9 +2229,14 @@ connection_ap_handshake_send_resolve(edge_connection_t *ap_conn) ap_conn->stream_id = get_unique_stream_id_by_circ(circ); if (ap_conn->stream_id==0) { + /* XXXX023 Instead of closing this stream, we should make it get + * retried on another circuit. */ connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL); - /*XXXX022 _close_ the circuit because it's full? That sounds dumb. */ - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT); + + /* Mark this circuit "unusable for new streams". */ + /* XXXX023 this is a kludgy way to do this. */ + tor_assert(circ->_base.timestamp_dirty); + circ->_base.timestamp_dirty -= get_options()->MaxCircuitDirtiness; return -1; } @@ -2343,7 +2394,7 @@ tell_controller_about_resolved_result(edge_connection_t *conn, * certain errors or for values that didn't come via DNS. <b>expires</b> is * a time when the answer expires, or -1 or TIME_MAX if there's a good TTL. **/ -/* XXXX022 the use of the ttl and expires fields is nutty. Let's make this +/* XXXX023 the use of the ttl and expires fields is nutty. Let's make this * interface and those that use it less ugly. */ void connection_ap_handshake_socks_resolved(edge_connection_t *conn, @@ -2784,13 +2835,13 @@ connection_exit_connect(edge_connection_t *edge_conn) log_debug(LD_EXIT,"about to try connecting"); switch (connection_connect(conn, conn->address, addr, port, &socket_error)) { - case -1: - /* XXX021 use socket_error below rather than trying to piece things - * together from the current errno, which may have been clobbered. */ - connection_edge_end_errno(edge_conn); + case -1: { + int reason = errno_to_stream_end_reason(socket_error); + connection_edge_end(edge_conn, reason); circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); connection_free(conn); return; + } case 0: conn->state = EXIT_CONN_STATE_CONNECTING; diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index fa5f91cf8d..0b08dd07ca 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -23,6 +23,7 @@ int connection_edge_process_inbuf(edge_connection_t *conn, int connection_edge_destroy(circid_t circ_id, edge_connection_t *conn); int connection_edge_end(edge_connection_t *conn, uint8_t reason); int connection_edge_end_errno(edge_connection_t *conn); +int connection_edge_flushed_some(edge_connection_t *conn); int connection_edge_finished_flushing(edge_connection_t *conn); int connection_edge_finished_connecting(edge_connection_t *conn); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index cf7c09a3cd..4b932ec51e 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -353,6 +353,9 @@ connection_or_digest_is_known_relay(const char *id_digest) * per-conn limits that are big enough they'll never matter. But if it's * not a known relay, first check if we set PerConnBwRate/Burst, then * check if the consensus sets them, else default to 'big enough'. + * + * If <b>reset</b> is true, set the bucket to be full. Otherwise, just + * clip the bucket if it happens to be <em>too</em> full. */ static void connection_or_update_token_buckets_helper(or_connection_t *conn, int reset, @@ -370,11 +373,11 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset, * bandwidth parameters in the consensus, but allow local config * options to override. */ rate = options->PerConnBWRate ? (int)options->PerConnBWRate : - (int)networkstatus_get_param(NULL, "perconnbwrate", - (int)options->BandwidthRate); + networkstatus_get_param(NULL, "perconnbwrate", + (int)options->BandwidthRate, 1, INT32_MAX); burst = options->PerConnBWBurst ? (int)options->PerConnBWBurst : - (int)networkstatus_get_param(NULL, "perconnbwburst", - (int)options->BandwidthBurst); + networkstatus_get_param(NULL, "perconnbwburst", + (int)options->BandwidthBurst, 1, INT32_MAX); } conn->bandwidthrate = rate; @@ -392,7 +395,8 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset, } /** Either our set of relays or our per-conn rate limits have changed. - * Go through all the OR connections and update their token buckets. */ + * Go through all the OR connections and update their token buckets to make + * sure they don't exceed their maximum values. */ void connection_or_update_token_buckets(smartlist_t *conns, or_options_t *options) { @@ -467,7 +471,7 @@ connection_or_init_conn_from_address(or_connection_t *conn, * Requires that both input connections are open; not is_bad_for_new_circs, * and not impossibly non-canonical. * - * If </b>forgive_new_connections</b> is true, then we do not call + * If <b>forgive_new_connections</b> is true, then we do not call * <b>a</b>better than <b>b</b> simply because b has no circuits, * unless b is also relatively old. */ @@ -1098,9 +1102,6 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, as_advertised = 0; } if (authdir_mode_tests_reachability(options)) { - /* We initiated this connection to address:port. Drop all routers - * with the same address:port and a different key. - */ dirserv_orconn_tls_done(conn->_base.address, conn->_base.port, digest_rcvd_out, as_advertised); } diff --git a/src/or/control.c b/src/or/control.c index fb9c1aeaf6..8f3af0bda1 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1785,12 +1785,12 @@ getinfo_helper_events(control_connection_t *control_conn, "information", question); } } else if (!strcmp(question, "status/clients-seen")) { - const char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL)); + char *bridge_stats = geoip_get_bridge_stats_controller(time(NULL)); if (!bridge_stats) { *errmsg = "No bridge-client stats available"; return -1; } - *answer = tor_strdup(bridge_stats); + *answer = bridge_stats; } else { return 0; } diff --git a/src/or/directory.c b/src/or/directory.c index 7360a47e46..8f33a608d4 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -372,7 +372,7 @@ directory_get_from_dirserver(uint8_t dir_purpose, uint8_t router_purpose, if (!get_via_tor) { if (options->UseBridges && type != BRIDGE_AUTHORITY) { /* want to ask a running bridge for which we have a descriptor. */ - /* XXX022 we assume that all of our bridges can answer any + /* XXX023 we assume that all of our bridges can answer any * possible directory question. This won't be true forever. -RD */ /* It certainly is not true with conditional consensus downloading, * so, for now, never assume the server supports that. */ @@ -1527,9 +1527,10 @@ connection_dir_client_reached_eof(dir_connection_t *conn) delta>0 ? "ahead" : "behind", dbuf, delta>0 ? "behind" : "ahead"); skewed = 1; /* don't check the recommended-versions line */ - control_event_general_status(trusted ? LOG_WARN : LOG_NOTICE, - "CLOCK_SKEW SKEW=%ld SOURCE=DIRSERV:%s:%d", - delta, conn->_base.address, conn->_base.port); + if (trusted) + control_event_general_status(LOG_WARN, + "CLOCK_SKEW SKEW=%ld SOURCE=DIRSERV:%s:%d", + delta, conn->_base.address, conn->_base.port); } else { log_debug(LD_HTTP, "Time on received directory is within tolerance; " "we are %ld seconds skewed. (That's okay.)", delta); @@ -1538,26 +1539,19 @@ connection_dir_client_reached_eof(dir_connection_t *conn) (void) skewed; /* skewed isn't used yet. */ if (status_code == 503) { - if (body_len < 16) { - routerstatus_t *rs; - trusted_dir_server_t *ds; - log_info(LD_DIR,"Received http status code %d (%s) from server " - "'%s:%d'. I'll try again soon.", - status_code, escaped(reason), conn->_base.address, - conn->_base.port); - if ((rs = router_get_consensus_status_by_id(conn->identity_digest))) - rs->last_dir_503_at = now; - if ((ds = router_get_trusteddirserver_by_digest(conn->identity_digest))) - ds->fake_status.last_dir_503_at = now; + routerstatus_t *rs; + trusted_dir_server_t *ds; + log_info(LD_DIR,"Received http status code %d (%s) from server " + "'%s:%d'. I'll try again soon.", + status_code, escaped(reason), conn->_base.address, + conn->_base.port); + if ((rs = router_get_consensus_status_by_id(conn->identity_digest))) + rs->last_dir_503_at = now; + if ((ds = router_get_trusteddirserver_by_digest(conn->identity_digest))) + ds->fake_status.last_dir_503_at = now; - tor_free(body); tor_free(headers); tor_free(reason); - return -1; - } - /* XXXX022 Remove this once every server with bug 539 is obsolete. */ - log_info(LD_DIR, "Server at '%s:%d' sent us a 503 response, but included " - "a body anyway. We'll pretend it gave us a 200.", - conn->_base.address, conn->_base.port); - status_code = 200; + tor_free(body); tor_free(headers); tor_free(reason); + return -1; } plausible = body_is_plausible(body, body_len, conn->_base.purpose); @@ -1875,7 +1869,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) ds->nickname); /* XXXX use this information; be sure to upload next one * sooner. -NM */ - /* XXXX021 On further thought, the task above implies that we're + /* XXXX023 On further thought, the task above implies that we're * basing our regenerate-descriptor time on when we uploaded the * last descriptor, not on the published time of the last * descriptor. If those are different, that's a bad thing to @@ -2705,7 +2699,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, ssize_t estimated_len = 0; smartlist_t *items = smartlist_create(); smartlist_t *dir_items = smartlist_create(); - int lifetime = 60; /* XXXX022 should actually use vote intervals. */ + int lifetime = 60; /* XXXX023 should actually use vote intervals. */ url += strlen("/tor/status-vote/"); current = !strcmpstart(url, "current/"); url = strchr(url, '/'); @@ -3258,6 +3252,8 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, write_http_status_line(conn, status, "Vote stored"); } else { tor_assert(msg); + log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").", + conn->_base.address, msg); write_http_status_line(conn, status, msg); } goto done; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 876698a2dc..f65f25811b 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -43,6 +43,8 @@ extern time_t time_of_process_start; /* from main.c */ +extern long stats_n_seconds_working; /* from main.c */ + /** Do we need to regenerate the v1 directory when someone asks for it? */ static time_t the_directory_is_dirty = 1; /** Do we need to regenerate the v1 runningrouters document when somebody @@ -944,7 +946,7 @@ running_long_enough_to_decide_unreachable(void) void dirserv_set_router_is_running(routerinfo_t *router, time_t now) { - /*XXXX022 This function is a mess. Separate out the part that calculates + /*XXXX023 This function is a mess. Separate out the part that calculates whether it's reachable and the part that tells rephist that the router was unreachable. */ @@ -969,8 +971,18 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) } if (!answer && running_long_enough_to_decide_unreachable()) { - /* not considered reachable. tell rephist. */ - rep_hist_note_router_unreachable(router->cache_info.identity_digest, now); + /* Not considered reachable. tell rephist about that. + + Because we launch a reachability test for each router every + REACHABILITY_TEST_CYCLE_PERIOD seconds, then the router has probably + been down since at least that time after we last successfully reached + it. + */ + time_t when = now; + if (router->last_reachable && + router->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD < now) + when = router->last_reachable + REACHABILITY_TEST_CYCLE_PERIOD; + rep_hist_note_router_unreachable(router->cache_info.identity_digest, when); } router->is_running = answer; @@ -1742,9 +1754,12 @@ dirserv_thinks_router_is_unreliable(time_t now, { if (need_uptime) { if (!enough_mtbf_info) { - /* XXX022 Once most authorities are on v3, we should change the rule from + /* XXX023 Once most authorities are on v3, we should change the rule from * "use uptime if we don't have mtbf data" to "don't advertise Stable on - * v3 if we don't have enough mtbf data." */ + * v3 if we don't have enough mtbf data." Or maybe not, since if we ever + * hit a point where we need to reset a lot of authorities at once, + * none of them would be in a position to declare Stable. + */ long uptime = real_uptime(router, now); if ((unsigned)uptime < stable_uptime && (unsigned)uptime < UPTIME_TO_GUARANTEE_STABLE) @@ -1775,7 +1790,22 @@ dirserv_thinks_router_is_unreliable(time_t now, static int dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now) { - long uptime = real_uptime(router, now); + + long uptime; + + /* If we haven't been running for at least + * get_options()->MinUptimeHidServDirectoryV2 seconds, we can't + * have accurate data telling us a relay has been up for at least + * that long. We also want to allow a bit of slack: Reachability + * tests aren't instant. If we haven't been running long enough, + * trust the relay. */ + + if (stats_n_seconds_working > + get_options()->MinUptimeHidServDirectoryV2 * 1.1) + uptime = MIN(rep_hist_get_uptime(router->cache_info.identity_digest, now), + real_uptime(router, now)); + else + uptime = real_uptime(router, now); /* XXX We shouldn't need to check dir_port, but we do because of * bug 1693. In the future, once relays set wants_to_be_hs_dir @@ -2970,6 +3000,8 @@ dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key, SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r, smartlist_add(fps_out, tor_memdup(r->cache_info.identity_digest, DIGEST_LEN))); + /* Treat "all" requests as if they were unencrypted */ + for_unencrypted_conn = 1; } else if (!strcmp(key, "authority")) { routerinfo_t *ri = router_get_my_routerinfo(); if (ri) @@ -3114,19 +3146,27 @@ dirserv_orconn_tls_done(const char *address, tor_assert(address); tor_assert(digest_rcvd); - SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri, { + /* XXX023 Doing a loop like this is stupid. We should just look up the + * router by digest_rcvd, and see if address, orport, and as_advertised + * match up. -NM */ + SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) { if (!strcasecmp(address, ri->address) && or_port == ri->or_port && as_advertised && !memcmp(ri->cache_info.identity_digest, digest_rcvd, DIGEST_LEN)) { /* correct digest. mark this router reachable! */ if (!bridge_auth || ri->purpose == ROUTER_PURPOSE_BRIDGE) { - log_info(LD_DIRSERV, "Found router %s to be reachable. Yay.", - ri->nickname); - rep_hist_note_router_reachable(digest_rcvd, now); + tor_addr_t addr, *addrp=NULL; + log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.", + ri->nickname, address, ri->or_port ); + if (tor_addr_from_str(&addr, ri->address) != -1) + addrp = &addr; + else + log_warn(LD_BUG, "Couldn't parse IP address \"%s\"", ri->address); + rep_hist_note_router_reachable(digest_rcvd, addrp, or_port, now); ri->last_reachable = now; } } - }); + } SMARTLIST_FOREACH_END(ri); /* FFFF Maybe we should reinstate the code that dumps routers with the same * addr/port but with nonmatching keys, but instead of dumping, we should * skip testing. */ @@ -3177,8 +3217,8 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) * try a few connections per call. * * The load balancing is such that if we get called once every ten - * seconds, we will cycle through all the tests in 1280 seconds (a - * bit over 20 minutes). + * seconds, we will cycle through all the tests in + * REACHABILITY_TEST_CYCLE_PERIOD seconds (a bit over 20 minutes). */ void dirserv_test_reachability(time_t now) @@ -3204,11 +3244,11 @@ dirserv_test_reachability(time_t now) continue; /* bridge authorities only test reachability on bridges */ // if (router->cache_info.published_on > cutoff) // continue; - if ((((uint8_t)id_digest[0]) % 128) == ctr) { + if ((((uint8_t)id_digest[0]) % REACHABILITY_MODULO_PER_TEST) == ctr) { dirserv_single_reachability_test(now, router); } } SMARTLIST_FOREACH_END(router); - ctr = (ctr + 1) % 128; /* increment ctr */ + ctr = (ctr + 1) % REACHABILITY_MODULO_PER_TEST; /* increment ctr */ } /** Given a fingerprint <b>fp</b> which is either set if we're looking for a @@ -3223,7 +3263,7 @@ lookup_cached_dir_by_fp(const char *fp) d = strmap_get(cached_consensuses, "ns"); else if (memchr(fp, '\0', DIGEST_LEN) && cached_consensuses && (d = strmap_get(cached_consensuses, fp))) { - /* this here interface is a nasty hack XXXX022 */; + /* this here interface is a nasty hack XXXX023 */; } else if (router_digest_is_me(fp) && the_v2_networkstatus) d = the_v2_networkstatus; else if (cached_v2_networkstatus) diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 56ad7a6a56..a8a7060a36 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -12,6 +12,18 @@ #ifndef _TOR_DIRSERV_H #define _TOR_DIRSERV_H +/** What fraction (1 over this number) of the relay ID space do we + * (as a directory authority) launch connections to at each reachability + * test? */ +#define REACHABILITY_MODULO_PER_TEST 128 + +/** How often (in seconds) do we launch reachability tests? */ +#define REACHABILITY_TEST_INTERVAL 10 + +/** How many seconds apart are the reachability tests for a given relay? */ +#define REACHABILITY_TEST_CYCLE_PERIOD \ + (REACHABILITY_TEST_INTERVAL*REACHABILITY_MODULO_PER_TEST) + /** Maximum length of an exit policy summary. */ #define MAX_EXITPOLICY_SUMMARY_LEN 1000 @@ -40,7 +52,7 @@ MAX_V_LINE_LEN \ ) -#define UNNAMED_ROUTER_NICKNAME "Unnamed" +#define UNNAMED_ROUTER_NICKNAME "Unnamed" int connection_dirserv_flushed_some(dir_connection_t *conn); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 41985d1485..9273dbc90d 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -50,7 +50,7 @@ static int dirvote_publish_consensus(void); static char *make_consensus_method_list(int low, int high, const char *sep); /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 10 +#define MAX_SUPPORTED_CONSENSUS_METHOD 11 /** Lowest consensus method that contains a 'directory-footer' marker */ #define MIN_METHOD_FOR_FOOTER 9 @@ -1693,7 +1693,7 @@ networkstatus_compute_consensus(smartlist_t *votes, const char *chosen_name = NULL; int exitsummary_disagreement = 0; int is_named = 0, is_unnamed = 0, is_running = 0; - int is_guard = 0, is_exit = 0; + int is_guard = 0, is_exit = 0, is_bad_exit = 0; int naming_conflict = 0; int n_listing = 0; int i; @@ -1819,6 +1819,8 @@ networkstatus_compute_consensus(smartlist_t *votes, is_guard = 1; else if (!strcmp(fl, "Running")) is_running = 1; + else if (!strcmp(fl, "BadExit")) + is_bad_exit = 1; } } }); @@ -1845,6 +1847,11 @@ networkstatus_compute_consensus(smartlist_t *votes, rs_out.bandwidth = median_uint32(bandwidths, num_bandwidths); } + /* Fix bug 2203: Do not count BadExit nodes as Exits for bw weights */ + if (consensus_method >= 11) { + is_exit = is_exit && !is_bad_exit; + } + if (consensus_method >= MIN_METHOD_FOR_BW_WEIGHTS) { if (rs_out.has_bandwidth) { T += rs_out.bandwidth; @@ -3073,7 +3080,7 @@ dirvote_compute_consensuses(void) n_votes = smartlist_len(pending_vote_list); if (n_votes <= n_voters/2) { log_warn(LD_DIR, "We don't have enough votes to generate a consensus: " - "%d of %d", n_votes, n_voters/2); + "%d of %d", n_votes, n_voters/2+1); goto err; } tor_assert(pending_vote_list); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index cd5fe86bc1..67540a37fb 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -19,9 +19,6 @@ /** Smallest allowable voting interval. */ #define MIN_VOTE_INTERVAL 300 -/** Precision multiplier for the Bw weights */ -#define BW_WEIGHT_SCALE 10000 - void dirvote_free_all(void); /* vote manipulation */ diff --git a/src/or/dns.c b/src/or/dns.c index 5c763011ba..61c8f32c98 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -54,12 +54,19 @@ struct evdns_request; evdns_config_windows_nameservers() #define evdns_base_set_option_(base, opt, val) \ evdns_set_option((opt),(val),DNS_OPTIONS_ALL) +/* Note: our internal eventdns.c, plus Libevent 1.4, used a 1 return to + * signify failure to launch a resolve. Libevent 2.0 uses a -1 return to + * signify a failure on a resolve, though if we're on Libevent 2.0, we should + * have event2/dns.h and never hit these macros. Regardless, 0 is success. */ #define evdns_base_resolve_ipv4(base, addr, options, cb, ptr) \ - ((evdns_resolve_ipv4(addr, options, cb, ptr)<0) ? NULL : ((void*)1)) -#define evdns_base_resolve_reverse(base, addr, options, cb, ptr) \ - ((evdns_resolve_reverse(addr, options, cb, ptr)<0) ? NULL : ((void*)1)) -#define evdns_base_resolve_reverse_ipv6(base, addr, options, cb, ptr) \ - ((evdns_resolve_reverse_ipv6(addr, options, cb, ptr)<0) ? NULL : ((void*)1)) + ((evdns_resolve_ipv4((addr), (options), (cb), (ptr))!=0) \ + ? NULL : ((void*)1)) +#define evdns_base_resolve_reverse(base, addr, options, cb, ptr) \ + ((evdns_resolve_reverse((addr), (options), (cb), (ptr))!=0) \ + ? NULL : ((void*)1)) +#define evdns_base_resolve_reverse_ipv6(base, addr, options, cb, ptr) \ + ((evdns_resolve_reverse_ipv6((addr), (options), (cb), (ptr))!=0) \ + ? NULL : ((void*)1)) #elif defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER < 0x02000303 #define evdns_base_set_option_(base, opt, val) \ @@ -1199,7 +1206,7 @@ configure_nameservers(int force) struct sockaddr_storage ss; socklen = tor_addr_to_sockaddr(&addr, 0, (struct sockaddr *)&ss, sizeof(ss)); - if (socklen < 0) { + if (socklen <= 0) { log_warn(LD_BUG, "Couldn't convert outbound bind address to sockaddr." " Ignoring."); } else { diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index 63ecbd2266..243b730cbf 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -19,7 +19,7 @@ #ifdef HAVE_EVENT2_DNS_H #include <event2/dns.h> #include <event2/dns_compat.h> -/* XXXX022 this implies we want an improved evdns */ +/* XXXX023 this implies we want an improved evdns */ #include <event2/dns_struct.h> #else #include "eventdns.h" @@ -282,11 +282,12 @@ dnsserv_resolved(edge_connection_t *conn, name, 1, answer, ttl); } else if (answer_type == RESOLVED_TYPE_HOSTNAME && + answer_len < 256 && conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) { char *ans = tor_strndup(answer, answer_len); evdns_server_request_add_ptr_reply(req, NULL, name, - (char*)answer, ttl); + ans, ttl); tor_free(ans); } else if (answer_type == RESOLVED_TYPE_ERROR) { err = DNS_ERR_NOTEXIST; diff --git a/src/or/eventdns.c b/src/or/eventdns.c index 04913bf6f7..cf583d0682 100644 --- a/src/or/eventdns.c +++ b/src/or/eventdns.c @@ -1904,7 +1904,7 @@ server_request_free(struct server_request *req) if (req->port) { if (req->port->pending_replies == req) { - if (req->next_pending) + if (req->next_pending && req->next_pending != req) req->port->pending_replies = req->next_pending; else req->port->pending_replies = NULL; @@ -1999,7 +1999,7 @@ evdns_request_timeout_callback(int fd, short events, void *arg) { /* retransmit it */ /* Stop waiting for the timeout. No need to do this in * request_finished; that one already deletes the timeout event. - * XXXX021 port this change to libevent. */ + * XXXX023 port this change to libevent. */ del_timeout_event(req); evdns_request_transmit(req); } diff --git a/src/or/geoip.c b/src/or/geoip.c index d4e279f538..5bb2410a75 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -214,7 +214,7 @@ geoip_load_file(const char *filename, or_options_t *options) smartlist_free(geoip_entries); } geoip_entries = smartlist_create(); - log_notice(LD_GENERAL, "Parsing GEOIP file."); + log_notice(LD_GENERAL, "Parsing GEOIP file %s.", filename); while (!feof(f)) { char buf[512]; if (fgets(buf, (int)sizeof(buf), f) == NULL) @@ -284,11 +284,18 @@ geoip_is_loaded(void) typedef struct clientmap_entry_t { HT_ENTRY(clientmap_entry_t) node; uint32_t ipaddr; + /** Time when we last saw this IP address, in MINUTES since the epoch. + * + * (This will run out of space around 4011 CE. If Tor is still in use around + * 4000 CE, please remember to add more bits to last_seen_in_minutes.) */ unsigned int last_seen_in_minutes:30; unsigned int action:2; } clientmap_entry_t; -#define ACTION_MASK 3 +/** Largest allowable value for last_seen_in_minutes. (It's a 30-bit field, + * so it can hold up to (1u<<30)-1, or 0x3fffffffu. + */ +#define MAX_LAST_SEEN_IN_MINUTES 0X3FFFFFFFu /** Map from client IP address to last time seen. */ static HT_HEAD(clientmap, clientmap_entry_t) client_history = @@ -413,15 +420,16 @@ geoip_note_client_seen(geoip_client_action_t action, lookup.ipaddr = addr; lookup.action = (int)action; ent = HT_FIND(clientmap, &client_history, &lookup); - if (ent) { - ent->last_seen_in_minutes = now / 60; - } else { + if (! ent) { ent = tor_malloc_zero(sizeof(clientmap_entry_t)); ent->ipaddr = addr; - ent->last_seen_in_minutes = now / 60; ent->action = (int)action; HT_INSERT(clientmap, &client_history, ent); } + if (now / 60 <= (int)MAX_LAST_SEEN_IN_MINUTES && now >= 0) + ent->last_seen_in_minutes = (unsigned)(now/60); + else + ent->last_seen_in_minutes = 0; if (action == GEOIP_CLIENT_NETWORKSTATUS || action == GEOIP_CLIENT_NETWORKSTATUS_V2) { @@ -593,8 +601,9 @@ _dirreq_map_put(dirreq_map_entry_t *entry, dirreq_type_t type, tor_assert(entry->type == type); tor_assert(entry->dirreq_id == dirreq_id); - /* XXXX022 once we're sure the bug case never happens, we can switch - * to HT_INSERT */ + /* XXXX we could switch this to HT_INSERT some time, since it seems that + * this bug doesn't happen. But since this function doesn't seem to be + * critical-path, it's sane to leave it alone. */ old_ent = HT_REPLACE(dirreqmap, &dirreq_map, entry); if (old_ent && old_ent != entry) { log_warn(LD_BUG, "Error when putting directory request into local " @@ -1075,14 +1084,14 @@ geoip_bridge_stats_term(void) start_of_bridge_stats_interval = 0; } -/** Parse the bridge statistics as they are written to extra-info - * descriptors for being returned to controller clients. Return the - * controller string if successful, or NULL otherwise. */ -static char * -parse_bridge_stats_controller(const char *stats_str, time_t now) +/** Validate a bridge statistics string as it would be written to a + * current extra-info descriptor. Return 1 if the string is valid and + * recent enough, or 0 otherwise. */ +static int +validate_bridge_stats(const char *stats_str, time_t now) { char stats_end_str[ISO_TIME_LEN+1], stats_start_str[ISO_TIME_LEN+1], - *controller_str, *eos, *eol, *summary; + *eos; const char *BRIDGE_STATS_END = "bridge-stats-end "; const char *BRIDGE_IPS = "bridge-ips "; @@ -1096,63 +1105,90 @@ parse_bridge_stats_controller(const char *stats_str, time_t now) "bridge-stats-end YYYY-MM-DD HH:MM:SS (N s)" */ tmp = find_str_at_start_of_line(stats_str, BRIDGE_STATS_END); if (!tmp) - return NULL; + return 0; tmp += strlen(BRIDGE_STATS_END); if (strlen(tmp) < ISO_TIME_LEN + 6) - return NULL; + return 0; strlcpy(stats_end_str, tmp, sizeof(stats_end_str)); if (parse_iso_time(stats_end_str, &stats_end_time) < 0) - return NULL; + return 0; if (stats_end_time < now - (25*60*60) || stats_end_time > now + (1*60*60)) - return NULL; + return 0; seconds = (int)strtol(tmp + ISO_TIME_LEN + 2, &eos, 10); if (!eos || seconds < 23*60*60) - return NULL; + return 0; format_iso_time(stats_start_str, stats_end_time - seconds); /* Parse: "bridge-ips CC=N,CC=N,..." */ tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS); - if (tmp) { - tmp += strlen(BRIDGE_IPS); - tmp = eat_whitespace_no_nl(tmp); - eol = strchr(tmp, '\n'); - if (eol) - summary = tor_strndup(tmp, eol-tmp); - else - summary = tor_strdup(tmp); - } else { + if (!tmp) { /* Look if there is an empty "bridge-ips" line */ tmp = find_str_at_start_of_line(stats_str, BRIDGE_IPS_EMPTY_LINE); if (!tmp) - return NULL; - summary = tor_strdup(""); + return 0; } - tor_asprintf(&controller_str, - "TimeStarted=\"%s\" CountrySummary=%s", - stats_start_str, summary); - tor_free(summary); - return controller_str; + return 1; } /** Most recent bridge statistics formatted to be written to extra-info * descriptors. */ static char *bridge_stats_extrainfo = NULL; -/** Most recent bridge statistics formatted to be returned to controller - * clients. */ -static char *bridge_stats_controller = NULL; +/** Return a newly allocated string holding our bridge usage stats by country + * in a format suitable for inclusion in an extrainfo document. Return NULL on + * failure. */ +static char * +format_bridge_stats_extrainfo(time_t now) +{ + char *out = NULL, *data = NULL; + long duration = now - start_of_bridge_stats_interval; + char written[ISO_TIME_LEN+1]; + + if (duration < 0) + return NULL; + + format_iso_time(written, now); + data = geoip_get_client_history(GEOIP_CLIENT_CONNECT); + + tor_asprintf(&out, + "bridge-stats-end %s (%ld s)\n" + "bridge-ips %s\n", + written, duration, + data ? data : ""); + tor_free(data); + + return out; +} + +/** Return a newly allocated string holding our bridge usage stats by country + * in a format suitable for the answer to a controller request. Return NULL on + * failure. */ +static char * +format_bridge_stats_controller(time_t now) +{ + char *out = NULL, *data = NULL; + char started[ISO_TIME_LEN+1]; + (void) now; + + format_iso_time(started, start_of_bridge_stats_interval); + data = geoip_get_client_history(GEOIP_CLIENT_CONNECT); + + tor_asprintf(&out, + "TimeStarted=\"%s\" CountrySummary=%s", + started, data ? data : ""); + tor_free(data); + return out; +} /** Write bridge statistics to $DATADIR/stats/bridge-stats and return * when we should next try to write statistics. */ time_t geoip_bridge_stats_write(time_t now) { - char *statsdir = NULL, *filename = NULL, *data = NULL, - written[ISO_TIME_LEN+1], *out = NULL, *controller_str; - size_t len; + char *filename = NULL, *val = NULL, *statsdir = NULL; /* Check if 24 hours have passed since starting measurements. */ if (now < start_of_bridge_stats_interval + WRITE_STATS_INTERVAL) @@ -1161,64 +1197,54 @@ geoip_bridge_stats_write(time_t now) /* Discard all items in the client history that are too old. */ geoip_remove_old_clients(start_of_bridge_stats_interval); + /* Generate formatted string */ + val = format_bridge_stats_extrainfo(now); + if (val == NULL) + goto done; + + /* Update the stored value. */ + tor_free(bridge_stats_extrainfo); + bridge_stats_extrainfo = val; + start_of_bridge_stats_interval = now; + + /* Write it to disk. */ statsdir = get_datadir_fname("stats"); if (check_private_dir(statsdir, CPD_CREATE) < 0) goto done; filename = get_datadir_fname2("stats", "bridge-stats"); - data = geoip_get_client_history(GEOIP_CLIENT_CONNECT); - format_iso_time(written, now); - len = strlen("bridge-stats-end (999999 s)\nbridge-ips \n") + - ISO_TIME_LEN + (data ? strlen(data) : 0) + 42; - out = tor_malloc(len); - if (tor_snprintf(out, len, "bridge-stats-end %s (%u s)\nbridge-ips %s\n", - written, (unsigned) (now - start_of_bridge_stats_interval), - data ? data : "") < 0) - goto done; - write_str_to_file(filename, out, 0); - controller_str = parse_bridge_stats_controller(out, now); - if (!controller_str) - goto done; - start_of_bridge_stats_interval = now; - tor_free(bridge_stats_extrainfo); - tor_free(bridge_stats_controller); - bridge_stats_extrainfo = out; - out = NULL; - bridge_stats_controller = controller_str; - control_event_clients_seen(controller_str); + + write_str_to_file(filename, bridge_stats_extrainfo, 0); + + /* Tell the controller, "hey, there are clients!" */ + { + char *controller_str = format_bridge_stats_controller(now); + if (controller_str) + control_event_clients_seen(controller_str); + tor_free(controller_str); + } done: tor_free(filename); tor_free(statsdir); - tor_free(data); - tor_free(out); - return start_of_bridge_stats_interval + - WRITE_STATS_INTERVAL; + + return start_of_bridge_stats_interval + WRITE_STATS_INTERVAL; } /** Try to load the most recent bridge statistics from disk, unless we - * have finished a measurement interval lately. */ + * have finished a measurement interval lately, and check whether they + * are still recent enough. */ static void load_bridge_stats(time_t now) { - char *statsdir, *fname=NULL, *contents, *controller_str; + char *fname, *contents; if (bridge_stats_extrainfo) return; - statsdir = get_datadir_fname("stats"); - if (check_private_dir(statsdir, CPD_CREATE) < 0) - goto done; + fname = get_datadir_fname2("stats", "bridge-stats"); contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); - if (contents) { - controller_str = parse_bridge_stats_controller(contents, now); - if (controller_str) { - bridge_stats_extrainfo = contents; - bridge_stats_controller = controller_str; - } else { - tor_free(contents); - } - } - done: + if (contents && validate_bridge_stats(contents, now)) + bridge_stats_extrainfo = contents; + tor_free(fname); - tor_free(statsdir); } /** Return most recent bridge statistics for inclusion in extra-info @@ -1230,13 +1256,12 @@ geoip_get_bridge_stats_extrainfo(time_t now) return bridge_stats_extrainfo; } -/** Return most recent bridge statistics to be returned to controller - * clients, or NULL if we don't have recent bridge statistics. */ -const char * +/** Return a new string containing the recent bridge statistics to be returned + * to controller clients, or NULL if we don't have any bridge statistics. */ +char * geoip_get_bridge_stats_controller(time_t now) { - load_bridge_stats(now); - return bridge_stats_controller; + return format_bridge_stats_controller(now); } /** Start time of entry stats or 0 if we're not collecting entry diff --git a/src/or/geoip.h b/src/or/geoip.h index bafbeea0f2..24f7c5b931 100644 --- a/src/or/geoip.h +++ b/src/or/geoip.h @@ -51,7 +51,7 @@ void geoip_bridge_stats_init(time_t now); time_t geoip_bridge_stats_write(time_t now); void geoip_bridge_stats_term(void); const char *geoip_get_bridge_stats_extrainfo(time_t); -const char *geoip_get_bridge_stats_controller(time_t); +char *geoip_get_bridge_stats_controller(time_t); #endif diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 48a7ce75ba..1878d5d52d 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -783,7 +783,8 @@ hibernate_begin(hibernate_state_t new_state, time_t now) /* XXX upload rendezvous service descriptors with no intro points */ if (new_state == HIBERNATE_STATE_EXITING) { - log_notice(LD_GENERAL,"Interrupt: will shut down in %d seconds. Interrupt " + log_notice(LD_GENERAL,"Interrupt: we have stopped accepting new " + "connections, and will shut down in %d seconds. Interrupt " "again to exit now.", options->ShutdownWaitLength); shutdown_time = time(NULL) + options->ShutdownWaitLength; } else { /* soft limit reached */ @@ -940,7 +941,8 @@ consider_hibernation(time_t now) if (hibernate_state == HIBERNATE_STATE_LIVE) { if (hibernate_soft_limit_reached()) { log_notice(LD_ACCT, - "Bandwidth soft limit reached; commencing hibernation."); + "Bandwidth soft limit reached; commencing hibernation. " + "No new conncetions will be accepted"); hibernate_begin(HIBERNATE_STATE_LOWBANDWIDTH, now); } else if (accounting_enabled && now < interval_wakeup_time) { format_local_iso_time(buf,interval_wakeup_time); diff --git a/src/or/main.c b/src/or/main.c index 4b512905c3..83d1e1e517 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -872,6 +872,7 @@ run_scheduled_events(time_t now) static time_t time_to_check_for_expired_networkstatus = 0; static time_t time_to_write_stats_files = 0; static time_t time_to_write_bridge_stats = 0; + static time_t time_to_launch_reachability_tests = 0; static int should_init_bridge_stats = 1; static time_t time_to_retry_dns_init = 0; or_options_t *options = get_options(); @@ -962,8 +963,10 @@ run_scheduled_events(time_t now) if (accounting_is_enabled(options)) accounting_run_housekeeping(now); - if (now % 10 == 0 && (authdir_mode_tests_reachability(options)) && - !we_are_hibernating()) { + if (time_to_launch_reachability_tests < now && + (authdir_mode_tests_reachability(options)) && + !we_are_hibernating()) { + time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; /* try to determine reachability of the other Tor relays */ dirserv_test_reachability(now); } @@ -1147,7 +1150,9 @@ run_scheduled_events(time_t now) * We do this before step 4, so it can try building more if * it's not comfortable with the number of available circuits. */ - circuit_expire_building(now); + /* XXXX022 If our circuit build timeout is much lower than a second, maybe + we should do this more often? */ + circuit_expire_building(); /** 3b. Also look at pending streams and prune the ones that 'began' * a long time ago but haven't gotten a 'connected' yet. @@ -2194,6 +2199,19 @@ tor_main(int argc, char *argv[]) } #endif +#ifdef MS_WINDOWS + /* Call SetProcessDEPPolicy to permanently enable DEP. + The function will not resolve on earlier versions of Windows, + and failure is not dangerous. */ + HMODULE hMod = GetModuleHandleA("Kernel32.dll"); + if (hMod) { + typedef BOOL (WINAPI *PSETDEP)(DWORD); + PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod, + "SetProcessDEPPolicy"); + if (setdeppolicy) setdeppolicy(1); /* PROCESS_DEP_ENABLE */ + } +#endif + update_approx_time(time(NULL)); tor_threads_init(); init_logging(); diff --git a/src/or/main.h b/src/or/main.h index d399aaaaf3..ed0fb97703 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -23,9 +23,11 @@ int connection_is_on_closeable_list(connection_t *conn); smartlist_t *get_connection_array(void); +/** Bitmask for events that we can turn on and off with + * connection_watch_events. */ typedef enum watchable_events { - READ_EVENT=0x02, - WRITE_EVENT=0x04 + READ_EVENT=0x02, /**< We want to know when a connection is readable */ + WRITE_EVENT=0x04 /**< We want to know when a connection is writable */ } watchable_events_t; void connection_watch_events(connection_t *conn, watchable_events_t events); int connection_is_reading(connection_t *conn); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 94bcb41002..4f6fe15409 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -439,6 +439,7 @@ networkstatus_check_document_signature(const networkstatus_t *consensus, signed_digest = tor_malloc(signed_digest_len); if (crypto_pk_public_checksig(cert->signing_key, signed_digest, + signed_digest_len, sig->signature, sig->signature_len) < dlen || memcmp(signed_digest, consensus->digests.d[sig->alg], dlen)) { @@ -1605,7 +1606,7 @@ networkstatus_set_current_consensus(const char *consensus, if (from_cache && !accept_obsolete && c->valid_until < now-OLD_ROUTER_DESC_MAX_AGE) { - /* XXX022 when we try to make fallbackconsensus work again, we should + /* XXXX If we try to make fallbackconsensus work again, we should * consider taking this out. Until then, believing obsolete consensuses * is causing more harm than good. See also bug 887. */ log_info(LD_DIR, "Loaded an expired consensus. Discarding."); @@ -1746,7 +1747,8 @@ networkstatus_set_current_consensus(const char *consensus, routerstatus_list_update_named_server_map(); cell_ewma_set_scale_factor(options, current_consensus); - /* XXX022 where is the right place to put this call? */ + /* XXXX023 this call might be unnecessary here: can changing the + * current consensus really alter our view of any OR's rate limits? */ connection_or_update_token_buckets(get_connection_array(), options); circuit_build_times_new_consensus_params(&circ_times, current_consensus); @@ -1763,7 +1765,11 @@ networkstatus_set_current_consensus(const char *consensus, write_str_to_file(consensus_fname, consensus, 0); } - if (ftime_definitely_before(now, current_consensus->valid_after)) { +/** If a consensus appears more than this many seconds before its declared + * valid-after time, declare that our clock is skewed. */ +#define EARLY_CONSENSUS_NOTICE_SKEW 60 + + if (now < current_consensus->valid_after - EARLY_CONSENSUS_NOTICE_SKEW) { char tbuf[ISO_TIME_LEN+1]; char dbuf[64]; long delta = now - current_consensus->valid_after; @@ -2124,32 +2130,52 @@ networkstatus_dump_bridge_status_to_file(time_t now) tor_free(status); } -int32_t +static int32_t get_net_param_from_list(smartlist_t *net_params, const char *param_name, - int default_val) + int32_t default_val, int32_t min_val, int32_t max_val) { + int32_t res = default_val; size_t name_len = strlen(param_name); + tor_assert(max_val > min_val); + tor_assert(min_val <= default_val); + tor_assert(max_val >= default_val); + SMARTLIST_FOREACH_BEGIN(net_params, const char *, p) { if (!strcmpstart(p, param_name) && p[name_len] == '=') { int ok=0; long v = tor_parse_long(p+name_len+1, 10, INT32_MIN, INT32_MAX, &ok, NULL); - if (ok) - return (int32_t) v; + if (ok) { + res = (int32_t) v; + break; + } } } SMARTLIST_FOREACH_END(p); - return default_val; + if (res < min_val) { + log_warn(LD_DIR, "Consensus parameter %s is too small. Got %d, raising to " + "%d.", param_name, res, min_val); + res = min_val; + } else if (res > max_val) { + log_warn(LD_DIR, "Consensus parameter %s is too large. Got %d, capping to " + "%d.", param_name, res, max_val); + res = max_val; + } + + return res; } /** Return the value of a integer parameter from the networkstatus <b>ns</b> * whose name is <b>param_name</b>. If <b>ns</b> is NULL, try loading the * latest consensus ourselves. Return <b>default_val</b> if no latest - * consensus, or if it has no parameter called <b>param_name</b>. */ + * consensus, or if it has no parameter called <b>param_name</b>. + * Make sure the value parsed from the consensus is at least + * <b>min_val</b> and at most <b>max_val</b> and raise/cap the parsed value + * if necessary. */ int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name, - int32_t default_val) + int32_t default_val, int32_t min_val, int32_t max_val) { if (!ns) /* if they pass in null, go find it ourselves */ ns = networkstatus_get_latest_consensus(); @@ -2157,24 +2183,36 @@ networkstatus_get_param(networkstatus_t *ns, const char *param_name, if (!ns || !ns->net_params) return default_val; - return get_net_param_from_list(ns->net_params, param_name, default_val); + return get_net_param_from_list(ns->net_params, param_name, + default_val, min_val, max_val); } /** Return the value of a integer bw weight parameter from the networkstatus * <b>ns</b> whose name is <b>weight_name</b>. If <b>ns</b> is NULL, try * loading the latest consensus ourselves. Return <b>default_val</b> if no - * latest consensus, or if it has no parameter called <b>param_name</b>. */ + * latest consensus, or if it has no parameter called <b>weight_name</b>. */ int32_t networkstatus_get_bw_weight(networkstatus_t *ns, const char *weight_name, - int32_t default_val) + int32_t default_val) { + int32_t param; + int max; if (!ns) /* if they pass in null, go find it ourselves */ ns = networkstatus_get_latest_consensus(); if (!ns || !ns->weight_params) return default_val; - return get_net_param_from_list(ns->weight_params, weight_name, default_val); + max = circuit_build_times_get_bw_scale(ns); + param = get_net_param_from_list(ns->weight_params, weight_name, + default_val, -1, + BW_MAX_WEIGHT_SCALE); + if (param > max) { + log_warn(LD_DIR, "Value of consensus weight %s was too large, capping " + "to %d", weight_name, max); + param = max; + } + return param; } /** Return the name of the consensus flavor <b>flav</b> as used to identify diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 2f18dc9525..ec2e8f884d 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -81,10 +81,9 @@ void signed_descs_update_status_from_consensus_networkstatus( char *networkstatus_getinfo_helper_single(routerstatus_t *rs); char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now); void networkstatus_dump_bridge_status_to_file(time_t now); -int32_t get_net_param_from_list(smartlist_t *net_params, const char *name, - int default_val); int32_t networkstatus_get_param(networkstatus_t *ns, const char *param_name, - int32_t default_val); + int32_t default_val, int32_t min_val, + int32_t max_val); int getinfo_helper_networkstatus(control_connection_t *conn, const char *question, char **answer, const char **errmsg); diff --git a/src/or/ntmain.c b/src/or/ntmain.c index 06ca2df91e..b2fee648cc 100644 --- a/src/or/ntmain.c +++ b/src/or/ntmain.c @@ -54,6 +54,11 @@ static int nt_service_cmd_stop(void); struct service_fns { int loaded; + /** @{ */ + /** Function pointers for Windows API functions related to service + * management. These are NULL, or they point to the . They're set by + * calling the LOAD macro below. */ + BOOL (WINAPI *ChangeServiceConfig2A_fn)( SC_HANDLE hService, DWORD dwInfoLevel, @@ -122,6 +127,7 @@ struct service_fns { LPTSTR ReferencedDomainName, LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse); + /** @} */ } service_fns = { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -144,6 +150,10 @@ nt_service_loadlibrary(void) goto err; } +/* Helper macro: try to load a function named <b>f</b> from "library" into + * service_functions.<b>f</b>_fn. On failure, log an error message, and goto + * err. + */ #define LOAD(f) STMT_BEGIN \ if (!(fn = GetProcAddress(library, #f))) { \ log_err(LD_BUG, \ diff --git a/src/or/onion.c b/src/or/onion.c index 9db9145c78..e1d10a60bb 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -184,7 +184,7 @@ onion_skin_create(crypto_pk_env_t *dest_router_key, *handshake_state_out = NULL; memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN); - if (!(dh = crypto_dh_new())) + if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT))) goto err; dhbytes = crypto_dh_get_bytes(dh); @@ -199,6 +199,7 @@ onion_skin_create(crypto_pk_env_t *dest_router_key, /* set meeting point, meeting cookie, etc here. Leave zero for now. */ if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out, + ONIONSKIN_CHALLENGE_LEN, challenge, DH_KEY_LEN, PK_PKCS1_OAEP_PADDING, 1)<0) goto err; @@ -241,6 +242,7 @@ onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/ break; note_crypto_pk_op(DEC_ONIONSKIN); len = crypto_pk_private_hybrid_decrypt(k, challenge, + ONIONSKIN_CHALLENGE_LEN, onion_skin, ONIONSKIN_CHALLENGE_LEN, PK_PKCS1_OAEP_PADDING,0); if (len>0) @@ -256,7 +258,11 @@ onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/ goto err; } - dh = crypto_dh_new(); + dh = crypto_dh_new(DH_TYPE_CIRCUIT); + if (!dh) { + log_warn(LD_BUG, "Couldn't allocate DH key"); + goto err; + } if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) { log_info(LD_GENERAL, "crypto_dh_get_public failed."); goto err; diff --git a/src/or/or.h b/src/or/or.h index cb36126d99..1688a08f96 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -583,6 +583,9 @@ typedef enum { /** This is a connection on the NATD port, and the destination IP:Port was * either ill-formed or out-of-range. */ #define END_STREAM_REASON_INVALID_NATD_DEST 261 +/** The target address is in a private network (like 127.0.0.1 or 10.0.0.1); + * you don't want to do that over a randomly chosen exit */ +#define END_STREAM_REASON_PRIVATE_ADDR 262 /** Bitwise-and this value with endreason to mask out all flags. */ #define END_STREAM_REASON_MASK 511 @@ -765,6 +768,8 @@ typedef enum { /** Initial value for both sides of a circuit transmission window when the * circuit is initialized. Measured in cells. */ #define CIRCWINDOW_START 1000 +#define CIRCWINDOW_START_MIN 100 +#define CIRCWINDOW_START_MAX 1000 /** Amount to increment a circuit window when we get a circuit SENDME. */ #define CIRCWINDOW_INCREMENT 100 /** Initial value on both sides of a stream transmission window when the @@ -843,9 +848,13 @@ typedef struct cell_t { /** Parsed variable-length onion routing cell. */ typedef struct var_cell_t { + /** Type of the cell: CELL_VERSIONS, etc. */ uint8_t command; + /** Circuit thich received the cell */ circid_t circ_id; + /** Number of bytes actually stored in <b>payload</b> */ uint16_t payload_len; + /** Payload of this cell */ uint8_t payload[1]; } var_cell_t; @@ -997,7 +1006,7 @@ typedef struct connection_t { /** Unique identifier for this connection on this Tor instance. */ uint64_t global_identifier; - /* XXXX022 move this field, and all the listener-only fields (just + /* XXXX023 move this field, and all the listener-only fields (just socket_family, I think), into a new listener_connection_t subtype. */ /** If the connection is a CONN_TYPE_AP_DNS_LISTENER, this field points * to the evdns_server_port is uses to listen to and answer connections. */ @@ -1168,6 +1177,10 @@ typedef struct edge_connection_t { * zero, abandon the associated mapaddress. */ unsigned int chosen_exit_retries:3; + /** True iff this is an AP connection that came from a transparent or + * NATd connection */ + unsigned int is_transparent_ap:1; + /** If this is a DNSPort connection, this field holds the pending DNS * request that we're going to try to answer. */ struct evdns_server_request *dns_server_request; @@ -1670,8 +1683,13 @@ typedef struct networkstatus_v2_t { * sorted by identity_digest. */ } networkstatus_v2_t; +/** Linked list of microdesc hash lines for a single router in a directory + * vote. + */ typedef struct vote_microdesc_hash_t { + /** Next element in the list, or NULL. */ struct vote_microdesc_hash_t *next; + /** The raw contents of the microdesc hash line, excluding the "m". */ char *microdesc_hash_line; } vote_microdesc_hash_t; @@ -1683,6 +1701,7 @@ typedef struct vote_routerstatus_t { * networkstatus_t.known_flags. */ char *version; /**< The version that the authority says this router is * running. */ + /** The hash or hashes that the authority claims this microdesc has. */ vote_microdesc_hash_t *microdesc; } vote_routerstatus_t; @@ -2107,10 +2126,15 @@ typedef struct circuit_t { * length ONIONSKIN_CHALLENGE_LEN. */ char *n_conn_onionskin; - time_t timestamp_created; /**< When was this circuit created? */ - time_t timestamp_dirty; /**< When the circuit was first used, or 0 if the - * circuit is clean. */ - struct timeval highres_created; /**< When exactly was the circuit created? */ + struct timeval timestamp_created; /**< When was the circuit created? */ + /** When the circuit was first used, or 0 if the circuit is clean. + * + * XXXX023 Note that some code will artifically adjust this value backward + * in time in order to indicate that a circuit shouldn't be used for new + * streams, but that it can stay alive as long as it has streams on it. + * That's a kludge we should fix. + */ + time_t timestamp_dirty; uint16_t marked_for_close; /**< Should we close this circuit at the end of * the main loop? (If true, holds the line number @@ -2336,6 +2360,9 @@ typedef struct { config_line_t *Logs; /**< New-style list of configuration lines * for logs */ + int LogMessageDomains; /**< Boolean: Should we log the domain(s) in which + * each log message occurs? */ + char *DebugLogFile; /**< Where to send verbose log messages. */ char *DataDirectory; /**< OR only: where to store long-term data. */ char *Nickname; /**< OR only: nickname of this onion router. */ @@ -2747,6 +2774,10 @@ typedef struct { * Helps avoid some cross-site attacks. */ int ClientDNSRejectInternalAddresses; + /** If true, do not accept any requests to connect to internal addresses + * over randomly chosen exits. */ + int ClientRejectInternalAddresses; + /** The length of time that we think a consensus should be fresh. */ int V3AuthVotingInterval; /** The length of time we think it will take to distribute votes. */ @@ -2825,6 +2856,11 @@ typedef struct { */ double CircuitPriorityHalflife; + /** Set to true if the TestingTorNetwork configuration option is set. + * This is used so that options_validate() has a chance to realize that + * the defaults have changed. */ + int _UsingTestNetworkDefaults; + } or_options_t; /** Persistent state for an onion router, as saved to disk. */ @@ -2856,19 +2892,25 @@ typedef struct { * bandwidth usage. The "Interval" fields hold the granularity, in seconds, * of the entries of Values. The "Values" lists hold decimal string * representations of the number of bytes read or written in each - * interval. */ + * interval. The "Maxima" list holds decimal strings describing the highest + * rate achieved during the interval. + */ time_t BWHistoryReadEnds; int BWHistoryReadInterval; smartlist_t *BWHistoryReadValues; + smartlist_t *BWHistoryReadMaxima; time_t BWHistoryWriteEnds; int BWHistoryWriteInterval; smartlist_t *BWHistoryWriteValues; + smartlist_t *BWHistoryWriteMaxima; time_t BWHistoryDirReadEnds; int BWHistoryDirReadInterval; smartlist_t *BWHistoryDirReadValues; + smartlist_t *BWHistoryDirReadMaxima; time_t BWHistoryDirWriteEnds; int BWHistoryDirWriteInterval; smartlist_t *BWHistoryDirWriteValues; + smartlist_t *BWHistoryDirWriteMaxima; /** Build time histogram */ config_line_t * BuildtimeHistogram; @@ -2942,6 +2984,11 @@ struct socks_request_t { /* Circuit Build Timeout "public" structures. */ +/** Precision multiplier for the Bw weights */ +#define BW_WEIGHT_SCALE 10000 +#define BW_MIN_WEIGHT_SCALE 1 +#define BW_MAX_WEIGHT_SCALE INT32_MAX + /** Total size of the circuit timeout history to accumulate. * 1000 is approx 2.5 days worth of continual-use circuits. */ #define CBT_NCIRCUITS_TO_OBSERVE 1000 @@ -2951,6 +2998,8 @@ struct socks_request_t { /** Number of modes to use in the weighted-avg computation of Xm */ #define CBT_DEFAULT_NUM_XM_MODES 3 +#define CBT_MIN_NUM_XM_MODES 1 +#define CBT_MAX_NUM_XM_MODES 20 /** A build_time_t is milliseconds */ typedef uint32_t build_time_t; @@ -2972,12 +3021,16 @@ typedef uint32_t build_time_t; * build in terms of CDF quantile. */ #define CBT_DEFAULT_CLOSE_QUANTILE 95 +#define CBT_MIN_CLOSE_QUANTILE CBT_MIN_QUANTILE_CUTOFF +#define CBT_MAX_CLOSE_QUANTILE CBT_MAX_QUANTILE_CUTOFF /** * How many circuits count as recent when considering if the * connection has gone gimpy or changed. */ #define CBT_DEFAULT_RECENT_CIRCUITS 20 +#define CBT_MIN_RECENT_CIRCUITS 3 +#define CBT_MAX_RECENT_CIRCUITS 1000 /** * Maximum count of timeouts that finish the first hop in the past @@ -2988,25 +3041,37 @@ typedef uint32_t build_time_t; * gives us. */ #define CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT (CBT_DEFAULT_RECENT_CIRCUITS*9/10) +#define CBT_MIN_MAX_RECENT_TIMEOUT_COUNT 3 +#define CBT_MAX_MAX_RECENT_TIMEOUT_COUNT 10000 /** Minimum circuits before estimating a timeout */ #define CBT_DEFAULT_MIN_CIRCUITS_TO_OBSERVE 100 +#define CBT_MIN_MIN_CIRCUITS_TO_OBSERVE 1 +#define CBT_MAX_MIN_CIRCUITS_TO_OBSERVE 10000 /** Cutoff percentile on the CDF for our timeout estimation. */ #define CBT_DEFAULT_QUANTILE_CUTOFF 80 +#define CBT_MIN_QUANTILE_CUTOFF 10 +#define CBT_MAX_QUANTILE_CUTOFF 99 double circuit_build_times_quantile_cutoff(void); /** How often in seconds should we build a test circuit */ #define CBT_DEFAULT_TEST_FREQUENCY 60 +#define CBT_MIN_TEST_FREQUENCY 1 +#define CBT_MAX_TEST_FREQUENCY INT32_MAX /** Lowest allowable value for CircuitBuildTimeout in milliseconds */ #define CBT_DEFAULT_TIMEOUT_MIN_VALUE (1500) +#define CBT_MIN_TIMEOUT_MIN_VALUE 500 +#define CBT_MAX_TIMEOUT_MIN_VALUE INT32_MAX /** Initial circuit build timeout in milliseconds */ #define CBT_DEFAULT_TIMEOUT_INITIAL_VALUE (60*1000) +#define CBT_MIN_TIMEOUT_INITIAL_VALUE CBT_MIN_TIMEOUT_MIN_VALUE +#define CBT_MAX_TIMEOUT_INITIAL_VALUE INT32_MAX int32_t circuit_build_times_initial_timeout(void); -#if CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT < 1 +#if CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT < CBT_MIN_MAX_RECENT_TIMEOUT_COUNT #error "RECENT_CIRCUITS is set too low." #endif @@ -3168,8 +3233,19 @@ typedef struct { } fp_pair_t; /********************************* dirserv.c ***************************/ + +/** An enum to describe what format we're generating a routerstatus line in. + */ typedef enum { - NS_V2, NS_V3_CONSENSUS, NS_V3_VOTE, NS_CONTROL_PORT, + /** For use in a v2 opinion */ + NS_V2, + /** For use in a consensus networkstatus document (ns flavor) */ + NS_V3_CONSENSUS, + /** For use in a vote networkstatus document */ + NS_V3_VOTE, + /** For passing to the controlport in response to a GETINFO request */ + NS_CONTROL_PORT, + /** For use in a consensus networkstatus document (microdesc flavor) */ NS_V3_CONSENSUS_MICRODESC } routerstatus_format_type_t; @@ -3186,9 +3262,14 @@ typedef struct measured_bw_line_t { /** Describes the schedule by which votes should be generated. */ typedef struct vote_timing_t { + /** Length in seconds between one consensus becoming valid and the next + * becoming valid. */ int vote_interval; + /** For how many intervals is a consensus valid? */ int n_intervals_valid; + /** Time in seconds allowed to propagate votes */ int vote_delay; + /** Time in seconds allowed to propagate signatures */ int dist_delay; } vote_timing_t; @@ -3463,6 +3544,7 @@ typedef enum was_router_added_t { ROUTER_NOT_IN_CONSENSUS = -3, ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS = -4, ROUTER_AUTHDIR_REJECTS = -5, + ROUTER_WAS_NOT_WANTED = -6 } was_router_added_t; /********************************* routerparse.c ************************/ diff --git a/src/or/policies.c b/src/or/policies.c index 6947222b2e..e48f42058f 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -858,6 +858,14 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, return 0; } +/** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating + * *<b>dest</b> as needed. */ +void +policies_exit_policy_append_reject_star(smartlist_t **dest) +{ + append_exit_policy_string(dest, "reject *:*"); +} + /** Replace the exit policy of <b>r</b> with reject *:*. */ void policies_set_router_exitpolicy_to_reject_all(routerinfo_t *r) @@ -880,6 +888,8 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port) memset(subnet_status, 0, sizeof(subnet_status)); SMARTLIST_FOREACH(policy, addr_policy_t *, p, { + if (tor_addr_family(&p->addr) != AF_INET) + continue; /* IPv4 only for now */ if (p->prt_min > port || p->prt_max < port) continue; /* Doesn't cover our port. */ mask = 0; @@ -1243,8 +1253,8 @@ policy_summarize(smartlist_t *policy) accepts_str = smartlist_join_strings(accepts, ",", 0, &accepts_len); rejects_str = smartlist_join_strings(rejects, ",", 0, &rejects_len); - if (rejects_len > MAX_EXITPOLICY_SUMMARY_LEN && - accepts_len > MAX_EXITPOLICY_SUMMARY_LEN) { + if (rejects_len > MAX_EXITPOLICY_SUMMARY_LEN-strlen("reject")-1 && + accepts_len > MAX_EXITPOLICY_SUMMARY_LEN-strlen("accept")-1) { char *c; shorter_str = accepts_str; prefix = "accept"; diff --git a/src/or/policies.h b/src/or/policies.h index a954ac4f5f..b2947c67e7 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -41,6 +41,7 @@ addr_policy_result_t compare_addr_to_addr_policy(uint32_t addr, int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, int rejectprivate, const char *local_address, int add_default_policy); +void policies_exit_policy_append_reject_star(smartlist_t **dest); void policies_set_router_exitpolicy_to_reject_all(routerinfo_t *exitrouter); int exit_policy_is_general_exit(smartlist_t *policy); int policy_is_reject_star(const smartlist_t *policy); diff --git a/src/or/reasons.c b/src/or/reasons.c index 1401552223..319e6c055a 100644 --- a/src/or/reasons.c +++ b/src/or/reasons.c @@ -40,6 +40,8 @@ stream_end_reason_to_control_string(int reason) case END_STREAM_REASON_NET_UNREACHABLE: return "NET_UNREACHABLE"; case END_STREAM_REASON_SOCKSPROTOCOL: return "SOCKS_PROTOCOL"; + case END_STREAM_REASON_PRIVATE_ADDR: return "PRIVATE_ADDR"; + default: return NULL; } } @@ -125,6 +127,9 @@ stream_end_reason_to_socks5_response(int reason) return SOCKS5_NET_UNREACHABLE; case END_STREAM_REASON_SOCKSPROTOCOL: return SOCKS5_GENERAL_ERROR; + case END_STREAM_REASON_PRIVATE_ADDR: + return SOCKS5_GENERAL_ERROR; + default: log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Reason for ending (%d) not recognized; " @@ -169,13 +174,7 @@ errno_to_stream_end_reason(int e) S_CASE(ENETUNREACH): return END_STREAM_REASON_INTERNAL; S_CASE(EHOSTUNREACH): - /* XXXX022 - * The correct behavior is END_STREAM_REASON_NOROUTE, but older - * clients don't recognize it. So we're going to continue sending - * "MISC" until 0.2.1.27 or later is "well established". - */ - /* return END_STREAM_REASON_NOROUTE; */ - return END_STREAM_REASON_MISC; + return END_STREAM_REASON_NOROUTE; S_CASE(ECONNREFUSED): return END_STREAM_REASON_CONNECTREFUSED; S_CASE(ECONNRESET): diff --git a/src/or/relay.c b/src/or/relay.c index 32ac96edf4..807cb3d435 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -52,9 +52,9 @@ static int circuit_consider_stop_edge_reading(circuit_t *circ, crypt_path_t *layer_hint); static int circuit_queue_streams_are_blocked(circuit_t *circ); +/* XXXX023 move this all to compat_libevent */ /** Cache the current hi-res time; the cache gets reset when libevent * calls us. */ - static struct timeval cached_time_hires = {0, 0}; /** Stop reading on edge connections when we have this many cells @@ -76,7 +76,7 @@ tor_gettimeofday_cached(struct timeval *tv) void tor_gettimeofday_cache_clear(void) { - cached_time_hires.tv_sec = 0; + cached_time_hires.tv_sec = 0; } /** Stats: how many relay cells have originated at this hop, or have @@ -791,6 +791,8 @@ connection_ap_process_end_not_open( < MAX_RESOLVE_FAILURES) { /* We haven't retried too many times; reattach the connection. */ circuit_log_path(LOG_INFO,LD_APP,circ); + /* Mark this circuit "unusable for new streams". */ + /* XXXX023 this is a kludgy way to do this. */ tor_assert(circ->_base.timestamp_dirty); circ->_base.timestamp_dirty -= get_options()->MaxCircuitDirtiness; @@ -1409,8 +1411,9 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial, goto repeat_connection_edge_package_raw_inbuf; } -/** Called when we've just received a relay data cell, or when - * we've just finished flushing all bytes to stream <b>conn</b>. +/** Called when we've just received a relay data cell, when + * we've just finished flushing all bytes to stream <b>conn</b>, + * or when we've flushed *some* bytes to the stream <b>conn</b>. * * If conn->outbuf is not too full, and our deliver window is * low, send back a suitable number of stream-level sendme cells. @@ -1999,9 +2002,9 @@ cell_ewma_set_scale_factor(or_options_t *options, networkstatus_t *consensus) if (options && options->CircuitPriorityHalflife >= -EPSILON) { halflife = options->CircuitPriorityHalflife; source = "CircuitPriorityHalflife in configuration"; - } else if (consensus && - (halflife_ms = networkstatus_get_param( - consensus, "CircuitPriorityHalflifeMsec", -1)) >= 0) { + } else if (consensus && (halflife_ms = networkstatus_get_param( + consensus, "CircuitPriorityHalflifeMsec", + -1, -1, INT32_MAX)) >= 0) { halflife = ((double)halflife_ms)/1000.0; source = "CircuitPriorityHalflifeMsec in consensus"; } else { diff --git a/src/or/relay.h b/src/or/relay.h index dbba09239a..f64752da5d 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -64,5 +64,7 @@ void cell_ewma_set_scale_factor(or_options_t *options, networkstatus_t *consensus); void circuit_clear_cell_queue(circuit_t *circ, or_connection_t *orconn); +void tor_gettimeofday_cache_clear(void); + #endif diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 7c626c6a64..8ac909fc80 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -121,7 +121,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, cpath = rendcirc->build_state->pending_final_cpath = tor_malloc_zero(sizeof(crypt_path_t)); cpath->magic = CRYPT_PATH_MAGIC; - if (!(cpath->dh_handshake_state = crypto_dh_new())) { + if (!(cpath->dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) { log_warn(LD_BUG, "Internal error: couldn't allocate DH."); goto err; } @@ -184,6 +184,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg, * to avoid buffer overflows? */ r = crypto_pk_public_hybrid_encrypt(intro_key, payload+DIGEST_LEN, + sizeof(payload)-DIGEST_LEN, tmp, (int)(dh_offset+DH_KEY_LEN), PK_PKCS1_OAEP_PADDING, 0); @@ -402,7 +403,7 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) tor_assert(rend_query); /* Determine responsible dirs. Even if we can't get all we want, * work with the ones we have. If it's empty, we'll notice below. */ - (int) hid_serv_get_responsible_directories(responsible_dirs, desc_id); + hid_serv_get_responsible_directories(responsible_dirs, desc_id); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, DIGEST_LEN); @@ -598,7 +599,7 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request, log_info(LD_REND,"Got rendezvous ack. This circuit is now ready for " "rendezvous."); circ->_base.purpose = CIRCUIT_PURPOSE_C_REND_READY; - /* XXXX022 This is a pretty brute-force approach. It'd be better to + /* XXXX023 This is a pretty brute-force approach. It'd be better to * attach only the connections that are waiting on this circuit, rather * than trying to attach them all. See comments bug 743. */ /* If we already have the introduction circuit built, make sure we send @@ -668,12 +669,14 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, onion_append_to_cpath(&circ->cpath, hop); circ->build_state->pending_final_cpath = NULL; /* prevent double-free */ - /* XXXX022 This is a pretty brute-force approach. It'd be better to + /* XXXX023 This is a pretty brute-force approach. It'd be better to * attach only the connections that are waiting on this circuit, rather * than trying to attach them all. See comments bug 743. */ connection_ap_attach_pending(); + memset(keys, 0, sizeof(keys)); return 0; err: + memset(keys, 0, sizeof(keys)); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL); return -1; } diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 290e8f8389..f4c8888c04 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -932,7 +932,7 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e) if (!*e) return 0; tor_assert((*e)->parsed && (*e)->parsed->intro_nodes); - /* XXX022 hack for now, to return "not found" if there are no intro + /* XXX023 hack for now, to return "not found" if there are no intro * points remaining. See bug 997. */ if (smartlist_len((*e)->parsed->intro_nodes) == 0) return 0; diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 44b5a4b4c0..45039822f8 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -928,7 +928,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, /* Next N bytes is encrypted with service key */ note_crypto_pk_op(REND_SERVER); r = crypto_pk_private_hybrid_decrypt( - intro_key,buf,(char*)(request+DIGEST_LEN),request_len-DIGEST_LEN, + intro_key,buf,sizeof(buf), + (char*)(request+DIGEST_LEN),request_len-DIGEST_LEN, PK_PKCS1_OAEP_PADDING,1); if (r<0) { log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell."); @@ -1099,7 +1100,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, } /* Try DH handshake... */ - dh = crypto_dh_new(); + dh = crypto_dh_new(DH_TYPE_REND); if (!dh || crypto_dh_generate_public(dh)<0) { log_warn(LD_BUG,"Internal error: couldn't build DH state " "or generate public key."); @@ -1164,8 +1165,10 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, memcpy(cpath->handshake_digest, keys, DIGEST_LEN); if (extend_info) extend_info_free(extend_info); + memset(keys, 0, sizeof(keys)); return 0; err: + memset(keys, 0, sizeof(keys)); if (dh) crypto_dh_free(dh); if (launched) circuit_mark_for_close(TO_CIRCUIT(launched), reason); @@ -1365,7 +1368,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) goto err; len += 20; note_crypto_pk_op(REND_SERVER); - r = crypto_pk_private_sign_digest(intro_key, buf+len, buf, len); + r = crypto_pk_private_sign_digest(intro_key, buf+len, sizeof(buf)-len, + buf, len); if (r<0) { log_warn(LD_BUG, "Internal error: couldn't sign introduction request."); reason = END_CIRC_REASON_INTERNAL; diff --git a/src/or/rephist.c b/src/or/rephist.c index d85a8b7c3a..5703e3edcd 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -14,6 +14,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "networkstatus.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@ -73,6 +74,13 @@ typedef struct or_history_t { /** If nonzero, we have been unable to connect since this time. */ time_t down_since; + /** The address at which we most recently connected to this OR + * successfully. */ + tor_addr_t last_reached_addr; + + /** The port at which we most recently connected to this OR successfully */ + uint16_t last_reached_port; + /* === For MTBF tracking: */ /** Weighted sum total of all times that this router has been online. */ @@ -119,6 +127,7 @@ get_or_history(const char* id) rephist_total_num++; hist->link_history_map = digestmap_new(); hist->since = hist->changed = time(NULL); + tor_addr_make_unspec(&hist->last_reached_addr); digestmap_set(history_map, id, hist); } return hist; @@ -289,13 +298,20 @@ rep_hist_note_connection_died(const char* id, time_t when) /** We have just decided that this router with identity digest <b>id</b> is * reachable, meaning we will give it a "Running" flag for the next while. */ void -rep_hist_note_router_reachable(const char *id, time_t when) +rep_hist_note_router_reachable(const char *id, const tor_addr_t *at_addr, + const uint16_t at_port, time_t when) { or_history_t *hist = get_or_history(id); int was_in_run = 1; char tbuf[ISO_TIME_LEN+1]; + int addr_changed, port_changed; tor_assert(hist); + tor_assert((!at_addr && !at_port) || (at_addr && at_port)); + + addr_changed = at_addr && + tor_addr_compare(at_addr, &hist->last_reached_addr, CMP_EXACT) != 0; + port_changed = at_port && at_port != hist->last_reached_port; if (!started_tracking_stability) started_tracking_stability = time(NULL); @@ -315,6 +331,27 @@ rep_hist_note_router_reachable(const char *id, time_t when) down_length = when - hist->start_of_downtime; hist->total_weighted_time += down_length; hist->start_of_downtime = 0; + } else if (addr_changed || port_changed) { + /* If we're reachable, but the address changed, treat this as some + * downtime. */ + int penalty = get_options()->TestingTorNetwork ? 240 : 3600; + networkstatus_t *ns; + + if ((ns = networkstatus_get_latest_consensus())) { + int fresh_interval = (int)(ns->fresh_until - ns->valid_after); + int live_interval = (int)(ns->valid_until - ns->valid_after); + /* on average, a descriptor addr change takes .5 intervals to make it + * into a consensus, and half a liveness period to make it to + * clients. */ + penalty = (int)(fresh_interval + live_interval) / 2; + } + format_local_iso_time(tbuf, hist->start_of_run); + log_info(LD_HIST,"Router %s still seems Running, but its address appears " + "to have changed since the last time it was reachable. I'm " + "going to treat it as having been down for %d seconds", + hex_str(id, DIGEST_LEN), penalty); + rep_hist_note_router_unreachable(id, when-penalty); + rep_hist_note_router_reachable(id, NULL, 0, when); } else { format_local_iso_time(tbuf, hist->start_of_run); if (was_in_run) @@ -324,6 +361,10 @@ rep_hist_note_router_reachable(const char *id, time_t when) log_info(LD_HIST,"Router %s is now Running; it was previously untracked", hex_str(id, DIGEST_LEN)); } + if (at_addr) + tor_addr_copy(&hist->last_reached_addr, at_addr); + if (at_port) + hist->last_reached_port = at_port; } /** We have just decided that this router is unreachable, meaning @@ -344,12 +385,20 @@ rep_hist_note_router_unreachable(const char *id, time_t when) long run_length = when - hist->start_of_run; format_local_iso_time(tbuf, hist->start_of_run); - hist->weighted_run_length += run_length; hist->total_run_weights += 1.0; hist->start_of_run = 0; - hist->weighted_uptime += run_length; - hist->total_weighted_time += run_length; + if (run_length < 0) { + unsigned long penalty = -run_length; +#define SUBTRACT_CLAMPED(var, penalty) \ + do { (var) = (var) < (penalty) ? 0 : (var) - (penalty); } while (0) + SUBTRACT_CLAMPED(hist->weighted_run_length, penalty); + SUBTRACT_CLAMPED(hist->weighted_uptime, penalty); + } else { + hist->weighted_run_length += run_length; + hist->weighted_uptime += run_length; + hist->total_weighted_time += run_length; + } was_running = 1; log_info(LD_HIST, "Router %s is now non-Running: it had previously been " "Running since %s. Its total weighted uptime is %lu/%lu.", @@ -422,7 +471,7 @@ rep_hist_downrate_old_runs(time_t now) static double get_stability(or_history_t *hist, time_t when) { - unsigned long total = hist->weighted_run_length; + long total = hist->weighted_run_length; double total_weights = hist->total_run_weights; if (hist->start_of_run) { @@ -458,8 +507,8 @@ get_total_weighted_time(or_history_t *hist, time_t when) static double get_weighted_fractional_uptime(or_history_t *hist, time_t when) { - unsigned long total = hist->total_weighted_time; - unsigned long up = hist->weighted_uptime; + long total = hist->total_weighted_time; + long up = hist->weighted_uptime; if (hist->start_of_run) { long run_length = (when - hist->start_of_run); @@ -479,6 +528,20 @@ get_weighted_fractional_uptime(or_history_t *hist, time_t when) return ((double) up) / total; } +/** Return how long the router whose identity digest is <b>id</b> has + * been reachable. Return 0 if the router is unknown or currently deemed + * unreachable. */ +long +rep_hist_get_uptime(const char *id, time_t when) +{ + or_history_t *hist = get_or_history(id); + if (!hist) + return 0; + if (!hist->start_of_run || when < hist->start_of_run) + return 0; + return when - hist->start_of_run; +} + /** Return an estimated MTBF for the router whose identity digest is * <b>id</b>. Return 0 if the router is unknown. */ double @@ -524,7 +587,7 @@ rep_hist_get_weighted_time_known(const char *id, time_t when) int rep_hist_have_measured_enough_stability(void) { - /* XXXX021 This doesn't do so well when we change our opinion + /* XXXX022 This doesn't do so well when we change our opinion * as to whether we're tracking router stability. */ return started_tracking_stability < time(NULL) - 4*60*60; } @@ -929,10 +992,10 @@ find_next_with(smartlist_t *sl, int i, const char *prefix) return -1; } -/** How many bad times has parse_possibly_bad_iso_time parsed? */ +/** How many bad times has parse_possibly_bad_iso_time() parsed? */ static int n_bogus_times = 0; /** Parse the ISO-formatted time in <b>s</b> into *<b>time_out</b>, but - * rounds any pre-1970 date to Jan 1, 1970. */ + * round any pre-1970 date to Jan 1, 1970. */ static int parse_possibly_bad_iso_time(const char *s, time_t *time_out) { @@ -1165,6 +1228,8 @@ rep_hist_load_mtbf_data(time_t now) * totals? */ #define NUM_SECS_ROLLING_MEASURE 10 /** How large are the intervals for which we track and report bandwidth use? */ +/* XXXX Watch out! Before Tor 0.2.2.21-alpha, using any other value here would + * generate an unparseable state file. */ #define NUM_SECS_BW_SUM_INTERVAL (15*60) /** How far in the past do we remember and publish bandwidth use? */ #define NUM_SECS_BW_SUM_IS_VALID (24*60*60) @@ -1223,7 +1288,7 @@ commit_max(bw_array_t *b) b->total_in_period = 0; } -/** Shift the current observation time of 'b' forward by one second. */ +/** Shift the current observation time of <b>b</b> forward by one second. */ static INLINE void advance_obs(bw_array_t *b) { @@ -1253,14 +1318,18 @@ advance_obs(bw_array_t *b) static INLINE void add_obs(bw_array_t *b, time_t when, uint64_t n) { - /* Don't record data in the past. */ - if (when<b->cur_obs_time) - return; + if (when < b->cur_obs_time) + return; /* Don't record data in the past. */ + /* If we're currently adding observations for an earlier second than * 'when', advance b->cur_obs_time and b->cur_obs_idx by an - * appropriate number of seconds, and do all the other housekeeping */ - while (when>b->cur_obs_time) + * appropriate number of seconds, and do all the other housekeeping. */ + while (when > b->cur_obs_time) { + /* Doing this one second at a time is potentially inefficient, if we start + with a state file that is very old. Fortunately, it doesn't seem to + show up in profiles, so we can just ignore it for now. */ advance_obs(b); + } b->obs[b->cur_obs_idx] += n; b->total_in_period += n; @@ -1291,17 +1360,22 @@ static bw_array_t *dir_read_array = NULL; directory protocol. */ static bw_array_t *dir_write_array = NULL; -/** Set up [dir-]read_array and [dir-]write_array. */ +/** Set up [dir-]read_array and [dir-]write_array, freeing them if they + * already exist. */ static void bw_arrays_init(void) { + tor_free(read_array); + tor_free(write_array); + tor_free(dir_read_array); + tor_free(dir_write_array); read_array = bw_array_new(); write_array = bw_array_new(); dir_read_array = bw_array_new(); dir_write_array = bw_array_new(); } -/** We read <b>num_bytes</b> more bytes in second <b>when</b>. +/** Remember that we read <b>num_bytes</b> bytes in second <b>when</b>. * * Add num_bytes to the current running total for <b>when</b>. * @@ -1322,7 +1396,7 @@ rep_hist_note_bytes_written(size_t num_bytes, time_t when) add_obs(write_array, when, num_bytes); } -/** We wrote <b>num_bytes</b> more bytes in second <b>when</b>. +/** Remember that we wrote <b>num_bytes</b> bytes in second <b>when</b>. * (like rep_hist_note_bytes_written() above) */ void @@ -1332,8 +1406,8 @@ rep_hist_note_bytes_read(size_t num_bytes, time_t when) add_obs(read_array, when, num_bytes); } -/** We wrote <b>num_bytes</b> more directory bytes in second <b>when</b>. - * (like rep_hist_note_bytes_written() above) +/** Remember that we wrote <b>num_bytes</b> directory bytes in second + * <b>when</b>. (like rep_hist_note_bytes_written() above) */ void rep_hist_note_dir_bytes_written(size_t num_bytes, time_t when) @@ -1341,8 +1415,8 @@ rep_hist_note_dir_bytes_written(size_t num_bytes, time_t when) add_obs(dir_write_array, when, num_bytes); } -/** We read <b>num_bytes</b> more directory bytes in second <b>when</b>. - * (like rep_hist_note_bytes_written() above) +/** Remember that we read <b>num_bytes</b> directory bytes in second + * <b>when</b>. (like rep_hist_note_bytes_written() above) */ void rep_hist_note_dir_bytes_read(size_t num_bytes, time_t when) @@ -1392,7 +1466,7 @@ rep_hist_bandwidth_assess(void) * It returns the number of bytes written. */ static size_t -rep_hist_fill_bandwidth_history(char *buf, size_t len, bw_array_t *b) +rep_hist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b) { char *cp = buf; int i, n; @@ -1437,7 +1511,8 @@ rep_hist_fill_bandwidth_history(char *buf, size_t len, bw_array_t *b) } /** Allocate and return lines for representing this server's bandwidth - * history in its descriptor. + * history in its descriptor. We publish these lines in our extra-info + * descriptor. */ char * rep_hist_get_bandwidth_lines(void) @@ -1484,163 +1559,196 @@ rep_hist_get_bandwidth_lines(void) return buf; } -/** Update <b>state</b> with the newest bandwidth history. */ +/** Write a single bw_array_t into the Values, Ends, Interval, and Maximum + * entries of an or_state_t. Done before writing out a new state file. */ +static void +rep_hist_update_bwhist_state_section(or_state_t *state, + const bw_array_t *b, + smartlist_t **s_values, + smartlist_t **s_maxima, + time_t *s_begins, + int *s_interval) +{ + char *cp; + int i,j; + uint64_t maxval; + + if (*s_values) { + SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); + smartlist_free(*s_values); + } + if (*s_maxima) { + SMARTLIST_FOREACH(*s_maxima, char *, val, tor_free(val)); + smartlist_free(*s_maxima); + } + if (! server_mode(get_options())) { + /* Clients don't need to store bandwidth history persistently; + * force these values to the defaults. */ + /* FFFF we should pull the default out of config.c's state table, + * so we don't have two defaults. */ + if (*s_begins != 0 || *s_interval != 900) { + time_t now = time(NULL); + time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600; + or_state_mark_dirty(state, save_at); + } + *s_begins = 0; + *s_interval = 900; + *s_values = smartlist_create(); + *s_maxima = smartlist_create(); + return; + } + *s_begins = b->next_period; + *s_interval = NUM_SECS_BW_SUM_INTERVAL; + + *s_values = smartlist_create(); + *s_maxima = smartlist_create(); + /* Set i to first position in circular array */ + i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx; + for (j=0; j < b->num_maxes_set; ++j,++i) { + if (i >= NUM_TOTALS) + i = 0; + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->totals[i] & ~0x3ff)); + smartlist_add(*s_values, cp); + maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE; + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(maxval & ~0x3ff)); + smartlist_add(*s_maxima, cp); + } + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->total_in_period & ~0x3ff)); + smartlist_add(*s_values, cp); + maxval = b->max_total / NUM_SECS_ROLLING_MEASURE; + tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(maxval & ~0x3ff)); + smartlist_add(*s_maxima, cp); +} + +/** Update <b>state</b> with the newest bandwidth history. Done before + * writing out a new state file. */ void rep_hist_update_state(or_state_t *state) { - int len, r; - char *buf, *cp; - smartlist_t **s_values = NULL; - time_t *s_begins = NULL; - int *s_interval = NULL; - bw_array_t *b = NULL; +#define UPDATE(arrname,st) \ + rep_hist_update_bwhist_state_section(state,\ + (arrname),\ + &state->BWHistory ## st ## Values, \ + &state->BWHistory ## st ## Maxima, \ + &state->BWHistory ## st ## Ends, \ + &state->BWHistory ## st ## Interval) - len = 20*NUM_TOTALS+1; - buf = tor_malloc_zero(len); + UPDATE(write_array, Write); + UPDATE(read_array, Read); + UPDATE(dir_write_array, DirWrite); + UPDATE(dir_read_array, DirRead); - for (r=0;r<4;++r) { - switch (r) { - case 0: - b = write_array; - s_begins = &state->BWHistoryWriteEnds; - s_interval = &state->BWHistoryWriteInterval; - s_values = &state->BWHistoryWriteValues; - break; - case 1: - b = read_array; - s_begins = &state->BWHistoryReadEnds; - s_interval = &state->BWHistoryReadInterval; - s_values = &state->BWHistoryReadValues; - break; - case 2: - b = dir_write_array; - s_begins = &state->BWHistoryDirWriteEnds; - s_interval = &state->BWHistoryDirWriteInterval; - s_values = &state->BWHistoryDirWriteValues; - break; - case 3: - b = dir_read_array; - s_begins = &state->BWHistoryDirReadEnds; - s_interval = &state->BWHistoryDirReadInterval; - s_values = &state->BWHistoryDirReadValues; - break; - } - if (*s_values) { - SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); - smartlist_free(*s_values); - } - if (! server_mode(get_options())) { - /* Clients don't need to store bandwidth history persistently; - * force these values to the defaults. */ - /* FFFF we should pull the default out of config.c's state table, - * so we don't have two defaults. */ - if (*s_begins != 0 || *s_interval != 900) { - time_t now = time(NULL); - time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600; - or_state_mark_dirty(state, save_at); - } - *s_begins = 0; - *s_interval = 900; - *s_values = smartlist_create(); - continue; - } - *s_begins = b->next_period; - *s_interval = NUM_SECS_BW_SUM_INTERVAL; - cp = buf; - cp += rep_hist_fill_bandwidth_history(cp, len, b); - tor_snprintf(cp, len-(cp-buf), cp == buf ? U64_FORMAT : ","U64_FORMAT, - U64_PRINTF_ARG(b->total_in_period)); - *s_values = smartlist_create(); - if (server_mode(get_options())) - smartlist_split_string(*s_values, buf, ",", SPLIT_SKIP_SPACE, 0); - } - tor_free(buf); if (server_mode(get_options())) { - or_state_mark_dirty(get_or_state(), time(NULL)+(2*3600)); + or_state_mark_dirty(state, time(NULL)+(2*3600)); } +#undef UPDATE } -/** Set bandwidth history from our saved state. */ -int -rep_hist_load_state(or_state_t *state, char **err) +/** Load a single bw_array_t from its Values, Ends, Maxima, and Interval + * entries in an or_state_t. Done while reading the state file. */ +static int +rep_hist_load_bwhist_state_section(bw_array_t *b, + const smartlist_t *s_values, + const smartlist_t *s_maxima, + const time_t s_begins, + const int s_interval) { - time_t s_begins = 0, start; time_t now = time(NULL); - uint64_t v; - int r,i,ok; - int all_ok = 1; - int s_interval = 0; - smartlist_t *s_values = NULL; - bw_array_t *b = NULL; - - /* Assert they already have been malloced */ - tor_assert(read_array && write_array); + int retval = 0; + time_t start; - for (r=0;r<4;++r) { - switch (r) { - case 0: - b = write_array; - s_begins = state->BWHistoryWriteEnds; - s_interval = state->BWHistoryWriteInterval; - s_values = state->BWHistoryWriteValues; - break; - case 1: - b = read_array; - s_begins = state->BWHistoryReadEnds; - s_interval = state->BWHistoryReadInterval; - s_values = state->BWHistoryReadValues; - break; - case 2: - b = dir_write_array; - s_begins = state->BWHistoryDirWriteEnds; - s_interval = state->BWHistoryDirWriteInterval; - s_values = state->BWHistoryDirWriteValues; - break; - case 3: - b = dir_read_array; - s_begins = state->BWHistoryDirReadEnds; - s_interval = state->BWHistoryDirReadInterval; - s_values = state->BWHistoryDirReadValues; - break; - } - if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { - start = s_begins - s_interval*(smartlist_len(s_values)); - if (start > now) - continue; - b->cur_obs_time = start; - b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; - SMARTLIST_FOREACH(s_values, char *, cp, { + uint64_t v, mv; + int i,ok,ok_m; + int have_maxima = (smartlist_len(s_values) == smartlist_len(s_maxima)); + + if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { + start = s_begins - s_interval*(smartlist_len(s_values)); + if (start > now) + return 0; + b->cur_obs_time = start; + b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; + SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) { + const char *maxstr = NULL; v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL); + if (have_maxima) { + maxstr = smartlist_get(s_maxima, cp_sl_idx); + mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL); + mv *= NUM_SECS_ROLLING_MEASURE; + } else { + /* No maxima known; guess average rate to be conservative. */ + mv = (v / s_interval) * NUM_SECS_ROLLING_MEASURE; + } if (!ok) { - all_ok=0; - log_notice(LD_HIST, "Could not parse '%s' into a number.'", cp); + retval = -1; + log_notice(LD_HIST, "Could not parse value '%s' into a number.'",cp); } + if (maxstr && !ok_m) { + retval = -1; + log_notice(LD_HIST, "Could not parse maximum '%s' into a number.'", + maxstr); + } + if (start < now) { - add_obs(b, start, v); - start += NUM_SECS_BW_SUM_INTERVAL; + time_t cur_start = start; + time_t actual_interval_len = s_interval; + uint64_t cur_val = 0; + /* Calculate the average per second. This is the best we can do + * because our state file doesn't have per-second resolution. */ + if (start + s_interval > now) + actual_interval_len = now - start; + cur_val = v / actual_interval_len; + /* This is potentially inefficient, but since we don't do it very + * often it should be ok. */ + while (cur_start < start + actual_interval_len) { + add_obs(b, cur_start, cur_val); + ++cur_start; + } + b->max_total = mv; + /* This will result in some fairly choppy history if s_interval + * is not the same as NUM_SECS_BW_SUM_INTERVAL. XXXX */ + start += actual_interval_len; } - }); - } + } SMARTLIST_FOREACH_END(cp); + } - /* Clean up maxima and observed */ - /* Do we really want to zero this for the purpose of max capacity? */ - for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) { - b->obs[i] = 0; - } - b->total_obs = 0; - for (i=0; i<NUM_TOTALS; ++i) { - b->maxima[i] = 0; - } - b->max_total = 0; + /* Clean up maxima and observed */ + for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) { + b->obs[i] = 0; } + b->total_obs = 0; + return retval; +} + +/** Set bandwidth history from the state file we just loaded. */ +int +rep_hist_load_state(or_state_t *state, char **err) +{ + int all_ok = 1; + + /* Assert they already have been malloced */ + tor_assert(read_array && write_array); + tor_assert(dir_read_array && dir_write_array); + +#define LOAD(arrname,st) \ + if (rep_hist_load_bwhist_state_section( \ + (arrname), \ + state->BWHistory ## st ## Values, \ + state->BWHistory ## st ## Maxima, \ + state->BWHistory ## st ## Ends, \ + state->BWHistory ## st ## Interval)<0) \ + all_ok = 0 + + LOAD(write_array, Write); + LOAD(read_array, Read); + LOAD(dir_write_array, DirWrite); + LOAD(dir_read_array, DirRead); + +#undef LOAD if (!all_ok) { *err = tor_strdup("Parsing of bandwidth history values failed"); /* and create fresh arrays */ - tor_free(read_array); - tor_free(write_array); - read_array = bw_array_new(); - write_array = bw_array_new(); + bw_arrays_init(); return -1; } return 0; @@ -1660,7 +1768,7 @@ static smartlist_t *predicted_ports_times=NULL; static void add_predicted_port(time_t now, uint16_t port) { - /* XXXX we could just use uintptr_t here, I think. */ + /* XXXX we could just use uintptr_t here, I think. -NM */ uint16_t *tmp_port = tor_malloc(sizeof(uint16_t)); time_t *tmp_time = tor_malloc(sizeof(time_t)); *tmp_port = port; @@ -2238,15 +2346,19 @@ rep_hist_buffer_stats_init(time_t now) start_of_buffer_stats_interval = now; } +/** Statistics from a single circuit. Collected when the circuit closes, or + * when we flush statistics to disk. */ typedef struct circ_buffer_stats_t { - uint32_t processed_cells; + /** Average number of cells in the circuit's queue */ double mean_num_cells_in_queue; + /** Average time a cell waits in the queue. */ double mean_time_cells_in_queue; - uint32_t local_circ_id; + /** Total number of cells sent over this circuit */ + uint32_t processed_cells; } circ_buffer_stats_t; -/** Holds stats. */ -smartlist_t *circuits_for_buffer_stats = NULL; +/** List of circ_buffer_stats_t. */ +static smartlist_t *circuits_for_buffer_stats = NULL; /** Remember cell statistics for circuit <b>circ</b> at time * <b>end_of_interval</b> and reset cell counters in case the circuit @@ -2265,11 +2377,13 @@ rep_hist_buffer_stats_add_circ(circuit_t *circ, time_t end_of_interval) return; if (!circuits_for_buffer_stats) circuits_for_buffer_stats = smartlist_create(); - start_of_interval = circ->timestamp_created > + start_of_interval = circ->timestamp_created.tv_sec > start_of_buffer_stats_interval ? - circ->timestamp_created : + circ->timestamp_created.tv_sec : start_of_buffer_stats_interval; interval_length = (int) (end_of_interval - start_of_interval); + if (interval_length <= 0) + return; stat = tor_malloc_zero(sizeof(circ_buffer_stats_t)); stat->processed_cells = orcirc->processed_cells; /* 1000.0 for s -> ms; 2.0 because of app-ward and exit-ward queues */ @@ -2447,5 +2561,11 @@ rep_hist_free_all(void) tor_free(exit_streams); built_last_stability_doc_at = 0; predicted_ports_free(); + if (circuits_for_buffer_stats) { + SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s, + tor_free(s)); + smartlist_free(circuits_for_buffer_stats); + circuits_for_buffer_stats = NULL; + } } diff --git a/src/or/rephist.h b/src/or/rephist.h index 9a39070817..b06a39ed59 100644 --- a/src/or/rephist.h +++ b/src/or/rephist.h @@ -33,12 +33,14 @@ void rep_hist_update_state(or_state_t *state); int rep_hist_load_state(or_state_t *state, char **err); void rep_history_clean(time_t before); -void rep_hist_note_router_reachable(const char *id, time_t when); +void rep_hist_note_router_reachable(const char *id, const tor_addr_t *at_addr, + uint16_t at_port, time_t when); void rep_hist_note_router_unreachable(const char *id, time_t when); int rep_hist_record_mtbf_data(time_t now, int missing_means_down); int rep_hist_load_mtbf_data(time_t now); time_t rep_hist_downrate_old_runs(time_t now); +long rep_hist_get_uptime(const char *id, time_t when); double rep_hist_get_stability(const char *id, time_t when); double rep_hist_get_weighted_fractional_uptime(const char *id, time_t when); long rep_hist_get_weighted_time_known(const char *id, time_t when); diff --git a/src/or/router.c b/src/or/router.c index 3bb37de8cf..c15b9b236e 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -629,7 +629,7 @@ init_keys(void) /* 4. Build our router descriptor. */ /* Must be called after keys are initialized. */ mydesc = router_get_my_descriptor(); - if (authdir_mode(options)) { + if (authdir_mode_handles_descs(options, ROUTER_PURPOSE_GENERAL)) { const char *m = NULL; routerinfo_t *ri; /* We need to add our own fingerprint so it gets recognized. */ @@ -881,19 +881,14 @@ consider_testing_reachability(int test_or, int test_dir) void router_orport_found_reachable(void) { - if (!can_reach_or_port) { - routerinfo_t *me = router_get_my_routerinfo(); + routerinfo_t *me = router_get_my_routerinfo(); + if (!can_reach_or_port && me) { log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from " "the outside. Excellent.%s", get_options()->_PublishServerDescriptor != NO_AUTHORITY ? " Publishing server descriptor." : ""); can_reach_or_port = 1; mark_my_descriptor_dirty(); - if (!me) { /* should never happen */ - log_warn(LD_BUG, "ORPort found reachable, but I have no routerinfo " - "yet. Failing to inform controller of success."); - return; - } control_event_server_status(LOG_NOTICE, "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d", me->address, me->or_port); @@ -904,18 +899,13 @@ router_orport_found_reachable(void) void router_dirport_found_reachable(void) { - if (!can_reach_dir_port) { - routerinfo_t *me = router_get_my_routerinfo(); + routerinfo_t *me = router_get_my_routerinfo(); + if (!can_reach_dir_port && me) { log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable " "from the outside. Excellent."); can_reach_dir_port = 1; - if (!me || decide_to_advertise_dirport(get_options(), me->dir_port)) + if (decide_to_advertise_dirport(get_options(), me->dir_port)) mark_my_descriptor_dirty(); - if (!me) { /* should never happen */ - log_warn(LD_BUG, "DirPort found reachable, but I have no routerinfo " - "yet. Failing to inform controller of success."); - return; - } control_event_server_status(LOG_NOTICE, "REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d", me->address, me->dir_port); @@ -1068,7 +1058,7 @@ should_refuse_unknown_exits(or_options_t *options) if (options->RefuseUnknownExits_ != -1) { return options->RefuseUnknownExits_; } else { - return networkstatus_get_param(NULL, "refuseunknownexits", 1); + return networkstatus_get_param(NULL, "refuseunknownexits", 1, 0, 1); } } @@ -1410,9 +1400,14 @@ router_rebuild_descriptor(int force) ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess(); - policies_parse_exit_policy(options->ExitPolicy, &ri->exit_policy, - options->ExitPolicyRejectPrivate, - ri->address, !options->BridgeRelay); + if (dns_seems_to_be_broken() || has_dns_init_failed()) { + /* DNS is screwed up; don't claim to be an exit. */ + policies_exit_policy_append_reject_star(&ri->exit_policy); + } else { + policies_parse_exit_policy(options->ExitPolicy, &ri->exit_policy, + options->ExitPolicyRejectPrivate, + ri->address, !options->BridgeRelay); + } ri->policy_is_reject_star = policy_is_reject_star(ri->exit_policy); @@ -1866,9 +1861,7 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router, } /* Write the exit policy to the end of 's'. */ - if (dns_seems_to_be_broken() || has_dns_init_failed() || - !router->exit_policy || !smartlist_len(router->exit_policy)) { - /* DNS is screwed up; don't claim to be an exit. */ + if (!router->exit_policy || !smartlist_len(router->exit_policy)) { strlcat(s+written, "reject *:*\n", maxlen-written); written += strlen("reject *:*\n"); tmpe = NULL; diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 253b787217..c02654feef 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -328,7 +328,7 @@ trusted_dirs_remove_old_certs(void) time_t cert_published; if (newest == cert) continue; - expired = ftime_definitely_after(now, cert->expires); + expired = now > cert->expires; cert_published = cert->cache_info.published_on; /* Store expired certs for 48 hours after a newer arrives; */ @@ -520,7 +520,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now) continue; cl = get_cert_list(ds->v3_identity_digest); SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert, { - if (!ftime_definitely_after(now, cert->expires)) { + if (now < cert->expires) { /* It's not expired, and we weren't looking for something to * verify a consensus with. Call it done. */ download_status_reset(&cl->dl_status); @@ -531,8 +531,8 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now) if (!found && download_status_is_ready(&cl->dl_status, now,MAX_CERT_DL_FAILURES) && !digestmap_get(pending, ds->v3_identity_digest)) { - log_notice(LD_DIR, "No current certificate known for authority %s; " - "launching request.", ds->nickname); + log_info(LD_DIR, "No current certificate known for authority %s; " + "launching request.", ds->nickname); smartlist_add(missing_digests, ds->v3_identity_digest); } } SMARTLIST_FOREACH_END(ds); @@ -1650,8 +1650,7 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl, return NULL; } - weight_scale = networkstatus_get_param(NULL, "bwweightscale", - BW_WEIGHT_SCALE); + weight_scale = circuit_build_times_get_bw_scale(NULL); if (rule == WEIGHT_FOR_GUARD) { Wg = networkstatus_get_bw_weight(NULL, "Wgg", -1); @@ -1723,7 +1722,7 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl, double weight = 1; if (statuses) { routerstatus_t *status = smartlist_get(sl, i); - is_exit = status->is_exit; + is_exit = status->is_exit && !status->is_bad_exit; is_guard = status->is_possible_guard; is_dir = (status->dir_port != 0); if (!status->has_bandwidth) { @@ -1743,7 +1742,7 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl, routerinfo_t *router = smartlist_get(sl, i); rs = router_get_consensus_status_by_id( router->cache_info.identity_digest); - is_exit = router->is_exit; + is_exit = router->is_exit && !router->is_bad_exit; is_guard = router->is_possible_guard; is_dir = (router->dir_port != 0); if (rs && rs->has_bandwidth) { @@ -1771,7 +1770,7 @@ smartlist_choose_by_bandwidth_weights(smartlist_t *sl, sl_last_weighted_bw_of_me = weight*this_bw; } - /* XXXX022 this is a kludge to expose these values. */ + /* XXXX023 this is a kludge to expose these values. */ sl_last_total_weighted_bw = weighted_bw; log_debug(LD_CIRC, "Choosing node for rule %s based on weights " @@ -1894,7 +1893,7 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule, if (status->has_bandwidth) { this_bw = kb_to_bytes(status->bandwidth); } else { /* guess */ - /* XXX022 once consensuses always list bandwidths, we can take + /* XXX023 once consensuses always list bandwidths, we can take * this guessing business out. -RD */ is_known = 0; flags = status->is_fast ? 1 : 0; @@ -1913,7 +1912,7 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule, if (rs && rs->has_bandwidth) { this_bw = kb_to_bytes(rs->bandwidth); } else if (rs) { /* guess; don't trust the descriptor */ - /* XXX022 once consensuses always list bandwidths, we can take + /* XXX023 once consensuses always list bandwidths, we can take * this guessing business out. -RD */ is_known = 0; flags = router->is_fast ? 1 : 0; @@ -2029,7 +2028,7 @@ smartlist_choose_by_bandwidth(smartlist_t *sl, bandwidth_weight_rule_t rule, } } - /* XXXX022 this is a kludge to expose these values. */ + /* XXXX023 this is a kludge to expose these values. */ sl_last_total_weighted_bw = total_bw; log_debug(LD_CIRC, "Total weighted bw = "U64_FORMAT @@ -3210,7 +3209,8 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, int from_cache, int from_fetch) { const char *id_digest; - int authdir = authdir_mode_handles_descs(get_options(), router->purpose); + or_options_t *options = get_options(); + int authdir = authdir_mode_handles_descs(options, router->purpose); int authdir_believes_valid = 0; routerinfo_t *old_router; networkstatus_t *consensus = networkstatus_get_latest_consensus(); @@ -3315,6 +3315,20 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, return ROUTER_NOT_IN_CONSENSUS; } + /* If we're reading a bridge descriptor from our cache, and we don't + * recognize it as one of our currently configured bridges, drop the + * descriptor. Otherwise we could end up using it as one of our entry + * guards even if it isn't in our Bridge config lines. */ + if (router->purpose == ROUTER_PURPOSE_BRIDGE && from_cache && + !authdir_mode_bridge(options) && + !routerinfo_is_a_configured_bridge(router)) { + log_info(LD_DIR, "Dropping bridge descriptor for '%s' because we have " + "no bridge configured at that address.", router->nickname); + *msg = "Router descriptor was not a configured bridge."; + routerinfo_free(router); + return ROUTER_WAS_NOT_WANTED; + } + /* If we have a router with the same identity key, choose the newer one. */ if (old_router) { if (!in_consensus && (router->cache_info.published_on <= @@ -3381,7 +3395,7 @@ router_add_extrainfo_to_routerlist(extrainfo_t *ei, const char **msg, int inserted; (void)from_fetch; if (msg) *msg = NULL; - /*XXXX022 Do something with msg */ + /*XXXX023 Do something with msg */ inserted = extrainfo_insert(router_get_routerlist(), ei); @@ -4577,7 +4591,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, /** How often should we launch a server/authority request to be sure of getting * a guess for our IP? */ -/*XXXX021 this info should come from netinfo cells or something, or we should +/*XXXX023 this info should come from netinfo cells or something, or we should * do this only when we aren't seeing incoming data. see bug 652. */ #define DUMMY_DOWNLOAD_INTERVAL (20*60) @@ -4595,7 +4609,7 @@ update_router_descriptor_downloads(time_t now) update_consensus_router_descriptor_downloads(now, 0, networkstatus_get_reasonably_live_consensus(now)); - /* XXXX021 we could be smarter here; see notes on bug 652. */ + /* XXXX023 we could be smarter here; see notes on bug 652. */ /* If we're a server that doesn't have a configured address, we rely on * directory fetches to learn when our address changes. So if we haven't * tried to get any routerdescs in a long time, try a dummy fetch now. */ @@ -5016,7 +5030,8 @@ routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei, if (ei->pending_sig) { char signed_digest[128]; - if (crypto_pk_public_checksig(ri->identity_pkey, signed_digest, + if (crypto_pk_public_checksig(ri->identity_pkey, + signed_digest, sizeof(signed_digest), ei->pending_sig, ei->pending_sig_len) != DIGEST_LEN || memcmp(signed_digest, ei->cache_info.signed_descriptor_digest, DIGEST_LEN)) { diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 691b9beabc..ba29f056f1 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -11,6 +11,7 @@ #include "or.h" #include "config.h" +#include "circuitbuild.h" #include "dirserv.h" #include "dirvote.h" #include "policies.h" @@ -702,11 +703,13 @@ router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest, size_t digest_len, crypto_pk_env_t *private_key) { char *signature; - size_t i; + size_t i, keysize; int siglen; - signature = tor_malloc(crypto_pk_keysize(private_key)); - siglen = crypto_pk_private_sign(private_key, signature, digest, digest_len); + keysize = crypto_pk_keysize(private_key); + signature = tor_malloc(keysize); + siglen = crypto_pk_private_sign(private_key, signature, keysize, + digest, digest_len); if (siglen < 0) { log_warn(LD_BUG,"Couldn't sign digest."); goto err; @@ -1059,6 +1062,7 @@ check_signature_token(const char *digest, const char *doctype) { char *signed_digest; + size_t keysize; const int check_authority = (flags & CST_CHECK_AUTHORITY); const int check_objtype = ! (flags & CST_NO_CHECK_OBJTYPE); @@ -1080,9 +1084,10 @@ check_signature_token(const char *digest, } } - signed_digest = tor_malloc(tok->object_size); - if (crypto_pk_public_checksig(pkey, signed_digest, tok->object_body, - tok->object_size) + keysize = crypto_pk_keysize(pkey); + signed_digest = tor_malloc(keysize); + if (crypto_pk_public_checksig(pkey, signed_digest, keysize, + tok->object_body, tok->object_size) < digest_len) { log_warn(LD_DIR, "Error reading %s: invalid signature.", doctype); tor_free(signed_digest); @@ -1500,6 +1505,12 @@ router_parse_entry_from_string(const char *s, const char *end, router->has_old_dnsworkers = 1; } + if (find_opt_by_keyword(tokens, K_REJECT6) || + find_opt_by_keyword(tokens, K_ACCEPT6)) { + log_warn(LD_DIR, "Rejecting router with reject6/accept6 line: they crash " + "older Tors."); + goto err; + } exit_policy_tokens = find_all_exitpolicy(tokens); if (!smartlist_len(exit_policy_tokens)) { log_warn(LD_DIR, "No exit policy tokens in descriptor."); @@ -1715,6 +1726,10 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, authority_cert_t * authority_cert_parse_from_string(const char *s, const char **end_of_string) { + /** Reject any certificate at least this big; it is probably an overflow, an + * attack, a bug, or some other nonsense. */ +#define MAX_CERT_SIZE (128*1024) + authority_cert_t *cert = NULL, *old_cert; smartlist_t *tokens = NULL; char digest[DIGEST_LEN]; @@ -1742,6 +1757,12 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string) ++eos; len = eos - s; + if (len > MAX_CERT_SIZE) { + log_warn(LD_DIR, "Certificate is far too big (at %lu bytes long); " + "rejecting", (unsigned long)len); + return NULL; + } + tokens = smartlist_create(); area = memarea_new(); if (tokenize_string(area,s, eos, tokens, dir_key_certificate_table, 0) < 0) { @@ -1797,7 +1818,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string) struct in_addr in; char *address = NULL; tor_assert(tok->n_args); - /* XXX021 use tor_addr_port_parse() below instead. -RD */ + /* XXX023 use tor_addr_port_parse() below instead. -RD */ if (parse_addr_port(LOG_WARN, tok->args[0], &address, NULL, &cert->dir_port)<0 || tor_inet_aton(address, &in) == 0) { @@ -2369,7 +2390,7 @@ networkstatus_verify_bw_weights(networkstatus_t *ns) const char *casename = NULL; int valid = 1; - weight_scale = networkstatus_get_param(ns, "bwweightscale", BW_WEIGHT_SCALE); + weight_scale = circuit_build_times_get_bw_scale(ns); Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1); Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1); Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1); @@ -2801,7 +2822,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, ns->flavor = flav = flavor; } if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) { - log_warn(LD_DIR, "Flavor found on non-consenus networkstatus."); + log_warn(LD_DIR, "Flavor found on non-consensus networkstatus."); goto err; } @@ -3223,7 +3244,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } sig->good_signature = 1; } else { - if (tok->object_size >= INT_MAX) { + if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) { tor_free(sig); goto err; } @@ -3492,7 +3513,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos) sig->alg = alg; memcpy(sig->identity_digest, id_digest, DIGEST_LEN); memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN); - if (tok->object_size >= INT_MAX) { + if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) { tor_free(sig); goto err; } @@ -3810,6 +3831,13 @@ static directory_token_t * get_next_token(memarea_t *area, const char **s, const char *eos, token_rule_t *table) { + /** Reject any object at least this big; it is probably an overflow, an + * attack, a bug, or some other nonsense. */ +#define MAX_UNPARSED_OBJECT_SIZE (128*1024) + /** Reject any line at least this big; it is probably an overflow, an + * attack, a bug, or some other nonsense. */ +#define MAX_LINE_LENGTH (128*1024) + const char *next, *eol, *obstart; size_t obname_len; int i; @@ -3828,6 +3856,10 @@ get_next_token(memarea_t *area, eol = memchr(*s, '\n', eos-*s); if (!eol) eol = eos; + if (eol - *s > MAX_LINE_LENGTH) { + RET_ERR("Line far too long"); + } + next = find_whitespace_eos(*s, eol); if (!strcmp_len(*s, "opt", next-*s)) { @@ -3894,7 +3926,8 @@ get_next_token(memarea_t *area, obstart = *s; /* Set obstart to start of object spec */ if (*s+16 >= eol || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */ - strcmp_len(eol-5, "-----", 5)) { /* nuls or invalid endings */ + strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */ + (eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */ RET_ERR("Malformed object: bad begin line"); } tok->object_type = STRNDUP(*s+11, eol-*s-16); @@ -3919,13 +3952,16 @@ get_next_token(memarea_t *area, ebuf[sizeof(ebuf)-1] = '\0'; RET_ERR(ebuf); } + if (next - *s > MAX_UNPARSED_OBJECT_SIZE) + RET_ERR("Couldn't parse object: missing footer or object much too big."); + if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */ tok->key = crypto_new_pk_env(); if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart)) RET_ERR("Couldn't parse public key."); } else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */ tok->key = crypto_new_pk_env(); - if (crypto_pk_read_private_key_from_string(tok->key, obstart)) + if (crypto_pk_read_private_key_from_string(tok->key, obstart, eol-obstart)) RET_ERR("Couldn't parse private key."); } else { /* If it's something else, try to base64-decode it */ int r; @@ -4602,10 +4638,12 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, else eos = eos + 1; /* Check length. */ - if (strlen(desc) > REND_DESC_MAX_SIZE) { - log_warn(LD_REND, "Descriptor length is %i which exceeds " - "maximum rendezvous descriptor size of %i kilobytes.", - (int)strlen(desc), REND_DESC_MAX_SIZE); + if (eos-desc > REND_DESC_MAX_SIZE) { + /* XXX023 If we are parsing this descriptor as a server, this + * should be a protocol warning. */ + log_warn(LD_REND, "Descriptor length is %d which exceeds " + "maximum rendezvous descriptor size of %d bytes.", + (int)(eos-desc), REND_DESC_MAX_SIZE); goto err; } /* Tokenize descriptor. */ diff --git a/src/or/routerparse.h b/src/or/routerparse.h index bde7800b7d..8b8cde25f6 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -5,7 +5,7 @@ /* See LICENSE for licensing information */ /** - * \file routerpase.h + * \file routerparse.h * \brief Header file for routerparse.c. **/ @@ -82,7 +82,5 @@ int rend_parse_introduction_points(rend_service_descriptor_t *parsed, size_t intro_points_encoded_size); int rend_parse_client_keys(strmap_t *parsed_clients, const char *str); -void tor_gettimeofday_cache_clear(void); - #endif |