aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--changes/186406
-rw-r--r--changes/191223
-rw-r--r--changes/199776
-rw-r--r--changes/bug199053
-rw-r--r--changes/bug199646
-rw-r--r--changes/bug19999_prep20
-rw-r--r--changes/bug2001212
-rw-r--r--changes/bug200645
-rw-r--r--changes/bug200655
-rw-r--r--changes/bug200934
-rw-r--r--changes/feature200696
-rw-r--r--changes/reject-tap15
-rw-r--r--changes/ticket200024
-rw-r--r--configure.ac2
-rw-r--r--contrib/win32build/tor-mingw.nsi.in2
-rw-r--r--doc/HACKING/ReleasingTor.md6
-rw-r--r--doc/tor.1.txt26
-rwxr-xr-xscripts/maint/checkSpace.pl19
-rw-r--r--src/common/backtrace.c4
-rw-r--r--src/common/compat.c2
-rw-r--r--src/common/container.c2
-rw-r--r--src/common/container.h2
-rw-r--r--src/common/log.c21
-rw-r--r--src/common/torlog.h5
-rw-r--r--src/common/tortls.c4
-rw-r--r--src/common/util.c6
-rw-r--r--src/common/util.h10
-rw-r--r--src/common/util_bug.c46
-rw-r--r--src/common/util_bug.h19
-rw-r--r--src/common/util_format.c2
-rw-r--r--src/or/channel.c8
-rw-r--r--src/or/channel.h4
-rw-r--r--src/or/channeltls.c2
-rw-r--r--src/or/circuitbuild.c227
-rw-r--r--src/or/circuitbuild.h5
-rw-r--r--src/or/circuitlist.c6
-rw-r--r--src/or/config.c33
-rw-r--r--src/or/connection.c304
-rw-r--r--src/or/connection.h23
-rw-r--r--src/or/connection_or.c4
-rw-r--r--src/or/connection_or.h2
-rw-r--r--src/or/dirserv.c14
-rw-r--r--src/or/dirvote.c10
-rw-r--r--src/or/dirvote.h6
-rw-r--r--src/or/hibernate.c2
-rw-r--r--src/or/main.c21
-rw-r--r--src/or/main.h4
-rw-r--r--src/or/networkstatus.c6
-rw-r--r--src/or/nodelist.c28
-rw-r--r--src/or/onion.c4
-rw-r--r--src/or/or.h10
-rw-r--r--src/or/policies.c4
-rw-r--r--src/or/rendclient.c48
-rw-r--r--src/or/rendservice.c11
-rw-r--r--src/or/rendservice.h2
-rw-r--r--src/or/router.c9
-rw-r--r--src/or/routerlist.c51
-rw-r--r--src/or/routerlist.h3
-rw-r--r--src/or/shared_random.c4
-rw-r--r--src/test/bench.c4
-rw-r--r--src/test/include.am5
-rw-r--r--src/test/log_test_helpers.c118
-rw-r--r--src/test/log_test_helpers.h20
-rw-r--r--src/test/sr_commit_calc_ref.py51
-rw-r--r--src/test/test-memwipe.c13
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_addr.c8
-rw-r--r--src/test/test_address.c5
-rw-r--r--src/test/test_compat_libevent.c12
-rw-r--r--src/test/test_config.c2
-rw-r--r--src/test/test_crypto.c2
-rw-r--r--src/test/test_dir.c24
-rw-r--r--src/test/test_link_handshake.c105
-rw-r--r--src/test/test_oos.c456
-rw-r--r--src/test/test_shared_random.c44
-rw-r--r--src/test/test_tortls.c2
-rw-r--r--src/test/test_util.c273
-rw-r--r--src/test/testing_common.c4
-rw-r--r--src/win32/orconfig.h2
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"