diff options
55 files changed, 1194 insertions, 444 deletions
diff --git a/changes/bug1090-general b/changes/bug1090-general new file mode 100644 index 0000000000..465631592c --- /dev/null +++ b/changes/bug1090-general @@ -0,0 +1,73 @@ + o Major features and bugfixes (node selection) + + - Revise and unify the meaning of the ExitNodes, EntryNodes, + ExcludeEntryNodes, ExcludeExitNodes, ExcludeNodes, and + StrictNodes options. Previously, we had been ambiguous in + describing what counted as an "exit" node, and what operations + exactly "StrictNodes 0" would permit. This created confusion + when people saw nodes built through unexpected circuits, and + made it hard to tell real bugs from surprises. We now stipulate + that the intended behavior is: + + . "Exit", in the context of ExitNodes and ExcludeExitNodes, + means a node that delivers user traffic outside the Tor + network. + . "Entry", in the context of EntryNodes and ExcludeEntryNodes, + means a node used as the first hop of a multihop circuit: + it doesn't include direct connections to directory servers. + . "ExcludeNodes" applies to all nodes. + . "StrictNodes" changes the behavior of ExcludeNodes only. + When StrictNodes is set, Tor should avoid all nodes listed + in ExcludeNodes, even when it will make user requests + fail. When StrictNodes is *not* set, then Tor should + follow ExcludeNodes whenever it can, except when it must + use an excluded node to perform self-tests, connect to a + hidden service, provide a hidden service, fulfill a .exit + request, upload directory information, or fetch directory + information. + + Collectively, the changes to implement the behavior are a fix for + bug 1090. + + - ExcludeNodes now takes precedence over EntryNodes and ExitNodes: + if a node is listed in both, it's treated as excluded. + + - ExcludeNodes now applies to directory nodes: as a preference if + StrictNodes is 0, or an absolute requirement if StrictNodes is 1. + (Don't exclude all the directory authorities and set StrictNodes + to 1 unless you really want your Tor to break.) + + - ExcludeNodes and ExcludeExitNodes now override exit enclaving. + + - ExcludeExitNodes now overrides .exit requests. + + - We don't use bridges from ExcludeNodes. + + - When StrictNodes is 1: + . We now apply ExcludeNodes to hidden service introduction points + and to rendezvous points selected by hidden service users. + This can make your hidden service less reliable: use it with + caution! + . If we have used ExcludeNodes on ourself, do not try self-tests. + . If we have excluded all the directory authorities, we will + not even try to upload our descriptor if we're a server. + . Do not honor .exit requests to an excluded node. + + - Remove a misfeature that caused us to ignore the Fast/Stable flags + if ExitNodes was set. Bugfix on 0.2.2.7-alpha. + + - When the set of permitted nodes changes, we now remove any + mappings introduced via TrackExitHosts to now-excluded nodes. + Bugfix on 0.1.0.1-rc. + + - We never cannibalize a circuit that had excluded nodes on it, + even if StrictNodes is 0. Bugfix on 0.1.0.1-rc. + + - Improve log messages related to excluded nodes. + + - Revert a change where we would be laxer about attaching streams to + circuits than when building the circuits. This was meant to + prevent a set of bugs where streams were never attachable, but our + improved code here should make this unnecessary. Bugfix on + 0.2.2.7-alpha. + diff --git a/changes/bug1090-launch-warning b/changes/bug1090-launch-warning new file mode 100644 index 0000000000..3f3fbcb4d8 --- /dev/null +++ b/changes/bug1090-launch-warning @@ -0,0 +1,5 @@ + o Minor features: + - Keep track of how many times we launch a new circuit to handle + a given stream. Too many launches could indicate an inconsistency + between our "launch a circuit to handle this stream" logic and our + "attach our stream to one of the available circuits" logic. diff --git a/changes/bug2475 b/changes/bug2475 new file mode 100644 index 0000000000..d6f0595a59 --- /dev/null +++ b/changes/bug2475 @@ -0,0 +1,5 @@ + o Minor bugfixes: + - Avoid signed/unsigned comparisons by making SIZE_T_CEILING unsigned. + (None of the cases where we did this before were wrong, but by making + this change we can avoid warnings.) Fixes bug2475; bugfix on + Tor 0.2.1.28. diff --git a/changes/bug2704 b/changes/bug2704 new file mode 100644 index 0000000000..821b38bc08 --- /dev/null +++ b/changes/bug2704 @@ -0,0 +1,5 @@ + o Major bugfixes: + - When writing our maximum bw for the current interval to the state + file, don't wrongly inflate that value by a factor of 10 anymore. + Fixes more of bug 2704. + diff --git a/changes/bug2704_part1 b/changes/bug2704_part1 new file mode 100644 index 0000000000..eaf22812cc --- /dev/null +++ b/changes/bug2704_part1 @@ -0,0 +1,5 @@ + o Minor bugfixes: + - Fix an issue causing calculation of Tor's average bandwidth as saved + in the state file to be 10 times smaller than it should be. Fixes the + first part of bug 2704, bugfix on tor-0.2.2.23-alpha. + diff --git a/changes/bug2704_part2 b/changes/bug2704_part2 new file mode 100644 index 0000000000..962c8b7099 --- /dev/null +++ b/changes/bug2704_part2 @@ -0,0 +1,5 @@ + o Major bugfixes: + - Prevent relays that read their bandwidth history from their state file + from arbitrarily inflating that value. Fixes the second half of bug + 2704, bugfix on tor-0.2.2.23-alpha. + diff --git a/changes/bug2722 b/changes/bug2722 new file mode 100644 index 0000000000..ed132fc899 --- /dev/null +++ b/changes/bug2722 @@ -0,0 +1,11 @@ + o Minor bugfixes + - Ignore the TunnelDirConns option when determining which HSDir + relays are responsible for a hidden service descriptor ID. + Currently, clients and hidden services with TunnelDirConns off + will skip over HSDir relays which do not advertise a DirPort + when making a list of HSDirs responsible for a descriptor ID, + even though they would never try to use a HSDir's DirPort to + upload or fetch a hidden service descriptor. Fixes bug 2722; + bugfix on 0.2.1.6-alpha. + + diff --git a/changes/bug2750 b/changes/bug2750 new file mode 100644 index 0000000000..4371a0a4ee --- /dev/null +++ b/changes/bug2750 @@ -0,0 +1,6 @@ + o Minor bugfixes + - Correct the warning displayed when a rendezvous descriptor exceeds + the maximum size. Fixes bug 2750; bugfix on 0.2.1.5-alpha. Found + by John Brooks. + + diff --git a/changes/bug2899 b/changes/bug2899 new file mode 100644 index 0000000000..6af86d0676 --- /dev/null +++ b/changes/bug2899 @@ -0,0 +1,4 @@ + - Minor bugfixes: + o Downgrade "no current certificates known for authority" message from + Notice to Info. Bugfix on 0.2.0.10-alpha; fixes bug 2899. + diff --git a/changes/bug2917 b/changes/bug2917 new file mode 100644 index 0000000000..6b1e643343 --- /dev/null +++ b/changes/bug2917 @@ -0,0 +1,4 @@ + o Minor bugfixes + - Make the SIGNAL DUMP control-port command work on FreeBSD. Fixes + bug 2917. Bugfix on 0.1.1.1-alpha. + diff --git a/changes/bug2933 b/changes/bug2933 new file mode 100644 index 0000000000..7aaf526112 --- /dev/null +++ b/changes/bug2933 @@ -0,0 +1,4 @@ + o Minor bugfixes + - Fix an uncommon assertion failure when running with DNSPort under + heavy load. Fixes bug 2933; bugfix on 2.0.1-alpha. + diff --git a/changes/bug2948 b/changes/bug2948 new file mode 100644 index 0000000000..640ef625d9 --- /dev/null +++ b/changes/bug2948 @@ -0,0 +1,7 @@ + o Minor bugfixes + - Only limit the lengths of single HS descriptors, even when + multiple HS descriptors are published to an HSDir relay in a + single POST operation. Fixes bug 2948; bugfix on 0.2.1.5-alpha. + Found by hsdir. + + diff --git a/changes/bug2971 b/changes/bug2971 new file mode 100644 index 0000000000..8b71ce040b --- /dev/null +++ b/changes/bug2971 @@ -0,0 +1,6 @@ + o Minor bugfixes: + - Be more consistent in our treatment of file system paths. ~ should + get expanded to the user's home directory in the Log config option. + Bugfix on 0.2.0.1-alpha, which introduced the feature for the -f and + --DataDirectory options. + diff --git a/changes/bug2979 b/changes/bug2979 new file mode 100644 index 0000000000..fe1f45fe09 --- /dev/null +++ b/changes/bug2979 @@ -0,0 +1,9 @@ + o Minor bugfixes: + - If the Nickname configuration option wasn't given, Tor used to pick + a nickname based on the local hostname as the nickname for a relay. + Because nicknames are not very important in today's Tor and the + "Unnamed" nickname has been implemented, this is now problematic + behaviour: It leaks information about the hostname without being + useful at all. Bugfix on tor-0.1.2.2-alpha, which introduced the + Unnamed nickname. Fixes bug 2979, reported by tagnaq. + diff --git a/changes/bug3020 b/changes/bug3020 new file mode 100644 index 0000000000..b987161229 --- /dev/null +++ b/changes/bug3020 @@ -0,0 +1,7 @@ + o Minor bugfixes: + - When checking whether a hibernation period has fully elapsed, use + the amount of seconds we expect for that period instead of using + the new period that just started. This would cause an issue because + February is a really short month. Bugfix on 0.2.2.17-alpha; + fixes bug 3020. + diff --git a/changes/clear_trackexithost b/changes/clear_trackexithost new file mode 100644 index 0000000000..b9ac6fec44 --- /dev/null +++ b/changes/clear_trackexithost @@ -0,0 +1,5 @@ + o Minor bugfixes: + - Fix a bug in the code where we could keep trying to use a + TrackHostExits-based mapping after we failed to reach the intended + destination node. Fixes bug 2999. Bugfix on 0.2.0.20-rc. + diff --git a/changes/exitnodes_reliable b/changes/exitnodes_reliable new file mode 100644 index 0000000000..62ef03a0ce --- /dev/null +++ b/changes/exitnodes_reliable @@ -0,0 +1,7 @@ + o Minor features: + - If ExitNodes is set, still pay attention to the Fast/Stable + status of exits when picking exit nodes. (We used to ignore + these flags when ExitNodes was set, on the grounds that people + who set exitnodes wanted all of those nodes to get used, but + with the ability to pick exits by country and IP range, this + doesn't necessarily make sense any more.) diff --git a/changes/forget-rend-descs-on-newnym b/changes/forget-rend-descs-on-newnym new file mode 100644 index 0000000000..da7afbe201 --- /dev/null +++ b/changes/forget-rend-descs-on-newnym @@ -0,0 +1,21 @@ + o Security fixes: + - Forget all hidden service descriptors cached as a client when + processing a SIGNAL NEWNYM command. Fixes bug 3000. Bugfix on + 0.0.6. + o Major bugfixes: + - When we find that we have extended a hidden service's introduction + circuit to a relay which isn't listed as an introduction point in + the HS descriptor we currently have for the service, we now retry + one of the introduction points in the current HS descriptor. + Previously we would just give up. Bugfix on 0.2.0.10-alpha; fixes + bugs 1024 and 1930. + o Minor bugfixes: + - Don't allow v0 hidden service authorities to act as clients. + Required by fix for bug 3000. + - Ignore SIGNAL NEWNYM commands on relay-only Tor instances. + Required by fix for bug 3000. + o Code simplifications and refactoring: + - Allow rend_client_send_introduction to fail without closing the + AP connection permanently. + + diff --git a/changes/gmtime_null b/changes/gmtime_null new file mode 100644 index 0000000000..16a25408bf --- /dev/null +++ b/changes/gmtime_null @@ -0,0 +1,6 @@ + o Minor bugfixes + - On some platforms, gmtime and localtime can return NULL under + certain circumstances even for well-defined values of time_t. + Try to detect and make up for this deficiency. Possible fix for + bug 2077. Bugfix on all versions of Tor. Found by boboper. + diff --git a/changes/microdesc-double-free b/changes/microdesc-double-free new file mode 100644 index 0000000000..932cc754ba --- /dev/null +++ b/changes/microdesc-double-free @@ -0,0 +1,7 @@ + o Security fixes: + - Don't double-free a parsable, but invalid, microdescriptor, even + if it is followed in the blob we're parsing by an unparsable + microdescriptor. Fixes an issue reported in a comment on bug 2954. + Bugfix on 0.2.2.6-alpha; fix by "cypherpunks". + + diff --git a/changes/ticket2497 b/changes/ticket2497 new file mode 100644 index 0000000000..51171412bf --- /dev/null +++ b/changes/ticket2497 @@ -0,0 +1,4 @@ + o Minor features: + - Ensure that no empty [dirreq-](read|write)-history lines are added + to an extrainfo document. Implements ticket 2497. + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index f1734d2016..04a0537712 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -489,32 +489,76 @@ The following options are useful only for clients (that is, if **ExcludeNodes** __node__,__node__,__...__:: A list of identity fingerprints, nicknames, country codes and address - patterns of nodes to never use when building a circuit. (Example: - ExcludeNodes SlowServer, $ EFFFFFFFFFFFFFFF, \{cc}, 255.254.0.0/8) + patterns of nodes to avoid when building a circuit. + (Example: + ExcludeNodes SlowServer, $ EFFFFFFFFFFFFFFF, \{cc}, 255.254.0.0/8) + ++ + By default, this option is treated as a preference that Tor is allowed + to override in order to keep working. + For example, if you try to connect to a hidden service, + but you have excluded all of the hidden service's introduction points, + Tor will connect to one of them anyway. If you do not want this + behavior, set the StrictNodes option (documented below). + ++ + Note also that if you are a relay, this (and the other node selection + options below) only affects your own circuits that Tor builds for you. + Clients can still build circuits through you to any node. Controllers + can tell Tor to build circuits through any node. + **ExcludeExitNodes** __node__,__node__,__...__:: A list of identity fingerprints, nicknames, country codes and address - patterns of nodes to never use when picking an exit node. Note that any + patterns of nodes to never use when picking an exit node---that is, a + node that delivers traffic for you outside the Tor network. Note that any node listed in ExcludeNodes is automatically considered to be part of this - list. + list too. See also the caveats on the "ExitNodes" option below. -**EntryNodes** __node__,__node__,__...__:: - A list of identity fingerprints, nicknames and address - patterns of nodes to use for the first hop in normal circuits. These are - treated only as preferences unless StrictNodes (see below) is also set. **ExitNodes** __node__,__node__,__...__:: A list of identity fingerprints, nicknames, country codes and address - patterns of nodes to use for the last hop in normal exit circuits. These - are treated only as preferences unless StrictNodes (see below) is also set. + patterns of nodes to use as exit node---that is, a + node that delivers traffic for you outside the Tor network. + ++ + Note that if you list too few nodes here, or if you exclude too many exit + nodes with ExcludeExitNodes, you can degrade functionality. For example, + if none of the exits you list allows traffic on port 80 or 443, you won't + be able to browse the web. + ++ + Note also that not every circuit is used to deliver traffic outside of + the Tor network. It is normal to see non-exit circuits (such as those + used to connect to hidden services, those that do directory fetches, + those used for relay reachability self-tests, and so on) that end + at a non-exit node. To + keep a node from being used entirely, see ExcludeNodes and StrictNodes. + ++ + The ExcludeNodes option overrides this option: any node listed in both + ExitNodes and ExcludeNodes is treated as excluded. + ++ + The .exit address notation, if enabled via AllowDotExit, overrides + this option. + +**EntryNodes** __node__,__node__,__...__:: + A list of identity fingerprints and nicknames of nodes + to use for the first hop in your normal circuits. (Country codes and + address patterns are not yet supported.) Normal circuits include all + circuits except for direct connections to directory servers. The Bridge + option overrides this option; if you have configured bridges and + UseBridges is 1, the Bridges are used as your entry nodes. + ++ + The ExcludeNodes option overrides this option: any node listed in both + EntryNodes and ExcludeNodes is treated as excluded. **StrictNodes** **0**|**1**:: - If 1 and EntryNodes config option is set, Tor will never use any nodes - besides those listed in EntryNodes for the first hop of a normal circuit. - If 1 and ExitNodes config option is set, Tor will never use any nodes - besides those listed in ExitNodes for the last hop of a normal exit - circuit. Note that Tor might still use these nodes for non-exit circuits - such as one-hop directory fetches or hidden service support circuits. + If StrictNodes is set to 1, Tor will treat the ExcludeNodes option as a + requirement to follow for all the circuits you generate, even if doing so + will break functionality for you. If StrictNodes is set to 0, Tor will + still try to avoid nodes in the ExcludeNodes list, but it will err on the + side of avoiding unexpected errors. Specifically, StrictNodes 0 tells + Tor that it is okay to use an excluded node when it is *necessary* to + perform relay reachability self-tests, connect to + a hidden service, provide a hidden service to a client, fulfill a .exit + request, upload directory information, or download directory information. + (Default: 0) **FascistFirewall** **0**|**1**:: If 1, Tor will only create outgoing connections to ORs running on ports diff --git a/src/common/address.c b/src/common/address.c index adc0ef0f7c..aff517ca51 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -604,7 +604,7 @@ tor_addr_parse_mask_ports(const char *s, tor_addr_t *addr_out, if (family == AF_INET6 && v4map) { if (bits > 32 && bits < 96) { /* Crazy */ log_warn(LD_GENERAL, - "Bad mask bits %i for V4-mapped V6 address; rejecting.", + "Bad mask bits %d for V4-mapped V6 address; rejecting.", bits); goto err; } diff --git a/src/common/compat.c b/src/common/compat.c index 27489e568a..3644bd9996 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1996,14 +1996,88 @@ tor_gettimeofday(struct timeval *timeval) #define TIME_FNS_NEED_LOCKS #endif +static struct tm * +correct_tm(int islocal, const time_t *timep, struct tm *resultbuf, + struct tm *r) +{ + const char *outcome; + + if (PREDICT_LIKELY(r)) { + if (r->tm_year > 8099) { /* We can't strftime dates after 9999 CE. */ + r->tm_year = 8099; + r->tm_mon = 11; + r->tm_mday = 31; + r->tm_yday = 365; + r->tm_hour = 23; + r->tm_min = 59; + r->tm_sec = 59; + } + return r; + } + + /* If we get here, gmtime or localtime returned NULL. It might have done + * this because of overrun or underrun, or it might have done it because of + * some other weird issue. */ + if (timep) { + if (*timep < 0) { + r = resultbuf; + r->tm_year = 70; /* 1970 CE */ + r->tm_mon = 0; + r->tm_mday = 1; + r->tm_yday = 1; + r->tm_hour = 0; + r->tm_min = 0 ; + r->tm_sec = 0; + outcome = "Rounding up to 1970"; + goto done; + } else if (*timep >= INT32_MAX) { + /* Rounding down to INT32_MAX isn't so great, but keep in mind that we + * only do it if gmtime/localtime tells us NULL. */ + r = resultbuf; + r->tm_year = 137; /* 2037 CE */ + r->tm_mon = 11; + r->tm_mday = 31; + r->tm_yday = 365; + r->tm_hour = 23; + r->tm_min = 59; + r->tm_sec = 59; + outcome = "Rounding down to 2037"; + goto done; + } + } + + /* If we get here, then gmtime/localtime failed without getting an extreme + * value for *timep */ + + tor_fragile_assert(); + r = resultbuf; + memset(resultbuf, 0, sizeof(struct tm)); + outcome="can't recover"; + done: + log_warn(LD_BUG, "%s("I64_FORMAT") failed with error %s: %s", + islocal?"localtime":"gmtime", + timep?I64_PRINTF_ARG(*timep):0, + strerror(errno), + outcome); + return r; +} + + /** @{ */ /** As localtime_r, but defined for platforms that don't have it: * * Convert *<b>timep</b> to a struct tm in local time, and store the value in * *<b>result</b>. Return the result on success, or NULL on failure. */ -#ifndef HAVE_LOCALTIME_R -#ifdef TIME_FNS_NEED_LOCKS +#ifdef HAVE_LOCALTIME_R +struct tm * +tor_localtime_r(const time_t *timep, struct tm *result) +{ + struct tm *r; + r = localtime_r(timep, result); + return correct_tm(1, timep, result, r); +} +#elif defined(TIME_FNS_NEED_LOCKS) struct tm * tor_localtime_r(const time_t *timep, struct tm *result) { @@ -2013,9 +2087,10 @@ tor_localtime_r(const time_t *timep, struct tm *result) tor_assert(result); tor_mutex_acquire(m); r = localtime(timep); - memcpy(result, r, sizeof(struct tm)); + if (r) + memcpy(result, r, sizeof(struct tm)); tor_mutex_release(m); - return result; + return correct_tm(1, timep, result, r); } #else struct tm * @@ -2024,11 +2099,11 @@ tor_localtime_r(const time_t *timep, struct tm *result) struct tm *r; tor_assert(result); r = localtime(timep); - memcpy(result, r, sizeof(struct tm)); - return result; + if (r) + memcpy(result, r, sizeof(struct tm)); + return correct_tm(1, timep, result, r); } #endif -#endif /** @} */ /** @{ */ @@ -2037,8 +2112,15 @@ tor_localtime_r(const time_t *timep, struct tm *result) * Convert *<b>timep</b> to a struct tm in UTC, and store the value in * *<b>result</b>. Return the result on success, or NULL on failure. */ -#ifndef HAVE_GMTIME_R -#ifdef TIME_FNS_NEED_LOCKS +#ifdef HAVE_GMTIME_R +struct tm * +tor_gmtime_r(const time_t *timep, struct tm *result) +{ + struct tm *r; + r = gmtime_r(timep, result); + return correct_tm(0, timep, result, r); +} +#elif defined(TIME_FNS_NEED_LOCKS) struct tm * tor_gmtime_r(const time_t *timep, struct tm *result) { @@ -2048,9 +2130,10 @@ tor_gmtime_r(const time_t *timep, struct tm *result) tor_assert(result); tor_mutex_acquire(m); r = gmtime(timep); - memcpy(result, r, sizeof(struct tm)); + if (r) + memcpy(result, r, sizeof(struct tm)); tor_mutex_release(m); - return result; + return correct_tm(0, timep, result, r); } #else struct tm * @@ -2059,12 +2142,11 @@ tor_gmtime_r(const time_t *timep, struct tm *result) struct tm *r; tor_assert(result); r = gmtime(timep); - memcpy(result, r, sizeof(struct tm)); - return result; + if (r) + memcpy(result, r, sizeof(struct tm)); + return correct_tm(0, timep, result, r); } #endif -#endif -/** @} */ #if defined(USE_WIN32_THREADS) void diff --git a/src/common/compat.h b/src/common/compat.h index 550c08b533..af795ffba9 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -321,17 +321,8 @@ struct timeval { void tor_gettimeofday(struct timeval *timeval); -#ifdef HAVE_LOCALTIME_R -#define tor_localtime_r localtime_r -#else struct tm *tor_localtime_r(const time_t *timep, struct tm *result); -#endif - -#ifdef HAVE_GMTIME_R -#define tor_gmtime_r gmtime_r -#else struct tm *tor_gmtime_r(const time_t *timep, struct tm *result); -#endif #ifndef timeradd /** Replacement for timeradd on platforms that do not have it: sets tvout to diff --git a/src/common/crypto.c b/src/common/crypto.c index 2ef40c29c7..8d17a3daee 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -523,7 +523,7 @@ crypto_pk_read_private_key_from_string(crypto_pk_env_t *env, tor_assert(env); tor_assert(s); - tor_assert(len < INT_MAX && len < SIZE_T_CEILING); + tor_assert(len < INT_MAX && len < SSIZE_T_CEILING); /* Create a read-only memory BIO, backed by the string 's' */ b = BIO_new_mem_buf((char*)s, (int)len); diff --git a/src/common/torint.h b/src/common/torint.h index f5bebf8b9d..0b5c29adc0 100644 --- a/src/common/torint.h +++ b/src/common/torint.h @@ -329,8 +329,10 @@ typedef uint32_t uintptr_t; #endif #endif +/** Any ssize_t larger than this amount is likely to be an underflow. */ +#define SSIZE_T_CEILING ((ssize_t)(SSIZE_T_MAX-16)) /** Any size_t larger than this amount is likely to be an underflow. */ -#define SIZE_T_CEILING (SSIZE_T_MAX-16) +#define SIZE_T_CEILING ((size_t)(SSIZE_T_MAX-16)) #endif /* __TORINT_H */ diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 2d4d5c032a..fe94264555 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -2051,8 +2051,9 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) */ if (timediff < 0 || timediff > 2*circ_times.close_ms+1000) { log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. " - "Assuming clock jump. Purpose %d", timediff, - circ->_base.purpose); + "Assuming clock jump. Purpose %d (%s)", timediff, + circ->_base.purpose, + circuit_purpose_to_string(circ->_base.purpose)); } else if (!circuit_build_times_disabled()) { /* Only count circuit times if the network is live */ if (circuit_build_times_network_check_live(&circ_times)) { @@ -2685,16 +2686,24 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, n_supported[i] = -1; continue; /* skip routers that are known to be down or bad exits */ } - if (router_is_unreliable(router, need_uptime, need_capacity, 0) && - (!options->ExitNodes || - !routerset_contains_router(options->ExitNodes, router))) { - /* FFFF Someday, differentiate between a routerset that names - * routers, and a routerset that names countries, and only do this - * check if they've asked for specific exit relays. Or if the country - * they ask for is rare. Or something. */ + + if (options->_ExcludeExitNodesUnion && + routerset_contains_router(options->_ExcludeExitNodesUnion, router)) { + n_supported[i] = -1; + continue; /* user asked us not to use it, no matter what */ + } + if (options->ExitNodes && + !routerset_contains_router(options->ExitNodes, router)) { n_supported[i] = -1; - continue; /* skip routers that are not suitable, unless we have - * ExitNodes set, in which case we asked for it */ + continue; /* not one of our chosen exit nodes */ + } + + if (router_is_unreliable(router, need_uptime, need_capacity, 0)) { + n_supported[i] = -1; + continue; /* skip routers that are not suitable. Don't worry if + * this makes us reject all the possible routers: if so, + * we'll retry later in this function with need_update and + * need_capacity set to 0. */ } if (!(router->is_valid || options->_AllowInvalid & ALLOW_INVALID_EXIT)) { /* if it's invalid and we don't want it */ @@ -2719,7 +2728,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, { if (!ap_stream_wants_exit_attention(conn)) continue; /* Skip everything but APs in CIRCUIT_WAIT */ - if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), router, 1)) { + if (connection_ap_can_use_exit(TO_EDGE_CONN(conn), router)) { ++n_supported[i]; // log_fn(LOG_DEBUG,"%s is supported. n_supported[%d] now %d.", // router->nickname, i, n_supported[i]); @@ -2753,21 +2762,13 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, /* If any routers definitely support any pending connections, choose one * at random. */ if (best_support > 0) { - smartlist_t *supporting = smartlist_create(), *use = smartlist_create(); + smartlist_t *supporting = smartlist_create(); for (i = 0; i < smartlist_len(dir->routers); i++) if (n_supported[i] == best_support) smartlist_add(supporting, smartlist_get(dir->routers, i)); - routersets_get_disjunction(use, supporting, options->ExitNodes, - options->_ExcludeExitNodesUnion, 1); - if (smartlist_len(use) == 0 && options->ExitNodes && - !options->StrictNodes) { /* give up on exitnodes and try again */ - routersets_get_disjunction(use, supporting, NULL, - options->_ExcludeExitNodesUnion, 1); - } - router = routerlist_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT); - smartlist_free(use); + router = routerlist_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); smartlist_free(supporting); } else { /* Either there are no pending connections, or no routers even seem to @@ -2775,7 +2776,7 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, * at least one predicted exit port. */ int attempt; - smartlist_t *needed_ports, *supporting, *use; + smartlist_t *needed_ports, *supporting; if (best_support == -1) { if (need_uptime || need_capacity) { @@ -2792,7 +2793,6 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, options->_ExcludeExitNodesUnion ? " or are Excluded" : ""); } supporting = smartlist_create(); - use = smartlist_create(); needed_ports = circuit_get_unhandled_ports(time(NULL)); for (attempt = 0; attempt < 2; attempt++) { /* try once to pick only from routers that satisfy a needed port, @@ -2807,25 +2807,13 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, } } - routersets_get_disjunction(use, supporting, options->ExitNodes, - options->_ExcludeExitNodesUnion, 1); - if (smartlist_len(use) == 0 && options->ExitNodes && - !options->StrictNodes) { /* give up on exitnodes and try again */ - routersets_get_disjunction(use, supporting, NULL, - options->_ExcludeExitNodesUnion, 1); - } - /* FFF sometimes the above results in null, when the requested - * exit node is considered down by the consensus. we should pick - * it anyway, since the user asked for it. */ - router = routerlist_sl_choose_by_bandwidth(use, WEIGHT_FOR_EXIT); + router = routerlist_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT); if (router) break; smartlist_clear(supporting); - smartlist_clear(use); } SMARTLIST_FOREACH(needed_ports, uint16_t *, cp, tor_free(cp)); smartlist_free(needed_ports); - smartlist_free(use); smartlist_free(supporting); } @@ -2834,10 +2822,11 @@ choose_good_exit_server_general(routerlist_t *dir, int need_uptime, log_info(LD_CIRC, "Chose exit server '%s'", router->nickname); return router; } - if (options->ExitNodes && options->StrictNodes) { + if (options->ExitNodes) { log_warn(LD_CIRC, - "No specified exit routers seem to be running, and " - "StrictNodes is set: can't choose an exit."); + "No specified %sexit routers seem to be running: " + "can't choose an exit.", + options->_ExcludeExitNodesUnion ? "non-excluded " : ""); } return NULL; } @@ -2889,7 +2878,6 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit) or_options_t *options = get_options(); routerset_t *rs = options->ExcludeNodes; const char *description; - int domain = LD_CIRC; uint8_t purpose = circ->_base.purpose; if (circ->build_state->onehop_tunnel) @@ -2902,13 +2890,14 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit) case CIRCUIT_PURPOSE_INTRO_POINT: case CIRCUIT_PURPOSE_REND_POINT_WAITING: case CIRCUIT_PURPOSE_REND_ESTABLISHED: - log_warn(LD_BUG, "Called on non-origin circuit (purpose %d)", - (int)purpose); + log_warn(LD_BUG, "Called on non-origin circuit (purpose %d, %s)", + (int)purpose, + circuit_purpose_to_string(purpose)); return; case CIRCUIT_PURPOSE_C_GENERAL: if (circ->build_state->is_internal) return; - description = "Requested exit node"; + description = "requested exit node"; rs = options->_ExcludeExitNodesUnion; break; case CIRCUIT_PURPOSE_C_INTRODUCING: @@ -2923,22 +2912,34 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit) case CIRCUIT_PURPOSE_C_REND_READY: case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: case CIRCUIT_PURPOSE_C_REND_JOINED: - description = "Chosen rendezvous point"; - domain = LD_BUG; + description = "chosen rendezvous point"; break; case CIRCUIT_PURPOSE_CONTROLLER: rs = options->_ExcludeExitNodesUnion; - description = "Controller-selected circuit target"; + description = "controller-selected circuit target"; break; } if (routerset_contains_extendinfo(rs, exit)) { - log_fn(LOG_WARN, domain, "%s '%s' is in ExcludeNodes%s. Using anyway " - "(circuit purpose %d).", - description,exit->nickname, - rs==options->ExcludeNodes?"":" or ExcludeExitNodes", - (int)purpose); - circuit_log_path(LOG_WARN, domain, circ); + /* We should never get here if StrictNodes is set to 1. */ + if (options->StrictNodes) { + log_warn(LD_BUG, "Using %s '%s' which is listed in ExcludeNodes%s, " + "even though StrictNodes is set. Please report. " + "(Circuit purpose: %s)", + description, exit->nickname, + rs==options->ExcludeNodes?"":" or ExcludeExitNodes", + circuit_purpose_to_string(purpose)); + } else { + log_warn(LD_CIRC, "Using %s '%s' which is listed in " + "ExcludeNodes%s, because no better options were available. To " + "prevent this (and possibly break your Tor functionality), " + "set the StrictNodes configuration option. " + "(Circuit purpose: %s)", + description, exit->nickname, + rs==options->ExcludeNodes?"":" or ExcludeExitNodes", + circuit_purpose_to_string(purpose)); + } + circuit_log_path(LOG_WARN, LD_CIRC, circ); } return; @@ -3979,7 +3980,8 @@ entry_guards_prepend_from_config(or_options_t *options) * Perhaps we should do this calculation once whenever the list of routers * changes or the entrynodes setting changes. */ - routerset_get_all_routers(entry_routers, options->EntryNodes, 0); + routerset_get_all_routers(entry_routers, options->EntryNodes, + options->ExcludeNodes, 0); SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, smartlist_add(entry_fps,ri->cache_info.identity_digest)); SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, { @@ -4004,14 +4006,10 @@ entry_guards_prepend_from_config(or_options_t *options) SMARTLIST_FOREACH(entry_routers, routerinfo_t *, ri, { add_an_entry_guard(ri, 0); }); - /* Finally, the remaining previously configured guards that are not in - * EntryNodes, unless we're strict in which case we drop them */ - if (options->StrictNodes) { - SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e, - entry_guard_free(e)); - } else { - smartlist_add_all(entry_guards, old_entry_guards_not_on_list); - } + /* Finally, free the remaining previously configured guards that are not in + * EntryNodes. */ + SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e, + entry_guard_free(e)); smartlist_free(entry_routers); smartlist_free(entry_fps); @@ -4022,7 +4020,7 @@ entry_guards_prepend_from_config(or_options_t *options) /** Return 0 if we're fine adding arbitrary routers out of the * directory to our entry guard list, or return 1 if we have a - * list already and we'd prefer to stick to it. + * list already and we must stick to it. */ int entry_list_is_constrained(or_options_t *options) @@ -4034,18 +4032,6 @@ entry_list_is_constrained(or_options_t *options) return 0; } -/* Are we dead set against changing our entry guard list, or would we - * change it if it means keeping Tor usable? */ -static int -entry_list_is_totally_static(or_options_t *options) -{ - if (options->EntryNodes && options->StrictNodes) - return 1; - if (options->UseBridges) - return 1; - return 0; -} - /** Pick a live (up and listed) entry guard from entry_guards. If * <b>state</b> is non-NULL, this is for a specific circuit -- * make sure not to pick this circuit's exit or any node in the @@ -4090,6 +4076,7 @@ choose_random_entry(cpath_build_state_t *state) 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 0 /* since EntryNodes is always strict now, this clause is moot */ if (options->EntryNodes && !routerset_contains_router(options->EntryNodes, r)) { /* We've come to the end of our preferred entry nodes. */ @@ -4104,6 +4091,7 @@ choose_random_entry(cpath_build_state_t *state) "No relays from EntryNodes available. Using others."); } } +#endif smartlist_add(live_entry_guards, r); if (!entry->made_contact) { /* Always start with the first not-yet-contacted entry @@ -4129,7 +4117,7 @@ choose_random_entry(cpath_build_state_t *state) } if (smartlist_len(live_entry_guards) < preferred_min) { - if (!entry_list_is_totally_static(options)) { + if (!entry_list_is_constrained(options)) { /* still no? try adding a new entry then */ /* XXX if guard doesn't imply fast and stable, then we need * to tell add_an_entry_guard below what we want, or it might @@ -4154,13 +4142,18 @@ choose_random_entry(cpath_build_state_t *state) need_capacity = 0; goto retry; } +#if 0 + /* Removing this retry logic: if we only allow one exit, and it is in the + same family as all our entries, then we are just plain not going to win + here. */ if (!r && entry_list_is_constrained(options) && consider_exit_family) { - /* still no? if we're using bridges or have strictentrynodes - * set, and our chosen exit is in the same family as all our - * bridges/entry guards, then be flexible about families. */ + /* still no? if we're using bridges, + * and our chosen exit is in the same family as all our + * bridges, then be flexible about families. */ consider_exit_family = 0; goto retry; } +#endif /* live_entry_guards may be empty below. Oh well, we tried. */ } @@ -4561,6 +4554,24 @@ bridge_add_from_config(const tor_addr_t *addr, uint16_t port, char *digest) smartlist_add(bridge_list, b); } +/** Return true iff <b>routerset</b> contains the bridge <b>bridge</b>. */ +static int +routerset_contains_bridge(const routerset_t *routerset, + const bridge_info_t *bridge) +{ + int result; + extend_info_t *extinfo; + tor_assert(bridge); + if (!routerset) + return 0; + + extinfo = extend_info_alloc( + NULL, bridge->identity, NULL, &bridge->addr, bridge->port); + result = routerset_contains_extendinfo(routerset, extinfo); + extend_info_free(extinfo); + return result; +} + /** If <b>digest</b> is one of our known bridges, return it. */ static bridge_info_t * find_bridge_by_digest(const char *digest) @@ -4573,12 +4584,12 @@ find_bridge_by_digest(const char *digest) return NULL; } -/** We need to ask <b>bridge</b> for its server descriptor. <b>address</b> - * is a helpful string describing this bridge. */ +/** We need to ask <b>bridge</b> for its server descriptor. */ static void launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) { char *address; + or_options_t *options = get_options(); if (connection_get_by_type_addr_port_purpose( CONN_TYPE_DIR, &bridge->addr, bridge->port, @@ -4586,6 +4597,13 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) return; /* it's already on the way */ address = tor_dup_addr(&bridge->addr); + if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { + download_status_mark_impossible(&bridge->fetch_status); + log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", + safe_str_client(fmt_addr(&bridge->addr))); + return; + } + directory_initiate_command(address, &bridge->addr, bridge->port, 0, 0, /* does not matter */ @@ -4626,6 +4644,12 @@ fetch_bridge_descriptors(or_options_t *options, time_t now) if (!download_status_is_ready(&bridge->fetch_status, now, IMPOSSIBLE_TO_DOWNLOAD)) continue; /* don't bother, no need to retry yet */ + if (routerset_contains_bridge(options->ExcludeNodes, bridge)) { + download_status_mark_impossible(&bridge->fetch_status); + log_warn(LD_APP, "Not using bridge at %s: it is in ExcludeNodes.", + safe_str_client(fmt_addr(&bridge->addr))); + continue; + } /* schedule another fetch as if this one will fail, in case it does */ download_status_failed(&bridge->fetch_status, 0); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index d11b457944..33dc8f09ad 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -378,6 +378,62 @@ circuit_purpose_to_controller_string(uint8_t purpose) } } +/** Return a human-readable string for the circuit purpose <b>purpose</b>. */ +const char * +circuit_purpose_to_string(uint8_t purpose) +{ + static char buf[32]; + + switch (purpose) + { + case CIRCUIT_PURPOSE_OR: + return "Circuit at relay"; + case CIRCUIT_PURPOSE_INTRO_POINT: + return "Acting as intro point"; + case CIRCUIT_PURPOSE_REND_POINT_WAITING: + return "Acting as rendevous (pending)"; + case CIRCUIT_PURPOSE_REND_ESTABLISHED: + return "Acting as rendevous (established)"; + case CIRCUIT_PURPOSE_C_GENERAL: + return "General-purpose client"; + case CIRCUIT_PURPOSE_C_INTRODUCING: + return "Hidden service client: Connecting to intro point"; + case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT: + return "Hidden service client: Waiting for ack from intro point"; + case CIRCUIT_PURPOSE_C_INTRODUCE_ACKED: + return "Hidden service client: Received ack from intro point"; + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + return "Hidden service client: Establishing rendezvous point"; + case CIRCUIT_PURPOSE_C_REND_READY: + return "Hidden service client: Pending rendezvous point"; + case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: + return "Hidden service client: Pending rendezvous point (ack received)"; + case CIRCUIT_PURPOSE_C_REND_JOINED: + return "Hidden service client: Active rendezvous point"; + case CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT: + return "Measuring circuit timeout"; + + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: + return "Hidden service: Establishing introduction point"; + case CIRCUIT_PURPOSE_S_INTRO: + return "Hidden service: Introduction point"; + case CIRCUIT_PURPOSE_S_CONNECT_REND: + return "Hidden service: Connecting to rendezvous point"; + case CIRCUIT_PURPOSE_S_REND_JOINED: + return "Hidden service: Active rendezvous point"; + + case CIRCUIT_PURPOSE_TESTING: + return "Testing circuit"; + + case CIRCUIT_PURPOSE_CONTROLLER: + return "Circuit made by controller"; + + default: + tor_snprintf(buf, sizeof(buf), "UNKNOWN_%d", (int)purpose); + return buf; + } +} + /** Pick a reasonable package_window to start out for our circuits. * Originally this was hard-coded at 1000, but now the consensus votes * on the answer. See proposal 168. */ @@ -923,6 +979,7 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, int need_uptime = (flags & CIRCLAUNCH_NEED_UPTIME) != 0; int need_capacity = (flags & CIRCLAUNCH_NEED_CAPACITY) != 0; int internal = (flags & CIRCLAUNCH_IS_INTERNAL) != 0; + or_options_t *options = get_options(); /* Make sure we're not trying to create a onehop circ by * cannibalization. */ @@ -961,6 +1018,19 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, hop=hop->next; } while (hop!=circ->cpath); } + if (options->ExcludeNodes) { + /* Make sure no existing nodes in the circuit are excluded for + * general use. (This may be possible if StrictNodes is 0, and we + * thought we needed to use an otherwise excluded node for, say, a + * directory operation.) */ + crypt_path_t *hop = circ->cpath; + do { + if (routerset_contains_extendinfo(options->ExcludeNodes, + hop->extend_info)) + goto next; + hop = hop->next; + } while (hop != circ->cpath); + } if (!best || (best->build_state->need_uptime && !need_uptime)) best = circ; next: ; diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index ef6fc3a3d9..7b01ca3ae2 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -15,6 +15,7 @@ circuit_t * _circuit_get_global_list(void); const char *circuit_state_to_string(int state); const char *circuit_purpose_to_controller_string(uint8_t purpose); +const char *circuit_purpose_to_string(uint8_t purpose); void circuit_dump_by_conn(connection_t *conn, int severity); void circuit_set_p_circid_orconn(or_circuit_t *circ, circid_t id, or_connection_t *conn); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index cdf49e3983..fbe2e459a5 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -127,7 +127,7 @@ circuit_is_acceptable(circuit_t *circ, edge_connection_t *conn, return 0; } } - if (exitrouter && !connection_ap_can_use_exit(conn, exitrouter, 0)) { + if (exitrouter && !connection_ap_can_use_exit(conn, exitrouter)) { /* can't exit from this router */ return 0; } @@ -166,6 +166,10 @@ circuit_is_better(circuit_t *a, circuit_t *b, uint8_t purpose) return 1; if (CIRCUIT_IS_ORIGIN(b) && TO_ORIGIN_CIRCUIT(b)->build_state->is_internal) + /* XXX023 what the heck is this internal thing doing here. I + * think we can get rid of it. circuit_is_acceptable() already + * makes sure that is_internal is exactly what we need it to + * be. -RD */ return 1; } break; @@ -242,33 +246,34 @@ circuit_get_best(edge_connection_t *conn, int must_be_open, uint8_t purpose, return best ? TO_ORIGIN_CIRCUIT(best) : NULL; } +#if 0 /** Check whether, according to the policies in <b>options</b>, the * circuit <b>circ</b> makes sense. */ -/* XXXX currently only checks Exclude{Exit}Nodes. It should check more. */ +/* XXXX currently only checks Exclude{Exit}Nodes; it should check more. + * Also, it doesn't have the right definition of an exit circuit. Also, + * it's never called. */ int circuit_conforms_to_options(const origin_circuit_t *circ, const or_options_t *options) { const crypt_path_t *cpath, *cpath_next = NULL; - for (cpath = circ->cpath; cpath && cpath_next != circ->cpath; - cpath = cpath_next) { + /* first check if it includes any excluded nodes */ + for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) { cpath_next = cpath->next; - if (routerset_contains_extendinfo(options->ExcludeNodes, cpath->extend_info)) return 0; + } - if (cpath->next == circ->cpath) { - /* This is apparently the exit node. */ + /* then consider the final hop */ + if (routerset_contains_extendinfo(options->ExcludeExitNodes, + circ->cpath->prev->extend_info)) + return 0; - if (routerset_contains_extendinfo(options->ExcludeExitNodes, - cpath->extend_info)) - return 0; - } - } return 1; } +#endif /** Close all circuits that start at us, aren't open, and were born * at least CircuitBuildTimeout seconds ago. @@ -391,10 +396,11 @@ circuit_expire_building(void) TO_ORIGIN_CIRCUIT(victim)->cpath->state == CPATH_STATE_OPEN; if (TO_ORIGIN_CIRCUIT(victim)->p_streams != NULL) { - log_warn(LD_BUG, "Circuit %d (purpose %d) has timed out, " + log_warn(LD_BUG, "Circuit %d (purpose %d, %s) has timed out, " "yet has attached streams!", TO_ORIGIN_CIRCUIT(victim)->global_identifier, - victim->purpose); + victim->purpose, + circuit_purpose_to_string(victim->purpose)); tor_fragile_assert(); continue; } @@ -425,12 +431,13 @@ circuit_expire_building(void) 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", + "Assuming clock jump. Purpose %d (%s)", (long)(now.tv_sec - victim->timestamp_created.tv_sec), - victim->purpose); + victim->purpose, + circuit_purpose_to_string(victim->purpose)); } else if (circuit_build_times_count_close(&circ_times, - first_hop_succeeded, - victim->timestamp_created.tv_sec)) { + first_hop_succeeded, + victim->timestamp_created.tv_sec)) { circuit_build_times_set_timeout(&circ_times); } } @@ -508,7 +515,7 @@ circuit_stream_is_being_handled(edge_connection_t *conn, if (exitrouter && (!need_uptime || build_state->need_uptime)) { int ok; if (conn) { - ok = connection_ap_can_use_exit(conn, exitrouter, 0); + ok = connection_ap_can_use_exit(conn, exitrouter); } else { addr_policy_result_t r = compare_addr_to_addr_policy( 0, port, exitrouter->exit_policy); @@ -763,7 +770,8 @@ circuit_expire_old_circuits_clientside(void) * on it, mark it for close. */ if (circ->timestamp_dirty && - circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now.tv_sec && + 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 %ld sec ago, " "purpose %d)", @@ -793,12 +801,11 @@ circuit_expire_old_circuits_clientside(void) circ->purpose != CIRCUIT_PURPOSE_S_INTRO) { log_notice(LD_CIRC, "Ancient non-dirty circuit %d is still around after " - "%ld milliseconds. Purpose: %d", + "%ld milliseconds. Purpose: %d (%s)", TO_ORIGIN_CIRCUIT(circ)->global_identifier, 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 */ + circ->purpose, + circuit_purpose_to_string(circ->purpose)); TO_ORIGIN_CIRCUIT(circ)->is_ancient = 1; } } @@ -1135,8 +1142,9 @@ circuit_launch_by_extend_info(uint8_t purpose, * internal circs rather than exit circs? -RD */ circ = circuit_find_to_cannibalize(purpose, extend_info, flags); if (circ) { - log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d", - build_state_get_exit_nickname(circ->build_state), purpose); + log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d (%s)", + build_state_get_exit_nickname(circ->build_state), purpose, + circuit_purpose_to_string(purpose)); circ->_base.purpose = purpose; /* reset the birth date of this circ, else expire_building * will see it and think it's been trying to build since it @@ -1288,9 +1296,10 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn, * 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)) { + if (router && !connection_ap_can_use_exit(conn, router)) { log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, - "Requested exit point '%s' would refuse request. %s.", + "Requested exit point '%s' is excluded or " + "would refuse request. %s.", conn->chosen_exit_name, opt ? "Trying others" : "Closing"); if (opt) { conn->chosen_exit_optional = 0; @@ -1401,7 +1410,18 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn, extend_info_free(extend_info); - if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) { + if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) { + /* We just caused a circuit to get built because of this stream. + * If this stream has caused a _lot_ of circuits to be built, that's + * a bad sign: we should tell the user. */ + if (conn->num_circuits_launched < NUM_CIRCUITS_LAUNCHED_THRESHOLD && + ++conn->num_circuits_launched == NUM_CIRCUITS_LAUNCHED_THRESHOLD) + log_warn(LD_BUG, "The application request to %s:%d has launched " + "%d circuits without finding one it likes.", + escaped_safe_str_client(conn->socks_request->address), + conn->socks_request->port, + conn->num_circuits_launched); + } else { /* help predict this next time */ rep_hist_note_used_internal(time(NULL), need_uptime, 1); if (circ) { @@ -1608,9 +1628,10 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn) } return -1; } - if (router && !connection_ap_can_use_exit(conn, router, 0)) { + if (router && !connection_ap_can_use_exit(conn, router)) { log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, - "Requested exit point '%s' would refuse request. %s.", + "Requested exit point '%s' is excluded or " + "would refuse request. %s.", conn->chosen_exit_name, opt ? "Trying others" : "Closing"); if (opt) { conn->chosen_exit_optional = 0; @@ -1723,14 +1744,21 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn) "introduction. (stream %d sec old)", introcirc->_base.n_circ_id, rendcirc->_base.n_circ_id, conn_age); - if (rend_client_send_introduction(introcirc, rendcirc) < 0) { + switch (rend_client_send_introduction(introcirc, rendcirc)) { + case 0: /* success */ + rendcirc->_base.timestamp_dirty = time(NULL); + introcirc->_base.timestamp_dirty = time(NULL); + assert_circuit_ok(TO_CIRCUIT(rendcirc)); + assert_circuit_ok(TO_CIRCUIT(introcirc)); + return 0; + case -1: /* transient error */ + return 0; + case -2: /* permanent error */ + return -1; + default: /* oops */ + tor_fragile_assert(); return -1; } - rendcirc->_base.timestamp_dirty = time(NULL); - introcirc->_base.timestamp_dirty = time(NULL); - assert_circuit_ok(TO_CIRCUIT(rendcirc)); - assert_circuit_ok(TO_CIRCUIT(introcirc)); - return 0; } } diff --git a/src/or/circuituse.h b/src/or/circuituse.h index a121099aca..9f393ab378 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -16,8 +16,10 @@ 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); +#if 0 int circuit_conforms_to_options(const origin_circuit_t *circ, const or_options_t *options); +#endif void circuit_build_needed_circs(time_t now); void circuit_detach_stream(circuit_t *circ, edge_connection_t *conn); diff --git a/src/or/config.c b/src/or/config.c index 5000f5d60e..9384b3a68a 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1260,21 +1260,18 @@ 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)) || - (options->ExcludeExitNodes && - !routerset_equal(old_options->ExcludeExitNodes, - options->ExcludeExitNodes)) || - (options->EntryNodes && - !routerset_equal(old_options->EntryNodes, options->EntryNodes)) || - (options->ExitNodes && - !routerset_equal(old_options->ExitNodes, options->ExitNodes)) || + !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes) || + !routerset_equal(old_options->ExcludeExitNodes, + options->ExcludeExitNodes) || + !routerset_equal(old_options->EntryNodes, options->EntryNodes) || + !routerset_equal(old_options->ExitNodes, options->ExitNodes) || options->StrictNodes != old_options->StrictNodes) { log_info(LD_CIRC, "Changed to using entry guards, or changed preferred or " "excluded node lists. Abandoning previous circuits."); circuit_mark_all_unused_circs(); circuit_expire_all_dirty_circs(); + addressmap_clear_excluded_trackexithosts(options); } /* How long should we delay counting bridge stats after becoming a bridge? @@ -1412,7 +1409,8 @@ options_act(or_options_t *old_options) /* Check if we need to parse and add the EntryNodes config option. */ if (options->EntryNodes && (!old_options || - (!routerset_equal(old_options->EntryNodes,options->EntryNodes)))) + !routerset_equal(old_options->EntryNodes,options->EntryNodes) || + !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes))) entry_nodes_should_be_added(); /* Since our options changed, we might need to regenerate and upload our @@ -2500,54 +2498,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) @@ -2976,14 +2926,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)) { @@ -3135,6 +3078,10 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("FetchDirInfoExtraEarly requires that you also set " "FetchDirInfoEarly"); + if (options->HSAuthoritativeDir && proxy_mode(options)) + REJECT("Running as authoritative v0 HS directory, but also configured " + "as a client."); + if (options->ConnLimit <= 0) { tor_asprintf(msg, "ConnLimit must be greater than 0, but was set to %d", @@ -3243,6 +3190,12 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Servers must be able to freely connect to the rest " "of the Internet, so they must not set UseBridges."); + /* If both of these are set, we'll end up with funny behavior where we + * demand enough entrynodes be up and running else we won't build + * circuits, yet we never actually use them. */ + if (options->UseBridges && options->EntryNodes) + REJECT("You cannot set both UseBridges and EntryNodes."); + options->_AllowInvalid = 0; if (options->AllowInvalidNodes) { SMARTLIST_FOREACH(options->AllowInvalidNodes, const char *, cp, { @@ -4382,11 +4335,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; } diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 72e2c8a409..2c1196c0cd 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -799,7 +799,8 @@ clear_trackexithost_mappings(const char *exitname) tor_strlower(suffix); STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) { - if (ent->source == ADDRMAPSRC_TRACKEXIT && !strcmpend(address, suffix)) { + if (ent->source == ADDRMAPSRC_TRACKEXIT && + !strcmpend(ent->new_address, suffix)) { addressmap_ent_remove(address, ent); MAP_DEL_CURRENT(address); } @@ -808,6 +809,56 @@ clear_trackexithost_mappings(const char *exitname) tor_free(suffix); } +/** Remove all TRACKEXIT mappings from the addressmap for which the target + * host is unknown or no longer allowed. */ +void +addressmap_clear_excluded_trackexithosts(or_options_t *options) +{ + const routerset_t *allow_nodes = options->ExitNodes; + const routerset_t *exclude_nodes = options->_ExcludeExitNodesUnion; + + if (!addressmap) + return; + if (routerset_is_empty(allow_nodes)) + allow_nodes = NULL; + if (allow_nodes == NULL && routerset_is_empty(exclude_nodes)) + return; + + STRMAP_FOREACH_MODIFY(addressmap, address, addressmap_entry_t *, ent) { + size_t len; + const char *target = ent->new_address, *dot; + char *nodename; + routerinfo_t *ri; /* XXX023 Use node_t. */ + + if (strcmpend(target, ".exit")) { + /* Not a .exit mapping */ + continue; + } else if (ent->source != ADDRMAPSRC_TRACKEXIT) { + /* Not a trackexit mapping. */ + continue; + } + len = strlen(target); + if (len < 6) + continue; /* malformed. */ + dot = target + len - 6; /* dot now points to just before .exit */ + dot = strrchr(dot, '.'); /* dot now points to the . before .exit or NULL */ + if (!dot) { + nodename = tor_strndup(target, len-5); + } else { + nodename = tor_strndup(dot+1, strlen(dot+1)-5); + } + ri = router_get_by_nickname(nodename, 0); + tor_free(nodename); + if (!ri || + (allow_nodes && !routerset_contains_router(allow_nodes, ri)) || + routerset_contains_router(exclude_nodes, ri)) { + /* We don't know this one, or we want to be rid of it. */ + addressmap_ent_remove(address, ent); + MAP_DEL_CURRENT(address); + } + } STRMAP_FOREACH_END; +} + /** Remove all entries from the addressmap that were set via the * configuration file or the command line. */ void @@ -1494,9 +1545,13 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn, hostname_type_t addresstype; or_options_t *options = get_options(); struct in_addr addr_tmp; + /* We set this to true if this is an address we should automatically + * remap to a local address in VirtualAddrNetwork */ int automap = 0; char orig_address[MAX_SOCKS_ADDR_LEN]; time_t map_expires = TIME_MAX; + /* This will be set to true iff the address starts out as a non-.exit + address, and we remap it to one because of an entry in the addressmap. */ int remapped_to_exit = 0; time_t now = time(NULL); @@ -1607,14 +1662,24 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn, /* foo.exit -- modify conn->chosen_exit_node to specify the exit * node, and conn->address to hold only the address portion. */ char *s = strrchr(socks->address,'.'); + + /* If StrictNodes is not set, then .exit overrides ExcludeNodes. */ + routerset_t *excludeset = options->StrictNodes ? + options->_ExcludeExitNodesUnion : options->ExcludeExitNodes; + /*XXX023 make this a node_t. */ + routerinfo_t *router; + tor_assert(!automap); if (s) { + /* The address was of the form "(stuff).(name).exit */ if (s[1] != '\0') { conn->chosen_exit_name = tor_strdup(s+1); + router = router_get_by_nickname(conn->chosen_exit_name, 1); if (remapped_to_exit) /* 5 tries before it expires the addressmap */ conn->chosen_exit_retries = TRACKHOSTEXITS_RETRIES; *s = 0; } else { + /* Oops, the address was (stuff)..exit. That's not okay. */ log_warn(LD_APP,"Malformed exit address '%s.exit'. Refusing.", safe_str_client(socks->address)); control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", @@ -1623,20 +1688,33 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn, return -1; } } else { - routerinfo_t *r; + /* It looks like they just asked for "foo.exit". */ conn->chosen_exit_name = tor_strdup(socks->address); - r = router_get_by_nickname(conn->chosen_exit_name, 1); - *socks->address = 0; - if (r) { - strlcpy(socks->address, r->address, sizeof(socks->address)); - } else { - log_warn(LD_APP, - "Unrecognized server in exit address '%s.exit'. Refusing.", - safe_str_client(socks->address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; + router = router_get_by_nickname(conn->chosen_exit_name, 1); + if (router) { + *socks->address = 0; + strlcpy(socks->address, router->address, sizeof(socks->address)); } } + /* Now make sure that the chosen exit exists... */ + if (!router) { + log_warn(LD_APP, + "Unrecognized relay in exit address '%s.exit'. Refusing.", + safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); + return -1; + } + /* ...and make sure that it isn't excluded. */ + if (routerset_contains_router(excludeset, router)) { + log_warn(LD_APP, + "Excluded relay in exit address '%s.exit'. Refusing.", + safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); + return -1; + } + /* XXXX022-1090 Should we also allow foo.bar.exit if ExitNodes is set and + Bar is not listed in it? I say yes, but our revised manpage branch + implies no. */ } if (addresstype != ONION_HOSTNAME) { @@ -2966,13 +3044,9 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn) * to exit from it, or 0 if it probably will not allow it. * (We might be uncertain if conn's destination address has not yet been * resolved.) - * - * If <b>excluded_means_no</b> is 1 and Exclude*Nodes is set and excludes - * this relay, return 0. */ int -connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit, - int excluded_means_no) +connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit) { or_options_t *options = get_options(); @@ -3022,17 +3096,8 @@ connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit, return 0; } if (options->_ExcludeExitNodesUnion && - (options->StrictNodes || excluded_means_no) && routerset_contains_router(options->_ExcludeExitNodesUnion, exit)) { - /* If we are trying to avoid this node as exit, and we have StrictNodes - * set, then this is not a suitable exit. Refuse it. - * - * If we don't have StrictNodes set, then this function gets called in - * two contexts. First, we've got a circuit open and we want to know - * whether we can use it. In that case, we somehow built this circuit - * despite having the last hop in ExcludeExitNodes, so we should be - * willing to use it. Second, we are evaluating whether this is an - * acceptable exit for a new circuit. In that case, skip it. */ + /* Not a suitable exit. Refuse it. */ return 0; } diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 0b08dd07ca..70d0dd2713 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -47,8 +47,7 @@ int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ); void connection_exit_connect(edge_connection_t *conn); int connection_edge_is_rendezvous_stream(edge_connection_t *conn); -int connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit, - int excluded_means_no); +int connection_ap_can_use_exit(edge_connection_t *conn, routerinfo_t *exit); void connection_ap_expire_beginning(void); void connection_ap_attach_pending(void); void connection_ap_fail_onehop(const char *failed_digest, @@ -62,6 +61,7 @@ int connection_ap_process_transparent(edge_connection_t *conn); int address_is_invalid_destination(const char *address, int client); void addressmap_init(void); +void addressmap_clear_excluded_trackexithosts(or_options_t *options); void addressmap_clean(time_t now); void addressmap_clear_configured(void); void addressmap_clear_transient(void); diff --git a/src/or/control.c b/src/or/control.c index 8f3af0bda1..9f7739a402 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1222,7 +1222,9 @@ handle_control_signal(control_connection_t *conn, uint32_t len, /* Flush the "done" first if the signal might make us shut down. */ if (sig == SIGTERM || sig == SIGINT) connection_handle_write(TO_CONN(conn), 1); - control_signal_act(sig); + + process_signal(sig); + return 0; } diff --git a/src/or/directory.c b/src/or/directory.c index 8f33a608d4..0c095fe871 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -253,10 +253,13 @@ directories_have_accepted_server_descriptor(void) } /** Start a connection to every suitable directory authority, using - * connection purpose 'purpose' and uploading the payload 'payload' - * (length 'payload_len'). dir_purpose should be one of + * connection purpose <b>dir_purpose</b> and uploading <b>payload</b> + * (of length <b>payload_len</b>). The dir_purpose should be one of * 'DIR_PURPOSE_UPLOAD_DIR' or 'DIR_PURPOSE_UPLOAD_RENDDESC'. * + * <b>router_purpose</b> describes the type of descriptor we're + * publishing, if we're publishing a descriptor -- e.g. general or bridge. + * * <b>type</b> specifies what sort of dir authorities (V1, V2, * HIDSERV, BRIDGE) we should upload to. * @@ -272,6 +275,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, const char *payload, size_t payload_len, size_t extrainfo_len) { + or_options_t *options = get_options(); int post_via_tor; smartlist_t *dirservers = router_get_trusted_dir_servers(); int found = 0; @@ -287,6 +291,16 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, if ((type & ds->type) == 0) continue; + if (options->ExcludeNodes && options->StrictNodes && + routerset_contains_routerstatus(options->ExcludeNodes, rs)) { + log_warn(LD_DIR, "Wanted to contact authority '%s' for %s, but " + "it's in our ExcludedNodes list and StrictNodes is set. " + "Skipping.", + ds->nickname, + dir_conn_purpose_to_string(dir_purpose)); + continue; + } + found = 1; /* at least one authority of this type was listed */ if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR) ds->has_accepted_serverdesc = 0; @@ -496,12 +510,14 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status, time_t if_modified_since, const rend_data_t *rend_query) { + or_options_t *options = get_options(); routerinfo_t *router; char address_buf[INET_NTOA_BUF_LEN+1]; struct in_addr in; const char *address; tor_addr_t addr; router = router_get_by_digest(status->identity_digest); + if (!router && anonymized_connection) { log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we " "don't have its router descriptor.", status->nickname); @@ -514,6 +530,17 @@ directory_initiate_command_routerstatus_rend(routerstatus_t *status, address = address_buf; } tor_addr_from_ipv4h(&addr, status->addr); + + if (options->ExcludeNodes && options->StrictNodes && + routerset_contains_routerstatus(options->ExcludeNodes, status)) { + log_warn(LD_DIR, "Wanted to contact directory mirror '%s' for %s, but " + "it's in our ExcludedNodes list and StrictNodes is set. " + "Skipping. This choice might make your Tor not work.", + status->nickname, + dir_conn_purpose_to_string(dir_purpose)); + return; + } + directory_initiate_command_rend(address, &addr, status->or_port, status->dir_port, status->version_supports_conditional_consensus, diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index f7a8d35f78..243b730cbf 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -280,7 +280,7 @@ dnsserv_resolved(edge_connection_t *conn, conn->socks_request->command == SOCKS_COMMAND_RESOLVE) { evdns_server_request_add_a_reply(req, name, - 1, (char*)answer, ttl); + 1, answer, ttl); } else if (answer_type == RESOLVED_TYPE_HOSTNAME && answer_len < 256 && conn->socks_request->command == SOCKS_COMMAND_RESOLVE_PTR) { diff --git a/src/or/eventdns.c b/src/or/eventdns.c index 8b679f8985..cf583d0682 100644 --- a/src/or/eventdns.c +++ b/src/or/eventdns.c @@ -1668,7 +1668,7 @@ evdns_server_request_add_reply(struct evdns_server_request *_req, int section, c /* exported function */ int -evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) +evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl) { return evdns_server_request_add_reply( req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET, @@ -1677,7 +1677,7 @@ evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *n /* exported function */ int -evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl) +evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl) { return evdns_server_request_add_reply( req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET, @@ -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; diff --git a/src/or/eventdns.h b/src/or/eventdns.h index bf3b64d08a..2fe4ac9371 100644 --- a/src/or/eventdns.h +++ b/src/or/eventdns.h @@ -323,8 +323,8 @@ struct evdns_server_port *evdns_add_server_port(int socket, int is_tcp, evdns_re void evdns_close_server_port(struct evdns_server_port *port); int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data); -int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl); -int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, void *addrs, int ttl); +int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); +int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl); int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl); int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl); diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 1878d5d52d..aebce4cc88 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -378,7 +378,8 @@ configure_accounting(time_t now) /* We are in the interval we thought we were in. Do nothing.*/ interval_end_time = start_of_accounting_period_after(interval_start_time); } else { - long duration = length_of_accounting_period_containing(now); + long duration = + length_of_accounting_period_containing(interval_start_time); double delta = ((double)(s_now - interval_start_time)) / duration; if (-0.50 <= delta && delta <= 0.50) { /* The start of the period is now a little later or earlier than we diff --git a/src/or/main.c b/src/or/main.c index 83d1e1e517..a26be39fdf 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -64,7 +64,6 @@ static void dumpmemusage(int severity); static void dumpstats(int severity); /* log stats */ static void conn_read_callback(int fd, short event, void *_conn); static void conn_write_callback(int fd, short event, void *_conn); -static void signal_callback(int fd, short events, void *arg); static void second_elapsed_callback(periodic_timer_t *timer, void *args); static int conn_close_if_marked(int i); static void connection_start_reading_from_linked_conn(connection_t *conn); @@ -843,8 +842,17 @@ run_connection_housekeeping(int i, time_t now) static void signewnym_impl(time_t now) { + or_options_t *options = get_options(); + if (!proxy_mode(options)) { + log_info(LD_CONTROL, "Ignoring SIGNAL NEWNYM because client functionality " + "is disabled."); + return; + } + circuit_expire_all_dirty_circs(); addressmap_clear_transient(); + rend_cache_purge(); + rend_client_cancel_descriptor_fetches(); time_of_last_signewnym = now; signewnym_is_pending = 0; } @@ -1577,46 +1585,6 @@ do_main_loop(void) } } -/** Used to implement the SIGNAL control command: if we accept - * <b>the_signal</b> as a remote pseudo-signal, act on it. */ -/* We don't re-use catch() here because: - * 1. We handle a different set of signals than those allowed in catch. - * 2. Platforms without signal() are unlikely to define SIGfoo. - * 3. The control spec is defined to use fixed numeric signal values - * which just happen to match the Unix values. - */ -void -control_signal_act(int the_signal) -{ - switch (the_signal) - { - case 1: - signal_callback(0,0,(void*)(uintptr_t)SIGHUP); - break; - case 2: - signal_callback(0,0,(void*)(uintptr_t)SIGINT); - break; - case 10: - signal_callback(0,0,(void*)(uintptr_t)SIGUSR1); - break; - case 12: - signal_callback(0,0,(void*)(uintptr_t)SIGUSR2); - break; - case 15: - signal_callback(0,0,(void*)(uintptr_t)SIGTERM); - break; - case SIGNEWNYM: - signal_callback(0,0,(void*)(uintptr_t)SIGNEWNYM); - break; - case SIGCLEARDNSCACHE: - signal_callback(0,0,(void*)(uintptr_t)SIGCLEARDNSCACHE); - break; - default: - log_warn(LD_BUG, "Unrecognized signal number %d.", the_signal); - break; - } -} - /** Libevent callback: invoked when we get a signal. */ static void @@ -1625,6 +1593,14 @@ signal_callback(int fd, short events, void *arg) uintptr_t sig = (uintptr_t)arg; (void)fd; (void)events; + + process_signal(sig); +} + +/** Do the work of acting on a signal received in <b>sig</b> */ +void +process_signal(uintptr_t sig) +{ switch (sig) { case SIGTERM: diff --git a/src/or/main.h b/src/or/main.h index ed0fb97703..0551f7aaf9 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -46,8 +46,8 @@ void directory_info_has_arrived(time_t now, int from_cache); void ip_address_changed(int at_interface); void dns_servers_relaunch_checks(void); -void control_signal_act(int the_signal); void handle_signals(int is_parent); +void process_signal(uintptr_t sig); int try_locking(or_options_t *options, int err_if_locked); int have_lockfile(void); diff --git a/src/or/or.h b/src/or/or.h index 1688a08f96..7d354c8fe1 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -91,7 +91,7 @@ #include "compat_libevent.h" #include "ht.h" -/* These signals are defined to help control_signal_act work. +/* These signals are defined to help handle_control_signal work. */ #ifndef SIGHUP #define SIGHUP 1 @@ -1151,6 +1151,13 @@ typedef struct edge_connection_t { * already retried several times. */ uint8_t num_socks_retries; +#define NUM_CIRCUITS_LAUNCHED_THRESHOLD 10 + /** Number of times we've launched a circuit to handle this stream. If + * it gets too high, that could indicate an inconsistency between our + * "launch a circuit to handle this stream" logic and our "attach our + * stream to one of the available circuits" logic. */ + unsigned int num_circuits_launched:4; + /** True iff this connection is for a DNS request only. */ unsigned int is_dns_request:1; @@ -2387,7 +2394,7 @@ typedef struct { * ORs not to consider as exits. */ /** Union of ExcludeNodes and ExcludeExitNodes */ - struct routerset_t *_ExcludeExitNodesUnion; + routerset_t *_ExcludeExitNodesUnion; int DisableAllSwap; /**< Boolean: Attempt to call mlockall() on our * process for all current and future memory. */ @@ -3487,7 +3494,7 @@ typedef struct trusted_dir_server_t { #define ROUTER_MAX_DECLARED_BANDWIDTH INT32_MAX -/* Flags for pick_directory_server and pick_trusteddirserver. */ +/* Flags for pick_directory_server() and pick_trusteddirserver(). */ /** Flag to indicate that we should not automatically be willing to use * ourself to answer a directory request. * Passed to router_pick_directory_server (et al).*/ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 8ac909fc80..8d024d8ebb 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -22,6 +22,10 @@ #include "rephist.h" #include "routerlist.h" +static extend_info_t *rend_client_get_random_intro_impl( + const rend_cache_entry_t *rend_query, + const int strict, const int warnings); + /** Called when we've established a circuit to an introduction point: * send the introduction request. */ void @@ -62,6 +66,50 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ) return 0; } +/** Extend the introduction circuit <b>circ</b> to another valid + * introduction point for the hidden service it is trying to connect + * to, or mark it and launch a new circuit if we can't extend it. + * Return 0 on success. Return -1 and mark the introduction + * circuit on failure. + * + * On failure, the caller is responsible for marking the associated + * rendezvous circuit for close. */ +static int +rend_client_reextend_intro_circuit(origin_circuit_t *circ) +{ + extend_info_t *extend_info; + int result; + extend_info = rend_client_get_random_intro(circ->rend_data); + if (!extend_info) { + log_warn(LD_REND, + "No usable introduction points left for %s. Closing.", + safe_str_client(circ->rend_data->onion_address)); + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); + return -1; + } + if (circ->remaining_relay_early_cells) { + log_info(LD_REND, + "Re-extending circ %d, this time to %s.", + circ->_base.n_circ_id, extend_info->nickname); + result = circuit_extend_to_new_exit(circ, extend_info); + } else { + log_info(LD_REND, + "Building a new introduction circuit, this time to %s.", + extend_info->nickname); + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED); + if (!circuit_launch_by_extend_info(CIRCUIT_PURPOSE_C_INTRODUCING, + extend_info, + CIRCLAUNCH_IS_INTERNAL)) { + log_warn(LD_REND, "Building introduction circuit failed."); + result = -1; + } else { + result = 0; + } + } + extend_info_free(extend_info); + return result; +} + /** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell * down introcirc if possible. */ @@ -87,13 +135,25 @@ rend_client_send_introduction(origin_circuit_t *introcirc, if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, &entry) < 1) { - log_warn(LD_REND, - "query %s didn't have valid rend desc in cache. Failing.", - escaped_safe_str_client(introcirc->rend_data->onion_address)); - goto err; + log_info(LD_REND, + "query %s didn't have valid rend desc in cache. " + "Refetching descriptor.", + safe_str_client(introcirc->rend_data->onion_address)); + rend_client_refetch_v2_renddesc(introcirc->rend_data); + { + connection_t *conn; + + while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP, + AP_CONN_STATE_CIRCUIT_WAIT, + introcirc->rend_data->onion_address))) { + conn->state = AP_CONN_STATE_RENDDESC_WAIT; + } + } + + return -1; } - /* first 20 bytes of payload are the hash of the intro key */ + /* first 20 bytes of payload are the hash of Bob's pk */ intro_key = NULL; SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *, intro, { @@ -104,15 +164,22 @@ rend_client_send_introduction(origin_circuit_t *introcirc, } }); if (!intro_key) { - log_info(LD_REND, "Our introduction point knowledge changed in " - "mid-connect! Could not find intro key; we only have a " - "v2 rend desc with %d intro points. Giving up.", + log_info(LD_REND, "Could not find intro key for %s at %s; we " + "have a v2 rend desc with %d intro points. " + "Trying a different intro point...", + safe_str_client(introcirc->rend_data->onion_address), + introcirc->build_state->chosen_exit->nickname, smartlist_len(entry->parsed->intro_nodes)); - goto err; + + if (rend_client_reextend_intro_circuit(introcirc)) { + goto perm_err; + } else { + return -1; + } } if (crypto_pk_get_digest(intro_key, payload)<0) { log_warn(LD_BUG, "Internal error: couldn't hash public key."); - goto err; + goto perm_err; } /* Initialize the pending_final_cpath and start the DH handshake. */ @@ -123,11 +190,11 @@ rend_client_send_introduction(origin_circuit_t *introcirc, cpath->magic = CRYPT_PATH_MAGIC; if (!(cpath->dh_handshake_state = crypto_dh_new(DH_TYPE_REND))) { log_warn(LD_BUG, "Internal error: couldn't allocate DH."); - goto err; + goto perm_err; } if (crypto_dh_generate_public(cpath->dh_handshake_state)<0) { log_warn(LD_BUG, "Internal error: couldn't generate g^x."); - goto err; + goto perm_err; } } @@ -177,7 +244,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset, DH_KEY_LEN)<0) { log_warn(LD_BUG, "Internal error: couldn't extract g^x."); - goto err; + goto perm_err; } note_crypto_pk_op(REND_CLIENT); @@ -190,7 +257,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, PK_PKCS1_OAEP_PADDING, 0); if (r<0) { log_warn(LD_BUG,"Internal error: hybrid pk encrypt failed."); - goto err; + goto perm_err; } payload_len = DIGEST_LEN + r; @@ -203,17 +270,18 @@ rend_client_send_introduction(origin_circuit_t *introcirc, introcirc->cpath->prev)<0) { /* introcirc is already marked for close. leave rendcirc alone. */ log_warn(LD_BUG, "Couldn't send INTRODUCE1 cell"); - return -1; + return -2; } /* Now, we wait for an ACK or NAK on this circuit. */ introcirc->_base.purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT; return 0; - err: - circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_REASON_INTERNAL); +perm_err: + if (!introcirc->_base.marked_for_close) + circuit_mark_for_close(TO_CIRCUIT(introcirc), END_CIRC_REASON_INTERNAL); circuit_mark_for_close(TO_CIRCUIT(rendcirc), END_CIRC_REASON_INTERNAL); - return -1; + return -2; } /** Called when a rendezvous circuit is open; sends a establish @@ -274,45 +342,16 @@ rend_client_introduction_acked(origin_circuit_t *circ, * points. If any remain, extend to a new one and try again. * If none remain, refetch the service descriptor. */ + log_info(LD_REND, "Got nack for %s from %s...", + safe_str_client(circ->rend_data->onion_address), + circ->build_state->chosen_exit->nickname); if (rend_client_remove_intro_point(circ->build_state->chosen_exit, circ->rend_data) > 0) { /* There are introduction points left. Re-extend the circuit to * another intro point and try again. */ - extend_info_t *extend_info; - int result; - extend_info = rend_client_get_random_intro(circ->rend_data); - if (!extend_info) { - log_warn(LD_REND, "No introduction points left for %s. Closing.", - escaped_safe_str_client(circ->rend_data->onion_address)); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); - return -1; - } - if (circ->remaining_relay_early_cells) { - log_info(LD_REND, - "Got nack for %s from %s. Re-extending circ %d, " - "this time to %s.", - escaped_safe_str_client(circ->rend_data->onion_address), - circ->build_state->chosen_exit->nickname, - circ->_base.n_circ_id, extend_info->nickname); - result = circuit_extend_to_new_exit(circ, extend_info); - } else { - log_info(LD_REND, - "Got nack for %s from %s. Building a new introduction " - "circuit, this time to %s.", - escaped_safe_str_client(circ->rend_data->onion_address), - circ->build_state->chosen_exit->nickname, - extend_info->nickname); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED); - if (!circuit_launch_by_extend_info(CIRCUIT_PURPOSE_C_INTRODUCING, - extend_info, - CIRCLAUNCH_IS_INTERNAL)) { - log_warn(LD_REND, "Building introduction circuit failed."); - result = -1; - } else { - result = 0; - } - } - extend_info_free(extend_info); + int result = rend_client_reextend_intro_circuit(circ); + /* XXXX If that call failed, should we close the rend circuit, + * too? */ return result; } } @@ -522,8 +561,44 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) return; } +/** Cancel all rendezvous descriptor fetches currently in progress. + */ +void +rend_client_cancel_descriptor_fetches(void) +{ + smartlist_t *connection_array = get_connection_array(); + + SMARTLIST_FOREACH_BEGIN(connection_array, connection_t *, conn) { + if (conn->type == CONN_TYPE_DIR && + (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC || + conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)) { + /* It's a rendezvous descriptor fetch in progress -- cancel it + * by marking the connection for close. + * + * Even if this connection has already reached EOF, this is + * enough to make sure that if the descriptor hasn't been + * processed yet, it won't be. See the end of + * connection_handle_read; connection_reached_eof (indirectly) + * processes whatever response the connection received. */ + + const rend_data_t *rd = (TO_DIR_CONN(conn))->rend_data; + if (!rd) { + log_warn(LD_BUG | LD_REND, + "Marking for close dir conn fetching rendezvous " + "descriptor for unknown service!"); + } else { + log_debug(LD_REND, "Marking for close dir conn fetching " + "rendezvous descriptor for service %s", + safe_str(rd->onion_address)); + } + connection_mark_for_close(conn); + } + } SMARTLIST_FOREACH_END(conn); +} + /** Remove failed_intro from ent. If ent now has no intro points, or * service is unrecognized, then launch a new renddesc fetch. + * * Return -1 if error, 0 if no intro points remain or service * unrecognized, 1 if recognized and some intro points remain. @@ -559,7 +634,7 @@ rend_client_remove_intro_point(extend_info_t *failed_intro, } } - if (smartlist_len(ent->parsed->intro_nodes) == 0) { + if (! rend_client_any_intro_points_usable(ent)) { log_info(LD_REND, "No more intro points remain for %s. Re-fetching descriptor.", escaped_safe_str_client(rend_query->onion_address)); @@ -705,7 +780,7 @@ rend_client_desc_trynow(const char *query) assert_connection_ok(TO_CONN(conn), now); if (rend_cache_lookup_entry(conn->rend_data->onion_address, -1, &entry) == 1 && - smartlist_len(entry->parsed->intro_nodes) > 0) { + rend_client_any_intro_points_usable(entry)) { /* either this fetch worked, or it failed but there was a * valid entry from before which we should reuse */ log_info(LD_REND,"Rend desc is usable. Launching circuits."); @@ -739,24 +814,63 @@ rend_client_desc_trynow(const char *query) extend_info_t * rend_client_get_random_intro(const rend_data_t *rend_query) { - int i; + extend_info_t *result; rend_cache_entry_t *entry; - rend_intro_point_t *intro; - routerinfo_t *router; if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) { - log_warn(LD_REND, - "Query '%s' didn't have valid rend desc in cache. Failing.", - safe_str_client(rend_query->onion_address)); + log_warn(LD_REND, + "Query '%s' didn't have valid rend desc in cache. Failing.", + safe_str_client(rend_query->onion_address)); return NULL; } + /* See if we can get a node that complies with ExcludeNodes */ + if ((result = rend_client_get_random_intro_impl(entry, 1, 1))) + return result; + /* If not, and StrictNodes is not set, see if we can return any old node + */ + if (!get_options()->StrictNodes) + return rend_client_get_random_intro_impl(entry, 0, 1); + return NULL; +} + +/** As rend_client_get_random_intro, except assume that StrictNodes is set + * iff <b>strict</b> is true. If <b>warnings</b> is false, don't complain + * to the user when we're out of nodes, even if StrictNodes is true. + */ +static extend_info_t * +rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, + const int strict, + const int warnings) +{ + int i; + + rend_intro_point_t *intro; + routerinfo_t *router; + or_options_t *options = get_options(); + smartlist_t *usable_nodes; + int n_excluded = 0; + + /* We'll keep a separate list of the usable nodes. If this becomes empty, + * no nodes are usable. */ + usable_nodes = smartlist_create(); + smartlist_add_all(usable_nodes, entry->parsed->intro_nodes); + again: - if (smartlist_len(entry->parsed->intro_nodes) == 0) + if (smartlist_len(usable_nodes) == 0) { + if (n_excluded && get_options()->StrictNodes && warnings) { + /* We only want to warn if StrictNodes is really set. Otherwise + * we're just about to retry anyways. + */ + log_warn(LD_REND, "All introduction points for hidden service are " + "at excluded relays, and StrictNodes is set. Skipping."); + } + smartlist_free(usable_nodes); return NULL; + } - i = crypto_rand_int(smartlist_len(entry->parsed->intro_nodes)); - intro = smartlist_get(entry->parsed->intro_nodes, i); + i = crypto_rand_int(smartlist_len(usable_nodes)); + intro = smartlist_get(usable_nodes, i); /* Do we need to look up the router or is the extend info complete? */ if (!intro->extend_info->onion_key) { if (tor_digest_is_zero(intro->extend_info->identity_digest)) @@ -766,16 +880,34 @@ rend_client_get_random_intro(const rend_data_t *rend_query) if (!router) { log_info(LD_REND, "Unknown router with nickname '%s'; trying another.", intro->extend_info->nickname); - rend_intro_point_free(intro); - smartlist_del(entry->parsed->intro_nodes, i); + smartlist_del(usable_nodes, i); goto again; } extend_info_free(intro->extend_info); intro->extend_info = extend_info_from_router(router); } + /* Check if we should refuse to talk to this router. */ + if (options->ExcludeNodes && strict && + routerset_contains_extendinfo(options->ExcludeNodes, + intro->extend_info)) { + n_excluded++; + smartlist_del(usable_nodes, i); + goto again; + } + + smartlist_free(usable_nodes); return extend_info_dup(intro->extend_info); } +/** Return true iff any introduction points still listed in <b>entry</b> are + * usable. */ +int +rend_client_any_intro_points_usable(const rend_cache_entry_t *entry) +{ + return rend_client_get_random_intro_impl( + entry, get_options()->StrictNodes, 0) != NULL; +} + /** Client-side authorizations for hidden services; map of onion address to * rend_service_authorization_t*. */ static strmap_t *auth_hid_servs = NULL; diff --git a/src/or/rendclient.h b/src/or/rendclient.h index 56ccde1464..6910c1a97b 100644 --- a/src/or/rendclient.h +++ b/src/or/rendclient.h @@ -18,6 +18,7 @@ int rend_client_introduction_acked(origin_circuit_t *circ, const uint8_t *request, size_t request_len); void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query); +void rend_client_cancel_descriptor_fetches(void); int rend_client_remove_intro_point(extend_info_t *failed_intro, const rend_data_t *rend_query); int rend_client_rendezvous_acked(origin_circuit_t *circ, @@ -29,6 +30,7 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ, void rend_client_desc_trynow(const char *query); extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query); +int rend_client_any_intro_points_usable(const rend_cache_entry_t *entry); int rend_client_send_introduction(origin_circuit_t *introcirc, origin_circuit_t *rendcirc); diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index f4c8888c04..f8e7f9bbb0 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -834,6 +834,16 @@ rend_cache_clean(void) } } +/** Remove ALL entries from the rendezvous service descriptor cache. + */ +void +rend_cache_purge(void) +{ + if (rend_cache) + strmap_free(rend_cache, _rend_cache_entry_free); + rend_cache = strmap_new(); +} + /** Remove all old v2 descriptors and those for which this hidden service * directory is not responsible for any more. */ void @@ -934,7 +944,7 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e) tor_assert((*e)->parsed && (*e)->parsed->intro_nodes); /* 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) + if (! rend_client_any_intro_points_usable(*e)) return 0; return 1; } diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index 5014957458..44b5227cf5 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -36,6 +36,7 @@ void rend_intro_point_free(rend_intro_point_t *intro); void rend_cache_init(void); void rend_cache_clean(void); void rend_cache_clean_v2_descs_as_dir(void); +void rend_cache_purge(void); void rend_cache_free_all(void); int rend_valid_service_id(const char *query); int rend_cache_lookup_desc(const char *query, int version, const char **desc, diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 45039822f8..cd8f9eabeb 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -848,6 +848,7 @@ clean_accepted_intros(rend_service_t *service, time_t now) /** Respond to an INTRODUCE2 cell by launching a circuit to the chosen * rendezvous point. */ + /* XXX022 this function sure could use some organizing. -RD */ int rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, size_t request_len) @@ -875,6 +876,8 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, time_t now = time(NULL); char diffie_hellman_hash[DIGEST_LEN]; time_t *access_time; + or_options_t *options = get_options(); + tor_assert(circuit->rend_data); base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, @@ -1047,6 +1050,15 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, goto err; } + /* Check if we'd refuse to talk to this router */ + if (options->ExcludeNodes && options->StrictNodes && + routerset_contains_extendinfo(options->ExcludeNodes, extend_info)) { + log_warn(LD_REND, "Client asked to rendezvous at a relay that we " + "exclude, and StrictNodes is set. Refusing service."); + reason = END_CIRC_REASON_INTERNAL; /* XXX might leak why we refused */ + goto err; + } + r_cookie = ptr; base16_encode(hexcookie,9,r_cookie,4); @@ -1335,14 +1347,26 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) } /* If we already have enough introduction circuits for this service, - * redefine this one as a general circuit. */ + * redefine this one as a general circuit or close it, depending. */ if (count_established_intro_points(serviceid) > NUM_INTRO_POINTS) { - log_info(LD_CIRC|LD_REND, "We have just finished an introduction " - "circuit, but we already have enough. Redefining purpose to " - "general."); - TO_CIRCUIT(circuit)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - circuit_has_opened(circuit); - return; + or_options_t *options = get_options(); + if (options->ExcludeNodes) { + /* XXXX in some future version, we can test whether the transition is + allowed or not given the actual nodes in the circuit. But for now, + this case, we might as well close the thing. */ + log_info(LD_CIRC|LD_REND, "We have just finished an introduction " + "circuit, but we already have enough. Closing it."); + circuit_mark_for_close(TO_CIRCUIT(circuit), END_CIRC_REASON_NONE); + return; + } else { + tor_assert(circuit->build_state->is_internal); + log_info(LD_CIRC|LD_REND, "We have just finished an introduction " + "circuit, but we already have enough. Redefining purpose to " + "general; leaving as internal."); + TO_CIRCUIT(circuit)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + circuit_has_opened(circuit); + return; + } } log_info(LD_REND, @@ -1394,7 +1418,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) /** Called when we get an INTRO_ESTABLISHED cell; mark the circuit as a * live introduction point, and note that the service descriptor is - * now out-of-date.*/ + * now out-of-date. */ int rend_service_intro_established(origin_circuit_t *circuit, const uint8_t *request, diff --git a/src/or/rephist.c b/src/or/rephist.c index 9b7eefecf2..02db247389 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -992,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) { @@ -1288,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) { @@ -1318,16 +1318,16 @@ 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. */ + show up in profiles, so we can just ignore it for now. */ advance_obs(b); } @@ -1375,7 +1375,7 @@ bw_arrays_init(void) 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>. * @@ -1396,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 @@ -1406,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) @@ -1415,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) @@ -1511,7 +1511,8 @@ rep_hist_fill_bandwidth_history(char *buf, size_t len, const 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) @@ -1524,10 +1525,15 @@ rep_hist_get_bandwidth_lines(void) size_t len; /* opt [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */ - len = (67+21*NUM_TOTALS)*4; +/* The n,n,n part above. Largest representation of a uint64_t is 20 chars + * long, plus the comma. */ +#define MAX_HIST_VALUE_LEN 21*NUM_TOTALS + len = (67+MAX_HIST_VALUE_LEN)*4; buf = tor_malloc_zero(len); cp = buf; for (r=0;r<4;++r) { + char tmp[MAX_HIST_VALUE_LEN]; + size_t slen; switch (r) { case 0: b = write_array; @@ -1547,11 +1553,16 @@ rep_hist_get_bandwidth_lines(void) break; } tor_assert(b); + slen = rep_hist_fill_bandwidth_history(tmp, MAX_HIST_VALUE_LEN, b); + /* If we don't have anything to write, skip to the next entry. */ + if (slen == 0) + continue; format_iso_time(t, b->next_period-NUM_SECS_BW_SUM_INTERVAL); tor_snprintf(cp, len-(cp-buf), "%s %s (%d s) ", desc, t, NUM_SECS_BW_SUM_INTERVAL); cp += strlen(cp); - cp += rep_hist_fill_bandwidth_history(cp, len-(cp-buf), b); + strlcat(cp, tmp, len-(cp-buf)); + cp += slen; strlcat(cp, "\n", len-(cp-buf)); ++cp; } @@ -1559,7 +1570,7 @@ rep_hist_get_bandwidth_lines(void) } /** Write a single bw_array_t into the Values, Ends, Interval, and Maximum - * entries of an or_state_t. */ + * 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, @@ -1570,6 +1581,7 @@ rep_hist_update_bwhist_state_section(or_state_t *state, { char *cp; int i,j; + uint64_t maxval; if (*s_values) { SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); @@ -1603,7 +1615,6 @@ rep_hist_update_bwhist_state_section(or_state_t *state, /* 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) { - uint64_t maxval; if (i >= NUM_TOTALS) i = 0; tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->totals[i] & ~0x3ff)); @@ -1614,11 +1625,13 @@ rep_hist_update_bwhist_state_section(or_state_t *state, } tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->total_in_period & ~0x3ff)); smartlist_add(*s_values, cp); - tor_asprintf(&cp, U64_FORMAT, U64_PRINTF_ARG(b->max_total & ~0x3ff)); + 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. */ +/** 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) { @@ -1642,7 +1655,7 @@ rep_hist_update_state(or_state_t *state) } /** Load a single bw_array_t from its Values, Ends, Maxima, and Interval - * entries in an or_state_t. */ + * 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, @@ -1673,7 +1686,7 @@ rep_hist_load_bwhist_state_section(bw_array_t *b, mv *= NUM_SECS_ROLLING_MEASURE; } else { /* No maxima known; guess average rate to be conservative. */ - mv = v / s_interval; + mv = (v / s_interval) * NUM_SECS_ROLLING_MEASURE; } if (!ok) { retval = -1; @@ -1686,11 +1699,24 @@ rep_hist_load_bwhist_state_section(bw_array_t *b, } if (start < now) { - add_obs(b, start, v); + 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 notthe same as NUM_SECS_BW_SUM_INTERVAL. XXXX */ - start += s_interval; + * is not the same as NUM_SECS_BW_SUM_INTERVAL. XXXX */ + start += actual_interval_len; } } SMARTLIST_FOREACH_END(cp); } @@ -1704,7 +1730,7 @@ rep_hist_load_bwhist_state_section(bw_array_t *b, return retval; } -/** Set bandwidth history from our saved state. */ +/** Set bandwidth history from the state file we just loaded. */ int rep_hist_load_state(or_state_t *state, char **err) { @@ -1752,7 +1778,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; diff --git a/src/or/router.c b/src/or/router.c index c15b9b236e..65afd49f7f 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -850,9 +850,29 @@ consider_testing_reachability(int test_or, int test_dir) routerinfo_t *me = router_get_my_routerinfo(); int orport_reachable = check_whether_orport_reachable(); tor_addr_t addr; + or_options_t *options = get_options(); if (!me) return; + if (routerset_contains_router(options->ExcludeNodes, me) && + options->StrictNodes) { + /* If we've excluded ourself, and StrictNodes is set, we can't test + * ourself. */ + if (test_or || test_dir) { +#define SELF_EXCLUDED_WARN_INTERVAL 3600 + static ratelim_t warning_limit=RATELIM_INIT(SELF_EXCLUDED_WARN_INTERVAL); + char *msg; + if ((msg = rate_limit_log(&warning_limit, approx_time()))) { + log_warn(LD_CIRC, "Can't peform self-tests for this relay: we have " + "listed ourself in ExcludeNodes, and StrictNodes is set. " + "We cannot learn whether we are usable, and will not " + "be able to advertise ourself.%s", msg); + tor_free(msg); + } + } + return; + } + if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) { log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.", !orport_reachable ? "reachability" : "bandwidth", @@ -1087,10 +1107,10 @@ set_server_advertised(int s) int proxy_mode(or_options_t *options) { - return (options->SocksPort != 0 || options->SocksListenAddress || - options->TransPort != 0 || options->TransListenAddress || - options->NATDPort != 0 || options->NATDListenAddress || - options->DNSPort != 0 || options->DNSListenAddress); + return (options->SocksPort != 0 || + options->TransPort != 0 || + options->NATDPort != 0 || + options->DNSPort != 0); } /** Decide if we're a publishable server. We are a publishable server if: diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 4deff53a3c..f567ccdf3c 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -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); @@ -1070,6 +1070,7 @@ router_pick_trusteddirserver(authority_type_t type, int flags) static routerstatus_t * router_pick_directory_server_impl(authority_type_t type, int flags) { + or_options_t *options = get_options(); routerstatus_t *result; smartlist_t *direct, *tunnel; smartlist_t *trusted_direct, *trusted_tunnel; @@ -1079,10 +1080,13 @@ router_pick_directory_server_impl(authority_type_t type, int flags) int requireother = ! (flags & PDS_ALLOW_SELF); int fascistfirewall = ! (flags & PDS_IGNORE_FASCISTFIREWALL); int prefer_tunnel = (flags & _PDS_PREFER_TUNNELED_DIR_CONNS); + int try_excluding = 1, n_excluded = 0; if (!consensus) return NULL; + retry_without_exclude: + direct = smartlist_create(); tunnel = smartlist_create(); trusted_direct = smartlist_create(); @@ -1114,6 +1118,11 @@ router_pick_directory_server_impl(authority_type_t type, int flags) if ((type & EXTRAINFO_CACHE) && !router_supports_extrainfo(status->identity_digest, 0)) continue; + if (try_excluding && options->ExcludeNodes && + routerset_contains_routerstatus(options->ExcludeNodes, status)) { + ++n_excluded; + continue; + } /* XXXX IP6 proposal 118 */ tor_addr_from_ipv4h(&addr, status->addr); @@ -1155,6 +1164,15 @@ router_pick_directory_server_impl(authority_type_t type, int flags) smartlist_free(trusted_tunnel); smartlist_free(overloaded_direct); smartlist_free(overloaded_tunnel); + + if (result == NULL && try_excluding && !options->StrictNodes && n_excluded) { + /* If we got no result, and we are excluding nodes, and StrictNodes is + * not set, try again without excluding nodes. */ + try_excluding = 0; + n_excluded = 0; + goto retry_without_exclude; + } + return result; } @@ -1165,6 +1183,7 @@ static routerstatus_t * router_pick_trusteddirserver_impl(authority_type_t type, int flags, int *n_busy_out) { + or_options_t *options = get_options(); smartlist_t *direct, *tunnel; smartlist_t *overloaded_direct, *overloaded_tunnel; routerinfo_t *me = router_get_my_routerinfo(); @@ -1175,10 +1194,13 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags, const int prefer_tunnel = (flags & _PDS_PREFER_TUNNELED_DIR_CONNS); const int no_serverdesc_fetching =(flags & PDS_NO_EXISTING_SERVERDESC_FETCH); int n_busy = 0; + int try_excluding = 1, n_excluded = 0; if (!trusted_dir_servers) return NULL; + retry_without_exclude: + direct = smartlist_create(); tunnel = smartlist_create(); overloaded_direct = smartlist_create(); @@ -1197,6 +1219,12 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags, continue; if (requireother && me && router_digest_is_me(d->digest)) continue; + if (try_excluding && options->ExcludeNodes && + routerset_contains_routerstatus(options->ExcludeNodes, + &d->fake_status)) { + ++n_excluded; + continue; + } /* XXXX IP6 proposal 118 */ tor_addr_from_ipv4h(&addr, d->addr); @@ -1243,6 +1271,15 @@ router_pick_trusteddirserver_impl(authority_type_t type, int flags, smartlist_free(tunnel); smartlist_free(overloaded_direct); smartlist_free(overloaded_tunnel); + + if (result == NULL && try_excluding && !options->StrictNodes && n_excluded) { + /* If we got no result, and we are excluding nodes, and StrictNodes is + * not set, try again without excluding nodes. */ + try_excluding = 0; + n_excluded = 0; + goto retry_without_exclude; + } + return result; } @@ -1501,6 +1538,8 @@ routerlist_find_my_routerinfo(void) /** Find a router that's up, that has this IP address, and * that allows exit to this address:port, or return NULL if there * isn't a good one. + * Don't exit enclave to excluded relays -- it wouldn't actually + * hurt anything, but this way there are fewer confused users. */ routerinfo_t * router_find_exact_exit_enclave(const char *address, uint16_t port) @@ -1508,6 +1547,7 @@ router_find_exact_exit_enclave(const char *address, uint16_t port) uint32_t addr; struct in_addr in; tor_addr_t a; + or_options_t *options = get_options(); if (!tor_inet_aton(address, &in)) return NULL; /* it's not an IP already */ @@ -1520,7 +1560,8 @@ router_find_exact_exit_enclave(const char *address, uint16_t port) if (router->addr == addr && router->is_running && compare_tor_addr_to_addr_policy(&a, port, router->exit_policy) == - ADDR_POLICY_ACCEPTED) + ADDR_POLICY_ACCEPTED && + !routerset_contains_router(options->_ExcludeExitNodesUnion, router)) return router; }); return NULL; @@ -5433,7 +5474,7 @@ routerset_needs_geoip(const routerset_t *set) } /** Return true iff there are no entries in <b>set</b>. */ -static int +int routerset_is_empty(const routerset_t *set) { return !set || smartlist_len(set->list) == 0; @@ -5516,10 +5557,11 @@ routerset_contains_routerstatus(const routerset_t *set, routerstatus_t *rs) } /** Add every known routerinfo_t that is a member of <b>routerset</b> to - * <b>out</b>. If <b>running_only</b>, only add the running ones. */ + * <b>out</b>, but never add any that are part of <b>excludeset</b>. + * If <b>running_only</b>, only add the running ones. */ void routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset, - int running_only) + const routerset_t *excludeset, int running_only) { tor_assert(out); if (!routerset || !routerset->list) @@ -5529,12 +5571,13 @@ routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset, if (routerset_is_list(routerset)) { /* No routers are specified by type; all are given by name or digest. - * we can do a lookup in O(len(list)). */ + * we can do a lookup in O(len(routerset)). */ SMARTLIST_FOREACH(routerset->list, const char *, name, { routerinfo_t *router = router_get_by_nickname(name, 1); if (router) { if (!running_only || router->is_running) - smartlist_add(out, router); + if (!routerset_contains_router(excludeset, router)) + smartlist_add(out, router); } }); } else { @@ -5544,12 +5587,14 @@ routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset, SMARTLIST_FOREACH(rl->routers, routerinfo_t *, router, { if (running_only && !router->is_running) continue; - if (routerset_contains_router(routerset, router)) + if (routerset_contains_router(routerset, router) && + !routerset_contains_router(excludeset, router)) smartlist_add(out, router); }); } } +#if 0 /** Add to <b>target</b> every routerinfo_t from <b>source</b> except: * * 1) Don't add it if <b>include</b> is non-empty and the relay isn't in @@ -5580,6 +5625,7 @@ routersets_get_disjunction(smartlist_t *target, } }); } +#endif /** Remove every routerinfo_t from <b>lst</b> that is in <b>routerset</b>. */ void @@ -5611,10 +5657,15 @@ routerset_to_string(const routerset_t *set) int routerset_equal(const routerset_t *old, const routerset_t *new) { - if (old == NULL && new == NULL) + if (routerset_is_empty(old) && routerset_is_empty(new)) { + /* Two empty sets are equal */ return 1; - else if (old == NULL || new == NULL) + } else if (routerset_is_empty(old) || routerset_is_empty(new)) { + /* An empty set is equal to nothing else. */ return 0; + } + tor_assert(old != NULL); + tor_assert(new != NULL); if (smartlist_len(old->list) != smartlist_len(new->list)) return 0; @@ -5676,7 +5727,6 @@ hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, { int start, found, n_added = 0, i; networkstatus_t *c = networkstatus_get_latest_consensus(); - int use_begindir = get_options()->TunnelDirConns; if (!c || !smartlist_len(c->routerstatus_list)) { log_warn(LD_REND, "We don't have a consensus, so we can't perform v2 " "rendezvous operations."); @@ -5689,14 +5739,9 @@ hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, do { routerstatus_t *r = smartlist_get(c->routerstatus_list, i); if (r->is_hs_dir) { - if (r->dir_port || use_begindir) - smartlist_add(responsible_dirs, r); - else - log_info(LD_REND, "Not adding router '%s' to list of responsible " - "hidden service directories, because we have no way of " - "reaching it.", r->nickname); + smartlist_add(responsible_dirs, r); if (++n_added == REND_NUMBER_OF_CONSECUTIVE_REPLICAS) - break; + return 0; } if (++i == smartlist_len(c->routerstatus_list)) i = 0; diff --git a/src/or/routerlist.h b/src/or/routerlist.h index ca428114ed..fec18705b3 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -167,16 +167,20 @@ int routerset_parse(routerset_t *target, const char *s, void routerset_union(routerset_t *target, const routerset_t *source); int routerset_is_list(const routerset_t *set); int routerset_needs_geoip(const routerset_t *set); +int routerset_is_empty(const routerset_t *set); int routerset_contains_router(const routerset_t *set, routerinfo_t *ri); int routerset_contains_routerstatus(const routerset_t *set, routerstatus_t *rs); int routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei); void routerset_get_all_routers(smartlist_t *out, const routerset_t *routerset, + const routerset_t *excludeset, int running_only); +#if 0 void routersets_get_disjunction(smartlist_t *target, const smartlist_t *source, const routerset_t *include, const routerset_t *exclude, int running_only); +#endif void routerset_subtract_routers(smartlist_t *out, const routerset_t *routerset); char *routerset_to_string(const routerset_t *routerset); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 95e174e550..d0138e638b 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -4357,6 +4357,7 @@ microdescs_parse_from_string(const char *s, const char *eos, md = NULL; next: microdesc_free(md); + md = NULL; memarea_clear(area); smartlist_clear(tokens); @@ -4638,10 +4639,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/test/test_addr.c b/src/test/test_addr.c index 20ffaa0c52..6db4ee2483 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -477,9 +477,9 @@ test_addr_ip6_helpers(void) i = get_interface_address6(LOG_DEBUG, AF_INET6, &t2); #if 0 tor_inet_ntop(AF_INET, &t1.sa.sin_addr, buf, sizeof(buf)); - printf("\nv4 address: %s (family=%i)", buf, IN_FAMILY(&t1)); + printf("\nv4 address: %s (family=%d)", buf, IN_FAMILY(&t1)); tor_inet_ntop(AF_INET6, &t2.sa6.sin6_addr, buf, sizeof(buf)); - printf("\nv6 address: %s (family=%i)", buf, IN_FAMILY(&t2)); + printf("\nv6 address: %s (family=%d)", buf, IN_FAMILY(&t2)); #endif done: |