diff options
81 files changed, 1913 insertions, 371 deletions
diff --git a/Makefile.am b/Makefile.am index e45856c367..c160af9732 100644 --- a/Makefile.am +++ b/Makefile.am @@ -100,11 +100,11 @@ test-network: need-chutney-path $(TESTING_TOR_BINARY) src/tools/tor-gencert # only run IPv6 tests if we can ping6 ::1 (localhost) # some IPv6 tests will fail without an IPv6 DNS server (see #16971 and #17011) # only run mixed tests if we have a tor-stable binary -# see #17015 for autodetection of different tor versions +# Try both the BSD and the Linux ping6 syntax, because they're incompatible test-network-all: need-chutney-path test-driver $(TESTING_TOR_BINARY) src/tools/tor-gencert mkdir -p $(TEST_NETWORK_ALL_LOG_DIR) @flavors="$(TEST_CHUTNEY_FLAVORS)"; \ - if ping6 -q -c 1 -o ::1 >/dev/null 2>&1; then \ + if ping6 -q -c 1 -o ::1 >/dev/null 2>&1 || ping6 -q -c 1 -W 1 ::1 >/dev/null 2>&1; then \ echo "ping6 ::1 succeeded, running IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \ flavors="$$flavors $(TEST_CHUTNEY_FLAVORS_IPV6)"; \ else \ diff --git a/changes/18640 b/changes/18640 new file mode 100644 index 0000000000..de63da6655 --- /dev/null +++ b/changes/18640 @@ -0,0 +1,6 @@ + o Major features (resource management): + - Tor now includes support for noticing when we are about to run out of + sockets, and preemptively closing connections of lower priority. + (This feature is off by default for now, since the current prioritizing + method is not mature enough yet. You can enable it by setting + "DisableOOSCheck 0".) Closes ticket 18640. diff --git a/changes/19122 b/changes/19122 new file mode 100644 index 0000000000..cface80679 --- /dev/null +++ b/changes/19122 @@ -0,0 +1,3 @@ + o Minor bugfixes (documentation): + - man page incorrectly states the User option takes a UID when it really + takes a username. Fixes bug 19122. diff --git a/changes/19977 b/changes/19977 new file mode 100644 index 0000000000..0ca1af1cea --- /dev/null +++ b/changes/19977 @@ -0,0 +1,6 @@ + o Minor bugfixes (unit test) + - Fix shared random unit test that was failing on big endian architecture + due to internal representation of a integer copied to a buffer. The test + is changed to take a full 32 bytes of data and use the output of a + python script that make the COMMIT and REVEAL calculation according to + the spec. Fixes #19977; bugfix on tor-0.2.9.1-alpha. diff --git a/changes/bug19905 b/changes/bug19905 new file mode 100644 index 0000000000..b689740594 --- /dev/null +++ b/changes/bug19905 @@ -0,0 +1,3 @@ + o Minor bugfixes (IPv6, testing): + - Check for IPv6 correctly on Linux when running test networks. + Fixes bug 19905; bugfix on 0.2.7.3-rc; patch by teor. diff --git a/changes/bug19964 b/changes/bug19964 new file mode 100644 index 0000000000..af719424cb --- /dev/null +++ b/changes/bug19964 @@ -0,0 +1,6 @@ + o Minor bugfixes (logging): + - When we are unable to remove the bw_accounting file, do not warn + if the reason we couldn't remove it was that it didn't exist. + Fixes bug 19964; bugfix on 0.2.5.4-alpha. Patch + from 'pastly'. + diff --git a/changes/bug19999_prep b/changes/bug19999_prep new file mode 100644 index 0000000000..e8bb4a571b --- /dev/null +++ b/changes/bug19999_prep @@ -0,0 +1,20 @@ + o Minor features (unit tests): + - The unit tests now log all warning messages with the "BUG" flag. + Previously, they only logged errors by default. This change will + help us make our testing code more correct, and make sure that + we only hit this code when we mean to. This is preparatory work + for ticket 19999. + - Our unit testing code that captures log messages no longer prevents + them from being written out if the user asked for them (by passing + --debug or --info or or --notice --warn to the "test" binary). This + change will prevent us from missing unexpected log messages simply + because we were looking for others. Related to ticket 19999. + - Our link-handshake unit tests now check, that when invalid + handshakes fail, they fail with the error messages we + expected. + + o Minor bugfixes (unit tests): + - The tor_tls_server_info_callback unit test no longer crashes when + debug-level logging is turned on. Fixes bug 20041; bugfix on + 0.2.8.1-alpha. + diff --git a/changes/bug20012 b/changes/bug20012 new file mode 100644 index 0000000000..f67ee65867 --- /dev/null +++ b/changes/bug20012 @@ -0,0 +1,12 @@ + o Major bugfixes (hidden services): + - Clients require hidden services to include the TAP keys + for their intro points in the hidden service descriptor. + This prevents an inadvertent upgrade to ntor, which a + malicious hidden service could use to discover which + consensus a client has. + Fixes bug 20012; bugfix on 0.2.4.8-alpha. Patch by teor. + o Minor bugfixes (hidden services): + - Stop logging intro point details to the client log on + certain error conditions. + Fixed as part of bug 20012; bugfix on 0.2.4.8-alpha. + Patch by teor. diff --git a/changes/bug20064 b/changes/bug20064 new file mode 100644 index 0000000000..38d3b91cfa --- /dev/null +++ b/changes/bug20064 @@ -0,0 +1,5 @@ + o Minor bugfixes (Directory Authority): + - When allowing private addresses, mark Exits that only exit to + private locations as such. Fixes bug 20064; bugfix on + 0.2.2.9-alpha. + diff --git a/changes/bug20065 b/changes/bug20065 new file mode 100644 index 0000000000..cdeb698991 --- /dev/null +++ b/changes/bug20065 @@ -0,0 +1,5 @@ + o Minor bugfixes (directory authorities): + - Die with a useful error when the operator forgets to place the + authority_signing_key file into the keys directory. This avoids an + uninformative assert & traceback about having an invalid key. + Fixes bug 20065; bugfix on 0.2.0.1-alpha. diff --git a/changes/bug20093 b/changes/bug20093 new file mode 100644 index 0000000000..ef3260143b --- /dev/null +++ b/changes/bug20093 @@ -0,0 +1,4 @@ + o Minor bugfixes (logging): + - When logging a message from the BUG() macro, be explicit about what + we were asserting. Previously we were confusing what we were asserting + with what the bug was. Fixes bug 20093; bugfix on 0.2.9.1-alpha. diff --git a/changes/feature20069 b/changes/feature20069 new file mode 100644 index 0000000000..039fb754d2 --- /dev/null +++ b/changes/feature20069 @@ -0,0 +1,6 @@ + o Minor feature (testing, ipv6): + - Add the hs-ipv6 chutney target to make test-network-all's IPv6 + tests. Remove bridges+hs, as it's somewhat redundant. + This requires a recent chutney version that supports IPv6 clients, + relays, and authorities. + Closes ticket 20069; patch by teor. diff --git a/changes/reject-tap b/changes/reject-tap new file mode 100644 index 0000000000..8e616de301 --- /dev/null +++ b/changes/reject-tap @@ -0,0 +1,15 @@ + o Major bug fixes (circuit building): + - Tor authorities, relays, and clients only use ntor, except for + rare cases in the hidden service protocol. + - Authorities, relays and clients specifically check that each + descriptor has an ntor key. + - Clients avoid downloading a descriptor if the relay version is + too old to support ntor. + - Client code never chooses nodes without ntor keys: they will not + be selected during circuit-building, or as guards, or as directory + mirrors, or as introduction or rendezvous points. + - Circuit-building code assumes that all hops can use ntor, + except for rare hidden service protocol cases. + - Hidden service client to intro point and service to rendezvous point + connections use the TAP key supplied by the protocol. + Fixes bug 19163; bugfix on 0.2.4.18-rc. diff --git a/changes/ticket20002 b/changes/ticket20002 new file mode 100644 index 0000000000..28eaa8d60d --- /dev/null +++ b/changes/ticket20002 @@ -0,0 +1,4 @@ + o Minor features (directory authority): + - After voting, if the authorities decide that a relay is not "Valid", + they no longer include it in the consensus at all. Closes ticket + 20002; implements part of proposal 272. diff --git a/configure.ac b/configure.ac index 19276eaccb..a90c15c465 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2015, The Tor Project, Inc. dnl See LICENSE for licensing information AC_PREREQ([2.63]) -AC_INIT([tor],[0.2.9.2-alpha]) +AC_INIT([tor],[0.2.9.2-alpha-dev]) AC_CONFIG_SRCDIR([src/or/main.c]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in index 543a834ae2..9eec555dbe 100644 --- a/contrib/win32build/tor-mingw.nsi.in +++ b/contrib/win32build/tor-mingw.nsi.in @@ -8,7 +8,7 @@ !include "LogicLib.nsh" !include "FileFunc.nsh" !insertmacro GetParameters -!define VERSION "0.2.9.2-alpha" +!define VERSION "0.2.9.2-alpha-dev" !define INSTALLER "tor-${VERSION}-win32.exe" !define WEBSITE "https://www.torproject.org/" !define LICENSE "LICENSE" diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md index 7c6144657b..7595398241 100644 --- a/doc/HACKING/ReleasingTor.md +++ b/doc/HACKING/ReleasingTor.md @@ -149,9 +149,13 @@ new Tor release: - {blueness} at gentoo dot org - {paul} at invizbox dot io - {lfleischer} at archlinux dot org - - {tails-dev} at boum dot org - {Nathan} at freitas dot net - {mike} at tig dot as + - {tails-rm} at boum dot org (for pre-release announcments) + + + - {tails-dev} at boum dot org (for at-release announcements) + 4. Add the version number to Trac. To do this, go to Trac, log in, select "Admin" near the top of the screen, then select "Versions" from diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 2748f54546..1856592a9d 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -321,7 +321,7 @@ GENERAL OPTIONS specify one or more of **HashedControlPassword** or **CookieAuthentication**, setting this option will cause Tor to allow any process on the local host to control it. (Setting both authentication - methods means eithermethod is sufficient to authenticate to Tor.) This + methods means either method is sufficient to authenticate to Tor.) This option is required for many Tor controllers; most use the value of 9051. Set it to "auto" to have Tor pick a port for you. (Default: 0) + + @@ -668,7 +668,7 @@ GENERAL OPTIONS relay, all log messages generated when acting as a relay are sanitized, but all messages generated when acting as a client are not. (Default: 1) -[[User]] **User** __UID__:: +[[User]] **User** __Username__:: On startup, setuid to this user and setgid to their primary group. [[KeepBindCapabilities]] **KeepBindCapabilities** **0**|**1**|**auto**:: @@ -1467,16 +1467,6 @@ The following options are useful only for clients (that is, if "auto" (recommended) then it is on for all clients that do not set FetchUselessDescriptors. (Default: auto) -[[UseNTorHandshake]] **UseNTorHandshake** **0**|**1**|**auto**:: - The "ntor" circuit-creation handshake is faster and (we think) more - secure than the original ("TAP") circuit handshake, but starting to use - it too early might make your client stand out. If this option is 0, your - Tor client won't use the ntor handshake. If it's 1, your Tor client - will use the ntor handshake to extend circuits through servers that - support it. If this option is "auto", then your client - will use the ntor handshake once enough directory authorities recommend - it. (Default: 1) - [[PathBiasCircThreshold]] **PathBiasCircThreshold** __NUM__ + [[PathBiasNoticeRate]] **PathBiasNoticeRate** __NUM__ + @@ -2056,6 +2046,12 @@ is non-zero): this. If this option is set to 0, Tor will try to pick a reasonable default based on your system's physical memory. (Default: 0) +[[DisableOOSCheck]] **DisableOOSCheck** **0**|**1**:: + This option disables the code that closes connections when Tor notices + that it is running low on sockets. Right now, it is on by default, + since the existing out-of-sockets mechanism tends to kill OR connections + more than it should. (Default: 1) + [[SigningKeyLifetime]] **SigningKeyLifetime** __N__ **days**|**weeks**|**months**:: For how long should each Ed25519 signing key be valid? Tor uses a permanent master identity key that can be kept offline, and periodically @@ -2130,7 +2126,7 @@ on the public Tor network. When this option is set in addition to **AuthoritativeDirectory**, Tor generates version 3 network statuses and serves descriptors, etc as described in dir-spec.txt file of https://spec.torproject.org/[torspec] - (for Tor clients and servers running atleast 0.2.0.x). + (for Tor clients and servers running at least 0.2.0.x). [[VersioningAuthoritativeDirectory]] **VersioningAuthoritativeDirectory** **0**|**1**:: When this option is set to 1, Tor adds information on which versions of @@ -2182,7 +2178,9 @@ on the public Tor network. [[DirAllowPrivateAddresses]] **DirAllowPrivateAddresses** **0**|**1**:: If set to 1, Tor will accept server descriptors with arbitrary "Address" elements. Otherwise, if the address is not an IP address or is a private IP - address, it will reject the server descriptor. (Default: 0) + address, it will reject the server descriptor. Additionally, Tor + will allow exit policies for private networks to fulfill Exit flag + requirements. (Default: 0) [[AuthDirBadExit]] **AuthDirBadExit** __AddressPattern...__:: Authoritative directories only. A set of address patterns for servers that diff --git a/scripts/maint/checkSpace.pl b/scripts/maint/checkSpace.pl index 906281112d..e90f5b330b 100755 --- a/scripts/maint/checkSpace.pl +++ b/scripts/maint/checkSpace.pl @@ -156,6 +156,25 @@ for $fn (@ARGV) { $in_func_head = 0; } } + + ## Check for forbidden functions except when they are + # explicitly permitted + if (/\bassert\(/ && not /assert OK/) { + print "assert :$fn:$. (use tor_assert)\n"; + } + if (/\bmemcmp\(/ && not /memcmp OK/) { + print "memcmp :$fn:$. (use {tor,fast}_mem{eq,neq,cmp}\n"; + } + # always forbidden. + if (not / OVERRIDE /) { + if (/\bstrcat\(/ or /\bstrcpy\(/ or /\bsprintf\(/) { + print "$& :$fn:$.\n"; + } + if (/\bmalloc\(/ or /\bfree\(/ or /\brealloc\(/ or + /\bstrdup\(/ or /\bstrndup\(/ or /\bcalloc\(/) { + print "$& :$fn:$. (use tor_malloc, tor_free, etc)\n"; + } + } } } ## Warn if the file doesn't end with a blank line. diff --git a/src/common/backtrace.c b/src/common/backtrace.c index 2841281927..81e04e94eb 100644 --- a/src/common/backtrace.c +++ b/src/common/backtrace.c @@ -117,7 +117,7 @@ log_backtrace(int severity, int domain, const char *msg) for (i=0; i < depth; ++i) { tor_log(severity, domain, " %s", symbols[i]); } - free(symbols); + raw_free(symbols); done: tor_mutex_release(&cb_buf_mutex); @@ -190,7 +190,7 @@ install_bt_handler(void) size_t depth = backtrace(cb_buf, MAX_DEPTH); symbols = backtrace_symbols(cb_buf, (int) depth); if (symbols) - free(symbols); + raw_free(symbols); } return rv; diff --git a/src/common/compat.c b/src/common/compat.c index 4614ef94d5..5385bd871c 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -2350,7 +2350,7 @@ make_path_absolute(char *fname) /* We don't want to assume that tor_free can free a string allocated * with malloc. On failure, return fname (it's better than nothing). */ char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname); - if (absfname_malloced) free(absfname_malloced); + if (absfname_malloced) raw_free(absfname_malloced); return absfname; #else diff --git a/src/common/container.c b/src/common/container.c index ddf3bafa91..ec59dccf62 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -544,7 +544,7 @@ smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b)) /** Given a smartlist <b>sl</b> sorted with the function <b>compare</b>, * return the most frequent member in the list. Break ties in favor of * later elements. If the list is empty, return NULL. If count_out is - * non-null, set it to the most frequent member. + * non-null, set it to the count of the most frequent member. */ void * smartlist_get_most_frequent_(const smartlist_t *sl, diff --git a/src/common/container.h b/src/common/container.h index 92ad3f5ec7..71495b660a 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -526,7 +526,7 @@ void* strmap_remove_lc(strmap_t *map, const char *key); return (valtype*)digestmap_remove((digestmap_t*)map, key); \ } \ ATTR_UNUSED static inline void \ - prefix##free(maptype *map, void (*free_val)(void*)) \ + prefix##f##ree(maptype *map, void (*free_val)(void*)) \ { \ digestmap_free((digestmap_t*)map, free_val); \ } \ diff --git a/src/common/log.c b/src/common/log.c index cb62a37e52..56adc77f84 100644 --- a/src/common/log.c +++ b/src/common/log.c @@ -47,6 +47,8 @@ #define TRUNCATED_STR_LEN 14 /** @} */ +#define raw_assert(x) assert(x) // assert OK + /** Information for a single logfile; only used in log.c */ typedef struct logfile_t { struct logfile_t *next; /**< Next logfile_t in the linked list. */ @@ -75,7 +77,7 @@ sev_to_string(int severity) case LOG_ERR: return "err"; default: /* Call assert, not tor_assert, since tor_assert * calls log on failure. */ - assert(0); return "UNKNOWN"; // LCOV_EXCL_LINE + raw_assert(0); return "UNKNOWN"; // LCOV_EXCL_LINE } } @@ -95,7 +97,7 @@ should_log_function_name(log_domain_mask_t domain, int severity) return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG; default: /* Call assert, not tor_assert, since tor_assert calls log on failure. */ - assert(0); return 0; // LCOV_EXCL_LINE + raw_assert(0); return 0; // LCOV_EXCL_LINE } } @@ -293,7 +295,7 @@ format_msg(char *buf, size_t buf_len, char *end_of_prefix; char *buf_end; - assert(buf_len >= 16); /* prevent integer underflow and general stupidity */ + raw_assert(buf_len >= 16); /* prevent integer underflow and stupidity */ buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */ buf_end = buf+buf_len; /* point *after* the last char we can write to */ @@ -482,12 +484,12 @@ logv,(int severity, log_domain_mask_t domain, const char *funcname, int callbacks_deferred = 0; /* Call assert, not tor_assert, since tor_assert calls log on failure. */ - assert(format); + raw_assert(format); /* check that severity is sane. Overrunning the masks array leads to * interesting and hard to diagnose effects */ - assert(severity >= LOG_ERR && severity <= LOG_DEBUG); + raw_assert(severity >= LOG_ERR && severity <= LOG_DEBUG); /* check that we've initialised the log mutex before we try to lock it */ - assert(log_mutex_initialized); + raw_assert(log_mutex_initialized); LOCK_LOGS(); if ((! (domain & LD_NOCB)) && pending_cb_messages @@ -534,6 +536,11 @@ tor_log(int severity, log_domain_mask_t domain, const char *format, ...) if (severity > log_global_min_severity_) return; va_start(ap,format); +#ifdef TOR_UNIT_TESTS + if (domain & LD_NO_MOCK) + logv__real(severity, domain, NULL, NULL, format, ap); + else +#endif logv(severity, domain, NULL, NULL, format, ap); va_end(ap); } @@ -653,7 +660,7 @@ tor_log_update_sigsafe_err_fds(void) if (!found_real_stderr && int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) { /* Don't use a virtual stderr when we're also logging to stdout. */ - assert(n_sigsafe_log_fds >= 2); /* Don't use assert inside log functions*/ + raw_assert(n_sigsafe_log_fds >= 2); /* Don't tor_assert inside log fns */ sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds]; } diff --git a/src/common/torlog.h b/src/common/torlog.h index 80f37e0e48..6732a42741 100644 --- a/src/common/torlog.h +++ b/src/common/torlog.h @@ -109,6 +109,11 @@ * would. Used as a flag, not a log domain. */ #define LD_NOFUNCNAME (1u<<30) +#ifdef TOR_UNIT_TESTS +/** This log message should not be intercepted by mock_saving_logv */ +#define LD_NO_MOCK (1u<<29) +#endif + /** Mask of zero or more log domains, OR'd together. */ typedef uint32_t log_domain_mask_t; diff --git a/src/common/tortls.c b/src/common/tortls.c index a62efb5575..23889be259 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1489,6 +1489,10 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val) tor_tls_t *tls; (void) val; + IF_BUG_ONCE(ssl == NULL) { + return; // LCOV_EXCL_LINE + } + tor_tls_debug_state_callback(ssl, type, val); if (type != SSL_CB_ACCEPT_LOOP) diff --git a/src/common/util.c b/src/common/util.c index c7dd2a8af7..211ed7f8d2 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -147,7 +147,7 @@ tor_malloc_(size_t size DMALLOC_PARAMS) #ifdef USE_DMALLOC result = dmalloc_malloc(file, line, size, DMALLOC_FUNC_MALLOC, 0, 0); #else - result = malloc(size); + result = raw_malloc(size); #endif if (PREDICT_UNLIKELY(result == NULL)) { @@ -246,7 +246,7 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS) #ifdef USE_DMALLOC result = dmalloc_realloc(file, line, ptr, size, DMALLOC_FUNC_REALLOC, 0); #else - result = realloc(ptr, size); + result = raw_realloc(ptr, size); #endif if (PREDICT_UNLIKELY(result == NULL)) { @@ -285,7 +285,7 @@ tor_strdup_(const char *s DMALLOC_PARAMS) #ifdef USE_DMALLOC duplicate = dmalloc_strdup(file, line, s, 0); #else - duplicate = strdup(s); + duplicate = raw_strdup(s); #endif if (PREDICT_UNLIKELY(duplicate == NULL)) { /* LCOV_EXCL_START */ diff --git a/src/common/util.h b/src/common/util.h index 7a6203aeea..57605ccfd1 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -82,7 +82,7 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, */ #define tor_free(p) STMT_BEGIN \ if (PREDICT_LIKELY((p)!=NULL)) { \ - free(p); \ + raw_free(p); \ (p)=NULL; \ } \ STMT_END @@ -99,6 +99,14 @@ extern int dmalloc_free(const char *file, const int line, void *pnt, #define tor_memdup(s, n) tor_memdup_(s, n DMALLOC_ARGS) #define tor_memdup_nulterm(s, n) tor_memdup_nulterm_(s, n DMALLOC_ARGS) +/* Aliases for the underlying system malloc/realloc/free. Only use + * them to indicate "I really want the underlying system function, I know + * what I'm doing." */ +#define raw_malloc malloc +#define raw_realloc realloc +#define raw_free free +#define raw_strdup strdup + void tor_log_mallinfo(int severity); /** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */ diff --git a/src/common/util_bug.c b/src/common/util_bug.c index e3e1d6df90..f1cd33642e 100644 --- a/src/common/util_bug.c +++ b/src/common/util_bug.c @@ -11,6 +11,44 @@ #include "util_bug.h" #include "torlog.h" #include "backtrace.h" +#include "container.h" + +#ifdef TOR_UNIT_TESTS +static int n_bugs_to_capture = 0; +static smartlist_t *bug_messages = NULL; +#define capturing_bugs() (bug_messages != NULL && n_bugs_to_capture) +void +tor_capture_bugs_(int n) +{ + tor_end_capture_bugs_(); + bug_messages = smartlist_new(); + n_bugs_to_capture = n; +} +void +tor_end_capture_bugs_(void) +{ + n_bugs_to_capture = 0; + if (!bug_messages) + return; + SMARTLIST_FOREACH(bug_messages, char *, cp, tor_free(cp)); + smartlist_free(bug_messages); + bug_messages = NULL; +} +const smartlist_t * +tor_get_captured_bug_log_(void) +{ + return bug_messages; +} +static void +add_captured_bug(const char *s) +{ + --n_bugs_to_capture; + smartlist_add(bug_messages, tor_strdup(s)); +} +#else +#define capturing_bugs() (0) +#define add_captured_bug(s) do { } while (0) +#endif /** Helper for tor_assert: report the assertion failure. */ void @@ -36,12 +74,20 @@ tor_bug_occurred_(const char *fname, unsigned int line, const char *once_str = once ? " (Future instances of this warning will be silenced.)": ""; if (! expr) { + if (capturing_bugs()) { + add_captured_bug("This line should not have been reached."); + return; + } log_warn(LD_BUG, "%s:%u: %s: This line should not have been reached.%s", fname, line, func, once_str); tor_snprintf(buf, sizeof(buf), "Line unexpectedly reached at %s at %s:%u", func, fname, line); } else { + if (capturing_bugs()) { + add_captured_bug(expr); + return; + } log_warn(LD_BUG, "%s:%u: %s: Non-fatal assertion %s failed.%s", fname, line, func, expr, once_str); tor_snprintf(buf, sizeof(buf), diff --git a/src/common/util_bug.h b/src/common/util_bug.h index 3f77e0a99e..049ca1a6ef 100644 --- a/src/common/util_bug.h +++ b/src/common/util_bug.h @@ -65,7 +65,8 @@ #define tor_assert_nonfatal_once(cond) tor_assert((cond)) #define BUG(cond) \ (PREDICT_UNLIKELY(cond) ? \ - (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,#cond), abort(), 1) \ + (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,"!("#cond")"), \ + abort(), 1) \ : 0) #elif defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) #define tor_assert_nonfatal_unreached() STMT_NIL @@ -98,18 +99,19 @@ STMT_END #define BUG(cond) \ (PREDICT_UNLIKELY(cond) ? \ - (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,#cond,0), 1) \ + (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",0), 1) \ : 0) #endif #ifdef __GNUC__ #define IF_BUG_ONCE__(cond,var) \ - if (( { \ + if (( { \ static int var = 0; \ int bool_result = (cond); \ if (PREDICT_UNLIKELY(bool_result) && !var) { \ var = 1; \ - tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1); \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \ + "!("#cond")", 1); \ } \ PREDICT_UNLIKELY(bool_result); } )) #else @@ -118,7 +120,8 @@ if (PREDICT_UNLIKELY(cond)) ? \ (var ? 1 : \ (var=1, \ - tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1), \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \ + "!("#cond")", 1), \ 1)) \ : 0) #endif @@ -146,5 +149,11 @@ void tor_bug_occurred_(const char *fname, unsigned int line, const char *func, const char *expr, int once); +#ifdef TOR_UNIT_TESTS +void tor_capture_bugs_(int n); +void tor_end_capture_bugs_(void); +const struct smartlist_t *tor_get_captured_bug_log_(void); +#endif + #endif diff --git a/src/common/util_format.c b/src/common/util_format.c index 9009e1a814..aef9db85c8 100644 --- a/src/common/util_format.c +++ b/src/common/util_format.c @@ -88,7 +88,7 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen) else if (src[j] > 0x31 && src[j] < 0x38) tmp[j] = src[j] - 0x18; else if (src[j] > 0x40 && src[j] < 0x5B) tmp[j] = src[j] - 0x41; else { - log_warn(LD_BUG, "illegal character in base32 encoded string"); + log_warn(LD_GENERAL, "illegal character in base32 encoded string"); tor_free(tmp); return -1; } diff --git a/src/or/channel.c b/src/or/channel.c index 87fa721089..6a78b21988 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -838,7 +838,7 @@ channel_free(channel_t *chan) } /* Call a free method if there is one */ - if (chan->free) chan->free(chan); + if (chan->free_fn) chan->free_fn(chan); channel_clear_remote_end(chan); @@ -878,7 +878,7 @@ channel_listener_free(channel_listener_t *chan_l) tor_assert(!(chan_l->registered)); /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); + if (chan_l->free_fn) chan_l->free_fn(chan_l); /* * We're in CLOSED or ERROR, so the incoming channel queue is already @@ -916,7 +916,7 @@ channel_force_free(channel_t *chan) } /* Call a free method if there is one */ - if (chan->free) chan->free(chan); + if (chan->free_fn) chan->free_fn(chan); channel_clear_remote_end(chan); @@ -958,7 +958,7 @@ channel_listener_force_free(channel_listener_t *chan_l) chan_l); /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); + if (chan_l->free_fn) chan_l->free_fn(chan_l); /* * The incoming list just gets emptied and freed; we request close on diff --git a/src/or/channel.h b/src/or/channel.h index 78e1b71014..a711b56d44 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -90,7 +90,7 @@ struct channel_s { /* Methods implemented by the lower layer */ /** Free a channel */ - void (*free)(channel_t *); + void (*free_fn)(channel_t *); /** Close an open channel */ void (*close)(channel_t *); /** Describe the transport subclass for this channel */ @@ -273,7 +273,7 @@ struct channel_listener_s { /* Methods implemented by the lower layer */ /** Free a channel */ - void (*free)(channel_listener_t *); + void (*free_fn)(channel_listener_t *); /** Close an open channel */ void (*close)(channel_listener_t *); /** Describe the transport subclass for this channel */ diff --git a/src/or/channeltls.c b/src/or/channeltls.c index a62f80ef91..9c2411ede8 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -117,7 +117,7 @@ channel_tls_common_init(channel_tls_t *tlschan) chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; chan->describe_transport = channel_tls_describe_transport_method; - chan->free = channel_tls_free_method; + chan->free_fn = channel_tls_free_method; chan->get_overhead_estimate = channel_tls_get_overhead_estimate_method; chan->get_remote_addr = channel_tls_get_remote_addr_method; chan->get_remote_descr = channel_tls_get_remote_descr_method; diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 14d40150db..12c75530e2 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -58,7 +58,6 @@ static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); static int count_acceptable_nodes(smartlist_t *routers); static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); -static int circuits_can_use_ntor(void); /** This function tries to get a channel to the specified endpoint, * and then calls command_setup_channel() to give it the right @@ -365,7 +364,7 @@ circuit_rep_hist_note_result(origin_circuit_t *circ) } while (hop!=circ->cpath); } -/** Return 1 iff at least one node in circ's cpath supports ntor. */ +/** Return 1 iff every node in circ's cpath definitely supports ntor. */ static int circuit_cpath_supports_ntor(const origin_circuit_t *circ) { @@ -373,16 +372,19 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ) cpath = head = circ->cpath; do { - if (cpath->extend_info && - !tor_mem_is_zero( - (const char*)cpath->extend_info->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) - return 1; + /* if the extend_info is missing, we can't tell if it supports ntor */ + if (!cpath->extend_info) { + return 0; + } + /* if the key is blank, it definitely doesn't support ntor */ + if (!extend_info_supports_ntor(cpath->extend_info)) { + return 0; + } cpath = cpath->next; } while (cpath != head); - return 0; + return 1; } /** Pick all the entries in our cpath. Stop and return 0 when we're @@ -390,41 +392,61 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ) static int onion_populate_cpath(origin_circuit_t *circ) { - int n_tries = 0; - const int using_ntor = circuits_can_use_ntor(); + int r = 0; -#define MAX_POPULATE_ATTEMPTS 32 + /* onion_extend_cpath assumes these are non-NULL */ + tor_assert(circ); + tor_assert(circ->build_state); - while (1) { - int r = onion_extend_cpath(circ); + while (r == 0) { + r = onion_extend_cpath(circ); if (r < 0) { log_info(LD_CIRC,"Generating cpath hop failed."); return -1; } - if (r == 1) { - /* This circuit doesn't need/shouldn't be forced to have an ntor hop */ - if (circ->build_state->desired_path_len <= 1 || ! using_ntor) - return 0; + } - /* This circuit has an ntor hop. great! */ - if (circuit_cpath_supports_ntor(circ)) - return 0; + /* The path is complete */ + tor_assert(r == 1); - /* No node in the circuit supports ntor. Have we already tried too many - * times? */ - if (++n_tries >= MAX_POPULATE_ATTEMPTS) - break; + /* Does every node in this path support ntor? */ + int path_supports_ntor = circuit_cpath_supports_ntor(circ); - /* Clear the path and retry */ - circuit_clear_cpath(circ); + /* We would like every path to support ntor, but we have to allow for some + * edge cases. */ + tor_assert(circuit_get_cpath_len(circ)); + if (circuit_can_use_tap(circ)) { + /* Circuits from clients to intro points, and hidden services to + * rend points do not support ntor, because the hidden service protocol + * does not include ntor onion keys. This is also true for Tor2web clients + * and Single Onion Services. */ + return 0; + } + + if (circuit_get_cpath_len(circ) == 1) { + /* Allow for bootstrapping: when we're fetching directly from a fallback, + * authority, or bridge, we have no way of knowing its ntor onion key + * before we connect to it. So instead, we try connecting, and end up using + * CREATE_FAST. */ + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + const node_t *node = node_get_by_id( + circ->cpath->extend_info->identity_digest); + /* If we don't know the node and its descriptor, we must be bootstrapping. + */ + if (!node || !node_has_descriptor(node)) { + return 0; } } - log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop " - "circuit with at least one node that supports ntor.", - MAX_POPULATE_ATTEMPTS, - circ->build_state->desired_path_len); - return -1; + if (BUG(!path_supports_ntor)) { + /* If we're building a multi-hop path, and it's not one of the HS or + * bootstrapping exceptions, and it doesn't support ntor, something has + * gone wrong. */ + return -1; + } + + return 0; } /** Create and return a new origin circuit. Initialize its purpose and @@ -757,10 +779,13 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ) tor_assert(circ->cpath); tor_assert(circ->cpath->extend_info); - if (!circ->cpath->extend_info->onion_key) - return 1; /* our hand is forced: only a create_fast will work. */ + if (!circuit_has_usable_onion_key(circ)) { + /* We don't have ntor, and we don't have or can't use TAP, + * so our hand is forced: only a create_fast will work. */ + return 1; + } if (public_server_mode(options)) { - /* We're a server, and we know an onion key. We can choose. + /* We're a server, and we have a usable onion key. We can choose. * Prefer to blend our circuit into the other circuits we are * creating on behalf of others. */ return 0; @@ -785,30 +810,20 @@ circuit_timeout_want_to_count_circ(origin_circuit_t *circ) && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN; } -/** Return true if the ntor handshake is enabled in the configuration, or if - * it's been set to "auto" in the configuration and it's enabled in the - * consensus. */ -static int -circuits_can_use_ntor(void) -{ - const or_options_t *options = get_options(); - if (options->UseNTorHandshake != -1) - return options->UseNTorHandshake; - return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1); -} - /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b> * directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b> - * accordingly. */ + * accordingly. + * Note that TAP handshakes are only used for direct connections: + * - from Tor2web to intro points not in the client's consensus, and + * - from Single Onions to rend points not in the service's consensus. + * This is checked in onion_populate_cpath. */ static void circuit_pick_create_handshake(uint8_t *cell_type_out, uint16_t *handshake_type_out, const extend_info_t *ei) { - /* XXXX029 Remove support for deciding to use TAP. */ - if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN) && - circuits_can_use_ntor()) { + /* XXXX030 Remove support for deciding to use TAP. */ + if (extend_info_supports_ntor(ei)) { *cell_type_out = CELL_CREATE2; *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR; return; @@ -822,7 +837,11 @@ circuit_pick_create_handshake(uint8_t *cell_type_out, * directly, and set *<b>handshake_type_out</b> accordingly. Decide whether, * in extending through <b>node</b> to do so, we should use an EXTEND2 or an * EXTEND cell to do so, and set *<b>cell_type_out</b> and - * *<b>create_cell_type_out</b> accordingly. */ + * *<b>create_cell_type_out</b> accordingly. + * Note that TAP handshakes are only used for extend handshakes: + * - from clients to intro points, and + * - from hidden services to rend points. + * This is checked in onion_populate_cpath. */ static void circuit_pick_extend_handshake(uint8_t *cell_type_out, uint8_t *create_cell_type_out, @@ -833,11 +852,25 @@ circuit_pick_extend_handshake(uint8_t *cell_type_out, uint8_t t; circuit_pick_create_handshake(&t, handshake_type_out, ei); - /* XXXX029 Remove support for deciding to use TAP. */ - if (node_prev && - *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP && + /* XXXX030 Remove support for deciding to use TAP. */ + + /* It is an error to extend if there is no previous node. */ + if (BUG(node_prev == NULL)) { + *cell_type_out = RELAY_COMMAND_EXTEND; + *create_cell_type_out = CELL_CREATE; + return; + } + + /* It is an error for a node with a known version to be so old it does not + * support ntor. */ + tor_assert_nonfatal(routerstatus_version_supports_ntor(node_prev->rs, 1)); + + /* Assume relays without tor versions or routerstatuses support ntor. + * The authorities enforce ntor support, and assuming and failing is better + * than allowing a malicious node to perform a protocol downgrade to TAP. */ + if (*handshake_type_out != ONION_HANDSHAKE_TYPE_TAP && (node_has_curve25519_onion_key(node_prev) || - (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) { + (routerstatus_version_supports_ntor(node_prev->rs, 1)))) { *cell_type_out = RELAY_COMMAND_EXTEND2; *create_cell_type_out = CELL_CREATE2; } else { @@ -2058,15 +2091,18 @@ count_acceptable_nodes(smartlist_t *nodes) if (! node->is_running) // log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i); continue; + /* XXX This clause makes us count incorrectly: if AllowInvalidRouters + * allows this node in some places, then we're getting an inaccurate + * count. For now, be conservative and don't count it. But later we + * should try to be smarter. */ if (! node->is_valid) // log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i); continue; if (! node_has_descriptor(node)) continue; - /* XXX This clause makes us count incorrectly: if AllowInvalidRouters - * allows this node in some places, then we're getting an inaccurate - * count. For now, be conservative and don't count it. But later we - * should try to be smarter. */ + /* The node has a descriptor, so we can just check the ntor key directly */ + if (!node_has_curve25519_onion_key(node)) + continue; ++num; } SMARTLIST_FOREACH_END(node); @@ -2356,6 +2392,14 @@ extend_info_from_node(const node_t *node, int for_direct_connect) log_warn(LD_CIRC, "Could not choose valid address for %s", node->ri ? node->ri->nickname : node->rs->nickname); + /* Every node we connect or extend to must support ntor */ + if (!node_has_curve25519_onion_key(node)) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Attempted to create extend_info for a node that does not support " + "ntor: %s", node_describe(node)); + return NULL; + } + if (valid_addr && node->ri) return extend_info_new(node->ri->nickname, node->identity, @@ -2441,3 +2485,66 @@ extend_info_addr_is_allowed(const tor_addr_t *addr) return 0; } +/* Does ei have a valid TAP key? */ +int +extend_info_supports_tap(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid TAP keys are not NULL */ + return ei->onion_key != NULL; +} + +/* Does ei have a valid ntor key? */ +int +extend_info_supports_ntor(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid ntor keys have at least one non-zero byte */ + return !tor_mem_is_zero( + (const char*)ei->curve25519_onion_key.public_key, + CURVE25519_PUBKEY_LEN); +} + +/* Is circuit purpose allowed to use the deprecated TAP encryption protocol? + * The hidden service protocol still uses TAP for some connections, because + * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ +static int +circuit_purpose_can_use_tap_impl(uint8_t purpose) +{ + return (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND || + purpose == CIRCUIT_PURPOSE_C_INTRODUCING); +} + +/* Is circ allowed to use the deprecated TAP encryption protocol? + * The hidden service protocol still uses TAP for some connections, because + * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ +int +circuit_can_use_tap(const origin_circuit_t *circ) +{ + tor_assert(circ); + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + return (circuit_purpose_can_use_tap_impl(circ->base_.purpose) && + extend_info_supports_tap(circ->cpath->extend_info)); +} + +/* Does circ have an onion key which it's allowed to use? */ +int +circuit_has_usable_onion_key(const origin_circuit_t *circ) +{ + tor_assert(circ); + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + return (extend_info_supports_ntor(circ->cpath->extend_info) || + circuit_can_use_tap(circ)); +} + +/* Does ei have an onion key which it would prefer to use? + * Currently, we prefer ntor keys*/ +int +extend_info_has_preferred_onion_key(const extend_info_t* ei) +{ + tor_assert(ei); + return extend_info_supports_ntor(ei); +} + diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 7f5fd511a9..1244601f71 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -54,6 +54,11 @@ extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); void extend_info_free(extend_info_t *info); int extend_info_addr_is_allowed(const tor_addr_t *addr); +int extend_info_supports_tap(const extend_info_t* ei); +int extend_info_supports_ntor(const extend_info_t* ei); +int circuit_can_use_tap(const origin_circuit_t *circ); +int circuit_has_usable_onion_key(const origin_circuit_t *circ); +int extend_info_has_preferred_onion_key(const extend_info_t* ei); const node_t *build_state_get_exit_node(cpath_build_state_t *state); const char *build_state_get_exit_nickname(cpath_build_state_t *state); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 5c691644a4..3c92baa274 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -1613,7 +1613,8 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, return best; } -/** Return the number of hops in circuit's path. */ +/** Return the number of hops in circuit's path. If circ has no entries, + * or is NULL, returns 0. */ int circuit_get_cpath_len(origin_circuit_t *circ) { @@ -1629,7 +1630,8 @@ circuit_get_cpath_len(origin_circuit_t *circ) } /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there - * aren't that many hops in the list. */ + * aren't that many hops in the list. <b>hopnum</b> starts at 1. + * Returns NULL if <b>hopnum</b> is 0 or negative. */ crypt_path_t * circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum) { diff --git a/src/or/config.c b/src/or/config.c index 10002ff620..9c5514f1da 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -211,6 +211,7 @@ static config_var_t option_vars_[] = { V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), V(DataDirectoryGroupReadable, BOOL, "0"), + V(DisableOOSCheck, BOOL, "1"), V(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, "0"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), @@ -437,7 +438,7 @@ static config_var_t option_vars_[] = { V(UseEntryGuardsAsDirGuards, BOOL, "1"), V(UseGuardFraction, AUTOBOOL, "auto"), V(UseMicrodescriptors, AUTOBOOL, "auto"), - V(UseNTorHandshake, AUTOBOOL, "1"), + OBSOLETE("UseNTorHandshake"), V(User, STRING, NULL), OBSOLETE("UserspaceIOCPBuffers"), V(AuthDirSharedRandomness, BOOL, "1"), @@ -612,7 +613,6 @@ static const config_deprecation_t option_deprecation_notes_[] = { "to accidentally lose your anonymity by leaking DNS information" }, { "TLSECGroup", "The default is a nice secure choice; the other option " "is less secure." }, - { "UseNTorHandshake", "The ntor handshake should always be used." }, { "ControlListenAddress", "Use ControlPort instead." }, { "DirListenAddress", "Use DirPort instead, possibly with the " "NoAdvertise sub-option" }, @@ -1374,6 +1374,35 @@ options_act_reversible(const or_options_t *old_options, char **msg) connection_mark_for_close(conn); } }); + + if (set_conn_limit) { + /* + * If we adjusted the conn limit, recompute the OOS threshold too + * + * How many possible sockets to keep in reserve? If we have lots of + * possible sockets, keep this below a limit and set ConnLimit_high_thresh + * very close to ConnLimit_, but if ConnLimit_ is low, shrink it in + * proportion. + * + * Somewhat arbitrarily, set socks_in_reserve to 5% of ConnLimit_, but + * cap it at 64. + */ + int socks_in_reserve = options->ConnLimit_ / 20; + if (socks_in_reserve > 64) socks_in_reserve = 64; + + options->ConnLimit_high_thresh = options->ConnLimit_ - socks_in_reserve; + options->ConnLimit_low_thresh = (options->ConnLimit_ / 4) * 3; + log_info(LD_GENERAL, + "Recomputed OOS thresholds: ConnLimit %d, ConnLimit_ %d, " + "ConnLimit_high_thresh %d, ConnLimit_low_thresh %d", + options->ConnLimit, options->ConnLimit_, + options->ConnLimit_high_thresh, + options->ConnLimit_low_thresh); + + /* Give the OOS handler a chance with the new thresholds */ + connection_check_oos(get_n_open_sockets(), 0); + } + goto done; rollback: diff --git a/src/or/connection.c b/src/or/connection.c index 68e442df54..5ecd1ad7bf 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -754,9 +754,9 @@ connection_mark_for_close_(connection_t *conn, int line, const char *file) * For all other cases, use connection_mark_and_flush() instead, which * checks for or_connection_t properly, instead. See below. */ -void -connection_mark_for_close_internal_(connection_t *conn, - int line, const char *file) +MOCK_IMPL(void, +connection_mark_for_close_internal_, (connection_t *conn, + int line, const char *file)) { assert_connection_ok(conn,0); tor_assert(line); @@ -1090,6 +1090,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, int start_reading = 0; static int global_next_session_group = SESSION_GROUP_FIRST_AUTO; tor_addr_t addr; + int exhaustion = 0; if (listensockaddr->sa_family == AF_INET || listensockaddr->sa_family == AF_INET6) { @@ -1108,6 +1109,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, int e = tor_socket_errno(s); if (ERRNO_IS_RESOURCE_LIMIT(e)) { warn_too_many_conns(); + /* + * We'll call the OOS handler at the error exit, so set the + * exhaustion flag for it. + */ + exhaustion = 1; } else { log_warn(LD_NET, "Socket creation failed: %s", tor_socket_strerror(e)); @@ -1226,6 +1232,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, int e = tor_socket_errno(s); if (ERRNO_IS_RESOURCE_LIMIT(e)) { warn_too_many_conns(); + /* + * We'll call the OOS handler at the error exit, so set the + * exhaustion flag for it. + */ + exhaustion = 1; } else { log_warn(LD_NET,"Socket creation failed: %s.", strerror(e)); } @@ -1344,6 +1355,12 @@ connection_listener_new(const struct sockaddr *listensockaddr, dnsserv_configure_listener(conn); } + /* + * Normal exit; call the OOS handler since connection count just changed; + * the exhaustion flag will always be zero here though. + */ + connection_check_oos(get_n_open_sockets(), 0); + return conn; err: @@ -1352,6 +1369,9 @@ connection_listener_new(const struct sockaddr *listensockaddr, if (conn) connection_free(conn); + /* Call the OOS handler, indicate if we saw an exhaustion-related error */ + connection_check_oos(get_n_open_sockets(), exhaustion); + return NULL; } @@ -1442,21 +1462,34 @@ connection_handle_listener_read(connection_t *conn, int new_type) if (!SOCKET_OK(news)) { /* accept() error */ int e = tor_socket_errno(conn->s); if (ERRNO_IS_ACCEPT_EAGAIN(e)) { - return 0; /* they hung up before we could accept(). that's fine. */ + /* + * they hung up before we could accept(). that's fine. + * + * give the OOS handler a chance to run though + */ + connection_check_oos(get_n_open_sockets(), 0); + return 0; } else if (ERRNO_IS_RESOURCE_LIMIT(e)) { warn_too_many_conns(); + /* Exhaustion; tell the OOS handler */ + connection_check_oos(get_n_open_sockets(), 1); return 0; } /* else there was a real error. */ log_warn(LD_NET,"accept() failed: %s. Closing listener.", tor_socket_strerror(e)); connection_mark_for_close(conn); + /* Tell the OOS handler about this too */ + connection_check_oos(get_n_open_sockets(), 0); return -1; } log_debug(LD_NET, "Connection accepted on socket %d (child of fd %d).", (int)news,(int)conn->s); + /* We accepted a new conn; run OOS handler */ + connection_check_oos(get_n_open_sockets(), 0); + if (make_socket_reuseable(news) < 0) { if (tor_socket_errno(news) == EINVAL) { /* This can happen on OSX if we get a badly timed shutdown. */ @@ -1661,12 +1694,18 @@ connection_connect_sockaddr,(connection_t *conn, s = tor_open_socket_nonblocking(protocol_family, SOCK_STREAM, proto); if (! SOCKET_OK(s)) { + /* + * Early OOS handler calls; it matters if it's an exhaustion-related + * error or not. + */ *socket_error = tor_socket_errno(s); if (ERRNO_IS_RESOURCE_LIMIT(*socket_error)) { warn_too_many_conns(); + connection_check_oos(get_n_open_sockets(), 1); } else { log_warn(LD_NET,"Error creating network socket: %s", tor_socket_strerror(*socket_error)); + connection_check_oos(get_n_open_sockets(), 0); } return -1; } @@ -1676,6 +1715,13 @@ connection_connect_sockaddr,(connection_t *conn, tor_socket_strerror(errno)); } + /* + * We've got the socket open; give the OOS handler a chance to check + * against configuured maximum socket number, but tell it no exhaustion + * failure. + */ + connection_check_oos(get_n_open_sockets(), 0); + if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) { *socket_error = tor_socket_errno(s); log_warn(LD_NET,"Error binding network socket: %s", @@ -4454,6 +4500,256 @@ connection_reached_eof(connection_t *conn) } } +/** Comparator for the two-orconn case in OOS victim sort */ +static int +oos_victim_comparator_for_orconns(or_connection_t *a, or_connection_t *b) +{ + int a_circs, b_circs; + /* Fewer circuits == higher priority for OOS kill, sort earlier */ + + a_circs = connection_or_get_num_circuits(a); + b_circs = connection_or_get_num_circuits(b); + + if (a_circs < b_circs) return 1; + else if (a_circs > b_circs) return -1; + else return 0; +} + +/** Sort comparator for OOS victims; better targets sort before worse + * ones. */ +static int +oos_victim_comparator(const void **a_v, const void **b_v) +{ + connection_t *a = NULL, *b = NULL; + + /* Get connection pointers out */ + + a = (connection_t *)(*a_v); + b = (connection_t *)(*b_v); + + tor_assert(a != NULL); + tor_assert(b != NULL); + + /* + * We always prefer orconns as victims currently; we won't even see + * these non-orconn cases, but if we do, sort them after orconns. + */ + if (a->type == CONN_TYPE_OR && b->type == CONN_TYPE_OR) { + return oos_victim_comparator_for_orconns(TO_OR_CONN(a), TO_OR_CONN(b)); + } else { + /* + * One isn't an orconn; if one is, it goes first. We currently have no + * opinions about cases where neither is an orconn. + */ + if (a->type == CONN_TYPE_OR) return -1; + else if (b->type == CONN_TYPE_OR) return 1; + else return 0; + } +} + +/** Pick n victim connections for the OOS handler and return them in a + * smartlist. + */ +MOCK_IMPL(STATIC smartlist_t *, +pick_oos_victims, (int n)) +{ + smartlist_t *eligible = NULL, *victims = NULL; + smartlist_t *conns; + int conn_counts_by_type[CONN_TYPE_MAX_ + 1], i; + + /* + * Big damn assumption (someone improve this someday!): + * + * Socket exhaustion normally happens on high-volume relays, and so + * most of the connections involved are orconns. We should pick victims + * by assembling a list of all orconns, and sorting them in order of + * how much 'damage' by some metric we'd be doing by dropping them. + * + * If we move on from orconns, we should probably think about incoming + * directory connections next, or exit connections. Things we should + * probably never kill are controller connections and listeners. + * + * This function will count how many connections of different types + * exist and log it for purposes of gathering data on typical OOS + * situations to guide future improvements. + */ + + /* First, get the connection array */ + conns = get_connection_array(); + /* + * Iterate it and pick out eligible connection types, and log some stats + * along the way. + */ + eligible = smartlist_new(); + memset(conn_counts_by_type, 0, sizeof(conn_counts_by_type)); + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { + /* Bump the counter */ + tor_assert(c->type <= CONN_TYPE_MAX_); + ++(conn_counts_by_type[c->type]); + + /* Skip anything without a socket we can free */ + if (!(SOCKET_OK(c->s))) { + continue; + } + + /* Skip anything we would count as moribund */ + if (connection_is_moribund(c)) { + continue; + } + + switch (c->type) { + case CONN_TYPE_OR: + /* We've got an orconn, it's eligible to be OOSed */ + smartlist_add(eligible, c); + break; + default: + /* We don't know what to do with it, ignore it */ + break; + } + } SMARTLIST_FOREACH_END(c); + + /* Log some stats */ + if (smartlist_len(conns) > 0) { + /* At least one counter must be non-zero */ + log_info(LD_NET, "Some stats on conn types seen during OOS follow"); + for (i = CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) { + /* Did we see any? */ + if (conn_counts_by_type[i] > 0) { + log_info(LD_NET, "%s: %d conns", + conn_type_to_string(i), + conn_counts_by_type[i]); + } + } + log_info(LD_NET, "Done with OOS conn type stats"); + } + + /* Did we find more eligible targets than we want to kill? */ + if (smartlist_len(eligible) > n) { + /* Sort the list in order of target preference */ + smartlist_sort(eligible, oos_victim_comparator); + /* Pick first n as victims */ + victims = smartlist_new(); + for (i = 0; i < n; ++i) { + smartlist_add(victims, smartlist_get(eligible, i)); + } + /* Free the original list */ + smartlist_free(eligible); + } else { + /* No, we can just call them all victims */ + victims = eligible; + } + + return victims; +} + +/** Kill a list of connections for the OOS handler. */ +MOCK_IMPL(STATIC void, +kill_conn_list_for_oos, (smartlist_t *conns)) +{ + if (!conns) return; + + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) { + /* Make sure the channel layer gets told about orconns */ + if (c->type == CONN_TYPE_OR) { + connection_or_close_for_error(TO_OR_CONN(c), 1); + } else { + connection_mark_for_close(c); + } + } SMARTLIST_FOREACH_END(c); + + log_notice(LD_NET, + "OOS handler marked %d connections", + smartlist_len(conns)); +} + +/** Out-of-Sockets handler; n_socks is the current number of open + * sockets, and failed is non-zero if a socket exhaustion related + * error immediately preceded this call. This is where to do + * circuit-killing heuristics as needed. + */ +void +connection_check_oos(int n_socks, int failed) +{ + int target_n_socks = 0, moribund_socks, socks_to_kill; + smartlist_t *conns; + + /* Early exit: is OOS checking disabled? */ + if (get_options()->DisableOOSCheck) { + return; + } + + /* Sanity-check args */ + tor_assert(n_socks >= 0); + + /* + * Make some log noise; keep it at debug level since this gets a chance + * to run on every connection attempt. + */ + log_debug(LD_NET, + "Running the OOS handler (%d open sockets, %s)", + n_socks, (failed != 0) ? "exhaustion seen" : "no exhaustion"); + + /* + * Check if we're really handling an OOS condition, and if so decide how + * many sockets we want to get down to. Be sure we check if the threshold + * is distinct from zero first; it's possible for this to be called a few + * times before we've finished reading the config. + */ + if (n_socks >= get_options()->ConnLimit_high_thresh && + get_options()->ConnLimit_high_thresh != 0 && + get_options()->ConnLimit_ != 0) { + /* Try to get down to the low threshold */ + target_n_socks = get_options()->ConnLimit_low_thresh; + log_notice(LD_NET, + "Current number of sockets %d is greater than configured " + "limit %d; OOS handler trying to get down to %d", + n_socks, get_options()->ConnLimit_high_thresh, + target_n_socks); + } else if (failed) { + /* + * If we're not at the limit but we hit a socket exhaustion error, try to + * drop some (but not as aggressively as ConnLimit_low_threshold, which is + * 3/4 of ConnLimit_) + */ + target_n_socks = (n_socks * 9) / 10; + log_notice(LD_NET, + "We saw socket exhaustion at %d open sockets; OOS handler " + "trying to get down to %d", + n_socks, target_n_socks); + } + + if (target_n_socks > 0) { + /* + * It's an OOS! + * + * Count moribund sockets; it's be important that anything we decide + * to get rid of here but don't immediately close get counted as moribund + * on subsequent invocations so we don't try to kill too many things if + * connection_check_oos() gets called multiple times. + */ + moribund_socks = connection_count_moribund(); + + if (moribund_socks < n_socks - target_n_socks) { + socks_to_kill = n_socks - target_n_socks - moribund_socks; + + conns = pick_oos_victims(socks_to_kill); + if (conns) { + kill_conn_list_for_oos(conns); + log_notice(LD_NET, + "OOS handler killed %d conns", smartlist_len(conns)); + smartlist_free(conns); + } else { + log_notice(LD_NET, "OOS handler failed to pick any victim conns"); + } + } else { + log_notice(LD_NET, + "Not killing any sockets for OOS because there are %d " + "already moribund, and we only want to eliminate %d", + moribund_socks, n_socks - target_n_socks); + } + } +} + /** Log how many bytes are used by buffers of different kinds and sizes. */ void connection_dump_buffer_mem_stats(int severity) diff --git a/src/or/connection.h b/src/or/connection.h index f8e0f73246..d25e002fa4 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -34,8 +34,8 @@ void connection_about_to_close_connection(connection_t *conn); void connection_close_immediate(connection_t *conn); void connection_mark_for_close_(connection_t *conn, int line, const char *file); -void connection_mark_for_close_internal_(connection_t *conn, - int line, const char *file); +MOCK_DECL(void, connection_mark_for_close_internal_, + (connection_t *conn, int line, const char *file)); #define connection_mark_for_close(c) \ connection_mark_for_close_((c), __LINE__, SHORT_FILE__) @@ -247,6 +247,22 @@ void clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted, log_domain_mask_t domain, const char *received, const char *source); +/** Check if a connection is on the way out so the OOS handler doesn't try + * to kill more than it needs. */ +static inline int +connection_is_moribund(connection_t *conn) +{ + if (conn != NULL && + (conn->conn_array_index < 0 || + conn->marked_for_close)) { + return 1; + } else { + return 0; + } +} + +void connection_check_oos(int n_socks, int failed); + #ifdef CONNECTION_PRIVATE STATIC void connection_free_(connection_t *conn); @@ -265,6 +281,9 @@ MOCK_DECL(STATIC int,connection_connect_sockaddr, const struct sockaddr *bindaddr, socklen_t bindaddr_len, int *socket_error)); +MOCK_DECL(STATIC void, kill_conn_list_for_oos, (smartlist_t *conns)); +MOCK_DECL(STATIC smartlist_t *, pick_oos_victims, (int n)); + #endif #endif diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 1f0c4bdef5..72d8e13e90 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -394,8 +394,8 @@ connection_or_change_state(or_connection_t *conn, uint8_t state) * be an or_connection_t field, but it got moved to channel_t and we * shouldn't maintain two copies. */ -int -connection_or_get_num_circuits(or_connection_t *conn) +MOCK_IMPL(int, +connection_or_get_num_circuits, (or_connection_t *conn)) { tor_assert(conn); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index e2ec47a4f2..2e8c6066cc 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -64,7 +64,7 @@ void connection_or_init_conn_from_address(or_connection_t *conn, int connection_or_client_learned_peer_id(or_connection_t *conn, const uint8_t *peer_id); time_t connection_or_client_used(or_connection_t *conn); -int connection_or_get_num_circuits(or_connection_t *conn); +MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn)); void or_handshake_state_free(or_handshake_state_t *state); void or_handshake_state_record_cell(or_connection_t *conn, or_handshake_state_t *state, diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 64ebde6fdd..ff50ca4417 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -255,6 +255,20 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, return FP_REJECT; } + /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc, + * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha. + * But just in case a relay doesn't provide or lies about its version, or + * doesn't include an ntor key in its descriptor, check that it exists, + * and is non-zero (clients check that it's non-zero before using it). */ + if (!routerinfo_has_curve25519_onion_key(router)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s is missing an ntor curve25519 onion " + "key.", router_describe(router)); + if (msg) + *msg = "Missing ntor curve25519 onion key. Please upgrade!"; + return FP_REJECT; + } + if (router->cache_info.signing_key_cert) { /* This has an ed25519 identity key. */ if (KEYPIN_MISMATCH == diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 9748f4ae4d..ae869c9064 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -1582,7 +1582,7 @@ networkstatus_compute_consensus(smartlist_t *votes, const char *chosen_version; const char *chosen_name = NULL; int exitsummary_disagreement = 0; - int is_named = 0, is_unnamed = 0, is_running = 0; + int is_named = 0, is_unnamed = 0, is_running = 0, is_valid = 0; int is_guard = 0, is_exit = 0, is_bad_exit = 0; int naming_conflict = 0; int n_listing = 0; @@ -1733,6 +1733,8 @@ networkstatus_compute_consensus(smartlist_t *votes, is_running = 1; else if (!strcmp(fl, "BadExit")) is_bad_exit = 1; + else if (!strcmp(fl, "Valid")) + is_valid = 1; } } } SMARTLIST_FOREACH_END(fl); @@ -1742,6 +1744,12 @@ networkstatus_compute_consensus(smartlist_t *votes, if (!is_running) continue; + /* Starting with consensus method 24, we don't list servers + * that are not valid in a consensus. See Proposal 272 */ + if (!is_valid && + consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES) + continue; + /* Pick the version. */ if (smartlist_len(versions)) { sort_version_list(versions, 0); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index a1f71ce4bb..06bfe671bd 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -55,7 +55,7 @@ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 23 +#define MAX_SUPPORTED_CONSENSUS_METHOD 24 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -99,6 +99,10 @@ * value(s). */ #define MIN_METHOD_FOR_SHARED_RANDOM 23 +/** Lowest consensus method where authorities drop all nodes that don't get + * the Valid flag. */ +#define MIN_METHOD_FOR_EXCLUDING_INVALID_NODES 24 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 209aae01cf..7e25306234 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -692,7 +692,7 @@ read_bandwidth_usage(void) int res; res = unlink(fname); - if (res != 0) { + if (res != 0 && errno != ENOENT) { log_warn(LD_FS, "Failed to unlink %s: %s", fname, strerror(errno)); diff --git a/src/or/main.c b/src/or/main.c index 4dbd9a005b..03c2b7ed58 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -381,8 +381,8 @@ connection_in_array(connection_t *conn) /** Set <b>*array</b> to an array of all connections. <b>*array</b> must not * be modified. */ -smartlist_t * -get_connection_array(void) +MOCK_IMPL(smartlist_t *, +get_connection_array, (void)) { if (!connection_array) connection_array = smartlist_new(); @@ -651,6 +651,23 @@ close_closeable_connections(void) } } +/** Count moribund connections for the OOS handler */ +MOCK_IMPL(int, +connection_count_moribund, (void)) +{ + int moribund = 0; + + /* + * Count things we'll try to kill when close_closeable_connections() + * runs next. + */ + SMARTLIST_FOREACH_BEGIN(closeable_connection_lst, connection_t *, conn) { + if (SOCKET_OK(conn->s) && connection_is_moribund(conn)) ++moribund; + } SMARTLIST_FOREACH_END(conn); + + return moribund; +} + /** Libevent callback: this gets invoked when (connection_t*)<b>conn</b> has * some data to read. */ static void diff --git a/src/or/main.h b/src/or/main.h index 31a22de424..0220ae3c57 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -25,7 +25,7 @@ int connection_in_array(connection_t *conn); void add_connection_to_closeable_list(connection_t *conn); int connection_is_on_closeable_list(connection_t *conn); -smartlist_t *get_connection_array(void); +MOCK_DECL(smartlist_t *, get_connection_array, (void)); MOCK_DECL(uint64_t,get_bytes_read,(void)); MOCK_DECL(uint64_t,get_bytes_written,(void)); @@ -47,6 +47,8 @@ MOCK_DECL(void,connection_start_writing,(connection_t *conn)); void connection_stop_reading_from_linked_conn(connection_t *conn); +MOCK_DECL(int, connection_count_moribund, (void)); + void directory_all_unreachable(time_t now); void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index fe4b4562ff..72af505d19 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -2275,6 +2275,12 @@ client_would_use_router(const routerstatus_t *rs, time_t now, /* We'd drop it immediately for being too old. */ return 0; } + if (!routerstatus_version_supports_ntor(rs, 1)) { + /* We'd ignore it because it doesn't support ntor. + * If we don't know the version, download the descriptor so we can + * check if it supports ntor. */ + return 0; + } return 1; } diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 7b64cafd79..070e2e9e0d 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -1173,14 +1173,38 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out) } } +/** Return true iff <b>md</b> has a curve25519 onion key. + * Use node_has_curve25519_onion_key() instead of calling this directly. */ +static int +microdesc_has_curve25519_onion_key(const microdesc_t *md) +{ + if (!md) { + return 0; + } + + if (!md->onion_curve25519_pkey) { + return 0; + } + + if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key, + CURVE25519_PUBKEY_LEN)) { + return 0; + } + + return 1; +} + /** Return true iff <b>node</b> has a curve25519 onion key. */ int node_has_curve25519_onion_key(const node_t *node) { + if (!node) + return 0; + if (node->ri) - return node->ri->onion_curve25519_pkey != NULL; + return routerinfo_has_curve25519_onion_key(node->ri); else if (node->md) - return node->md->onion_curve25519_pkey != NULL; + return microdesc_has_curve25519_onion_key(node->md); else return 0; } diff --git a/src/or/onion.c b/src/or/onion.c index 5495074a83..8a566af766 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -11,6 +11,7 @@ **/ #include "or.h" +#include "circuitbuild.h" #include "circuitlist.h" #include "config.h" #include "cpuworker.h" @@ -438,8 +439,7 @@ onion_skin_create(int type, r = CREATE_FAST_LEN; break; case ONION_HANDSHAKE_TYPE_NTOR: - if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) + if (!extend_info_supports_ntor(node)) return -1; if (onion_skin_ntor_create((const uint8_t*)node->identity_digest, &node->curve25519_onion_key, diff --git a/src/or/or.h b/src/or/or.h index d83a921ae9..5b9b007ac1 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3701,6 +3701,10 @@ typedef struct { int ConnLimit; /**< Demanded minimum number of simultaneous connections. */ int ConnLimit_; /**< Maximum allowed number of simultaneous connections. */ + int ConnLimit_high_thresh; /**< start trying to lower socket usage if we + * have this many. */ + int ConnLimit_low_thresh; /**< try to get down to here after socket + * exhaustion. */ int RunAsDaemon; /**< If true, run in the background. (Unix only) */ int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */ smartlist_t *FirewallPorts; /**< Which ports our firewall allows @@ -4382,9 +4386,6 @@ typedef struct { char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */ - /** Autobool: should we use the ntor handshake if we can? */ - int UseNTorHandshake; - /** Fraction: */ double PathsNeededToBuildCircuits; @@ -4456,6 +4457,9 @@ typedef struct { * participate in the protocol. If on (default), a flag is added to the * vote indicating participation. */ int AuthDirSharedRandomness; + + /** If 1, we skip all OOS checks. */ + int DisableOOSCheck; } or_options_t; /** Persistent state for an onion router, as saved to disk. */ diff --git a/src/or/policies.c b/src/or/policies.c index 07f256f5cc..44a46d2fe2 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -2119,8 +2119,10 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port) if (subnet_status[i] != 0) continue; /* We already reject some part of this /8 */ tor_addr_from_ipv4h(&addr, i<<24); - if (tor_addr_is_internal(&addr, 0)) + if (tor_addr_is_internal(&addr, 0) && + !get_options()->DirAllowPrivateAddresses) { continue; /* Local or non-routable addresses */ + } if (p->policy_type == ADDR_POLICY_ACCEPT) { if (p->maskbits > 8) continue; /* Narrower than a /8. */ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3468b07561..263dd3d876 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1368,40 +1368,20 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, 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) { - const node_t *node; - extend_info_t *new_extend_info; - if (tor_digest_is_zero(intro->extend_info->identity_digest)) - node = node_get_by_hex_id(intro->extend_info->nickname); - else - node = node_get_by_id(intro->extend_info->identity_digest); - if (!node) { - log_info(LD_REND, "Unknown router with nickname '%s'; trying another.", - intro->extend_info->nickname); - smartlist_del(usable_nodes, i); - goto again; - } -#ifdef ENABLE_TOR2WEB_MODE - new_extend_info = extend_info_from_node(node, options->Tor2webMode); -#else - new_extend_info = extend_info_from_node(node, 0); -#endif - if (!new_extend_info) { - const char *alternate_reason = ""; -#ifdef ENABLE_TOR2WEB_MODE - alternate_reason = ", or we cannot connect directly to it"; -#endif - log_info(LD_REND, "We don't have a descriptor for the intro-point relay " - "'%s'%s; trying another.", - extend_info_describe(intro->extend_info), alternate_reason); - smartlist_del(usable_nodes, i); - goto again; - } else { - extend_info_free(intro->extend_info); - intro->extend_info = new_extend_info; - } - tor_assert(intro->extend_info != NULL); + if (BUG(!intro->extend_info)) { + /* This should never happen, but it isn't fatal, just try another */ + smartlist_del(usable_nodes, i); + goto again; + } + /* All version 2 HS descriptors come with a TAP onion key. + * Clients used to try to get the TAP onion key from the consensus, but this + * meant that hidden services could discover which consensus clients have. */ + if (!extend_info_supports_tap(intro->extend_info)) { + log_info(LD_REND, "The HS descriptor is missing a TAP onion key for the " + "intro-point relay '%s'; trying another.", + safe_str_client(extend_info_describe(intro->extend_info))); + smartlist_del(usable_nodes, i); + goto again; } /* Check if we should refuse to talk to this router. */ if (strict && diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 4c88f1fa5f..8d3a7d704c 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -508,7 +508,7 @@ rend_config_services(const or_options_t *options, int validate_only) if (!strcasecmp(line->key, "HiddenServiceDir")) { if (service) { /* register the one we just finished parsing */ if (validate_only) - rend_service_free(service); + rend_service_free(service); else rend_add_service(service); } @@ -3896,3 +3896,12 @@ rend_service_set_connection_addr_port(edge_connection_t *conn, return -2; } +/* Stub that should be replaced with the #17178 version of the function + * when merging. */ +int +rend_service_allow_direct_connection(const or_options_t *options) +{ + (void)options; + return 0; +} + diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 4966cb0302..1622086a99 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -131,5 +131,7 @@ void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, const char *service_id, int seconds_valid); void rend_service_desc_has_uploaded(const rend_data_t *rend_data); +int rend_service_allow_direct_connection(const or_options_t *options); + #endif diff --git a/src/or/router.c b/src/or/router.c index e9961d4594..8fa5799896 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -452,7 +452,8 @@ init_key_from_file(const char *fname, int generate, int severity, goto error; } } else { - log_info(LD_GENERAL, "No key found in \"%s\"", fname); + tor_log(severity, LD_GENERAL, "No key found in \"%s\"", fname); + goto error; } return prkey; case FN_FILE: @@ -560,7 +561,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, fname = get_datadir_fname2("keys", legacy ? "legacy_signing_key" : "authority_signing_key"); - signing_key = init_key_from_file(fname, 0, LOG_INFO, 0); + signing_key = init_key_from_file(fname, 0, LOG_ERR, 0); if (!signing_key) { log_warn(LD_DIR, "No version 3 directory key found in %s", fname); goto done; @@ -2836,6 +2837,10 @@ router_dump_router_to_string(routerinfo_t *router, (const char *)router->onion_curve25519_pkey->public_key, CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); + } else { + /* Authorities will start rejecting relays without ntor keys in 0.2.9 */ + log_err(LD_BUG, "A relay must have an ntor onion key"); + goto err; } /* Write the exit policy to the end of 's'. */ diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 1773f1d05c..74b8d1b1d3 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -2260,10 +2260,16 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid, continue; if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) continue; - /* Choose a node with an OR address that matches the firewall rules, - * if we are making a direct connection */ + /* Don't choose nodes if we are certain they can't do ntor */ + if (node->rs && !routerstatus_version_supports_ntor(node->rs, 1)) + continue; + if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) + continue; + /* Choose a node with an OR address that matches the firewall rules */ if (direct_conn && check_reach && - !fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr)) + !fascist_firewall_allows_node(node, + FIREWALL_OR_CONNECTION, + pref_addr)) continue; smartlist_add(sl, (void *)node); @@ -5497,6 +5503,45 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey, return r; } +/* Does ri have a valid ntor onion key? + * Valid ntor onion keys exist and have at least one non-zero byte. */ +int +routerinfo_has_curve25519_onion_key(const routerinfo_t *ri) +{ + if (!ri) { + return 0; + } + + if (!ri->onion_curve25519_pkey) { + return 0; + } + + if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key, + CURVE25519_PUBKEY_LEN)) { + return 0; + } + + return 1; +} + +/* Is rs running a tor version known to support ntor? + * If allow_unknown_versions is true, return true if the version is unknown. + * Otherwise, return false if the version is unknown. */ +int +routerstatus_version_supports_ntor(const routerstatus_t *rs, + int allow_unknown_versions) +{ + if (!rs) { + return allow_unknown_versions; + } + + if (!rs->version_known) { + return allow_unknown_versions; + } + + return rs->version_supports_extend2_cells; +} + /** Assert that the internal representation of <b>rl</b> is * self-consistent. */ void diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 72ab6d9bf3..47e5445e57 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -206,6 +206,9 @@ int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri, extrainfo_t *ei, signed_descriptor_t *sd, const char **msg); +int routerinfo_has_curve25519_onion_key(const routerinfo_t *ri); +int routerstatus_version_supports_ntor(const routerstatus_t *rs, + int allow_unknown_versions); void routerlist_assert_ok(const routerlist_t *rl); const char *esc_router_info(const routerinfo_t *router); diff --git a/src/or/shared_random.c b/src/or/shared_random.c index 19564f5924..e672a416be 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -578,8 +578,8 @@ commit_is_authoritative(const sr_commit_t *commit, tor_assert(commit); tor_assert(voter_key); - return !memcmp(commit->rsa_identity, voter_key, - sizeof(commit->rsa_identity)); + return fast_memeq(commit->rsa_identity, voter_key, + sizeof(commit->rsa_identity)); } /* Decide if the newly received <b>commit</b> should be kept depending on diff --git a/src/test/bench.c b/src/test/bench.c index f1cf715f30..f373019b95 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -557,7 +557,7 @@ bench_dh(void) dh_b, dh_pubkey_a, sizeof(dh_pubkey_a), secret_b, sizeof(secret_b)); tor_assert(slen_a == slen_b); - tor_assert(!memcmp(secret_a, secret_b, slen_a)); + tor_assert(fast_memeq(secret_a, secret_b, slen_a)); crypto_dh_free(dh_a); crypto_dh_free(dh_b); } @@ -595,7 +595,7 @@ bench_ecdh_impl(int nid, const char *name) NULL); tor_assert(slen_a == slen_b); - tor_assert(!memcmp(secret_a, secret_b, slen_a)); + tor_assert(fast_memeq(secret_a, secret_b, slen_a)); EC_KEY_free(dh_a); EC_KEY_free(dh_b); } diff --git a/src/test/include.am b/src/test/include.am index d0bc808877..0aff395091 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -28,9 +28,9 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ $(TESTSCRIPTS) # These flavors are run using automake's test-driver and test-network.sh -TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min bridges+hs +TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min # only run if we can ping6 ::1 (localhost) -TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min +TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-ipv6 # only run if we can find a stable (or simply another) version of tor TEST_CHUTNEY_FLAVORS_MIXED = mixed @@ -103,6 +103,7 @@ src_test_test_SOURCES = \ src/test/test_microdesc.c \ src/test/test_nodelist.c \ src/test/test_oom.c \ + src/test/test_oos.c \ src/test/test_options.c \ src/test/test_policy.c \ src/test/test_procmon.c \ diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c index 166a777747..1ad008aa50 100644 --- a/src/test/log_test_helpers.c +++ b/src/test/log_test_helpers.c @@ -4,19 +4,74 @@ #include "torlog.h" #include "log_test_helpers.h" +/** + * \file log_test_helpers.c + * \brief Code to check for expected log messages during testing. + */ + +static void mock_saving_logv(int severity, log_domain_mask_t domain, + const char *funcname, const char *suffix, + const char *format, va_list ap) + CHECK_PRINTF(5, 0); + +/** + * Smartlist of all the logs we've received since we last set up + * log capture. + */ static smartlist_t *saved_logs = NULL; +/** Boolean: should we also send messages to the test-runner? */ +static int echo_to_real_logs = 1; + +/** Record logs at this level or more severe */ +static int record_logs_at_level = LOG_ERR; + +/** + * As setup_capture_of_logs, but do not relay log messages into the main + * logging system. + * + * Avoid using this function; use setup_capture_of_logs() instead if you + * can. If you must use this function, then make sure you detect any + * unexpected log messages, and treat them as test failures. */ +int +setup_full_capture_of_logs(int new_level) +{ + int result = setup_capture_of_logs(new_level); + echo_to_real_logs = 0; + return result; +} + +/** + * Temporarily capture all the messages logged at severity <b>new_level</b> or + * higher. Return the previous log level; you'll need to pass it into + * teardown_capture_of_logs(). + * + * This function does not prevent messages from being sent to the main + * logging system. + */ int setup_capture_of_logs(int new_level) { int previous_log = log_global_min_severity_; - log_global_min_severity_ = new_level; + + /* Only change the log_global_min_severity_ if we're making things _more_ + * verbose. Otherwise we could prevent real log messages that the test- + * runner wanted. + */ + if (log_global_min_severity_ < new_level) + log_global_min_severity_ = new_level; + + record_logs_at_level = new_level; mock_clean_saved_logs(); saved_logs = smartlist_new(); MOCK(logv, mock_saving_logv); + echo_to_real_logs = 1; return previous_log; } +/** + * Undo setup_capture_of_logs(). + */ void teardown_capture_of_logs(int prev) { @@ -25,6 +80,9 @@ teardown_capture_of_logs(int prev) mock_clean_saved_logs(); } +/** + * Clear all messages in mock_saved_logs() + */ void mock_clean_saved_logs(void) { @@ -36,30 +94,58 @@ mock_clean_saved_logs(void) saved_logs = NULL; } +/** + * Return a list of all the messages captured since the last + * setup_[full_]capture_of_logs() call. Each log call is recorded as a + * mock_saved_log_entry_t. + */ const smartlist_t * mock_saved_logs(void) { return saved_logs; } +/** + * Return true iff there is a message recorded by log capture + * that is exactly equal to <b>msg</b> + */ int mock_saved_log_has_message(const char *msg) { - int has_msg = 0; if (saved_logs) { SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m, { if (msg && m->generated_msg && !strcmp(msg, m->generated_msg)) { - has_msg = 1; + return 1; } }); } - return has_msg; + return 0; } -/* Do the saved logs have any messages with severity? */ +/** + * Return true iff there is a message recorded by log capture + * that contains <b>msg</b> as a substring. + */ +int +mock_saved_log_has_message_containing(const char *msg) +{ + if (saved_logs) { + SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m, + { + if (msg && m->generated_msg && + strstr(m->generated_msg, msg)) { + return 1; + } + }); + } + + return 0; +} + +/** Return true iff the saved logs have any messages with <b>severity</b> */ int mock_saved_log_has_severity(int severity) { @@ -76,7 +162,7 @@ mock_saved_log_has_severity(int severity) return has_sev; } -/* Do the saved logs have any messages? */ +/** Return true iff the the saved logs have at lease one message */ int mock_saved_log_has_entry(void) { @@ -86,12 +172,14 @@ mock_saved_log_has_entry(void) return 0; } -void +/* Replacement for logv: record the log message, and (maybe) send it + * into the logging system again. + */ +static void mock_saving_logv(int severity, log_domain_mask_t domain, const char *funcname, const char *suffix, const char *format, va_list ap) { - (void)domain; char *buf = tor_malloc_zero(10240); int n; n = tor_vsnprintf(buf,10240,format,ap); @@ -99,6 +187,18 @@ mock_saving_logv(int severity, log_domain_mask_t domain, buf[n]='\n'; buf[n+1]='\0'; + if (echo_to_real_logs) { + tor_log(severity, domain|LD_NO_MOCK, "%s", buf); + } + + if (severity > record_logs_at_level) { + tor_free(buf); + return; + } + + if (!saved_logs) + saved_logs = smartlist_new(); + mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t)); e->severity = severity; e->funcname = funcname; @@ -107,8 +207,6 @@ mock_saving_logv(int severity, log_domain_mask_t domain, e->generated_msg = tor_strdup(buf); tor_free(buf); - if (!saved_logs) - saved_logs = smartlist_new(); smartlist_add(saved_logs, e); } diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index 1966f170fb..f33ee67a90 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -6,25 +6,24 @@ #ifndef TOR_LOG_TEST_HELPERS_H #define TOR_LOG_TEST_HELPERS_H +/** An element of mock_saved_logs(); records the log element that we + * received. */ typedef struct mock_saved_log_entry_t { int severity; const char *funcname; const char *suffix; const char *format; char *generated_msg; - struct mock_saved_log_entry_t *next; } mock_saved_log_entry_t; -void mock_saving_logv(int severity, log_domain_mask_t domain, - const char *funcname, const char *suffix, - const char *format, va_list ap) - CHECK_PRINTF(5, 0); void mock_clean_saved_logs(void); const smartlist_t *mock_saved_logs(void); int setup_capture_of_logs(int new_level); +int setup_full_capture_of_logs(int new_level); void teardown_capture_of_logs(int prev); int mock_saved_log_has_message(const char *msg); +int mock_saved_log_has_message_containing(const char *msg); int mock_saved_log_has_severity(int severity); int mock_saved_log_has_entry(void); @@ -32,6 +31,17 @@ int mock_saved_log_has_entry(void); tt_assert_msg(mock_saved_log_has_message(str), \ "expected log to contain " # str); +#define expect_log_msg_containing(str) \ + tt_assert_msg(mock_saved_log_has_message_containing(str), \ + "expected log to contain " # str); + +#define expect_single_log_msg_containing(str) \ + do { \ + tt_assert_msg(mock_saved_log_has_message_containing(str), \ + "expected log to contain " # str); \ + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); \ + } while (0); + #define expect_no_log_msg(str) \ tt_assert_msg(!mock_saved_log_has_message(str), \ "expected log to not contain " # str); diff --git a/src/test/sr_commit_calc_ref.py b/src/test/sr_commit_calc_ref.py new file mode 100644 index 0000000000..45e629cfb0 --- /dev/null +++ b/src/test/sr_commit_calc_ref.py @@ -0,0 +1,51 @@ +# This is a reference implementation of the COMMIT/REVEAL calculation for +# prop250. We use it to generate a test vector for the test_encoding() +# unittest. +# +# Here is the computation formula: +# +# H = SHA3-256 +# TIMESTAMP = 8 bytes network-endian value +# RAND = H(32 bytes of random) +# +# REVEAL = base64-encode( TIMESTAMP || RAND ) +# COMMIT = base64-encode( TIMESTAMP || H(REVEAL) ) +# + +import sys +import hashlib +import struct +import base64 + +# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires +# the pysha3 package (pip install pysha3). +if sys.version_info < (3, 6): + import sha3 + +# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0 +# used the old Keccak implementation. During the finalization of SHA3, NIST +# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function +# stayed the same. pysha3 1.0 provides the previous Keccak hash, too. +TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51" +if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): + print("pysha3 version is < 1.0. Please install from:") + print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3") + sys.exit(1) + +# TIMESTAMP +ts = 1454333590 +# RAND +data = 'A' * 32 # Yes very very random, NIST grade :). +rand = hashlib.sha3_256(data) + +reveal = struct.pack('!Q', ts) + rand.digest() +b64_reveal = base64.b64encode(reveal) +print("REVEAL: %s" % (b64_reveal)) + +# Yes we do hash the _encoded_ reveal here that is H(REVEAL) +hashed_reveal = hashlib.sha3_256(b64_reveal) +commit = struct.pack('!Q', ts) + hashed_reveal.digest() +print("COMMIT: %s" % (base64.b64encode(commit))) + +# REVEAL: AAAAAFavXpZJxbwTupvaJCTeIUCQmOPxAMblc7ChL5H2nZKuGchdaA== +# COMMIT: AAAAAFavXpbkBMzMQG7aNoaGLFNpm2Wkk1ozXhuWWqL//GynltxVAg== diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index c28d5054a2..2d40283fb1 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -5,6 +5,7 @@ #include "crypto.h" #include "compat.h" +#include "util.h" static unsigned fill_a_buffer_memset(void) __attribute__((noinline)); static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline)); @@ -98,29 +99,29 @@ static char *heap_buf = NULL; static unsigned fill_heap_buffer_memset(void) { - char *buf = heap_buf = malloc(BUF_LEN); + char *buf = heap_buf = raw_malloc(BUF_LEN); FILL_BUFFER_IMPL() memset(buf, 0, BUF_LEN); - free(buf); + raw_free(buf); return sum; } static unsigned fill_heap_buffer_memwipe(void) { - char *buf = heap_buf = malloc(BUF_LEN); + char *buf = heap_buf = raw_malloc(BUF_LEN); FILL_BUFFER_IMPL() memwipe(buf, 0, BUF_LEN); - free(buf); + raw_free(buf); return sum; } static unsigned fill_heap_buffer_nothing(void) { - char *buf = heap_buf = malloc(BUF_LEN); + char *buf = heap_buf = raw_malloc(BUF_LEN); FILL_BUFFER_IMPL() - free(buf); + raw_free(buf); return sum; } diff --git a/src/test/test.c b/src/test/test.c index f8610168f6..2f10c7e90b 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1210,6 +1210,7 @@ struct testgroup_t testgroups[] = { { "link-handshake/", link_handshake_tests }, { "nodelist/", nodelist_tests }, { "oom/", oom_tests }, + { "oos/", oos_tests }, { "options/", options_tests }, { "policy/" , policy_tests }, { "procmon/", procmon_tests }, diff --git a/src/test/test.h b/src/test/test.h index 6744d255f1..c0643e154d 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -202,6 +202,7 @@ extern struct testcase_t logging_tests[]; extern struct testcase_t microdesc_tests[]; extern struct testcase_t nodelist_tests[]; extern struct testcase_t oom_tests[]; +extern struct testcase_t oos_tests[]; extern struct testcase_t options_tests[]; extern struct testcase_t policy_tests[]; extern struct testcase_t procmon_tests[]; diff --git a/src/test/test_addr.c b/src/test/test_addr.c index dcecb0b7dc..c8a9e6d384 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -81,7 +81,7 @@ test_addr_basic(void *arg) #define test_op_ip6_(a,op,b,e1,e2) \ STMT_BEGIN \ tt_assert_test_fmt_type(a,b,e1" "#op" "e2,struct in6_addr*, \ - (memcmp(val1_->s6_addr, val2_->s6_addr, 16) op 0), \ + (fast_memcmp(val1_->s6_addr, val2_->s6_addr, 16) op 0), \ char *, "%s", \ { char *cp; \ cp = print_ = tor_malloc(64); \ @@ -1037,17 +1037,17 @@ test_addr_make_null(void *data) (void) data; /* Ensure that before tor_addr_make_null, addr != 0's */ memset(addr, 1, sizeof(*addr)); - tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_NE, 0); + tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_NE, 0); /* Test with AF == AF_INET */ zeros->family = AF_INET; tor_addr_make_null(addr, AF_INET); - tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); + tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), OP_EQ, "0.0.0.0"); /* Test with AF == AF_INET6 */ memset(addr, 1, sizeof(*addr)); zeros->family = AF_INET6; tor_addr_make_null(addr, AF_INET6); - tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); + tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0); tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), OP_EQ, "::"); done: tor_free(addr); diff --git a/src/test/test_address.c b/src/test/test_address.c index b4638f0702..e984beab46 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -824,9 +824,12 @@ test_address_get_if_addrs6_list_no_internal(void *arg) (void)arg; /* We might drop a log_err */ - int prev_level = setup_capture_of_logs(LOG_ERR); + int prev_level = setup_full_capture_of_logs(LOG_ERR); results = get_interface_address6_list(LOG_ERR, AF_INET6, 0); tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1); + if (smartlist_len(mock_saved_logs()) == 1) { + expect_log_msg_containing("connect() failed"); + } teardown_capture_of_logs(prev_level); tt_assert(results != NULL); diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c index f13eb81124..5e14be5b33 100644 --- a/src/test/test_compat_libevent.c +++ b/src/test/test_compat_libevent.c @@ -20,31 +20,36 @@ static void test_compat_libevent_logging_callback(void *ignored) { (void)ignored; - int previous_log = setup_capture_of_logs(LOG_DEBUG); + int previous_log = setup_full_capture_of_logs(LOG_DEBUG); libevent_logging_callback(_EVENT_LOG_DEBUG, "hello world"); expect_log_msg("Message from libevent: hello world\n"); expect_log_severity(LOG_DEBUG); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_MSG, "hello world another time"); expect_log_msg("Message from libevent: hello world another time\n"); expect_log_severity(LOG_INFO); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_WARN, "hello world a third time"); expect_log_msg("Warning from libevent: hello world a third time\n"); expect_log_severity(LOG_WARN); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_ERR, "hello world a fourth time"); expect_log_msg("Error from libevent: hello world a fourth time\n"); expect_log_severity(LOG_ERR); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(42, "hello world a fifth time"); expect_log_msg("Message [42] from libevent: hello world a fifth time\n"); expect_log_severity(LOG_WARN); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_DEBUG, @@ -75,21 +80,26 @@ test_compat_libevent_logging_callback(void *ignored) "012345678901234567890123456789" "012345678901234567890123456789\n"); expect_log_severity(LOG_DEBUG); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(42, "xxx\n"); expect_log_msg("Message [42] from libevent: xxx\n"); expect_log_severity(LOG_WARN); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); suppress_libevent_log_msg("something"); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_MSG, "hello there"); expect_log_msg("Message from libevent: hello there\n"); expect_log_severity(LOG_INFO); + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); mock_clean_saved_logs(); libevent_logging_callback(_EVENT_LOG_MSG, "hello there something else"); expect_no_log_msg("hello there something else"); + if (mock_saved_logs()) + tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 0); // No way of verifying the result of this, it seems =/ configure_libevent_logging(); diff --git a/src/test/test_config.c b/src/test/test_config.c index 619477ca7d..80a172789b 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -3832,6 +3832,8 @@ test_config_parse_port_config__listenaddress(void *data) tt_int_op(ret, OP_EQ, 0); // Test warning nonlocal other + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); ret = parse_port_config(slout, config_port2, config_listen_address, "DNS", 0, NULL, 0, CL_PORT_WARN_NONLOCAL); tt_int_op(ret, OP_EQ, 0); diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 542512bd44..9cae1e8dd8 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -2258,7 +2258,7 @@ test_crypto_ed25519_simple(void *arg) tt_int_op(0, OP_EQ, ed25519_sign(&manual_sig, (uint8_t *)prefixed_msg, strlen(prefixed_msg), &kp1)); tor_free(prefixed_msg); - tt_assert(!memcmp(sig1.sig, manual_sig.sig, sizeof(sig1.sig))); + tt_assert(fast_memeq(sig1.sig, manual_sig.sig, sizeof(sig1.sig))); /* Test that prefixed checksig verifies it properly. */ tt_int_op(0, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len, diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 51a5ecad76..bfd89a917a 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -116,6 +116,7 @@ test_dir_formats(void *arg) const addr_policy_t *p; time_t now = time(NULL); port_cfg_t orport, dirport; + char cert_buf[256]; (void)arg; pk1 = pk_generate(0); @@ -135,6 +136,11 @@ test_dir_formats(void *arg) tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::"); r1->ipv6_orport = 9999; r1->onion_pkey = crypto_pk_dup_key(pk1); + /* Fake just enough of an ntor key to get by */ + curve25519_keypair_t r1_onion_keypair; + curve25519_keypair_generate(&r1_onion_keypair, 0); + r1->onion_curve25519_pkey = tor_memdup(&r1_onion_keypair.pubkey, + sizeof(curve25519_public_key_t)); r1->identity_pkey = crypto_pk_dup_key(pk2); r1->bandwidthrate = 1000; r1->bandwidthburst = 5000; @@ -167,11 +173,6 @@ test_dir_formats(void *arg) &kp2.pubkey, now, 86400, CERT_FLAG_INCLUDE_SIGNING_KEY); - char cert_buf[256]; - base64_encode(cert_buf, sizeof(cert_buf), - (const char*)r2->cache_info.signing_key_cert->encoded, - r2->cache_info.signing_key_cert->encoded_len, - BASE64_ENCODE_MULTILINE); r2->platform = tor_strdup(platform); r2->cache_info.published_on = 5; r2->or_port = 9005; @@ -247,6 +248,11 @@ test_dir_formats(void *arg) strlcat(buf2, "hidden-service-dir\n", sizeof(buf2)); strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n", sizeof(buf2)); + strlcat(buf2, "ntor-onion-key ", sizeof(buf2)); + base64_encode(cert_buf, sizeof(cert_buf), + (const char*)r1_onion_keypair.pubkey.public_key, 32, + BASE64_ENCODE_MULTILINE); + strlcat(buf2, cert_buf, sizeof(buf2)); strlcat(buf2, "reject *:*\n", sizeof(buf2)); strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2)); buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same @@ -276,6 +282,10 @@ test_dir_formats(void *arg) "router Fred 10.3.2.1 9005 0 0\n" "identity-ed25519\n" "-----BEGIN ED25519 CERT-----\n", sizeof(buf2)); + base64_encode(cert_buf, sizeof(cert_buf), + (const char*)r2->cache_info.signing_key_cert->encoded, + r2->cache_info.signing_key_cert->encoded_len, + BASE64_ENCODE_MULTILINE); strlcat(buf2, cert_buf, sizeof(buf2)); strlcat(buf2, "-----END ED25519 CERT-----\n", sizeof(buf2)); strlcat(buf2, "master-key-ed25519 ", sizeof(buf2)); @@ -2042,9 +2052,9 @@ test_a_networkstatus( tt_int_op(4,OP_EQ, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/ /* The voter id digests should be in this order. */ - tt_assert(memcmp(cert2->cache_info.identity_digest, + tt_assert(fast_memcmp(cert2->cache_info.identity_digest, cert1->cache_info.identity_digest,DIGEST_LEN)<0); - tt_assert(memcmp(cert1->cache_info.identity_digest, + tt_assert(fast_memcmp(cert1->cache_info.identity_digest, cert3->cache_info.identity_digest,DIGEST_LEN)<0); test_same_voter(smartlist_get(con->voters, 1), smartlist_get(v2->voters, 0)); diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index 4038783459..e1fd14bc5f 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -15,6 +15,7 @@ #include "scheduler.h" #include "test.h" +#include "log_test_helpers.h" static var_cell_t *mock_got_var_cell = NULL; @@ -173,6 +174,8 @@ test_link_handshake_certs_ok(void *arg) UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(connection_or_send_netinfo); + memset(c1->identity_digest, 0, sizeof(c1->identity_digest)); + memset(c2->identity_digest, 0, sizeof(c2->identity_digest)); connection_free_(TO_CONN(c1)); connection_free_(TO_CONN(c2)); tor_free(cell1); @@ -209,6 +212,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) if (d) { tor_free(d->cell); certs_cell_free(d->ccell); + connection_or_remove_from_identity_map(d->c); connection_free_(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); @@ -332,30 +336,51 @@ test_link_handshake_recv_certs_ok_server(void *arg) test_link_handshake_recv_certs_ ## name(void *arg) \ { \ certs_data_t *d = arg; \ + const char *require_failure_message = NULL; \ + const int prev_level = setup_capture_of_logs(LOG_INFO); \ { code ; } \ channel_tls_process_certs_cell(d->cell, d->chan); \ tt_int_op(1, ==, mock_close_called); \ tt_int_op(0, ==, mock_send_authenticate_called); \ tt_int_op(0, ==, mock_send_netinfo_called); \ + if (require_failure_message) { \ + tt_assert(mock_saved_log_has_message_containing( \ + require_failure_message)); \ + } \ done: \ - ; \ + teardown_capture_of_logs(prev_level); \ } -CERTS_FAIL(badstate, d->c->base_.state = OR_CONN_STATE_CONNECTING) -CERTS_FAIL(badproto, d->c->link_proto = 2) -CERTS_FAIL(duplicate, d->c->handshake_state->received_certs_cell = 1) +CERTS_FAIL(badstate, + require_failure_message = "We're not doing a v3 handshake!"; + d->c->base_.state = OR_CONN_STATE_CONNECTING;) +CERTS_FAIL(badproto, + require_failure_message = "not using link protocol >= 3"; + d->c->link_proto = 2) +CERTS_FAIL(duplicate, + require_failure_message = "We already got one"; + d->c->handshake_state->received_certs_cell = 1) CERTS_FAIL(already_authenticated, + require_failure_message = "We're already authenticated!"; d->c->handshake_state->authenticated = 1) -CERTS_FAIL(empty, d->cell->payload_len = 0) -CERTS_FAIL(bad_circid, d->cell->circ_id = 1) -CERTS_FAIL(truncated_1, d->cell->payload[0] = 5) +CERTS_FAIL(empty, + require_failure_message = "It had no body"; + d->cell->payload_len = 0) +CERTS_FAIL(bad_circid, + require_failure_message = "It had a nonzero circuit ID"; + d->cell->circ_id = 1) +CERTS_FAIL(truncated_1, + require_failure_message = "It couldn't be parsed"; + d->cell->payload[0] = 5) CERTS_FAIL(truncated_2, { + require_failure_message = "It couldn't be parsed"; d->cell->payload_len = 4; memcpy(d->cell->payload, "\x01\x01\x00\x05", 4); }) CERTS_FAIL(truncated_3, { + require_failure_message = "It couldn't be parsed"; d->cell->payload_len = 7; memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7); }) @@ -367,30 +392,35 @@ CERTS_FAIL(truncated_3, CERTS_FAIL(not_x509, { + require_failure_message = "Received undecodable certificate"; certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 0), 3); certs_cell_get_certs(d->ccell, 0)->cert_len = 3; REENCODE(); }) CERTS_FAIL(both_link, { + require_failure_message = "Duplicate x509 certificate"; certs_cell_get_certs(d->ccell, 0)->cert_type = 1; certs_cell_get_certs(d->ccell, 1)->cert_type = 1; REENCODE(); }) CERTS_FAIL(both_id_rsa, { + require_failure_message = "Duplicate x509 certificate"; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 2; REENCODE(); }) CERTS_FAIL(both_auth, { + require_failure_message = "Duplicate x509 certificate"; certs_cell_get_certs(d->ccell, 0)->cert_type = 3; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); }) CERTS_FAIL(wrong_labels_1, { + require_failure_message = "The link certificate was not valid"; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 1; REENCODE(); @@ -401,6 +431,7 @@ CERTS_FAIL(wrong_labels_2, const tor_x509_cert_t *b; const uint8_t *enca; size_t lena; + require_failure_message = "The link certificate was not valid"; tor_tls_get_my_certs(1, &a, &b); tor_x509_cert_get_der(a, &enca, &lena); certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 1), lena); @@ -411,16 +442,20 @@ CERTS_FAIL(wrong_labels_2, }) CERTS_FAIL(wrong_labels_3, { + require_failure_message = "The certs we wanted were missing"; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); }) CERTS_FAIL(server_missing_certs, { + require_failure_message = "The certs we wanted were missing"; d->c->handshake_state->started_here = 0; }) CERTS_FAIL(server_wrong_labels_1, { + require_failure_message = + "The authentication certificate was not valid"; d->c->handshake_state->started_here = 0; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; @@ -579,30 +614,47 @@ test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg) test_link_handshake_recv_authchallenge_ ## name(void *arg) \ { \ authchallenge_data_t *d = arg; \ + const char *require_failure_message = NULL; \ + const int prev_level = setup_capture_of_logs(LOG_INFO); \ { code ; } \ channel_tls_process_auth_challenge_cell(d->cell, d->chan); \ tt_int_op(1, ==, mock_close_called); \ tt_int_op(0, ==, mock_send_authenticate_called); \ tt_int_op(0, ==, mock_send_netinfo_called); \ + if (require_failure_message) { \ + tt_assert(mock_saved_log_has_message_containing( \ + require_failure_message)); \ + } \ done: \ - ; \ + teardown_capture_of_logs(prev_level); \ } AUTHCHALLENGE_FAIL(badstate, + require_failure_message = "We're not currently doing a " + "v3 handshake"; d->c->base_.state = OR_CONN_STATE_CONNECTING) AUTHCHALLENGE_FAIL(badproto, + require_failure_message = "not using link protocol >= 3"; d->c->link_proto = 2) AUTHCHALLENGE_FAIL(as_server, + require_failure_message = "We didn't originate this " + "connection"; d->c->handshake_state->started_here = 0;) AUTHCHALLENGE_FAIL(duplicate, + require_failure_message = "We already received one"; d->c->handshake_state->received_auth_challenge = 1) AUTHCHALLENGE_FAIL(nocerts, + require_failure_message = "We haven't gotten a CERTS " + "cell yet"; d->c->handshake_state->received_certs_cell = 0) AUTHCHALLENGE_FAIL(tooshort, + require_failure_message = "It was not well-formed"; d->cell->payload_len = 33) AUTHCHALLENGE_FAIL(truncated, + require_failure_message = "It was not well-formed"; d->cell->payload_len = 34) AUTHCHALLENGE_FAIL(nonzero_circid, + require_failure_message = "It had a nonzero circuit ID"; d->cell->circ_id = 1337) static tor_x509_cert_t *mock_peer_cert = NULL; @@ -650,6 +702,8 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) authenticate_data_t *d = arg; if (d) { tor_free(d->cell); + connection_or_remove_from_identity_map(d->c1); + connection_or_remove_from_identity_map(d->c2); connection_free_(TO_CONN(d->c1)); connection_free_(TO_CONN(d->c2)); circuitmux_free(d->chan2->base_.cmux); @@ -677,6 +731,8 @@ authenticate_data_setup(const struct testcase_t *test) MOCK(channel_set_circid_type, mock_set_circid_type); d->c1 = or_connection_new(CONN_TYPE_OR, AF_INET); d->c2 = or_connection_new(CONN_TYPE_OR, AF_INET); + tor_addr_from_ipv4h(&d->c1->base_.addr, 0x01020304); + tor_addr_from_ipv4h(&d->c2->base_.addr, 0x05060708); d->key1 = pk_generate(2); d->key2 = pk_generate(3); @@ -798,57 +854,84 @@ test_link_handshake_auth_cell(void *arg) test_link_handshake_auth_ ## name(void *arg) \ { \ authenticate_data_t *d = arg; \ + const char *require_failure_message = NULL; \ + const int prev_level = setup_capture_of_logs(LOG_INFO); \ { code ; } \ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \ channel_tls_process_authenticate_cell(d->cell, d->chan2); \ tt_int_op(mock_close_called, ==, 1); \ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \ - done: \ - ; \ + if (require_failure_message) { \ + tt_assert(mock_saved_log_has_message_containing( \ + require_failure_message)); \ + } \ + done: \ + teardown_capture_of_logs(prev_level); \ } AUTHENTICATE_FAIL(badstate, + require_failure_message = "We're not doing a v3 handshake"; d->c2->base_.state = OR_CONN_STATE_CONNECTING) AUTHENTICATE_FAIL(badproto, + require_failure_message = "not using link protocol >= 3"; d->c2->link_proto = 2) AUTHENTICATE_FAIL(atclient, + require_failure_message = "We originated this connection"; d->c2->handshake_state->started_here = 1) AUTHENTICATE_FAIL(duplicate, + require_failure_message = "We already got one"; d->c2->handshake_state->received_authenticate = 1) static void test_link_handshake_auth_already_authenticated(void *arg) { authenticate_data_t *d = arg; + const int prev_level = setup_capture_of_logs(LOG_INFO); d->c2->handshake_state->authenticated = 1; channel_tls_process_authenticate_cell(d->cell, d->chan2); tt_int_op(mock_close_called, ==, 1); tt_int_op(d->c2->handshake_state->authenticated, ==, 1); + expect_log_msg_containing("The peer is already authenticated"); done: - ; + teardown_capture_of_logs(prev_level); } + AUTHENTICATE_FAIL(nocerts, + require_failure_message = "We never got a certs cell"; d->c2->handshake_state->received_certs_cell = 0) AUTHENTICATE_FAIL(noidcert, + require_failure_message = "We never got an identity " + "certificate"; tor_x509_cert_free(d->c2->handshake_state->id_cert); d->c2->handshake_state->id_cert = NULL) AUTHENTICATE_FAIL(noauthcert, + require_failure_message = "We never got an authentication " + "certificate"; tor_x509_cert_free(d->c2->handshake_state->auth_cert); d->c2->handshake_state->auth_cert = NULL) AUTHENTICATE_FAIL(tooshort, + require_failure_message = "Cell was way too short"; d->cell->payload_len = 3) AUTHENTICATE_FAIL(badtype, + require_failure_message = "Authenticator type was not " + "recognized"; d->cell->payload[0] = 0xff) AUTHENTICATE_FAIL(truncated_1, + require_failure_message = "Authenticator was truncated"; d->cell->payload[2]++) AUTHENTICATE_FAIL(truncated_2, + require_failure_message = "Authenticator was truncated"; d->cell->payload[3]++) AUTHENTICATE_FAIL(tooshort_1, + require_failure_message = "Authenticator was too short"; tt_int_op(d->cell->payload_len, >=, 260); d->cell->payload[2] -= 1; d->cell->payload_len -= 256;) AUTHENTICATE_FAIL(badcontent, + require_failure_message = "Some field in the AUTHENTICATE " + "cell body was not as expected"; d->cell->payload[10] ^= 0xff) AUTHENTICATE_FAIL(badsig_1, + require_failure_message = "Signature wasn't valid"; d->cell->payload[d->cell->payload_len - 5] ^= 0xff) #define TEST(name, flags) \ diff --git a/src/test/test_oos.c b/src/test/test_oos.c new file mode 100644 index 0000000000..db06625116 --- /dev/null +++ b/src/test/test_oos.c @@ -0,0 +1,456 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/* Unit tests for OOS handler */ + +#define CONNECTION_PRIVATE + +#include "or.h" +#include "config.h" +#include "connection.h" +#include "connection_or.h" +#include "main.h" +#include "test.h" + +static or_options_t mock_options; + +static void +reset_options_mock(void) +{ + memset(&mock_options, 0, sizeof(or_options_t)); +} + +static const or_options_t * +mock_get_options(void) +{ + return &mock_options; +} + +static int moribund_calls = 0; +static int moribund_conns = 0; + +static int +mock_connection_count_moribund(void) +{ + ++moribund_calls; + + return moribund_conns; +} + +/* + * For unit test purposes it's sufficient to tell that + * kill_conn_list_for_oos() was called with an approximately + * sane argument; it's just the thing we returned from the + * mock for pick_oos_victims(). + */ + +static int kill_conn_list_calls = 0; +static int kill_conn_list_killed = 0; + +static void +kill_conn_list_mock(smartlist_t *conns) +{ + ++kill_conn_list_calls; + + tt_assert(conns != NULL); + + kill_conn_list_killed += smartlist_len(conns); + + done: + return; +} + +static int pick_oos_mock_calls = 0; +static int pick_oos_mock_fail = 0; +static int pick_oos_mock_last_n = 0; + +static smartlist_t * +pick_oos_victims_mock(int n) +{ + smartlist_t *l = NULL; + int i; + + ++pick_oos_mock_calls; + + tt_int_op(n, OP_GT, 0); + + if (!pick_oos_mock_fail) { + /* + * connection_check_oos() just passes the list onto + * kill_conn_list_for_oos(); we don't need to simulate + * its content for this mock, just its existence, but + * we do need to check the parameter. + */ + l = smartlist_new(); + for (i = 0; i < n; ++i) smartlist_add(l, NULL); + } else { + l = NULL; + } + + pick_oos_mock_last_n = n; + + done: + return l; +} + +/** Unit test for the logic in connection_check_oos(), which is concerned + * with comparing thresholds and connection counts to decide if an OOS has + * occurred and if so, how many connections to try to kill, and then using + * pick_oos_victims() and kill_conn_list_for_oos() to carry out its grim + * duty. + */ +static void +test_oos_connection_check_oos(void *arg) +{ + (void)arg; + + /* Set up mocks */ + reset_options_mock(); + /* OOS handling is only sensitive to these fields */ + mock_options.ConnLimit = 32; + mock_options.ConnLimit_ = 64; + mock_options.ConnLimit_high_thresh = 60; + mock_options.ConnLimit_low_thresh = 50; + MOCK(get_options, mock_get_options); + moribund_calls = 0; + moribund_conns = 0; + MOCK(connection_count_moribund, mock_connection_count_moribund); + kill_conn_list_calls = 0; + kill_conn_list_killed = 0; + MOCK(kill_conn_list_for_oos, kill_conn_list_mock); + pick_oos_mock_calls = 0; + pick_oos_mock_fail = 0; + MOCK(pick_oos_victims, pick_oos_victims_mock); + + /* No OOS case */ + connection_check_oos(50, 0); + tt_int_op(moribund_calls, OP_EQ, 0); + tt_int_op(pick_oos_mock_calls, OP_EQ, 0); + tt_int_op(kill_conn_list_calls, OP_EQ, 0); + + /* OOS from socket count, nothing moribund */ + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 1); + tt_int_op(pick_oos_mock_calls, OP_EQ, 1); + /* 12 == 62 - ConnLimit_low_thresh */ + tt_int_op(pick_oos_mock_last_n, OP_EQ, 12); + tt_int_op(kill_conn_list_calls, OP_EQ, 1); + tt_int_op(kill_conn_list_killed, OP_EQ, 12); + + /* OOS from socket count, some are moribund */ + kill_conn_list_killed = 0; + moribund_conns = 5; + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 2); + tt_int_op(pick_oos_mock_calls, OP_EQ, 2); + /* 7 == 62 - ConnLimit_low_thresh - moribund_conns */ + tt_int_op(pick_oos_mock_last_n, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + tt_int_op(kill_conn_list_killed, OP_EQ, 7); + + /* OOS from socket count, but pick fails */ + kill_conn_list_killed = 0; + moribund_conns = 0; + pick_oos_mock_fail = 1; + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 3); + tt_int_op(pick_oos_mock_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + tt_int_op(kill_conn_list_killed, OP_EQ, 0); + pick_oos_mock_fail = 0; + + /* + * OOS from socket count with so many moribund conns + * we have none to kill. + */ + kill_conn_list_killed = 0; + moribund_conns = 15; + connection_check_oos(62, 0); + tt_int_op(moribund_calls, OP_EQ, 4); + tt_int_op(pick_oos_mock_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_calls, OP_EQ, 2); + + /* + * OOS from socket exhaustion; OOS handler will try to + * kill 1/10 (5) of the connections. + */ + kill_conn_list_killed = 0; + moribund_conns = 0; + connection_check_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 5); + tt_int_op(pick_oos_mock_calls, OP_EQ, 4); + tt_int_op(kill_conn_list_calls, OP_EQ, 3); + tt_int_op(kill_conn_list_killed, OP_EQ, 5); + + /* OOS from socket exhaustion with moribund conns */ + kill_conn_list_killed = 0; + moribund_conns = 2; + connection_check_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 6); + tt_int_op(pick_oos_mock_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_calls, OP_EQ, 4); + tt_int_op(kill_conn_list_killed, OP_EQ, 3); + + /* OOS from socket exhaustion with many moribund conns */ + kill_conn_list_killed = 0; + moribund_conns = 7; + connection_check_oos(50, 1); + tt_int_op(moribund_calls, OP_EQ, 7); + tt_int_op(pick_oos_mock_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_calls, OP_EQ, 4); + + /* OOS with both socket exhaustion and above-threshold */ + kill_conn_list_killed = 0; + moribund_conns = 0; + connection_check_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 8); + tt_int_op(pick_oos_mock_calls, OP_EQ, 6); + tt_int_op(kill_conn_list_calls, OP_EQ, 5); + tt_int_op(kill_conn_list_killed, OP_EQ, 12); + + /* + * OOS with both socket exhaustion and above-threshold with some + * moribund conns + */ + kill_conn_list_killed = 0; + moribund_conns = 5; + connection_check_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 9); + tt_int_op(pick_oos_mock_calls, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 6); + tt_int_op(kill_conn_list_killed, OP_EQ, 7); + + /* + * OOS with both socket exhaustion and above-threshold with many + * moribund conns + */ + kill_conn_list_killed = 0; + moribund_conns = 15; + connection_check_oos(62, 1); + tt_int_op(moribund_calls, OP_EQ, 10); + tt_int_op(pick_oos_mock_calls, OP_EQ, 7); + tt_int_op(kill_conn_list_calls, OP_EQ, 6); + + done: + + UNMOCK(pick_oos_victims); + UNMOCK(kill_conn_list_for_oos); + UNMOCK(connection_count_moribund); + UNMOCK(get_options); + + return; +} + +static int cfe_calls = 0; + +static void +close_for_error_mock(or_connection_t *orconn, int flush) +{ + (void)flush; + + tt_assert(orconn != NULL); + ++cfe_calls; + + done: + return; +} + +static int mark_calls = 0; + +static void +mark_for_close_oos_mock(connection_t *conn, + int line, const char *file) +{ + (void)line; + (void)file; + + tt_assert(conn != NULL); + ++mark_calls; + + done: + return; +} + +static void +test_oos_kill_conn_list(void *arg) +{ + connection_t *c1, *c2; + or_connection_t *or_c1 = NULL; + dir_connection_t *dir_c2 = NULL; + smartlist_t *l = NULL; + (void)arg; + + /* Set up mocks */ + mark_calls = 0; + MOCK(connection_mark_for_close_internal_, mark_for_close_oos_mock); + cfe_calls = 0; + MOCK(connection_or_close_for_error, close_for_error_mock); + + /* Make fake conns */ + or_c1 = tor_malloc_zero(sizeof(*or_c1)); + or_c1->base_.magic = OR_CONNECTION_MAGIC; + or_c1->base_.type = CONN_TYPE_OR; + c1 = TO_CONN(or_c1); + dir_c2 = tor_malloc_zero(sizeof(*dir_c2)); + dir_c2->base_.magic = DIR_CONNECTION_MAGIC; + dir_c2->base_.type = CONN_TYPE_DIR; + dir_c2->base_.state = DIR_CONN_STATE_MIN_; + dir_c2->base_.purpose = DIR_PURPOSE_MIN_; + c2 = TO_CONN(dir_c2); + + tt_assert(c1 != NULL); + tt_assert(c2 != NULL); + + /* Make list */ + l = smartlist_new(); + smartlist_add(l, c1); + smartlist_add(l, c2); + + /* Run kill_conn_list_for_oos() */ + kill_conn_list_for_oos(l); + + /* Check call counters */ + tt_int_op(mark_calls, OP_EQ, 1); + tt_int_op(cfe_calls, OP_EQ, 1); + + done: + + UNMOCK(connection_or_close_for_error); + UNMOCK(connection_mark_for_close_internal_); + + if (l) smartlist_free(l); + tor_free(or_c1); + tor_free(dir_c2); + + return; +} + +static smartlist_t *conns_for_mock = NULL; + +static smartlist_t * +get_conns_mock(void) +{ + return conns_for_mock; +} + +/* + * For this mock, we pretend all conns have either zero or one circuits, + * depending on if this appears on the list of things to say have a circuit. + */ + +static smartlist_t *conns_with_circs = NULL; + +static int +get_num_circuits_mock(or_connection_t *conn) +{ + int circs = 0; + + tt_assert(conn != NULL); + + if (conns_with_circs && + smartlist_contains(conns_with_circs, TO_CONN(conn))) { + circs = 1; + } + + done: + return circs; +} + +static void +test_oos_pick_oos_victims(void *arg) +{ + (void)arg; + or_connection_t *ortmp; + dir_connection_t *dirtmp; + smartlist_t *picked; + + /* Set up mocks */ + conns_for_mock = smartlist_new(); + MOCK(get_connection_array, get_conns_mock); + conns_with_circs = smartlist_new(); + MOCK(connection_or_get_num_circuits, get_num_circuits_mock); + + /* Make some fake connections */ + ortmp = tor_malloc_zero(sizeof(*ortmp)); + ortmp->base_.magic = OR_CONNECTION_MAGIC; + ortmp->base_.type = CONN_TYPE_OR; + smartlist_add(conns_for_mock, TO_CONN(ortmp)); + /* We'll pretend this one has a circuit too */ + smartlist_add(conns_with_circs, TO_CONN(ortmp)); + /* Next one */ + ortmp = tor_malloc_zero(sizeof(*ortmp)); + ortmp->base_.magic = OR_CONNECTION_MAGIC; + ortmp->base_.type = CONN_TYPE_OR; + smartlist_add(conns_for_mock, TO_CONN(ortmp)); + /* Next one is moribund */ + ortmp = tor_malloc_zero(sizeof(*ortmp)); + ortmp->base_.magic = OR_CONNECTION_MAGIC; + ortmp->base_.type = CONN_TYPE_OR; + ortmp->base_.marked_for_close = 1; + smartlist_add(conns_for_mock, TO_CONN(ortmp)); + /* Last one isn't an orconn */ + dirtmp = tor_malloc_zero(sizeof(*dirtmp)); + dirtmp->base_.magic = DIR_CONNECTION_MAGIC; + dirtmp->base_.type = CONN_TYPE_DIR; + smartlist_add(conns_for_mock, TO_CONN(dirtmp)); + + /* Try picking one */ + picked = pick_oos_victims(1); + /* It should be the one with circuits */ + tt_assert(picked != NULL); + tt_int_op(smartlist_len(picked), OP_EQ, 1); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); + smartlist_free(picked); + + /* Try picking none */ + picked = pick_oos_victims(0); + /* We should get an empty list */ + tt_assert(picked != NULL); + tt_int_op(smartlist_len(picked), OP_EQ, 0); + smartlist_free(picked); + + /* Try picking two */ + picked = pick_oos_victims(2); + /* We should get both active orconns */ + tt_assert(picked != NULL); + tt_int_op(smartlist_len(picked), OP_EQ, 2); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1))); + smartlist_free(picked); + + /* Try picking three - only two are eligible */ + picked = pick_oos_victims(3); + tt_int_op(smartlist_len(picked), OP_EQ, 2); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0))); + tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1))); + smartlist_free(picked); + + done: + + /* Free leftover stuff */ + if (conns_with_circs) { + smartlist_free(conns_with_circs); + conns_with_circs = NULL; + } + + UNMOCK(connection_or_get_num_circuits); + + if (conns_for_mock) { + SMARTLIST_FOREACH(conns_for_mock, connection_t *, c, tor_free(c)); + smartlist_free(conns_for_mock); + conns_for_mock = NULL; + } + + UNMOCK(get_connection_array); + + return; +} + +struct testcase_t oos_tests[] = { + { "connection_check_oos", test_oos_connection_check_oos, + TT_FORK, NULL, NULL }, + { "kill_conn_list", test_oos_kill_conn_list, TT_FORK, NULL, NULL }, + { "pick_oos_victims", test_oos_pick_oos_victims, TT_FORK, NULL, NULL }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index d6787e4f45..ead8d0311e 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -274,6 +274,7 @@ test_sr_commit(void *arg) time_t now = time(NULL); sr_commit_t *our_commit = NULL; smartlist_t *args = smartlist_new(); + sr_commit_t *parsed_commit = NULL; (void) arg; @@ -340,13 +341,12 @@ test_sr_commit(void *arg) /* We'll build a list of values from our commit that our parsing function * takes from a vote line and see if we can parse it correctly. */ { - sr_commit_t *parsed_commit; smartlist_add(args, tor_strdup("1")); smartlist_add(args, tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg))); smartlist_add(args, tor_strdup(sr_commit_get_rsa_fpr(our_commit))); - smartlist_add(args, our_commit->encoded_commit); - smartlist_add(args, our_commit->encoded_reveal); + smartlist_add(args, tor_strdup(our_commit->encoded_commit)); + smartlist_add(args, tor_strdup(our_commit->encoded_reveal)); parsed_commit = sr_parse_commit(args); tt_assert(parsed_commit); /* That parsed commit should be _EXACTLY_ like our original commit (we @@ -354,15 +354,14 @@ test_sr_commit(void *arg) parsed_commit->valid = 1; tt_mem_op(parsed_commit, OP_EQ, our_commit, sizeof(*parsed_commit)); /* Cleanup */ - tor_free(smartlist_get(args, 0)); /* strdup here. */ - tor_free(smartlist_get(args, 1)); /* strdup here. */ - smartlist_clear(args); - sr_commit_free(parsed_commit); } done: + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); smartlist_free(args); sr_commit_free(our_commit); + sr_commit_free(parsed_commit); + authority_cert_free(auth_cert); } /* Test the encoding and decoding function for commit and reveal values. */ @@ -370,26 +369,23 @@ static void test_encoding(void *arg) { (void) arg; - int ret, duper_rand = 42; + int ret; /* Random number is 32 bytes. */ char raw_rand[32]; time_t ts = 1454333590; char hashed_rand[DIGEST256_LEN], hashed_reveal[DIGEST256_LEN]; sr_commit_t parsed_commit; - /* Encoded commit is: base64-encode( 1454333590 || H(H(42)) ). Remember - * that we do no expose the raw bytes of our PRNG to the network thus - * explaining the double H(). */ - static const char *encoded_commit = - "AAAAAFavXpZbx2LRneYFSLPCP8DLp9BXfeH5FXzbkxM4iRXKGeA54g=="; - /* Encoded reveal is: base64-encode( 1454333590 || H(42) ). */ + /* Those values were generated by sr_commit_calc_ref.py where the random + * value is 32 'A' and timestamp is the one in ts. */ static const char *encoded_reveal = - "AAAAAFavXpYk9x9kTjiQWUqjHwSAEOdPAfCaurXgjPy173SzYjeC2g=="; + "AAAAAFavXpZJxbwTupvaJCTeIUCQmOPxAMblc7ChL5H2nZKuGchdaA=="; + static const char *encoded_commit = + "AAAAAFavXpbkBMzMQG7aNoaGLFNpm2Wkk1ozXhuWWqL//GynltxVAg=="; /* Set up our raw random bytes array. */ - memset(raw_rand, 0, sizeof(raw_rand)); - memcpy(raw_rand, &duper_rand, sizeof(duper_rand)); - /* Hash random number. */ + memset(raw_rand, 'A', sizeof(raw_rand)); + /* Hash random number because we don't expose bytes of the RNG. */ ret = crypto_digest256(hashed_rand, raw_rand, sizeof(raw_rand), SR_DIGEST_ALG); tt_int_op(0, ==, ret); @@ -586,6 +582,7 @@ test_vote(void *arg) smartlist_free(tokens); smartlist_clear(args); smartlist_free(args); + tor_free(lines); } done: @@ -783,7 +780,7 @@ test_sr_setup_commits(void) tt_assert(!commit_has_reveal_value(commit_d)); done: - return; + authority_cert_free(auth_cert); } /** Verify that the SRV generation procedure is proper by testing it against @@ -970,6 +967,7 @@ test_utils(void *arg) /* Change the pubkey. */ memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity)); tt_int_op(commit_is_authoritative(&commit, digest), ==, 0); + crypto_pk_free(k); } /* Testing get_phase_str(). */ @@ -1047,6 +1045,7 @@ test_state_transition(void *arg) prev = sr_state_get_previous_srv(); tt_assert(prev == cur); tt_assert(!sr_state_get_current_srv()); + sr_state_clean_srvs(); } /* New protocol run. */ @@ -1095,14 +1094,16 @@ test_keep_commit(void *arg) sr_commit_t *commit = NULL, *dup_commit = NULL; sr_state_t *state; time_t now = time(NULL); + crypto_pk_t *k = NULL; (void) arg; MOCK(trusteddirserver_get_by_v3_auth_digest, trusteddirserver_get_by_v3_auth_digest_m); - { /* Setup a minimal dirauth environment for this test */ - crypto_pk_t *k = crypto_pk_new(); + { + k = crypto_pk_new(); + /* Setup a minimal dirauth environment for this test */ /* Have a key that is not the one from our commit. */ tt_int_op(0, ==, crypto_pk_generate_key(k)); tt_int_op(0, ==, crypto_pk_get_fingerprint(k, fp, 0)); @@ -1179,6 +1180,7 @@ test_keep_commit(void *arg) done: sr_commit_free(commit); sr_commit_free(dup_commit); + crypto_pk_free(k); UNMOCK(trusteddirserver_get_by_v3_auth_digest); } diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 3a048fb1f0..9115823f31 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -1831,8 +1831,6 @@ test_tortls_server_info_callback(void *ignored) tls->magic = TOR_TLS_MAGIC; tls->ssl = ssl; - tor_tls_server_info_callback(NULL, 0, 0); - SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A); mock_clean_saved_logs(); tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0); diff --git a/src/test/test_util.c b/src/test/test_util.c index 5432b2ccc4..3fd2dc3612 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1723,8 +1723,7 @@ static void test_util_strmisc(void *arg) { char buf[1024]; - int i; - char *cp, *cp_tmp = NULL; + char *cp_tmp = NULL; /* Test strl operations */ (void)arg; @@ -1749,122 +1748,6 @@ test_util_strmisc(void *arg) tor_strstrip(buf, "!? "); tt_str_op(buf,OP_EQ, "Testing123"); - /* Test parse_long */ - /* Empty/zero input */ - tt_int_op(0L,OP_EQ, tor_parse_long("",10,0,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(0L,OP_EQ, tor_parse_long("0",10,0,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - /* Normal cases */ - tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,10,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(10L,OP_EQ, tor_parse_long("10",10,10,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,100,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,0,&i,NULL)); - tt_int_op(1,OP_EQ, i); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-50,0,&i,NULL)); - tt_int_op(1,OP_EQ, i); - /* Extra garbage */ - tt_int_op(0L,OP_EQ, tor_parse_long("10m",10,0,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(0L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(10L,OP_EQ, tor_parse_long("10m",10,0,100,&i,&cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, "m"); - tt_int_op(-50L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,&cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, " plus garbage"); - /* Illogical min max */ - tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,4,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,100,-100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - /* Out of bounds */ - tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,0,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - /* Base different than 10 */ - tt_int_op(2L,OP_EQ, tor_parse_long("10",2,0,100,NULL,NULL)); - tt_int_op(0L,OP_EQ, tor_parse_long("2",2,0,100,NULL,NULL)); - tt_int_op(0L,OP_EQ, tor_parse_long("10",-2,0,100,NULL,NULL)); - tt_int_op(68284L,OP_EQ, tor_parse_long("10abc",16,0,70000,NULL,NULL)); - tt_int_op(68284L,OP_EQ, tor_parse_long("10ABC",16,0,70000,NULL,NULL)); - tt_int_op(0,OP_EQ, tor_parse_long("10ABC",-1,0,70000,&i,NULL)); - tt_int_op(i,OP_EQ, 0); - - /* Test parse_ulong */ - tt_int_op(0UL,OP_EQ, tor_parse_ulong("",10,0,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("0",10,0,100,NULL,NULL)); - tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("10",10,50,100,NULL,NULL)); - tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,10,NULL,NULL)); - tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,10,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("8",8,0,100,NULL,NULL)); - tt_int_op(50UL,OP_EQ, tor_parse_ulong("50",10,50,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,-100,100,NULL,NULL)); - tt_int_op(0UL,OP_EQ, tor_parse_ulong("50",-1,50,100,&i,NULL)); - tt_int_op(0,OP_EQ, i); - - /* Test parse_uint64 */ - tt_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, " x"); - tt_assert(U64_LITERAL(12345678901) == - tor_parse_uint64("12345678901",10,0,UINT64_MAX, &i, &cp)); - tt_int_op(1,OP_EQ, i); - tt_str_op(cp,OP_EQ, ""); - tt_assert(U64_LITERAL(0) == - tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp)); - tt_int_op(0,OP_EQ, i); - tt_assert(U64_LITERAL(0) == - tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp)); - tt_int_op(0,OP_EQ, i); - - { - /* Test parse_double */ - double d = tor_parse_double("10", 0, (double)UINT64_MAX,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_assert(DBL_TO_U64(d) == 10); - d = tor_parse_double("0", 0, (double)UINT64_MAX,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_assert(DBL_TO_U64(d) == 0); - d = tor_parse_double(" ", 0, (double)UINT64_MAX,&i,NULL); - tt_int_op(0,OP_EQ, i); - d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,NULL); - tt_int_op(0,OP_EQ, i); - d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,&cp); - tt_int_op(1,OP_EQ, i); - d = tor_parse_double("-.0", 0, (double)UINT64_MAX,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_assert(DBL_TO_U64(d) == 0); - d = tor_parse_double("-10", -100.0, 100.0,&i,NULL); - tt_int_op(1,OP_EQ, i); - tt_double_op(fabs(d - -10.0),OP_LT, 1E-12); - } - - { - /* Test tor_parse_* where we overflow/underflow the underlying type. */ - /* This string should overflow 64-bit ints. */ -#define TOOBIG "100000000000000000000000000" - tt_int_op(0L, OP_EQ, - tor_parse_long(TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - tt_int_op(0L,OP_EQ, - tor_parse_long("-"TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - tt_int_op(0UL,OP_EQ, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - tt_u64_op(U64_LITERAL(0), OP_EQ, tor_parse_uint64(TOOBIG, 10, - 0, UINT64_MAX, &i, NULL)); - tt_int_op(i,OP_EQ, 0); - } - /* Test snprintf */ /* Returning -1 when there's not enough room in the output buffer */ tt_int_op(-1,OP_EQ, tor_snprintf(buf, 0, "Foo")); @@ -2054,6 +1937,144 @@ test_util_strmisc(void *arg) } static void +test_util_parse_integer(void *arg) +{ + (void)arg; + int i; + char *cp; + + /* Test parse_long */ + /* Empty/zero input */ + tt_int_op(0L,OP_EQ, tor_parse_long("",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0L,OP_EQ, tor_parse_long("0",10,0,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + /* Normal cases */ + tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,10,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(10L,OP_EQ, tor_parse_long("10",10,10,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,100,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,0,&i,NULL)); + tt_int_op(1,OP_EQ, i); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-50,0,&i,NULL)); + tt_int_op(1,OP_EQ, i); + /* Extra garbage */ + tt_int_op(0L,OP_EQ, tor_parse_long("10m",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(10L,OP_EQ, tor_parse_long("10m",10,0,100,&i,&cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, "m"); + tt_int_op(-50L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,&cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, " plus garbage"); + /* Illogical min max */ + tor_capture_bugs_(1); + tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,4,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_())); + tt_str_op("!(max < min)", OP_EQ, + smartlist_get(tor_get_captured_bug_log_(), 0)); + tor_end_capture_bugs_(); + tor_capture_bugs_(1); + tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,100,-100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_())); + tt_str_op("!(max < min)", OP_EQ, + smartlist_get(tor_get_captured_bug_log_(), 0)); + tor_end_capture_bugs_(); + /* Out of bounds */ + tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + /* Base different than 10 */ + tt_int_op(2L,OP_EQ, tor_parse_long("10",2,0,100,NULL,NULL)); + tt_int_op(0L,OP_EQ, tor_parse_long("2",2,0,100,NULL,NULL)); + tt_int_op(0L,OP_EQ, tor_parse_long("10",-2,0,100,NULL,NULL)); + tt_int_op(68284L,OP_EQ, tor_parse_long("10abc",16,0,70000,NULL,NULL)); + tt_int_op(68284L,OP_EQ, tor_parse_long("10ABC",16,0,70000,NULL,NULL)); + tt_int_op(0,OP_EQ, tor_parse_long("10ABC",-1,0,70000,&i,NULL)); + tt_int_op(i,OP_EQ, 0); + + /* Test parse_ulong */ + tt_int_op(0UL,OP_EQ, tor_parse_ulong("",10,0,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("0",10,0,100,NULL,NULL)); + tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("10",10,50,100,NULL,NULL)); + tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,10,NULL,NULL)); + tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,10,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("8",8,0,100,NULL,NULL)); + tt_int_op(50UL,OP_EQ, tor_parse_ulong("50",10,50,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,0,100,NULL,NULL)); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("50",-1,50,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,0,100,&i,NULL)); + tt_int_op(0,OP_EQ, i); + + /* Test parse_uint64 */ + tt_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, " x"); + tt_assert(U64_LITERAL(12345678901) == + tor_parse_uint64("12345678901",10,0,UINT64_MAX, &i, &cp)); + tt_int_op(1,OP_EQ, i); + tt_str_op(cp,OP_EQ, ""); + tt_assert(U64_LITERAL(0) == + tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp)); + tt_int_op(0,OP_EQ, i); + tt_assert(U64_LITERAL(0) == + tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp)); + tt_int_op(0,OP_EQ, i); + + { + /* Test parse_double */ + double d = tor_parse_double("10", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_assert(DBL_TO_U64(d) == 10); + d = tor_parse_double("0", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_assert(DBL_TO_U64(d) == 0); + d = tor_parse_double(" ", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(0,OP_EQ, i); + d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(0,OP_EQ, i); + d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,&cp); + tt_int_op(1,OP_EQ, i); + d = tor_parse_double("-.0", 0, (double)UINT64_MAX,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_assert(DBL_TO_U64(d) == 0); + d = tor_parse_double("-10", -100.0, 100.0,&i,NULL); + tt_int_op(1,OP_EQ, i); + tt_double_op(fabs(d - -10.0),OP_LT, 1E-12); + } + + { + /* Test tor_parse_* where we overflow/underflow the underlying type. */ + /* This string should overflow 64-bit ints. */ +#define TOOBIG "100000000000000000000000000" + tt_int_op(0L, OP_EQ, + tor_parse_long(TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + tt_int_op(0L,OP_EQ, + tor_parse_long("-"TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + tt_int_op(0UL,OP_EQ, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + tt_u64_op(U64_LITERAL(0), OP_EQ, tor_parse_uint64(TOOBIG, 10, + 0, UINT64_MAX, &i, NULL)); + tt_int_op(i,OP_EQ, 0); + } + done: + tor_end_capture_bugs_(); +} + +static void test_util_pow2(void *arg) { /* Test tor_log2(). */ @@ -2217,9 +2238,15 @@ test_util_gzip_compression_bomb(void *arg) tor_zlib_state_t *state = NULL; /* Make sure we can't produce a compression bomb */ + const int prev_level = setup_full_capture_of_logs(LOG_WARN); tt_int_op(-1, OP_EQ, tor_gzip_compress(&result, &result_len, one_mb, one_million, ZLIB_METHOD)); + expect_single_log_msg_containing( + "We compressed something and got an insanely high " + "compression factor; other Tors would think this " + "was a zlib bomb."); + teardown_capture_of_logs(prev_level); /* Here's a compression bomb that we made manually. */ const char compression_bomb[1039] = @@ -5254,9 +5281,11 @@ test_util_pwdb(void *arg) tt_assert(found); tor_free(dir); - prev_level = setup_capture_of_logs(LOG_ERR); /* We should do a LOG_ERR */ + /* We should do a LOG_ERR */ + prev_level = setup_full_capture_of_logs(LOG_ERR); dir = get_user_homedir(badname); tt_assert(dir == NULL); + expect_log_msg_containing("not found"); tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1); teardown_capture_of_logs(prev_level); prev_level = -100; @@ -5321,6 +5350,8 @@ test_util_monotonic_time(void *arg) uint64_t nsec1, nsec2, usec1, msec1; uint64_t nsecc1, nsecc2, usecc1, msecc1; + monotime_init(); + monotime_get(&mt1); monotime_coarse_get(&mtc1); nsec1 = monotime_absolute_nsec(); @@ -5365,6 +5396,7 @@ static void test_util_monotonic_time_ratchet(void *arg) { (void)arg; + monotime_init(); monotime_reset_ratchets_for_testing(); /* win32, performance counter ratchet. */ @@ -5460,9 +5492,10 @@ struct testcase_t util_tests[] = { UTIL_LEGACY(escape_string_socks), UTIL_LEGACY(string_is_key_value), UTIL_LEGACY(strmisc), + UTIL_TEST(parse_integer, 0), UTIL_LEGACY(pow2), UTIL_LEGACY(gzip), - UTIL_LEGACY(gzip_compression_bomb), + UTIL_TEST(gzip_compression_bomb, TT_FORK), UTIL_LEGACY(datadir), UTIL_LEGACY(memarea), UTIL_LEGACY(control_formats), diff --git a/src/test/testing_common.c b/src/test/testing_common.c index ea9366305c..6460713f75 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -244,6 +244,8 @@ main(int c, const char **v) network_init(); + monotime_init(); + struct tor_libevent_cfg cfg; memset(&cfg, 0, sizeof(cfg)); tor_libevent_initialize(&cfg); @@ -272,6 +274,8 @@ main(int c, const char **v) log_severity_list_t s; memset(&s, 0, sizeof(s)); set_log_severity_config(loglevel, LOG_ERR, &s); + /* ALWAYS log bug warnings. */ + s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; add_stream_log(&s, "", fileno(stdout)); } diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index fb5343c820..1e1c31dff5 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -218,7 +218,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.2.9.2-alpha" +#define VERSION "0.2.9.2-alpha-dev" |