summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/address.c26
-rw-r--r--src/common/address.h12
-rw-r--r--src/common/aes.c18
-rw-r--r--src/common/aes.h2
-rw-r--r--src/common/backtrace.c12
-rw-r--r--src/common/backtrace.h4
-rw-r--r--src/common/buffers.c1077
-rw-r--r--src/common/buffers.h (renamed from src/or/buffers.h)88
-rw-r--r--src/common/buffers_tls.c179
-rw-r--r--src/common/buffers_tls.h19
-rw-r--r--src/common/compat.c250
-rw-r--r--src/common/compat.h82
-rw-r--r--src/common/compat_libevent.c15
-rw-r--r--src/common/compat_libevent.h4
-rw-r--r--src/common/compat_openssl.h8
-rw-r--r--src/common/compat_pthreads.c13
-rw-r--r--src/common/compat_rust.h2
-rw-r--r--src/common/compat_threads.c12
-rw-r--r--src/common/compat_threads.h8
-rw-r--r--src/common/compat_time.c40
-rw-r--r--src/common/compat_time.h25
-rw-r--r--src/common/compat_winthreads.c2
-rw-r--r--src/common/compress.c23
-rw-r--r--src/common/compress.h5
-rw-r--r--src/common/compress_lzma.c20
-rw-r--r--src/common/compress_lzma.h2
-rw-r--r--src/common/compress_none.h2
-rw-r--r--src/common/compress_zlib.h2
-rw-r--r--src/common/compress_zstd.c20
-rw-r--r--src/common/compress_zstd.h2
-rw-r--r--src/common/confline.c5
-rw-r--r--src/common/confline.h2
-rw-r--r--src/common/container.c4
-rw-r--r--src/common/container.h8
-rw-r--r--src/common/crypto.c111
-rw-r--r--src/common/crypto.h16
-rw-r--r--src/common/crypto_curve25519.c7
-rw-r--r--src/common/crypto_curve25519.h4
-rw-r--r--src/common/crypto_ed25519.c76
-rw-r--r--src/common/crypto_ed25519.h4
-rw-r--r--src/common/crypto_format.h2
-rw-r--r--src/common/crypto_pwbox.c2
-rw-r--r--src/common/crypto_pwbox.h2
-rw-r--r--src/common/crypto_s2k.c14
-rw-r--r--src/common/crypto_s2k.h4
-rw-r--r--src/common/di_ops.c4
-rw-r--r--src/common/di_ops.h2
-rw-r--r--src/common/handles.h2
-rw-r--r--src/common/include.am4
-rw-r--r--src/common/log.c10
-rw-r--r--src/common/memarea.c17
-rw-r--r--src/common/memarea.h2
-rw-r--r--src/common/procmon.c18
-rw-r--r--src/common/procmon.h2
-rw-r--r--src/common/pubsub.h2
-rw-r--r--src/common/sandbox.c174
-rw-r--r--src/common/sandbox.h22
-rw-r--r--src/common/storagedir.h2
-rw-r--r--src/common/testsupport.h8
-rw-r--r--src/common/timers.c4
-rw-r--r--src/common/timers.h2
-rw-r--r--src/common/torint.h62
-rw-r--r--src/common/torlog.h10
-rw-r--r--src/common/tortls.c59
-rw-r--r--src/common/tortls.h8
-rw-r--r--src/common/util.c240
-rw-r--r--src/common/util.h40
-rw-r--r--src/common/util_bug.c8
-rw-r--r--src/common/util_bug.h47
-rw-r--r--src/common/util_format.c5
-rw-r--r--src/common/util_format.h2
-rw-r--r--src/common/util_process.c2
-rw-r--r--src/common/util_process.h4
-rw-r--r--src/common/workqueue.h2
-rw-r--r--src/config/torrc.minimal.in-staging12
-rw-r--r--src/config/torrc.sample.in19
-rw-r--r--src/ext/ed25519/donna/ed25519_donna_tor.h5
-rw-r--r--src/ext/ed25519/donna/ed25519_tor.c39
-rw-r--r--src/ext/ed25519/ref10/blinding.c49
-rw-r--r--src/ext/ed25519/ref10/ed25519_ref10.h4
-rw-r--r--src/ext/ht.h2
-rw-r--r--src/ext/timeouts/timeout-bitops.c3
-rw-r--r--src/ext/trunnel/trunnel-impl.h2
-rw-r--r--src/ext/trunnel/trunnel.c2
-rw-r--r--src/ext/trunnel/trunnel.h2
-rw-r--r--src/or/addressmap.c8
-rw-r--r--src/or/addressmap.h4
-rw-r--r--src/or/bridges.c65
-rw-r--r--src/or/bridges.h3
-rw-r--r--src/or/buffers.c2192
-rw-r--r--src/or/channel.c78
-rw-r--r--src/or/channel.h12
-rw-r--r--src/or/channelpadding.c2
-rw-r--r--src/or/channelpadding.h2
-rw-r--r--src/or/channeltls.c51
-rw-r--r--src/or/channeltls.h6
-rw-r--r--src/or/circpathbias.c4
-rw-r--r--src/or/circpathbias.h2
-rw-r--r--src/or/circuitbuild.c613
-rw-r--r--src/or/circuitbuild.h15
-rw-r--r--src/or/circuitlist.c118
-rw-r--r--src/or/circuitlist.h9
-rw-r--r--src/or/circuitmux.c12
-rw-r--r--src/or/circuitmux.h2
-rw-r--r--src/or/circuitmux_ewma.c6
-rw-r--r--src/or/circuitmux_ewma.h2
-rw-r--r--src/or/circuitstats.c41
-rw-r--r--src/or/circuitstats.h8
-rw-r--r--src/or/circuituse.c277
-rw-r--r--src/or/circuituse.h10
-rw-r--r--src/or/command.c11
-rw-r--r--src/or/command.h2
-rw-r--r--src/or/config.c491
-rw-r--r--src/or/config.h11
-rw-r--r--src/or/confparse.h76
-rw-r--r--src/or/connection.c156
-rw-r--r--src/or/connection.h18
-rw-r--r--src/or/connection_edge.c693
-rw-r--r--src/or/connection_edge.h19
-rw-r--r--src/or/connection_or.c30
-rw-r--r--src/or/connection_or.h2
-rw-r--r--src/or/conscache.c14
-rw-r--r--src/or/conscache.h2
-rw-r--r--src/or/consdiff.h4
-rw-r--r--src/or/consdiffmgr.c2
-rw-r--r--src/or/consdiffmgr.h4
-rw-r--r--src/or/control.c342
-rw-r--r--src/or/control.h19
-rw-r--r--src/or/cpuworker.c4
-rw-r--r--src/or/cpuworker.h2
-rw-r--r--src/or/dircollate.c2
-rw-r--r--src/or/dircollate.h4
-rw-r--r--src/or/directory.c598
-rw-r--r--src/or/directory.h105
-rw-r--r--src/or/dirserv.c36
-rw-r--r--src/or/dirserv.h4
-rw-r--r--src/or/dirvote.c114
-rw-r--r--src/or/dirvote.h18
-rw-r--r--src/or/dns.c39
-rw-r--r--src/or/dns.h4
-rw-r--r--src/or/dns_structs.h2
-rw-r--r--src/or/dnsserv.c4
-rw-r--r--src/or/dnsserv.h2
-rw-r--r--src/or/entrynodes.c71
-rw-r--r--src/or/entrynodes.h27
-rw-r--r--src/or/ext_orport.c28
-rw-r--r--src/or/ext_orport.h4
-rw-r--r--src/or/fp_pair.h2
-rw-r--r--src/or/geoip.c9
-rw-r--r--src/or/geoip.h4
-rw-r--r--src/or/hibernate.c2
-rw-r--r--src/or/hibernate.h4
-rw-r--r--src/or/hs_cache.c576
-rw-r--r--src/or/hs_cache.h68
-rw-r--r--src/or/hs_cell.c948
-rw-r--r--src/or/hs_cell.h122
-rw-r--r--src/or/hs_circuit.c1213
-rw-r--r--src/or/hs_circuit.h76
-rw-r--r--src/or/hs_circuitmap.c72
-rw-r--r--src/or/hs_circuitmap.h12
-rw-r--r--src/or/hs_client.c1611
-rw-r--r--src/or/hs_client.h92
-rw-r--r--src/or/hs_common.c1491
-rw-r--r--src/or/hs_common.h207
-rw-r--r--src/or/hs_config.c590
-rw-r--r--src/or/hs_config.h24
-rw-r--r--src/or/hs_descriptor.c445
-rw-r--r--src/or/hs_descriptor.h54
-rw-r--r--src/or/hs_ident.c126
-rw-r--r--src/or/hs_ident.h141
-rw-r--r--src/or/hs_intropoint.c16
-rw-r--r--src/or/hs_intropoint.h26
-rw-r--r--src/or/hs_ntor.c50
-rw-r--r--src/or/hs_ntor.h26
-rw-r--r--src/or/hs_service.c3430
-rw-r--r--src/or/hs_service.h351
-rw-r--r--src/or/include.am47
-rw-r--r--src/or/keypin.h4
-rw-r--r--src/or/main.c114
-rw-r--r--src/or/main.h4
-rw-r--r--src/or/microdesc.c2
-rw-r--r--src/or/networkstatus.c153
-rw-r--r--src/or/networkstatus.h19
-rw-r--r--src/or/nodelist.c485
-rw-r--r--src/or/nodelist.h32
-rw-r--r--src/or/ntmain.c9
-rw-r--r--src/or/ntmain.h4
-rw-r--r--src/or/onion.c2
-rw-r--r--src/or/onion.h2
-rw-r--r--src/or/onion_fast.h2
-rw-r--r--src/or/onion_ntor.h4
-rw-r--r--src/or/onion_tap.c7
-rw-r--r--src/or/onion_tap.h2
-rw-r--r--src/or/or.h178
-rw-r--r--src/or/parsecommon.c3
-rw-r--r--src/or/parsecommon.h5
-rw-r--r--src/or/periodic.h2
-rw-r--r--src/or/policies.c15
-rw-r--r--src/or/policies.h4
-rw-r--r--src/or/proto_cell.c84
-rw-r--r--src/or/proto_cell.h17
-rw-r--r--src/or/proto_control0.c26
-rw-r--r--src/or/proto_control0.h14
-rw-r--r--src/or/proto_ext_or.c40
-rw-r--r--src/or/proto_ext_or.h17
-rw-r--r--src/or/proto_http.c171
-rw-r--r--src/or/proto_http.h24
-rw-r--r--src/or/proto_socks.c710
-rw-r--r--src/or/proto_socks.h20
-rw-r--r--src/or/protover.h11
-rw-r--r--src/or/reasons.c54
-rw-r--r--src/or/reasons.h3
-rw-r--r--src/or/relay.c73
-rw-r--r--src/or/relay.h4
-rw-r--r--src/or/rendcache.c8
-rw-r--r--src/or/rendcache.h6
-rw-r--r--src/or/rendclient.c425
-rw-r--r--src/or/rendclient.h6
-rw-r--r--src/or/rendcommon.c19
-rw-r--r--src/or/rendcommon.h13
-rw-r--r--src/or/rendmid.c1
-rw-r--r--src/or/rendmid.h2
-rw-r--r--src/or/rendservice.c614
-rw-r--r--src/or/rendservice.h34
-rw-r--r--src/or/rephist.c105
-rw-r--r--src/or/rephist.h5
-rw-r--r--src/or/replaycache.h6
-rw-r--r--src/or/router.c70
-rw-r--r--src/or/router.h4
-rw-r--r--src/or/routerkeys.c117
-rw-r--r--src/or/routerkeys.h3
-rw-r--r--src/or/routerlist.c60
-rw-r--r--src/or/routerlist.h7
-rw-r--r--src/or/routerparse.c39
-rw-r--r--src/or/routerparse.h4
-rw-r--r--src/or/routerset.c2
-rw-r--r--src/or/routerset.h4
-rw-r--r--src/or/scheduler.c877
-rw-r--r--src/or/scheduler.h212
-rw-r--r--src/or/scheduler_kist.c844
-rw-r--r--src/or/scheduler_vanilla.c197
-rw-r--r--src/or/shared_random.c56
-rw-r--r--src/or/shared_random.h7
-rw-r--r--src/or/shared_random_state.c68
-rw-r--r--src/or/shared_random_state.h13
-rw-r--r--src/or/statefile.c30
-rw-r--r--src/or/statefile.h2
-rw-r--r--src/or/status.h2
-rw-r--r--src/or/torcert.c46
-rw-r--r--src/or/torcert.h14
-rw-r--r--src/or/transports.c8
-rw-r--r--src/or/transports.h4
-rw-r--r--src/rust/Cargo.lock12
-rw-r--r--src/rust/tor_util/include.am2
-rw-r--r--src/test/bench.c5
-rw-r--r--src/test/ed25519_exts_ref.py36
-rw-r--r--src/test/ed25519_vectors.inc32
-rw-r--r--src/test/fuzz/dict/hsdescv36
-rw-r--r--src/test/fuzz/fuzz_hsdescv3.c99
-rw-r--r--src/test/fuzz/fuzz_http_connect.c106
-rw-r--r--src/test/fuzz/fuzzing_common.c7
-rw-r--r--src/test/fuzz/include.am54
-rw-r--r--src/test/hs_build_address.py38
-rw-r--r--src/test/hs_indexes.py70
-rw-r--r--src/test/hs_test_helpers.c61
-rw-r--r--src/test/hs_test_helpers.h5
-rw-r--r--src/test/include.am67
-rw-r--r--src/test/log_test_helpers.h2
-rw-r--r--src/test/rend_test_helpers.c16
-rw-r--r--src/test/rend_test_helpers.h3
-rw-r--r--src/test/test-child.c2
-rw-r--r--src/test/test.c83
-rw-r--r--src/test/test.h15
-rw-r--r--src/test/test_addr.c40
-rw-r--r--src/test/test_address.c80
-rw-r--r--src/test/test_bt_cl.c2
-rw-r--r--src/test/test_buffers.c312
-rw-r--r--src/test/test_channel.c317
-rw-r--r--src/test/test_channelpadding.c12
-rw-r--r--src/test/test_channeltls.c30
-rw-r--r--src/test/test_checkdir.c2
-rw-r--r--src/test/test_circuitlist.c11
-rw-r--r--src/test/test_circuitmux.c6
-rw-r--r--src/test/test_circuituse.c31
-rw-r--r--src/test/test_config.c727
-rw-r--r--src/test/test_connection.c409
-rw-r--r--src/test/test_connection.h13
-rw-r--r--src/test/test_conscache.c6
-rw-r--r--src/test/test_consdiff.c21
-rw-r--r--src/test/test_consdiffmgr.c6
-rw-r--r--src/test/test_containers.c73
-rw-r--r--src/test/test_controller.c278
-rw-r--r--src/test/test_crypto.c148
-rw-r--r--src/test/test_crypto_openssl.c8
-rw-r--r--src/test/test_crypto_slow.c34
-rw-r--r--src/test/test_dir.c938
-rw-r--r--src/test/test_dir_common.c2
-rw-r--r--src/test/test_dir_handle_get.c7
-rw-r--r--src/test/test_dns.c64
-rw-r--r--src/test/test_entryconn.c126
-rw-r--r--src/test/test_entrynodes.c265
-rw-r--r--src/test/test_extorport.c24
-rw-r--r--src/test/test_guardfraction.c48
-rw-r--r--src/test/test_helpers.c146
-rw-r--r--src/test/test_helpers.h10
-rw-r--r--src/test/test_hs.c280
-rw-r--r--src/test/test_hs_cache.c160
-rw-r--r--src/test/test_hs_cell.c130
-rw-r--r--src/test/test_hs_client.c599
-rw-r--r--src/test/test_hs_common.c1822
-rw-r--r--src/test/test_hs_config.c487
-rw-r--r--src/test/test_hs_descriptor.c126
-rw-r--r--src/test/test_hs_intropoint.c345
-rw-r--r--src/test/test_hs_ntor.c114
-rw-r--r--src/test/test_hs_service.c1685
-rw-r--r--src/test/test_introduce.c10
-rwxr-xr-xsrc/test/test_key_expiration.sh129
-rw-r--r--src/test/test_keypin.c103
-rw-r--r--src/test/test_link_handshake.c286
-rw-r--r--src/test/test_logging.c8
-rw-r--r--src/test/test_microdesc.c14
-rw-r--r--src/test/test_nodelist.c104
-rw-r--r--src/test/test_oom.c2
-rw-r--r--src/test/test_oos.c18
-rw-r--r--src/test/test_options.c352
-rw-r--r--src/test/test_policy.c365
-rw-r--r--src/test/test_proto_http.c213
-rw-r--r--src/test/test_proto_misc.c263
-rw-r--r--src/test/test_protover.c22
-rw-r--r--src/test/test_pt.c34
-rw-r--r--src/test/test_relay.c4
-rw-r--r--src/test/test_rendcache.c23
-rw-r--r--src/test/test_replay.c24
-rw-r--r--src/test/test_router.c112
-rw-r--r--src/test/test_routerkeys.c201
-rw-r--r--src/test/test_routerlist.c66
-rw-r--r--src/test/test_routerset.c18
-rw-r--r--src/test/test_scheduler.c831
-rw-r--r--src/test/test_shared_random.c407
-rw-r--r--src/test/test_socks.c593
-rw-r--r--src/test/test_status.c6
-rw-r--r--src/test/test_storagedir.c2
-rw-r--r--src/test/test_switch_id.c8
-rw-r--r--src/test/test_threads.c12
-rw-r--r--src/test/test_tortls.c95
-rw-r--r--src/test/test_util.c457
-rw-r--r--src/test/test_util_format.c8
-rw-r--r--src/test/test_util_process.c2
-rw-r--r--src/test/test_util_slow.c22
-rw-r--r--src/test/test_workqueue.c8
-rw-r--r--src/test/testing_common.c8
-rw-r--r--src/test/testing_rsakeys.c12
-rw-r--r--src/tools/include.am8
-rw-r--r--src/tools/tor-gencert.c16
-rw-r--r--src/trunnel/channelpadding_negotiation.c12
-rw-r--r--src/trunnel/channelpadding_negotiation.h10
-rw-r--r--src/trunnel/ed25519_cert.c4
-rw-r--r--src/trunnel/ed25519_cert.h2
-rw-r--r--src/trunnel/ed25519_cert.trunnel6
-rw-r--r--src/trunnel/hs/cell_common.c4
-rw-r--r--src/trunnel/hs/cell_common.h2
-rw-r--r--src/trunnel/hs/cell_establish_intro.c4
-rw-r--r--src/trunnel/hs/cell_establish_intro.h2
-rw-r--r--src/trunnel/hs/cell_introduce1.c4
-rw-r--r--src/trunnel/hs/cell_introduce1.h2
-rw-r--r--src/trunnel/hs/cell_rendezvous.c470
-rw-r--r--src/trunnel/hs/cell_rendezvous.h187
-rw-r--r--src/trunnel/hs/cell_rendezvous.trunnel29
-rw-r--r--src/trunnel/include.am2
-rw-r--r--src/trunnel/link_handshake.c4
-rw-r--r--src/trunnel/link_handshake.h2
-rw-r--r--src/trunnel/pwbox.c4
-rw-r--r--src/trunnel/pwbox.h2
-rw-r--r--src/win32/orconfig.h2
374 files changed, 33369 insertions, 10649 deletions
diff --git a/src/common/address.c b/src/common/address.c
index 555f63da06..c7aa97bfd1 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -33,7 +33,7 @@
#include <process.h>
#include <windows.h>
#include <iphlpapi.h>
-#endif
+#endif /* defined(_WIN32) */
#include "compat.h"
#include "util.h"
@@ -198,7 +198,7 @@ tor_sockaddr_to_str(const struct sockaddr *sa)
tor_asprintf(&result, "unix:%s", s_un->sun_path);
return result;
}
-#endif
+#endif /* defined(HAVE_SYS_UN_H) */
if (sa->sa_family == AF_UNSPEC)
return tor_strdup("unspec");
@@ -305,7 +305,7 @@ tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr))
return result;
}
return (err == EAI_AGAIN) ? 1 : -1;
-#else
+#else /* !(defined(HAVE_GETADDRINFO)) */
struct hostent *ent;
int err;
#ifdef HAVE_GETHOSTBYNAME_R_6_ARG
@@ -330,7 +330,7 @@ tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr))
#else
err = h_errno;
#endif
-#endif /* endif HAVE_GETHOSTBYNAME_R_6_ARG. */
+#endif /* defined(HAVE_GETHOSTBYNAME_R_6_ARG) || ... */
if (ent) {
if (ent->h_addrtype == AF_INET) {
tor_addr_from_in(addr, (struct in_addr*) ent->h_addr);
@@ -346,7 +346,7 @@ tor_addr_lookup,(const char *name, uint16_t family, tor_addr_t *addr))
#else
return (err == TRY_AGAIN) ? 1 : -1;
#endif
-#endif
+#endif /* defined(HAVE_GETADDRINFO) */
}
}
@@ -907,8 +907,8 @@ tor_addr_is_loopback(const tor_addr_t *addr)
return (tor_addr_to_ipv4h(addr) & 0xff000000) == 0x7f000000;
case AF_UNSPEC:
return 0;
- default:
/* LCOV_EXCL_START */
+ default:
tor_fragile_assert();
return 0;
/* LCOV_EXCL_STOP */
@@ -1031,8 +1031,10 @@ tor_addr_copy_tight(tor_addr_t *dest, const tor_addr_t *src)
memcpy(dest->addr.in6_addr.s6_addr, src->addr.in6_addr.s6_addr, 16);
case AF_UNSPEC:
break;
+ // LCOV_EXCL_START
default:
- tor_fragile_assert(); // LCOV_EXCL_LINE
+ tor_fragile_assert();
+ // LCOV_EXCL_STOP
}
}
@@ -1138,8 +1140,8 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
return 0;
else
return 1;
- default:
/* LCOV_EXCL_START */
+ default:
tor_fragile_assert();
return 0;
/* LCOV_EXCL_STOP */
@@ -1197,8 +1199,8 @@ tor_addr_hash(const tor_addr_t *addr)
return siphash24g(unspec_hash_input, sizeof(unspec_hash_input));
case AF_INET6:
return siphash24g(&addr->addr.in6_addr.s6_addr, 16);
- default:
/* LCOV_EXCL_START */
+ default:
tor_fragile_assert();
return 0;
/* LCOV_EXCL_STOP */
@@ -1434,7 +1436,7 @@ get_interface_addresses_ifaddrs(int severity, sa_family_t family)
return result;
}
-#endif
+#endif /* defined(HAVE_IFADDRS_TO_SMARTLIST) */
#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
@@ -1525,7 +1527,7 @@ get_interface_addresses_win32(int severity, sa_family_t family)
return result;
}
-#endif
+#endif /* defined(HAVE_IP_ADAPTER_TO_SMARTLIST) */
#ifdef HAVE_IFCONF_TO_SMARTLIST
@@ -1627,7 +1629,7 @@ get_interface_addresses_ioctl(int severity, sa_family_t family)
tor_free(ifc.ifc_buf);
return result;
}
-#endif
+#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
/** Try to ask our network interfaces what addresses they are bound to.
* Return a new smartlist of tor_addr_t on success, and NULL on failure.
diff --git a/src/common/address.h b/src/common/address.h
index 8091f8cd7b..2b9546782e 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -44,7 +44,7 @@
#endif
// TODO win32 specific includes
-#endif // ADDRESS_PRIVATE
+#endif /* defined(ADDRESS_PRIVATE) */
/** The number of bits from an address to consider while doing a masked
* comparison. */
@@ -360,23 +360,23 @@ STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa,
sa_family_t family);
STATIC smartlist_t *get_interface_addresses_ifaddrs(int severity,
sa_family_t family);
-#endif
+#endif /* defined(HAVE_IFADDRS_TO_SMARTLIST) */
#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
STATIC smartlist_t *ip_adapter_addresses_to_smartlist(
const IP_ADAPTER_ADDRESSES *addresses);
STATIC smartlist_t *get_interface_addresses_win32(int severity,
sa_family_t family);
-#endif
+#endif /* defined(HAVE_IP_ADAPTER_TO_SMARTLIST) */
#ifdef HAVE_IFCONF_TO_SMARTLIST
STATIC smartlist_t *ifreq_to_smartlist(char *ifr,
size_t buflen);
STATIC smartlist_t *get_interface_addresses_ioctl(int severity,
sa_family_t family);
-#endif
+#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
-#endif // ADDRESS_PRIVATE
+#endif /* defined(ADDRESS_PRIVATE) */
-#endif
+#endif /* !defined(TOR_ADDRESS_H) */
diff --git a/src/common/aes.c b/src/common/aes.c
index 73abef143e..20b51a6758 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -66,11 +66,11 @@ ENABLE_GCC_WARNING(redundant-decls)
#elif OPENSSL_VERSION_NUMBER >= OPENSSL_V_NOPATCH(1,0,1) && \
(defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || \
- defined(_M_AMD64) || defined(_M_X64) || defined(__INTEL__)) \
+ defined(_M_AMD64) || defined(_M_X64) || defined(__INTEL__))
#define USE_EVP_AES_CTR
-#endif
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_NOPATCH(1,1,0) || ... */
/* We have 2 strategies for getting the AES block cipher: Via OpenSSL's
* AES_encrypt function, or via OpenSSL's EVP_EncryptUpdate function.
@@ -142,7 +142,7 @@ evaluate_ctr_for_aes(void)
{
return 0;
}
-#else
+#else /* !(defined(USE_EVP_AES_CTR)) */
/*======================================================================*/
/* Interface to AES code, and counter implementation */
@@ -163,7 +163,7 @@ struct aes_cnt_cipher {
uint32_t counter2;
uint32_t counter1;
uint32_t counter0;
-#endif
+#endif /* !defined(WORDS_BIGENDIAN) */
union {
/** The counter, in big-endian order, as bytes. */
@@ -212,7 +212,7 @@ evaluate_evp_for_aes(int force_val)
log_info(LD_CRYPTO, "No AES engine found; using AES_* functions.");
should_use_EVP = 0;
}
-#endif
+#endif /* defined(DISABLE_ENGINES) */
return 0;
}
@@ -312,7 +312,7 @@ aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key, int key_bits)
cipher->counter1 = 0;
cipher->counter2 = 0;
cipher->counter3 = 0;
-#endif
+#endif /* defined(USING_COUNTER_VARS) */
memset(cipher->ctr_buf.buf, 0, sizeof(cipher->ctr_buf.buf));
@@ -341,7 +341,7 @@ aes_cipher_free(aes_cnt_cipher_t *cipher)
STMT_END
#else
#define UPDATE_CTR_BUF(c, n)
-#endif
+#endif /* defined(USING_COUNTER_VARS) */
/* Helper function to use EVP with openssl's counter-mode wrapper. */
static void
@@ -396,10 +396,10 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const uint8_t *iv)
cipher->counter2 = ntohl(get_uint32(iv+4));
cipher->counter1 = ntohl(get_uint32(iv+8));
cipher->counter0 = ntohl(get_uint32(iv+12));
-#endif
+#endif /* defined(USING_COUNTER_VARS) */
cipher->pos = 0;
memcpy(cipher->ctr_buf.buf, iv, 16);
}
-#endif
+#endif /* defined(USE_EVP_AES_CTR) */
diff --git a/src/common/aes.h b/src/common/aes.h
index 6fd9c3ea16..1e400a56e0 100644
--- a/src/common/aes.h
+++ b/src/common/aes.h
@@ -23,5 +23,5 @@ void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len);
int evaluate_evp_for_aes(int force_value);
int evaluate_ctr_for_aes(void);
-#endif
+#endif /* !defined(TOR_AES_H) */
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
index 0f0fa857bf..f2498b2aa6 100644
--- a/src/common/backtrace.c
+++ b/src/common/backtrace.c
@@ -37,7 +37,7 @@
#include <sys/ucontext.h>
#elif defined(HAVE_UCONTEXT_H)
#include <ucontext.h>
-#endif
+#endif /* defined(HAVE_CYGWIN_SIGNAL_H) || ... */
#define EXPOSE_CLEAN_BACKTRACE
#include "backtrace.h"
@@ -81,16 +81,16 @@ clean_backtrace(void **stack, size_t depth, const ucontext_t *ctx)
const size_t n = 2;
#else
const size_t n = 1;
-#endif
+#endif /* defined(__linux__) || ... */
if (depth <= n)
return;
stack[n] = (void*) ctx->PC_FROM_UCONTEXT;
-#else
+#else /* !(defined(PC_FROM_UCONTEXT)) */
(void) depth;
(void) ctx;
(void) stack;
-#endif
+#endif /* defined(PC_FROM_UCONTEXT) */
}
/** Log a message <b>msg</b> at <b>severity</b> in <b>domain</b>, and follow
@@ -202,7 +202,7 @@ remove_bt_handler(void)
{
tor_mutex_uninit(&cb_buf_mutex);
}
-#endif
+#endif /* defined(USE_BACKTRACE) */
#ifdef NO_BACKTRACE_IMPL
void
@@ -221,7 +221,7 @@ static void
remove_bt_handler(void)
{
}
-#endif
+#endif /* defined(NO_BACKTRACE_IMPL) */
/** Set up code to handle generating error messages on crashes. */
int
diff --git a/src/common/backtrace.h b/src/common/backtrace.h
index 8cd1dcf06c..3d0ab8a90a 100644
--- a/src/common/backtrace.h
+++ b/src/common/backtrace.h
@@ -15,7 +15,7 @@ void clean_up_backtrace_handler(void);
defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION)
void clean_backtrace(void **stack, size_t depth, const ucontext_t *ctx);
#endif
-#endif
+#endif /* defined(EXPOSE_CLEAN_BACKTRACE) */
-#endif
+#endif /* !defined(TOR_BACKTRACE_H) */
diff --git a/src/common/buffers.c b/src/common/buffers.c
new file mode 100644
index 0000000000..c45e13d551
--- /dev/null
+++ b/src/common/buffers.c
@@ -0,0 +1,1077 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file buffers.c
+ * \brief Implements a generic buffer interface.
+ *
+ * A buf_t is a (fairly) opaque byte-oriented FIFO that can read to or flush
+ * from memory, sockets, file descriptors, TLS connections, or another buf_t.
+ * Buffers are implemented as linked lists of memory chunks.
+ *
+ * All socket-backed and TLS-based connection_t objects have a pair of
+ * buffers: one for incoming data, and one for outcoming data. These are fed
+ * and drained from functions in connection.c, trigged by events that are
+ * monitored in main.c.
+ **/
+
+#define BUFFERS_PRIVATE
+#include "orconfig.h"
+#include <stddef.h>
+#include "buffers.h"
+#include "compat.h"
+#include "compress.h"
+#include "util.h"
+#include "torint.h"
+#include "torlog.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+//#define PARANOIA
+
+#ifdef PARANOIA
+/** Helper: If PARANOIA is defined, assert that the buffer in local variable
+ * <b>buf</b> is well-formed. */
+#define check() STMT_BEGIN buf_assert_ok(buf); STMT_END
+#else
+#define check() STMT_NIL
+#endif /* defined(PARANOIA) */
+
+/* Implementation notes:
+ *
+ * After flirting with memmove, and dallying with ring-buffers, we're finally
+ * getting up to speed with the 1970s and implementing buffers as a linked
+ * list of small chunks. Each buffer has such a list; data is removed from
+ * the head of the list, and added at the tail. The list is singly linked,
+ * and the buffer keeps a pointer to the head and the tail.
+ *
+ * Every chunk, except the tail, contains at least one byte of data. Data in
+ * each chunk is contiguous.
+ *
+ * When you need to treat the first N characters on a buffer as a contiguous
+ * string, use the buf_pullup function to make them so. Don't do this more
+ * than necessary.
+ *
+ * The major free Unix kernels have handled buffers like this since, like,
+ * forever.
+ */
+
+/* Chunk manipulation functions */
+
+#define CHUNK_HEADER_LEN offsetof(chunk_t, mem[0])
+
+/* We leave this many NUL bytes at the end of the buffer. */
+#ifdef DISABLE_MEMORY_SENTINELS
+#define SENTINEL_LEN 0
+#else
+#define SENTINEL_LEN 4
+#endif
+
+/* Header size plus NUL bytes at the end */
+#define CHUNK_OVERHEAD (CHUNK_HEADER_LEN + SENTINEL_LEN)
+
+/** Return the number of bytes needed to allocate a chunk to hold
+ * <b>memlen</b> bytes. */
+#define CHUNK_ALLOC_SIZE(memlen) (CHUNK_OVERHEAD + (memlen))
+/** Return the number of usable bytes in a chunk allocated with
+ * malloc(<b>memlen</b>). */
+#define CHUNK_SIZE_WITH_ALLOC(memlen) ((memlen) - CHUNK_OVERHEAD)
+
+#define DEBUG_SENTINEL
+
+#if defined(DEBUG_SENTINEL) && !defined(DISABLE_MEMORY_SENTINELS)
+#define DBG_S(s) s
+#else
+#define DBG_S(s) (void)0
+#endif
+
+#ifdef DISABLE_MEMORY_SENTINELS
+#define CHUNK_SET_SENTINEL(chunk, alloclen) STMT_NIL
+#else
+#define CHUNK_SET_SENTINEL(chunk, alloclen) do { \
+ uint8_t *a = (uint8_t*) &(chunk)->mem[(chunk)->memlen]; \
+ DBG_S(uint8_t *b = &((uint8_t*)(chunk))[(alloclen)-SENTINEL_LEN]); \
+ DBG_S(tor_assert(a == b)); \
+ memset(a,0,SENTINEL_LEN); \
+ } while (0)
+#endif /* defined(DISABLE_MEMORY_SENTINELS) */
+
+/** Move all bytes stored in <b>chunk</b> to the front of <b>chunk</b>->mem,
+ * to free up space at the end. */
+static inline void
+chunk_repack(chunk_t *chunk)
+{
+ if (chunk->datalen && chunk->data != &chunk->mem[0]) {
+ memmove(chunk->mem, chunk->data, chunk->datalen);
+ }
+ chunk->data = &chunk->mem[0];
+}
+
+/** Keep track of total size of allocated chunks for consistency asserts */
+static size_t total_bytes_allocated_in_chunks = 0;
+static void
+buf_chunk_free_unchecked(chunk_t *chunk)
+{
+ if (!chunk)
+ return;
+#ifdef DEBUG_CHUNK_ALLOC
+ tor_assert(CHUNK_ALLOC_SIZE(chunk->memlen) == chunk->DBG_alloc);
+#endif
+ tor_assert(total_bytes_allocated_in_chunks >=
+ CHUNK_ALLOC_SIZE(chunk->memlen));
+ total_bytes_allocated_in_chunks -= CHUNK_ALLOC_SIZE(chunk->memlen);
+ tor_free(chunk);
+}
+static inline chunk_t *
+chunk_new_with_alloc_size(size_t alloc)
+{
+ chunk_t *ch;
+ ch = tor_malloc(alloc);
+ ch->next = NULL;
+ ch->datalen = 0;
+#ifdef DEBUG_CHUNK_ALLOC
+ ch->DBG_alloc = alloc;
+#endif
+ ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc);
+ total_bytes_allocated_in_chunks += alloc;
+ ch->data = &ch->mem[0];
+ CHUNK_SET_SENTINEL(ch, alloc);
+ return ch;
+}
+
+/** Expand <b>chunk</b> until it can hold <b>sz</b> bytes, and return a
+ * new pointer to <b>chunk</b>. Old pointers are no longer valid. */
+static inline chunk_t *
+chunk_grow(chunk_t *chunk, size_t sz)
+{
+ off_t offset;
+ const size_t memlen_orig = chunk->memlen;
+ const size_t orig_alloc = CHUNK_ALLOC_SIZE(memlen_orig);
+ const size_t new_alloc = CHUNK_ALLOC_SIZE(sz);
+ tor_assert(sz > chunk->memlen);
+ offset = chunk->data - chunk->mem;
+ chunk = tor_realloc(chunk, new_alloc);
+ chunk->memlen = sz;
+ chunk->data = chunk->mem + offset;
+#ifdef DEBUG_CHUNK_ALLOC
+ tor_assert(chunk->DBG_alloc == orig_alloc);
+ chunk->DBG_alloc = new_alloc;
+#endif
+ total_bytes_allocated_in_chunks += new_alloc - orig_alloc;
+ CHUNK_SET_SENTINEL(chunk, new_alloc);
+ return chunk;
+}
+
+/** Every chunk should take up at least this many bytes. */
+#define MIN_CHUNK_ALLOC 256
+/** No chunk should take up more than this many bytes. */
+#define MAX_CHUNK_ALLOC 65536
+
+/** Return the allocation size we'd like to use to hold <b>target</b>
+ * bytes. */
+size_t
+buf_preferred_chunk_size(size_t target)
+{
+ tor_assert(target <= SIZE_T_CEILING - CHUNK_OVERHEAD);
+ if (CHUNK_ALLOC_SIZE(target) >= MAX_CHUNK_ALLOC)
+ return CHUNK_ALLOC_SIZE(target);
+ size_t sz = MIN_CHUNK_ALLOC;
+ while (CHUNK_SIZE_WITH_ALLOC(sz) < target) {
+ sz <<= 1;
+ }
+ return sz;
+}
+
+/** Collapse data from the first N chunks from <b>buf</b> into buf->head,
+ * growing it as necessary, until buf->head has the first <b>bytes</b> bytes
+ * of data from the buffer, or until buf->head has all the data in <b>buf</b>.
+ *
+ * Set *<b>head_out</b> to point to the first byte of available data, and
+ * *<b>len_out</b> to the number of bytes of data available at
+ * *<b>head_out</b>. Note that *<b>len_out</b> may be more or less than
+ * <b>bytes</b>, depending on the number of bytes available.
+ */
+void
+buf_pullup(buf_t *buf, size_t bytes, const char **head_out, size_t *len_out)
+{
+ chunk_t *dest, *src;
+ size_t capacity;
+ if (!buf->head) {
+ *head_out = NULL;
+ *len_out = 0;
+ return;
+ }
+
+ check();
+ if (buf->datalen < bytes)
+ bytes = buf->datalen;
+
+ capacity = bytes;
+ if (buf->head->datalen >= bytes) {
+ *head_out = buf->head->data;
+ *len_out = buf->head->datalen;
+ return;
+ }
+
+ if (buf->head->memlen >= capacity) {
+ /* We don't need to grow the first chunk, but we might need to repack it.*/
+ size_t needed = capacity - buf->head->datalen;
+ if (CHUNK_REMAINING_CAPACITY(buf->head) < needed)
+ chunk_repack(buf->head);
+ tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= needed);
+ } else {
+ chunk_t *newhead;
+ size_t newsize;
+ /* We need to grow the chunk. */
+ chunk_repack(buf->head);
+ newsize = CHUNK_SIZE_WITH_ALLOC(buf_preferred_chunk_size(capacity));
+ newhead = chunk_grow(buf->head, newsize);
+ tor_assert(newhead->memlen >= capacity);
+ if (newhead != buf->head) {
+ if (buf->tail == buf->head)
+ buf->tail = newhead;
+ buf->head = newhead;
+ }
+ }
+
+ dest = buf->head;
+ while (dest->datalen < bytes) {
+ size_t n = bytes - dest->datalen;
+ src = dest->next;
+ tor_assert(src);
+ if (n >= src->datalen) {
+ memcpy(CHUNK_WRITE_PTR(dest), src->data, src->datalen);
+ dest->datalen += src->datalen;
+ dest->next = src->next;
+ if (buf->tail == src)
+ buf->tail = dest;
+ buf_chunk_free_unchecked(src);
+ } else {
+ memcpy(CHUNK_WRITE_PTR(dest), src->data, n);
+ dest->datalen += n;
+ src->data += n;
+ src->datalen -= n;
+ tor_assert(dest->datalen == bytes);
+ }
+ }
+
+ check();
+ *head_out = buf->head->data;
+ *len_out = buf->head->datalen;
+}
+
+#ifdef TOR_UNIT_TESTS
+/* Write sz bytes from cp into a newly allocated buffer buf.
+ * Returns NULL when passed a NULL cp or zero sz.
+ * Asserts on failure: only for use in unit tests.
+ * buf must be freed using buf_free(). */
+buf_t *
+buf_new_with_data(const char *cp, size_t sz)
+{
+ /* Validate arguments */
+ if (!cp || sz <= 0) {
+ return NULL;
+ }
+
+ tor_assert(sz < SSIZE_T_CEILING);
+
+ /* Allocate a buffer */
+ buf_t *buf = buf_new_with_capacity(sz);
+ tor_assert(buf);
+ buf_assert_ok(buf);
+ tor_assert(!buf->head);
+
+ /* Allocate a chunk that is sz bytes long */
+ buf->head = chunk_new_with_alloc_size(CHUNK_ALLOC_SIZE(sz));
+ buf->tail = buf->head;
+ tor_assert(buf->head);
+ buf_assert_ok(buf);
+ tor_assert(buf_allocation(buf) >= sz);
+
+ /* Copy the data and size the buffers */
+ tor_assert(sz <= buf_slack(buf));
+ tor_assert(sz <= CHUNK_REMAINING_CAPACITY(buf->head));
+ memcpy(&buf->head->mem[0], cp, sz);
+ buf->datalen = sz;
+ buf->head->datalen = sz;
+ buf->head->data = &buf->head->mem[0];
+ buf_assert_ok(buf);
+
+ /* Make sure everything is large enough */
+ tor_assert(buf_allocation(buf) >= sz);
+ tor_assert(buf_allocation(buf) >= buf_datalen(buf) + buf_slack(buf));
+ /* Does the buffer implementation allocate more than the requested size?
+ * (for example, by rounding up). If so, these checks will fail. */
+ tor_assert(buf_datalen(buf) == sz);
+ tor_assert(buf_slack(buf) == 0);
+
+ return buf;
+}
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/** Remove the first <b>n</b> bytes from buf. */
+void
+buf_drain(buf_t *buf, size_t n)
+{
+ tor_assert(buf->datalen >= n);
+ while (n) {
+ tor_assert(buf->head);
+ if (buf->head->datalen > n) {
+ buf->head->datalen -= n;
+ buf->head->data += n;
+ buf->datalen -= n;
+ return;
+ } else {
+ chunk_t *victim = buf->head;
+ n -= victim->datalen;
+ buf->datalen -= victim->datalen;
+ buf->head = victim->next;
+ if (buf->tail == victim)
+ buf->tail = NULL;
+ buf_chunk_free_unchecked(victim);
+ }
+ }
+ check();
+}
+
+/** Create and return a new buf with default chunk capacity <b>size</b>.
+ */
+buf_t *
+buf_new_with_capacity(size_t size)
+{
+ buf_t *b = buf_new();
+ b->default_chunk_size = buf_preferred_chunk_size(size);
+ return b;
+}
+
+/** Allocate and return a new buffer with default capacity. */
+buf_t *
+buf_new(void)
+{
+ buf_t *buf = tor_malloc_zero(sizeof(buf_t));
+ buf->magic = BUFFER_MAGIC;
+ buf->default_chunk_size = 4096;
+ return buf;
+}
+
+size_t
+buf_get_default_chunk_size(const buf_t *buf)
+{
+ return buf->default_chunk_size;
+}
+
+/** Remove all data from <b>buf</b>. */
+void
+buf_clear(buf_t *buf)
+{
+ chunk_t *chunk, *next;
+ buf->datalen = 0;
+ for (chunk = buf->head; chunk; chunk = next) {
+ next = chunk->next;
+ buf_chunk_free_unchecked(chunk);
+ }
+ buf->head = buf->tail = NULL;
+}
+
+/** Return the number of bytes stored in <b>buf</b> */
+MOCK_IMPL(size_t,
+buf_datalen, (const buf_t *buf))
+{
+ return buf->datalen;
+}
+
+/** Return the total length of all chunks used in <b>buf</b>. */
+size_t
+buf_allocation(const buf_t *buf)
+{
+ size_t total = 0;
+ const chunk_t *chunk;
+ for (chunk = buf->head; chunk; chunk = chunk->next) {
+ total += CHUNK_ALLOC_SIZE(chunk->memlen);
+ }
+ return total;
+}
+
+/** Return the number of bytes that can be added to <b>buf</b> without
+ * performing any additional allocation. */
+size_t
+buf_slack(const buf_t *buf)
+{
+ if (!buf->tail)
+ return 0;
+ else
+ return CHUNK_REMAINING_CAPACITY(buf->tail);
+}
+
+/** Release storage held by <b>buf</b>. */
+void
+buf_free(buf_t *buf)
+{
+ if (!buf)
+ return;
+
+ buf_clear(buf);
+ buf->magic = 0xdeadbeef;
+ tor_free(buf);
+}
+
+/** Return a new copy of <b>in_chunk</b> */
+static chunk_t *
+chunk_copy(const chunk_t *in_chunk)
+{
+ chunk_t *newch = tor_memdup(in_chunk, CHUNK_ALLOC_SIZE(in_chunk->memlen));
+ total_bytes_allocated_in_chunks += CHUNK_ALLOC_SIZE(in_chunk->memlen);
+#ifdef DEBUG_CHUNK_ALLOC
+ newch->DBG_alloc = CHUNK_ALLOC_SIZE(in_chunk->memlen);
+#endif
+ newch->next = NULL;
+ if (in_chunk->data) {
+ off_t offset = in_chunk->data - in_chunk->mem;
+ newch->data = newch->mem + offset;
+ }
+ return newch;
+}
+
+/** Return a new copy of <b>buf</b> */
+buf_t *
+buf_copy(const buf_t *buf)
+{
+ chunk_t *ch;
+ buf_t *out = buf_new();
+ out->default_chunk_size = buf->default_chunk_size;
+ for (ch = buf->head; ch; ch = ch->next) {
+ chunk_t *newch = chunk_copy(ch);
+ if (out->tail) {
+ out->tail->next = newch;
+ out->tail = newch;
+ } else {
+ out->head = out->tail = newch;
+ }
+ }
+ out->datalen = buf->datalen;
+ return out;
+}
+
+/** Append a new chunk with enough capacity to hold <b>capacity</b> bytes to
+ * the tail of <b>buf</b>. If <b>capped</b>, don't allocate a chunk bigger
+ * than MAX_CHUNK_ALLOC. */
+chunk_t *
+buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
+{
+ chunk_t *chunk;
+
+ if (CHUNK_ALLOC_SIZE(capacity) < buf->default_chunk_size) {
+ chunk = chunk_new_with_alloc_size(buf->default_chunk_size);
+ } else if (capped && CHUNK_ALLOC_SIZE(capacity) > MAX_CHUNK_ALLOC) {
+ chunk = chunk_new_with_alloc_size(MAX_CHUNK_ALLOC);
+ } else {
+ chunk = chunk_new_with_alloc_size(buf_preferred_chunk_size(capacity));
+ }
+
+ chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec();
+
+ if (buf->tail) {
+ tor_assert(buf->head);
+ buf->tail->next = chunk;
+ buf->tail = chunk;
+ } else {
+ tor_assert(!buf->head);
+ buf->head = buf->tail = chunk;
+ }
+ check();
+ return chunk;
+}
+
+/** Return the age of the oldest chunk in the buffer <b>buf</b>, in
+ * milliseconds. Requires the current monotonic time, in truncated msec,
+ * as its input <b>now</b>.
+ */
+uint32_t
+buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now)
+{
+ if (buf->head) {
+ return now - buf->head->inserted_time;
+ } else {
+ return 0;
+ }
+}
+
+size_t
+buf_get_total_allocation(void)
+{
+ return total_bytes_allocated_in_chunks;
+}
+
+/** Read up to <b>at_most</b> bytes from the socket <b>fd</b> into
+ * <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set
+ * *<b>reached_eof</b> to 1. Return -1 on error, 0 on eof or blocking,
+ * and the number of bytes read otherwise. */
+static inline int
+read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
+ int *reached_eof, int *socket_error)
+{
+ ssize_t read_result;
+ if (at_most > CHUNK_REMAINING_CAPACITY(chunk))
+ at_most = CHUNK_REMAINING_CAPACITY(chunk);
+ read_result = tor_socket_recv(fd, CHUNK_WRITE_PTR(chunk), at_most, 0);
+
+ if (read_result < 0) {
+ int e = tor_socket_errno(fd);
+ if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
+#ifdef _WIN32
+ if (e == WSAENOBUFS)
+ log_warn(LD_NET,"recv() failed: WSAENOBUFS. Not enough ram?");
+#endif
+ *socket_error = e;
+ return -1;
+ }
+ return 0; /* would block. */
+ } else if (read_result == 0) {
+ log_debug(LD_NET,"Encountered eof on fd %d", (int)fd);
+ *reached_eof = 1;
+ return 0;
+ } else { /* actually got bytes. */
+ buf->datalen += read_result;
+ chunk->datalen += read_result;
+ log_debug(LD_NET,"Read %ld bytes. %d on inbuf.", (long)read_result,
+ (int)buf->datalen);
+ tor_assert(read_result < INT_MAX);
+ return (int)read_result;
+ }
+}
+
+/** Read from socket <b>s</b>, writing onto end of <b>buf</b>. Read at most
+ * <b>at_most</b> bytes, growing the buffer as necessary. If recv() returns 0
+ * (because of EOF), set *<b>reached_eof</b> to 1 and return 0. Return -1 on
+ * error; else return the number of bytes read.
+ */
+/* XXXX indicate "read blocked" somehow? */
+int
+buf_read_from_socket(buf_t *buf, tor_socket_t s, size_t at_most,
+ int *reached_eof,
+ int *socket_error)
+{
+ /* XXXX It's stupid to overload the return values for these functions:
+ * "error status" and "number of bytes read" are not mutually exclusive.
+ */
+ int r = 0;
+ size_t total_read = 0;
+
+ check();
+ tor_assert(reached_eof);
+ tor_assert(SOCKET_OK(s));
+
+ if (BUG(buf->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf->datalen >= INT_MAX - at_most))
+ return -1;
+
+ while (at_most > total_read) {
+ size_t readlen = at_most - total_read;
+ chunk_t *chunk;
+ if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
+ chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
+ if (readlen > chunk->memlen)
+ readlen = chunk->memlen;
+ } else {
+ size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
+ chunk = buf->tail;
+ if (cap < readlen)
+ readlen = cap;
+ }
+
+ r = read_to_chunk(buf, chunk, s, readlen, reached_eof, socket_error);
+ check();
+ if (r < 0)
+ return r; /* Error */
+ tor_assert(total_read+r < INT_MAX);
+ total_read += r;
+ if ((size_t)r < readlen) { /* eof, block, or no more to read. */
+ break;
+ }
+ }
+ return (int)total_read;
+}
+
+/** Helper for buf_flush_to_socket(): try to write <b>sz</b> bytes from chunk
+ * <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>. On success, deduct
+ * the bytes written from *<b>buf_flushlen</b>. Return the number of bytes
+ * written on success, 0 on blocking, -1 on failure.
+ */
+static inline int
+flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz,
+ size_t *buf_flushlen)
+{
+ ssize_t write_result;
+
+ if (sz > chunk->datalen)
+ sz = chunk->datalen;
+ write_result = tor_socket_send(s, chunk->data, sz, 0);
+
+ if (write_result < 0) {
+ int e = tor_socket_errno(s);
+ if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
+#ifdef _WIN32
+ if (e == WSAENOBUFS)
+ log_warn(LD_NET,"write() failed: WSAENOBUFS. Not enough ram?");
+#endif
+ return -1;
+ }
+ log_debug(LD_NET,"write() would block, returning.");
+ return 0;
+ } else {
+ *buf_flushlen -= write_result;
+ buf_drain(buf, write_result);
+ tor_assert(write_result < INT_MAX);
+ return (int)write_result;
+ }
+}
+
+/** Write data from <b>buf</b> to the socket <b>s</b>. Write at most
+ * <b>sz</b> bytes, decrement *<b>buf_flushlen</b> by
+ * the number of bytes actually written, and remove the written bytes
+ * from the buffer. Return the number of bytes written on success,
+ * -1 on failure. Return 0 if write() would block.
+ */
+int
+buf_flush_to_socket(buf_t *buf, tor_socket_t s, size_t sz,
+ size_t *buf_flushlen)
+{
+ /* XXXX It's stupid to overload the return values for these functions:
+ * "error status" and "number of bytes flushed" are not mutually exclusive.
+ */
+ int r;
+ size_t flushed = 0;
+ tor_assert(buf_flushlen);
+ tor_assert(SOCKET_OK(s));
+ if (BUG(*buf_flushlen > buf->datalen)) {
+ *buf_flushlen = buf->datalen;
+ }
+ if (BUG(sz > *buf_flushlen)) {
+ sz = *buf_flushlen;
+ }
+
+ check();
+ while (sz) {
+ size_t flushlen0;
+ tor_assert(buf->head);
+ if (buf->head->datalen >= sz)
+ flushlen0 = sz;
+ else
+ flushlen0 = buf->head->datalen;
+
+ r = flush_chunk(s, buf, buf->head, flushlen0, buf_flushlen);
+ check();
+ if (r < 0)
+ return r;
+ flushed += r;
+ sz -= r;
+ if (r == 0 || (size_t)r < flushlen0) /* can't flush any more now. */
+ break;
+ }
+ tor_assert(flushed < INT_MAX);
+ return (int)flushed;
+}
+
+/** Append <b>string_len</b> bytes from <b>string</b> to the end of
+ * <b>buf</b>.
+ *
+ * Return the new length of the buffer on success, -1 on failure.
+ */
+int
+buf_add(buf_t *buf, const char *string, size_t string_len)
+{
+ if (!string_len)
+ return (int)buf->datalen;
+ check();
+
+ if (BUG(buf->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf->datalen >= INT_MAX - string_len))
+ return -1;
+
+ while (string_len) {
+ size_t copy;
+ if (!buf->tail || !CHUNK_REMAINING_CAPACITY(buf->tail))
+ buf_add_chunk_with_capacity(buf, string_len, 1);
+
+ copy = CHUNK_REMAINING_CAPACITY(buf->tail);
+ if (copy > string_len)
+ copy = string_len;
+ memcpy(CHUNK_WRITE_PTR(buf->tail), string, copy);
+ string_len -= copy;
+ string += copy;
+ buf->datalen += copy;
+ buf->tail->datalen += copy;
+ }
+
+ check();
+ tor_assert(buf->datalen < INT_MAX);
+ return (int)buf->datalen;
+}
+
+/** Helper: copy the first <b>string_len</b> bytes from <b>buf</b>
+ * onto <b>string</b>.
+ */
+void
+buf_peek(const buf_t *buf, char *string, size_t string_len)
+{
+ chunk_t *chunk;
+
+ tor_assert(string);
+ /* make sure we don't ask for too much */
+ tor_assert(string_len <= buf->datalen);
+ /* buf_assert_ok(buf); */
+
+ chunk = buf->head;
+ while (string_len) {
+ size_t copy = string_len;
+ tor_assert(chunk);
+ if (chunk->datalen < copy)
+ copy = chunk->datalen;
+ memcpy(string, chunk->data, copy);
+ string_len -= copy;
+ string += copy;
+ chunk = chunk->next;
+ }
+}
+
+/** Remove <b>string_len</b> bytes from the front of <b>buf</b>, and store
+ * them into <b>string</b>. Return the new buffer size. <b>string_len</b>
+ * must be \<= the number of bytes on the buffer.
+ */
+int
+buf_get_bytes(buf_t *buf, char *string, size_t string_len)
+{
+ /* There must be string_len bytes in buf; write them onto string,
+ * then memmove buf back (that is, remove them from buf).
+ *
+ * Return the number of bytes still on the buffer. */
+
+ check();
+ buf_peek(buf, string, string_len);
+ buf_drain(buf, string_len);
+ check();
+ tor_assert(buf->datalen < INT_MAX);
+ return (int)buf->datalen;
+}
+
+/** Move up to *<b>buf_flushlen</b> bytes from <b>buf_in</b> to
+ * <b>buf_out</b>, and modify *<b>buf_flushlen</b> appropriately.
+ * Return the number of bytes actually copied.
+ */
+int
+buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen)
+{
+ /* We can do way better here, but this doesn't turn up in any profiles. */
+ char b[4096];
+ size_t cp, len;
+
+ if (BUG(buf_out->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf_out->datalen >= INT_MAX - *buf_flushlen))
+ return -1;
+
+ len = *buf_flushlen;
+ if (len > buf_in->datalen)
+ len = buf_in->datalen;
+
+ cp = len; /* Remember the number of bytes we intend to copy. */
+ tor_assert(cp < INT_MAX);
+ while (len) {
+ /* This isn't the most efficient implementation one could imagine, since
+ * it does two copies instead of 1, but I kinda doubt that this will be
+ * critical path. */
+ size_t n = len > sizeof(b) ? sizeof(b) : len;
+ buf_get_bytes(buf_in, b, n);
+ buf_add(buf_out, b, n);
+ len -= n;
+ }
+ *buf_flushlen -= cp;
+ return (int)cp;
+}
+
+/** Internal structure: represents a position in a buffer. */
+typedef struct buf_pos_t {
+ const chunk_t *chunk; /**< Which chunk are we pointing to? */
+ int pos;/**< Which character inside the chunk's data are we pointing to? */
+ size_t chunk_pos; /**< Total length of all previous chunks. */
+} buf_pos_t;
+
+/** Initialize <b>out</b> to point to the first character of <b>buf</b>.*/
+static void
+buf_pos_init(const buf_t *buf, buf_pos_t *out)
+{
+ out->chunk = buf->head;
+ out->pos = 0;
+ out->chunk_pos = 0;
+}
+
+/** Advance <b>out</b> to the first appearance of <b>ch</b> at the current
+ * position of <b>out</b>, or later. Return -1 if no instances are found;
+ * otherwise returns the absolute position of the character. */
+static off_t
+buf_find_pos_of_char(char ch, buf_pos_t *out)
+{
+ const chunk_t *chunk;
+ int pos;
+ tor_assert(out);
+ if (out->chunk) {
+ if (out->chunk->datalen) {
+ tor_assert(out->pos < (off_t)out->chunk->datalen);
+ } else {
+ tor_assert(out->pos == 0);
+ }
+ }
+ pos = out->pos;
+ for (chunk = out->chunk; chunk; chunk = chunk->next) {
+ char *cp = memchr(chunk->data+pos, ch, chunk->datalen - pos);
+ if (cp) {
+ out->chunk = chunk;
+ tor_assert(cp - chunk->data < INT_MAX);
+ out->pos = (int)(cp - chunk->data);
+ return out->chunk_pos + out->pos;
+ } else {
+ out->chunk_pos += chunk->datalen;
+ pos = 0;
+ }
+ }
+ return -1;
+}
+
+/** Advance <b>pos</b> by a single character, if there are any more characters
+ * in the buffer. Returns 0 on success, -1 on failure. */
+static inline int
+buf_pos_inc(buf_pos_t *pos)
+{
+ ++pos->pos;
+ if (pos->pos == (off_t)pos->chunk->datalen) {
+ if (!pos->chunk->next)
+ return -1;
+ pos->chunk_pos += pos->chunk->datalen;
+ pos->chunk = pos->chunk->next;
+ pos->pos = 0;
+ }
+ return 0;
+}
+
+/** Return true iff the <b>n</b>-character string in <b>s</b> appears
+ * (verbatim) at <b>pos</b>. */
+static int
+buf_matches_at_pos(const buf_pos_t *pos, const char *s, size_t n)
+{
+ buf_pos_t p;
+ if (!n)
+ return 1;
+
+ memcpy(&p, pos, sizeof(p));
+
+ while (1) {
+ char ch = p.chunk->data[p.pos];
+ if (ch != *s)
+ return 0;
+ ++s;
+ /* If we're out of characters that don't match, we match. Check this
+ * _before_ we test incrementing pos, in case we're at the end of the
+ * string. */
+ if (--n == 0)
+ return 1;
+ if (buf_pos_inc(&p)<0)
+ return 0;
+ }
+}
+
+/** Return the first position in <b>buf</b> at which the <b>n</b>-character
+ * string <b>s</b> occurs, or -1 if it does not occur. */
+int
+buf_find_string_offset(const buf_t *buf, const char *s, size_t n)
+{
+ buf_pos_t pos;
+ buf_pos_init(buf, &pos);
+ while (buf_find_pos_of_char(*s, &pos) >= 0) {
+ if (buf_matches_at_pos(&pos, s, n)) {
+ tor_assert(pos.chunk_pos + pos.pos < INT_MAX);
+ return (int)(pos.chunk_pos + pos.pos);
+ } else {
+ if (buf_pos_inc(&pos)<0)
+ return -1;
+ }
+ }
+ return -1;
+}
+
+/** Return 1 iff <b>buf</b> starts with <b>cmd</b>. <b>cmd</b> must be a null
+ * terminated string, of no more than PEEK_BUF_STARTSWITH_MAX bytes. */
+int
+buf_peek_startswith(const buf_t *buf, const char *cmd)
+{
+ char tmp[PEEK_BUF_STARTSWITH_MAX];
+ size_t clen = strlen(cmd);
+ if (clen == 0)
+ return 1;
+ if (BUG(clen > sizeof(tmp)))
+ return 0;
+ if (buf->datalen < clen)
+ return 0;
+ buf_peek(buf, tmp, clen);
+ return fast_memeq(tmp, cmd, clen);
+}
+
+/** Return the index within <b>buf</b> at which <b>ch</b> first appears,
+ * or -1 if <b>ch</b> does not appear on buf. */
+static off_t
+buf_find_offset_of_char(buf_t *buf, char ch)
+{
+ chunk_t *chunk;
+ off_t offset = 0;
+ for (chunk = buf->head; chunk; chunk = chunk->next) {
+ char *cp = memchr(chunk->data, ch, chunk->datalen);
+ if (cp)
+ return offset + (cp - chunk->data);
+ else
+ offset += chunk->datalen;
+ }
+ return -1;
+}
+
+/** Try to read a single LF-terminated line from <b>buf</b>, and write it
+ * (including the LF), NUL-terminated, into the *<b>data_len</b> byte buffer
+ * at <b>data_out</b>. Set *<b>data_len</b> to the number of bytes in the
+ * line, not counting the terminating NUL. Return 1 if we read a whole line,
+ * return 0 if we don't have a whole line yet, and return -1 if the line
+ * length exceeds *<b>data_len</b>.
+ */
+int
+buf_get_line(buf_t *buf, char *data_out, size_t *data_len)
+{
+ size_t sz;
+ off_t offset;
+
+ if (!buf->head)
+ return 0;
+
+ offset = buf_find_offset_of_char(buf, '\n');
+ if (offset < 0)
+ return 0;
+ sz = (size_t) offset;
+ if (sz+2 > *data_len) {
+ *data_len = sz + 2;
+ return -1;
+ }
+ buf_get_bytes(buf, data_out, sz+1);
+ data_out[sz+1] = '\0';
+ *data_len = sz+1;
+ return 1;
+}
+
+/** Compress or uncompress the <b>data_len</b> bytes in <b>data</b> using the
+ * compression state <b>state</b>, appending the result to <b>buf</b>. If
+ * <b>done</b> is true, flush the data in the state and finish the
+ * compression/uncompression. Return -1 on failure, 0 on success. */
+int
+buf_add_compress(buf_t *buf, tor_compress_state_t *state,
+ const char *data, size_t data_len,
+ const int done)
+{
+ char *next;
+ size_t old_avail, avail;
+ int over = 0;
+
+ do {
+ int need_new_chunk = 0;
+ if (!buf->tail || ! CHUNK_REMAINING_CAPACITY(buf->tail)) {
+ size_t cap = data_len / 4;
+ buf_add_chunk_with_capacity(buf, cap, 1);
+ }
+ next = CHUNK_WRITE_PTR(buf->tail);
+ avail = old_avail = CHUNK_REMAINING_CAPACITY(buf->tail);
+ switch (tor_compress_process(state, &next, &avail,
+ &data, &data_len, done)) {
+ case TOR_COMPRESS_DONE:
+ over = 1;
+ break;
+ case TOR_COMPRESS_ERROR:
+ return -1;
+ case TOR_COMPRESS_OK:
+ if (data_len == 0) {
+ tor_assert_nonfatal(!done);
+ over = 1;
+ }
+ break;
+ case TOR_COMPRESS_BUFFER_FULL:
+ if (avail) {
+ /* The compression module says we need more room
+ * (TOR_COMPRESS_BUFFER_FULL). Start a new chunk automatically,
+ * whether were going to or not. */
+ need_new_chunk = 1;
+ }
+ if (data_len == 0 && !done) {
+ /* We've consumed all the input data, though, so there's no
+ * point in forging ahead right now. */
+ over = 1;
+ }
+ break;
+ }
+ buf->datalen += old_avail - avail;
+ buf->tail->datalen += old_avail - avail;
+ if (need_new_chunk) {
+ buf_add_chunk_with_capacity(buf, data_len/4, 1);
+ }
+
+ } while (!over);
+ check();
+ return 0;
+}
+
+/** Set *<b>output</b> to contain a copy of the data in *<b>input</b> */
+int
+buf_set_to_copy(buf_t **output,
+ const buf_t *input)
+{
+ if (*output)
+ buf_free(*output);
+ *output = buf_copy(input);
+ return 0;
+}
+
+/** Log an error and exit if <b>buf</b> is corrupted.
+ */
+void
+buf_assert_ok(buf_t *buf)
+{
+ tor_assert(buf);
+ tor_assert(buf->magic == BUFFER_MAGIC);
+
+ if (! buf->head) {
+ tor_assert(!buf->tail);
+ tor_assert(buf->datalen == 0);
+ } else {
+ chunk_t *ch;
+ size_t total = 0;
+ tor_assert(buf->tail);
+ for (ch = buf->head; ch; ch = ch->next) {
+ total += ch->datalen;
+ tor_assert(ch->datalen <= ch->memlen);
+ tor_assert(ch->data >= &ch->mem[0]);
+ tor_assert(ch->data <= &ch->mem[0]+ch->memlen);
+ if (ch->data == &ch->mem[0]+ch->memlen) {
+ /* LCOV_EXCL_START */
+ static int warned = 0;
+ if (! warned) {
+ log_warn(LD_BUG, "Invariant violation in buf.c related to #15083");
+ warned = 1;
+ }
+ /* LCOV_EXCL_STOP */
+ }
+ tor_assert(ch->data+ch->datalen <= &ch->mem[0] + ch->memlen);
+ if (!ch->next)
+ tor_assert(ch == buf->tail);
+ }
+ tor_assert(buf->datalen == total);
+ }
+}
+
diff --git a/src/or/buffers.h b/src/common/buffers.h
index 23b58a571a..1eaa5f2d04 100644
--- a/src/or/buffers.h
+++ b/src/common/buffers.h
@@ -12,8 +12,15 @@
#ifndef TOR_BUFFERS_H
#define TOR_BUFFERS_H
+#include "compat.h"
+#include "compat.h"
+#include "torint.h"
#include "testsupport.h"
+typedef struct buf_t buf_t;
+
+struct tor_compress_state_t;
+
buf_t *buf_new(void);
buf_t *buf_new_with_capacity(size_t size);
size_t buf_get_default_chunk_size(const buf_t *buf);
@@ -28,47 +35,39 @@ size_t buf_slack(const buf_t *buf);
uint32_t buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now);
size_t buf_get_total_allocation(void);
-int read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof,
- int *socket_error);
-int read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf);
+int buf_read_from_socket(buf_t *buf, tor_socket_t s, size_t at_most,
+ int *reached_eof,
+ int *socket_error);
-int flush_buf(tor_socket_t s, buf_t *buf, size_t sz, size_t *buf_flushlen);
-int flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t sz, size_t *buf_flushlen);
+int buf_flush_to_socket(buf_t *buf, tor_socket_t s, size_t sz,
+ size_t *buf_flushlen);
-int write_to_buf(const char *string, size_t string_len, buf_t *buf);
-int write_to_buf_compress(buf_t *buf, tor_compress_state_t *state,
+int buf_add(buf_t *buf, const char *string, size_t string_len);
+int buf_add_compress(buf_t *buf, struct tor_compress_state_t *state,
const char *data, size_t data_len, int done);
-int move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen);
-int fetch_from_buf(char *string, size_t string_len, buf_t *buf);
-int fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto);
-int fetch_from_buf_http(buf_t *buf,
- char **headers_out, size_t max_headerlen,
- char **body_out, size_t *body_used, size_t max_bodylen,
- int force_complete);
-socks_request_t *socks_request_new(void);
-void socks_request_free(socks_request_t *req);
-int fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
- int log_sockstype, int safe_socks);
-int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason);
-int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
-
-int peek_buf_has_control0_command(buf_t *buf);
-
-int fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out);
+int buf_move_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen);
+void buf_peek(const buf_t *buf, char *string, size_t string_len);
+void buf_drain(buf_t *buf, size_t n);
+int buf_get_bytes(buf_t *buf, char *string, size_t string_len);
+int buf_get_line(buf_t *buf, char *data_out, size_t *data_len);
+
+#define PEEK_BUF_STARTSWITH_MAX 16
+int buf_peek_startswith(const buf_t *buf, const char *cmd);
int buf_set_to_copy(buf_t **output,
const buf_t *input);
-void assert_buf_ok(buf_t *buf);
+void buf_assert_ok(buf_t *buf);
+
+int buf_find_string_offset(const buf_t *buf, const char *s, size_t n);
+void buf_pullup(buf_t *buf, size_t bytes,
+ const char **head_out, size_t *len_out);
#ifdef BUFFERS_PRIVATE
-STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n);
-STATIC void buf_pullup(buf_t *buf, size_t bytes);
#ifdef TOR_UNIT_TESTS
-void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz);
buf_t *buf_new_with_data(const char *cp, size_t sz);
#endif
-STATIC size_t preferred_chunk_size(size_t target);
+size_t buf_preferred_chunk_size(size_t target);
#define DEBUG_CHUNK_ALLOC
/** A single chunk on a buffer. */
@@ -98,12 +97,29 @@ struct buf_t {
chunk_t *head; /**< First chunk in the list, or NULL for none. */
chunk_t *tail; /**< Last chunk in the list, or NULL for none. */
};
-#endif
-#ifdef BUFFERS_PRIVATE
-STATIC int buf_http_find_content_length(const char *headers, size_t headerlen,
- size_t *result_out);
-#endif
-
-#endif
+chunk_t *buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped);
+/** If a read onto the end of a chunk would be smaller than this number, then
+ * just start a new chunk. */
+#define MIN_READ_LEN 8
+
+/** Return the number of bytes that can be written onto <b>chunk</b> without
+ * running out of space. */
+static inline size_t
+CHUNK_REMAINING_CAPACITY(const chunk_t *chunk)
+{
+ return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen);
+}
+
+/** Return the next character in <b>chunk</b> onto which data can be appended.
+ * If the chunk is full, this might be off the end of chunk->mem. */
+static inline char *
+CHUNK_WRITE_PTR(chunk_t *chunk)
+{
+ return chunk->data + chunk->datalen;
+}
+
+#endif /* defined(BUFFERS_PRIVATE) */
+
+#endif /* !defined(TOR_BUFFERS_H) */
diff --git a/src/common/buffers_tls.c b/src/common/buffers_tls.c
new file mode 100644
index 0000000000..041f78b818
--- /dev/null
+++ b/src/common/buffers_tls.c
@@ -0,0 +1,179 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define BUFFERS_PRIVATE
+#include "orconfig.h"
+#include <stddef.h>
+#include "buffers.h"
+#include "buffers_tls.h"
+#include "compat.h"
+#include "compress.h"
+#include "util.h"
+#include "torint.h"
+#include "torlog.h"
+#include "tortls.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/** As read_to_chunk(), but return (negative) error code on error, blocking,
+ * or TLS, and the number of bytes read otherwise. */
+static inline int
+read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
+ size_t at_most)
+{
+ int read_result;
+
+ tor_assert(CHUNK_REMAINING_CAPACITY(chunk) >= at_most);
+ read_result = tor_tls_read(tls, CHUNK_WRITE_PTR(chunk), at_most);
+ if (read_result < 0)
+ return read_result;
+ buf->datalen += read_result;
+ chunk->datalen += read_result;
+ return read_result;
+}
+
+/** As read_to_buf, but reads from a TLS connection, and returns a TLS
+ * status value rather than the number of bytes read.
+ *
+ * Using TLS on OR connections complicates matters in two ways.
+ *
+ * First, a TLS stream has its own read buffer independent of the
+ * connection's read buffer. (TLS needs to read an entire frame from
+ * the network before it can decrypt any data. Thus, trying to read 1
+ * byte from TLS can require that several KB be read from the network
+ * and decrypted. The extra data is stored in TLS's decrypt buffer.)
+ * Because the data hasn't been read by Tor (it's still inside the TLS),
+ * this means that sometimes a connection "has stuff to read" even when
+ * poll() didn't return POLLIN. The tor_tls_get_pending_bytes function is
+ * used in connection.c to detect TLS objects with non-empty internal
+ * buffers and read from them again.
+ *
+ * Second, the TLS stream's events do not correspond directly to network
+ * events: sometimes, before a TLS stream can read, the network must be
+ * ready to write -- or vice versa.
+ */
+int
+buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most)
+{
+ int r = 0;
+ size_t total_read = 0;
+
+ check_no_tls_errors();
+
+ if (BUG(buf->datalen >= INT_MAX))
+ return -1;
+ if (BUG(buf->datalen >= INT_MAX - at_most))
+ return -1;
+
+ while (at_most > total_read) {
+ size_t readlen = at_most - total_read;
+ chunk_t *chunk;
+ if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
+ chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
+ if (readlen > chunk->memlen)
+ readlen = chunk->memlen;
+ } else {
+ size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
+ chunk = buf->tail;
+ if (cap < readlen)
+ readlen = cap;
+ }
+
+ r = read_to_chunk_tls(buf, chunk, tls, readlen);
+ if (r < 0)
+ return r; /* Error */
+ tor_assert(total_read+r < INT_MAX);
+ total_read += r;
+ if ((size_t)r < readlen) /* eof, block, or no more to read. */
+ break;
+ }
+ return (int)total_read;
+}
+
+/** Helper for buf_flush_to_tls(): try to write <b>sz</b> bytes from chunk
+ * <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>. (Tries to write
+ * more if there is a forced pending write size.) On success, deduct the
+ * bytes written from *<b>buf_flushlen</b>. Return the number of bytes
+ * written on success, and a TOR_TLS error code on failure or blocking.
+ */
+static inline int
+flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk,
+ size_t sz, size_t *buf_flushlen)
+{
+ int r;
+ size_t forced;
+ char *data;
+
+ forced = tor_tls_get_forced_write_size(tls);
+ if (forced > sz)
+ sz = forced;
+ if (chunk) {
+ data = chunk->data;
+ tor_assert(sz <= chunk->datalen);
+ } else {
+ data = NULL;
+ tor_assert(sz == 0);
+ }
+ r = tor_tls_write(tls, data, sz);
+ if (r < 0)
+ return r;
+ if (*buf_flushlen > (size_t)r)
+ *buf_flushlen -= r;
+ else
+ *buf_flushlen = 0;
+ buf_drain(buf, r);
+ log_debug(LD_NET,"flushed %d bytes, %d ready to flush, %d remain.",
+ r,(int)*buf_flushlen,(int)buf->datalen);
+ return r;
+}
+
+/** As buf_flush_to_socket(), but writes data to a TLS connection. Can write
+ * more than <b>flushlen</b> bytes.
+ */
+int
+buf_flush_to_tls(buf_t *buf, tor_tls_t *tls, size_t flushlen,
+ size_t *buf_flushlen)
+{
+ int r;
+ size_t flushed = 0;
+ ssize_t sz;
+ tor_assert(buf_flushlen);
+ if (BUG(*buf_flushlen > buf->datalen)) {
+ *buf_flushlen = buf->datalen;
+ }
+ if (BUG(flushlen > *buf_flushlen)) {
+ flushlen = *buf_flushlen;
+ }
+ sz = (ssize_t) flushlen;
+
+ /* we want to let tls write even if flushlen is zero, because it might
+ * have a partial record pending */
+ check_no_tls_errors();
+
+ do {
+ size_t flushlen0;
+ if (buf->head) {
+ if ((ssize_t)buf->head->datalen >= sz)
+ flushlen0 = sz;
+ else
+ flushlen0 = buf->head->datalen;
+ } else {
+ flushlen0 = 0;
+ }
+
+ r = flush_chunk_tls(tls, buf, buf->head, flushlen0, buf_flushlen);
+ if (r < 0)
+ return r;
+ flushed += r;
+ sz -= r;
+ if (r == 0) /* Can't flush any more now. */
+ break;
+ } while (sz > 0);
+ tor_assert(flushed < INT_MAX);
+ return (int)flushed;
+}
+
diff --git a/src/common/buffers_tls.h b/src/common/buffers_tls.h
new file mode 100644
index 0000000000..2f9fda45a0
--- /dev/null
+++ b/src/common/buffers_tls.h
@@ -0,0 +1,19 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_BUFFERS_TLS_H
+#define TOR_BUFFERS_TLS_H
+
+struct buf_t;
+struct tor_tls_t;
+
+int buf_read_from_tls(struct buf_t *buf,
+ struct tor_tls_t *tls, size_t at_most);
+int buf_flush_to_tls(struct buf_t *buf, struct tor_tls_t *tls,
+ size_t sz, size_t *buf_flushlen);
+
+#endif /* !defined(TOR_BUFFERS_TLS_H) */
+
diff --git a/src/common/compat.c b/src/common/compat.c
index 4a1f0013c6..83bb707e17 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -88,12 +88,12 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
while (cnt--)
*vcptr++ = 0;
}
-#endif
+#endif /* defined(HAVE_DECL_SECUREZEROMEMORY) && !HAVE_DECL_SECUREZEROMEMORY */
#elif defined(HAVE_READPASSPHRASE_H)
#include <readpassphrase.h>
#else
#include "tor_readpassphrase.h"
-#endif
+#endif /* defined(_WIN32) || ... */
/* Includes for the process attaching prevention */
#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
@@ -102,7 +102,7 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#elif defined(__APPLE__)
#include <sys/types.h>
#include <sys/ptrace.h>
-#endif
+#endif /* defined(HAVE_SYS_PRCTL_H) && defined(__linux__) || ... */
#ifdef HAVE_NETDB_H
#include <netdb.h>
@@ -161,7 +161,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode)
* are running on one without. */
if (errno != EINVAL)
return -1;
-#endif
+#endif /* defined(O_CLOEXEC) */
log_debug(LD_FS, "Opening %s with flags %x", p, flags);
fd = open(p, flags, mode);
@@ -173,7 +173,7 @@ tor_open_cloexec(const char *path, int flags, unsigned mode)
return -1;
}
}
-#endif
+#endif /* defined(FD_CLOEXEC) */
return fd;
}
@@ -191,7 +191,7 @@ tor_fopen_cloexec(const char *path, const char *mode)
return NULL;
}
}
-#endif
+#endif /* defined(FD_CLOEXEC) */
return result;
}
@@ -212,7 +212,8 @@ tor_rename(const char *path_old, const char *path_new)
#define COMPAT_HAS_MMAN_AND_PAGESIZE
#endif
-#if defined(COMPAT_HAS_MMAN_AND_PAGESIZE) || defined(RUNNING_DOXYGEN)
+#if defined(COMPAT_HAS_MMAN_AND_PAGESIZE) || \
+ defined(RUNNING_DOXYGEN)
/** Try to create a memory mapping for <b>filename</b> and return it. On
* failure, return NULL. Sets errno properly, using ERANGE to mean
* "empty file". */
@@ -450,7 +451,7 @@ tor_munmap_file(tor_mmap_t *handle)
/* Can't fail in this mmap()/munmap()-free case */
return 0;
}
-#endif
+#endif /* defined(COMPAT_HAS_MMAN_AND_PAGESIZE) || ... || ... */
/** Replacement for snprintf. Differs from platform snprintf in two
* ways: First, always NUL-terminates its output. Second, always
@@ -590,7 +591,7 @@ tor_vasprintf(char **strp, const char *fmt, va_list args)
}
*strp = strp_tmp;
return len;
-#endif
+#endif /* defined(HAVE_VASPRINTF) || ... */
}
/** Given <b>hlen</b> bytes at <b>haystack</b> and <b>nlen</b> bytes at
@@ -636,7 +637,7 @@ tor_memmem(const void *_haystack, size_t hlen,
}
}
return NULL;
-#endif
+#endif /* defined(HAVE_MEMMEM) && (!defined(__GNUC__) || __GNUC__ >= 2) */
}
/**
@@ -774,7 +775,7 @@ tor_fix_source_file(const char *fname)
}
return r;
}
-#endif
+#endif /* defined(_WIN32) */
/**
* Read a 16-bit value beginning at <b>cp</b>. Equivalent to
@@ -868,7 +869,7 @@ replace_file(const char *from, const char *to)
return -1;
}
return tor_rename(from,to);
-#endif
+#endif /* !defined(_WIN32) */
}
/** Change <b>fname</b>'s modification time to now. */
@@ -954,7 +955,7 @@ tor_lockfile_lock(const char *filename, int blocking, int *locked_out)
return NULL;
}
}
-#endif
+#endif /* defined(_WIN32) || ... */
result = tor_malloc(sizeof(tor_lockfile_t));
result->filename = tor_strdup(filename);
@@ -982,7 +983,7 @@ tor_lockfile_unlock(tor_lockfile_t *lockfile)
}
#else
/* Closing the lockfile is sufficient. */
-#endif
+#endif /* defined(_WIN32) || ... */
close(lockfile->fd);
lockfile->fd = -1;
@@ -1030,9 +1031,9 @@ tor_fd_seekend(int fd)
* no need to worry. */
if (rc < 0 && errno == ESPIPE)
rc = 0;
-#endif
+#endif /* defined(ESPIPE) */
return (rc < 0) ? -1 : 0;
-#endif
+#endif /* defined(_WIN32) */
}
/** Move <b>fd</b> to position <b>pos</b> in the file. Return -1 on error, 0
@@ -1071,7 +1072,7 @@ tor_ftruncate(int fd)
static bitarray_t *open_sockets = NULL;
/** The size of <b>open_sockets</b>, in bits. */
static int max_socket = -1;
-#endif
+#endif /* defined(DEBUG_SOCKET_COUNTING) */
/** Count of number of sockets currently open. (Undercounts sockets opened by
* eventdns and libevent.) */
@@ -1141,7 +1142,7 @@ tor_close_socket,(tor_socket_t s))
tor_assert(open_sockets && s <= max_socket);
bitarray_clear(open_sockets, s);
}
-#endif
+#endif /* defined(DEBUG_SOCKET_COUNTING) */
if (r == 0) {
--n_sockets_open;
} else {
@@ -1151,7 +1152,7 @@ tor_close_socket,(tor_socket_t s))
#else
if (r != EBADF)
--n_sockets_open; // LCOV_EXCL_LINE -- EIO and EINTR too hard to force.
-#endif
+#endif /* defined(_WIN32) */
r = -1;
}
@@ -1184,9 +1185,9 @@ mark_socket_open(tor_socket_t s)
}
bitarray_set(open_sockets, s);
}
-#else
+#else /* !(defined(DEBUG_SOCKET_COUNTING)) */
#define mark_socket_open(s) STMT_NIL
-#endif
+#endif /* defined(DEBUG_SOCKET_COUNTING) */
/** @} */
/** As socket(), but counts the number of open sockets. */
@@ -1244,7 +1245,7 @@ tor_open_socket_with_extensions(int domain, int type, int protocol,
* support, we are running on one without. */
if (errno != EINVAL)
return s;
-#endif /* SOCK_CLOEXEC && SOCK_NONBLOCK */
+#endif /* defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) */
s = socket(domain, type, protocol);
if (! SOCKET_OK(s))
@@ -1258,9 +1259,9 @@ tor_open_socket_with_extensions(int domain, int type, int protocol,
return TOR_INVALID_SOCKET;
}
}
-#else
+#else /* !(defined(FD_CLOEXEC)) */
(void)cloexec;
-#endif
+#endif /* defined(FD_CLOEXEC) */
if (nonblock) {
if (set_socket_nonblocking(s) == -1) {
@@ -1316,7 +1317,8 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
return TOR_INVALID_SOCKET;
}
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) \
+ && defined(SOCK_NONBLOCK)
int ext_flags = (cloexec ? SOCK_CLOEXEC : 0) |
(nonblock ? SOCK_NONBLOCK : 0);
s = accept4(sockfd, addr, len, ext_flags);
@@ -1328,7 +1330,7 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
* we are missing SOCK_CLOEXEC/SOCK_NONBLOCK support. */
if (errno != EINVAL && errno != ENOSYS)
return s;
-#endif
+#endif /* defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) ... */
s = accept(sockfd, addr, len);
if (!SOCKET_OK(s))
@@ -1342,9 +1344,9 @@ tor_accept_socket_with_extensions(tor_socket_t sockfd, struct sockaddr *addr,
return TOR_INVALID_SOCKET;
}
}
-#else
+#else /* !(defined(FD_CLOEXEC)) */
(void)cloexec;
-#endif
+#endif /* defined(FD_CLOEXEC) */
if (nonblock) {
if (set_socket_nonblocking(s) == -1) {
@@ -1404,7 +1406,7 @@ set_socket_nonblocking(tor_socket_t sock)
log_warn(LD_NET, "Couldn't set file status flags: %s", strerror(errno));
return -1;
}
-#endif
+#endif /* defined(_WIN32) */
return 0;
}
@@ -1442,7 +1444,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
* are running on one without. */
if (errno != EINVAL)
return -errno;
-#endif
+#endif /* defined(SOCK_CLOEXEC) */
r = socketpair(family, type, protocol, fd);
if (r < 0)
@@ -1465,7 +1467,7 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return -errno;
}
}
-#endif
+#endif /* defined(FD_CLOEXEC) */
goto sockets_ok; /* So that sockets_ok will not be unused. */
sockets_ok:
@@ -1481,9 +1483,9 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
socket_accounting_unlock();
return 0;
-#else
+#else /* !(defined(HAVE_SOCKETPAIR) && !defined(_WIN32)) */
return tor_ersatz_socketpair(family, type, protocol, fd);
-#endif
+#endif /* defined(HAVE_SOCKETPAIR) && !defined(_WIN32) */
}
#ifdef NEED_ERSATZ_SOCKETPAIR
@@ -1640,7 +1642,7 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
#undef SIZEOF_SOCKADDR
-#endif
+#endif /* defined(NEED_ERSATZ_SOCKETPAIR) */
/* Return the maximum number of allowed sockets. */
int
@@ -1694,7 +1696,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
#else
const char *platform = "unknown platforms with no getrlimit()";
const unsigned long MAX_CONNECTIONS = 15000;
-#endif
+#endif /* defined(CYGWIN) || defined(__CYGWIN__) || ... */
log_fn(LOG_INFO, LD_NET,
"This platform is missing getrlimit(). Proceeding.");
if (limit > MAX_CONNECTIONS) {
@@ -1705,7 +1707,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
return -1;
}
limit = MAX_CONNECTIONS;
-#else /* HAVE_GETRLIMIT */
+#else /* !(!defined(HAVE_GETRLIMIT)) */
struct rlimit rlim;
if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) {
@@ -1755,7 +1757,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
couldnt_set = 0;
}
}
-#endif /* OPEN_MAX */
+#endif /* defined(OPEN_MAX) */
if (couldnt_set) {
log_warn(LD_CONFIG,"Couldn't set maximum number of file descriptors: %s",
strerror(setrlimit_errno));
@@ -1763,7 +1765,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
}
/* leave some overhead for logs, etc, */
limit = rlim.rlim_cur;
-#endif /* HAVE_GETRLIMIT */
+#endif /* !defined(HAVE_GETRLIMIT) */
if (limit > INT_MAX)
limit = INT_MAX;
@@ -1801,7 +1803,7 @@ log_credential_status(void)
"UID is %u (real), %u (effective), %u (saved)",
(unsigned)ruid, (unsigned)euid, (unsigned)suid);
}
-#else
+#else /* !(defined(HAVE_GETRESUID)) */
/* getresuid is not present on MacOS X, so we can't get the saved (E)UID */
ruid = getuid();
euid = geteuid();
@@ -1810,7 +1812,7 @@ log_credential_status(void)
log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
"UID is %u (real), %u (effective), unknown (saved)",
(unsigned)ruid, (unsigned)euid);
-#endif
+#endif /* defined(HAVE_GETRESUID) */
/* log GIDs */
#ifdef HAVE_GETRESGID
@@ -1822,7 +1824,7 @@ log_credential_status(void)
"GID is %u (real), %u (effective), %u (saved)",
(unsigned)rgid, (unsigned)egid, (unsigned)sgid);
}
-#else
+#else /* !(defined(HAVE_GETRESGID)) */
/* getresgid is not present on MacOS X, so we can't get the saved (E)GID */
rgid = getgid();
egid = getegid();
@@ -1830,7 +1832,7 @@ log_credential_status(void)
log_fn(CREDENTIAL_LOG_LEVEL, LD_GENERAL,
"GID is %u (real), %u (effective), unknown (saved)",
(unsigned)rgid, (unsigned)egid);
-#endif
+#endif /* defined(HAVE_GETRESGID) */
/* log supplementary groups */
sup_gids_size = 64;
@@ -1870,7 +1872,7 @@ log_credential_status(void)
return 0;
}
-#endif
+#endif /* !defined(_WIN32) */
#ifndef _WIN32
/** Cached struct from the last getpwname() call we did successfully. */
@@ -1970,7 +1972,7 @@ tor_getpwuid(uid_t uid)
return NULL;
}
-#endif
+#endif /* !defined(_WIN32) */
/** Return true iff we were compiled with capability support, and capabilities
* seem to work. **/
@@ -1983,9 +1985,9 @@ have_capability_support(void)
return 0;
cap_free(caps);
return 1;
-#else
+#else /* !(defined(HAVE_LINUX_CAPABILITIES)) */
return 0;
-#endif
+#endif /* defined(HAVE_LINUX_CAPABILITIES) */
}
#ifdef HAVE_LINUX_CAPABILITIES
@@ -2044,7 +2046,7 @@ drop_capabilities(int pre_setuid)
return 0;
}
-#endif
+#endif /* defined(HAVE_LINUX_CAPABILITIES) */
/** Call setuid and setgid to run as <b>user</b> and switch to their
* primary group. Return 0 on success. On failure, log and return -1.
@@ -2094,13 +2096,13 @@ switch_id(const char *user, const unsigned flags)
if (drop_capabilities(1))
return -1;
}
-#else
+#else /* !(defined(HAVE_LINUX_CAPABILITIES)) */
(void) keep_bindlow;
if (warn_if_no_caps) {
log_warn(LD_CONFIG, "KeepBindCapabilities set, but no capability support "
"on this system.");
}
-#endif
+#endif /* defined(HAVE_LINUX_CAPABILITIES) */
/* Properly switch egid,gid,euid,uid here or bail out */
if (setgroups(1, &pw->pw_gid)) {
@@ -2160,7 +2162,7 @@ switch_id(const char *user, const unsigned flags)
if (drop_capabilities(0))
return -1;
}
-#endif
+#endif /* defined(HAVE_LINUX_CAPABILITIES) */
#if !defined(CYGWIN) && !defined(__CYGWIN__)
/* If we tried to drop privilege to a group/user other than root, attempt to
@@ -2184,7 +2186,7 @@ switch_id(const char *user, const unsigned flags)
return -1;
}
}
-#endif
+#endif /* !defined(CYGWIN) && !defined(__CYGWIN__) */
/* Check what really happened */
if (log_credential_status()) {
@@ -2193,8 +2195,8 @@ switch_id(const char *user, const unsigned flags)
have_already_switched_id = 1; /* mark success so we never try again */
-#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL)
-#ifdef PR_SET_DUMPABLE
+#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && \
+ defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
if (pw->pw_uid) {
/* Re-enable core dumps if we're not running as root. */
log_info(LD_CONFIG, "Re-enabling coredumps");
@@ -2202,17 +2204,16 @@ switch_id(const char *user, const unsigned flags)
log_warn(LD_CONFIG, "Unable to re-enable coredumps: %s",strerror(errno));
}
}
-#endif
-#endif
+#endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && ... */
return 0;
-#else
+#else /* !(!defined(_WIN32)) */
(void)user;
(void)flags;
log_warn(LD_CONFIG, "Switching users is unsupported on your OS.");
return -1;
-#endif
+#endif /* !defined(_WIN32) */
}
/* We only use the linux prctl for now. There is no Win32 support; this may
@@ -2235,35 +2236,32 @@ switch_id(const char *user, const unsigned flags)
int
tor_disable_debugger_attach(void)
{
- int r, attempted;
- r = -1;
- attempted = 0;
+ int r = -1;
log_debug(LD_CONFIG,
"Attemping to disable debugger attachment to Tor for "
"unprivileged users.");
-#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) && defined(HAVE_PRCTL)
-#ifdef PR_SET_DUMPABLE
- attempted = 1;
+#if defined(__linux__) && defined(HAVE_SYS_PRCTL_H) \
+ && defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
+#define TRIED_TO_DISABLE
r = prctl(PR_SET_DUMPABLE, 0);
-#endif
-#endif
-#if defined(__APPLE__) && defined(PT_DENY_ATTACH)
- if (r < 0) {
- attempted = 1;
- r = ptrace(PT_DENY_ATTACH, 0, 0, 0);
- }
-#endif
+#elif defined(__APPLE__) && defined(PT_DENY_ATTACH)
+#define TRIED_TO_ATTACH
+ r = ptrace(PT_DENY_ATTACH, 0, 0, 0);
+#endif /* defined(__linux__) && defined(HAVE_SYS_PRCTL_H) ... || ... */
// XXX: TODO - Mac OS X has dtrace and this may be disabled.
// XXX: TODO - Windows probably has something similar
- if (r == 0 && attempted) {
+#ifdef TRIED_TO_DISABLE
+ if (r == 0) {
log_debug(LD_CONFIG,"Debugger attachment disabled for "
"unprivileged users.");
return 1;
- } else if (attempted) {
+ } else {
log_warn(LD_CONFIG, "Unable to disable debugger attaching: %s",
strerror(errno));
}
+#endif /* defined(TRIED_TO_DISABLE) */
+#undef TRIED_TO_DISABLE
return r;
}
@@ -2282,7 +2280,7 @@ get_user_homedir(const char *username)
}
return tor_strdup(pw->pw_dir);
}
-#endif
+#endif /* defined(HAVE_PWD_H) */
/** Modify <b>fname</b> to contain the name of its parent directory. Doesn't
* actually examine the filesystem; does a purely syntactic modification.
@@ -2310,7 +2308,7 @@ get_parent_directory(char *fname)
if (fname[0] && fname[1] == ':') {
fname += 2;
}
-#endif
+#endif /* defined(_WIN32) */
/* Now we want to remove all path-separators at the end of the string,
* and to remove the end of the string starting with the path separator
* before the last non-path-separator. In perl, this would be
@@ -2349,17 +2347,36 @@ get_parent_directory(char *fname)
static char *
alloc_getcwd(void)
{
-#ifdef PATH_MAX
-#define MAX_CWD PATH_MAX
-#else
-#define MAX_CWD 4096
-#endif
+#ifdef HAVE_GET_CURRENT_DIR_NAME
+ /* Glibc makes this nice and simple for us. */
+ char *cwd = get_current_dir_name();
+ char *result = NULL;
+ if (cwd) {
+ /* We make a copy here, in case tor_malloc() is not malloc(). */
+ result = tor_strdup(cwd);
+ raw_free(cwd); // alias for free to avoid tripping check-spaces.
+ }
+ return result;
+#else /* !(defined(HAVE_GET_CURRENT_DIR_NAME)) */
+ size_t size = 1024;
+ char *buf = NULL;
+ char *ptr = NULL;
+
+ while (ptr == NULL) {
+ buf = tor_realloc(buf, size);
+ ptr = getcwd(buf, size);
- char path_buf[MAX_CWD];
- char *path = getcwd(path_buf, sizeof(path_buf));
- return path ? tor_strdup(path) : NULL;
+ if (ptr == NULL && errno != ERANGE) {
+ tor_free(buf);
+ return NULL;
+ }
+
+ size *= 2;
+ }
+ return buf;
+#endif /* defined(HAVE_GET_CURRENT_DIR_NAME) */
}
-#endif
+#endif /* !defined(_WIN32) */
/** Expand possibly relative path <b>fname</b> to an absolute path.
* Return a newly allocated string, possibly equal to <b>fname</b>. */
@@ -2375,7 +2392,7 @@ make_path_absolute(char *fname)
if (absfname_malloced) raw_free(absfname_malloced);
return absfname;
-#else
+#else /* !(defined(_WIN32)) */
char *absfname = NULL, *path = NULL;
tor_assert(fname);
@@ -2398,7 +2415,7 @@ make_path_absolute(char *fname)
}
}
return absfname;
-#endif
+#endif /* defined(_WIN32) */
}
#ifndef HAVE__NSGETENVIRON
@@ -2407,8 +2424,8 @@ make_path_absolute(char *fname)
#ifndef RUNNING_DOXYGEN
extern char **environ;
#endif
-#endif
-#endif
+#endif /* !defined(HAVE_EXTERN_ENVIRON_DECLARED) */
+#endif /* !defined(HAVE__NSGETENVIRON) */
/** Return the current environment. This is a portable replacement for
* 'environ'. */
@@ -2420,9 +2437,9 @@ get_environment(void)
* when we do a mostly-static build on OSX 10.7, the resulting binary won't
* work on OSX 10.6. */
return *_NSGetEnviron();
-#else
+#else /* !(defined(HAVE__NSGETENVIRON)) */
return environ;
-#endif
+#endif /* defined(HAVE__NSGETENVIRON) */
}
/** Get name of current host and write it to <b>name</b> array, whose
@@ -2563,6 +2580,7 @@ tor_inet_pton(int af, const char *src, void *dst)
int gapPos = -1, i, setWords=0;
const char *dot = strchr(src, '.');
const char *eow; /* end of words. */
+ memset(words, 0xf8, sizeof(words));
if (dot == src)
return 0;
else if (!dot)
@@ -2600,7 +2618,7 @@ tor_inet_pton(int af, const char *src, void *dst)
long r = strtol(src, &next, 16);
if (next == NULL || next == src) {
/* The 'next == src' error case can happen on versions of openbsd
- * where treats "0xfoo" as an error, rather than as "0" followed by
+ * which treat "0xfoo" as an error, rather than as "0" followed by
* "xfoo". */
return 0;
}
@@ -2699,7 +2717,7 @@ get_uname,(void))
/* (Linux says 0 is success, Solaris says 1 is success) */
strlcpy(uname_result, u.sysname, sizeof(uname_result));
} else
-#endif
+#endif /* defined(HAVE_UNAME) */
{
#ifdef _WIN32
OSVERSIONINFOEX info;
@@ -2761,12 +2779,12 @@ get_uname,(void))
info.wProductType == VER_NT_DOMAIN_CONTROLLER) {
strlcat(uname_result, " [server]", sizeof(uname_result));
}
-#endif
-#else
+#endif /* defined(VER_NT_SERVER) */
+#else /* !(defined(_WIN32)) */
/* LCOV_EXCL_START -- can't provoke uname failure */
strlcpy(uname_result, "Unknown platform", sizeof(uname_result));
/* LCOV_EXCL_STOP */
-#endif
+#endif /* defined(_WIN32) */
}
uname_result_is_set = 1;
}
@@ -2822,7 +2840,7 @@ compute_num_cpus_impl(void)
return -1;
#else
return -1;
-#endif
+#endif /* defined(_WIN32) || ... */
}
#define MAX_DETECTABLE_CPUS 16
@@ -2985,7 +3003,7 @@ tor_localtime_r(const time_t *timep, struct tm *result)
memcpy(result, r, sizeof(struct tm));
return correct_tm(1, timep, result, r);
}
-#endif
+#endif /* defined(HAVE_LOCALTIME_R) || ... */
/** @} */
/** @{ */
@@ -3028,9 +3046,13 @@ tor_gmtime_r(const time_t *timep, struct tm *result)
memcpy(result, r, sizeof(struct tm));
return correct_tm(0, timep, result, r);
}
-#endif
+#endif /* defined(HAVE_GMTIME_R) || ... */
#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
+#define HAVE_UNIX_MLOCKALL
+#endif
+
+#ifdef HAVE_UNIX_MLOCKALL
/** Attempt to raise the current and max rlimit to infinity for our process.
* This only needs to be done once and can probably only be done when we have
* not already dropped privileges.
@@ -3061,7 +3083,7 @@ tor_set_max_memlock(void)
return 0;
}
-#endif
+#endif /* defined(HAVE_UNIX_MLOCKALL) */
/** Attempt to lock all current and all future memory pages.
* This should only be called once and while we're privileged.
@@ -3086,7 +3108,7 @@ tor_mlockall(void)
* http://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx
*/
-#if defined(HAVE_MLOCKALL) && HAVE_DECL_MLOCKALL && defined(RLIMIT_MEMLOCK)
+#ifdef HAVE_UNIX_MLOCKALL
if (tor_set_max_memlock() == 0) {
log_debug(LD_GENERAL, "RLIMIT_MEMLOCK is now set to RLIM_INFINITY.");
}
@@ -3107,10 +3129,10 @@ tor_mlockall(void)
"pages: %s", strerror(errno));
return -1;
}
-#else
+#else /* !(defined(HAVE_UNIX_MLOCKALL)) */
log_warn(LD_GENERAL, "Unable to lock memory pages. mlockall() unsupported?");
return -1;
-#endif
+#endif /* defined(HAVE_UNIX_MLOCKALL) */
}
/**
@@ -3138,7 +3160,7 @@ tor_socket_errno(tor_socket_t sock)
}
return err;
}
-#endif
+#endif /* defined(_WIN32) */
#if defined(_WIN32)
#define E(code, s) { code, (s " [" #code " ]") }
@@ -3214,7 +3236,7 @@ tor_socket_strerror(int e)
}
return strerror(e);
}
-#endif
+#endif /* defined(_WIN32) */
/** Called before we make any calls to network-related functions.
* (Some operating systems require their network libraries to be
@@ -3240,7 +3262,7 @@ network_init(void)
/* WSAData.iMaxSockets might show the max sockets we're allowed to use.
* We might use it to complain if we're trying to be a server but have
* too few sockets available. */
-#endif
+#endif /* defined(_WIN32) */
return 0;
}
@@ -3276,9 +3298,9 @@ format_win32_error(DWORD err)
result = tor_malloc(len);
wcstombs(result,str,len);
result[len-1] = '\0';
-#else
+#else /* !(defined(UNICODE)) */
result = tor_strdup(str);
-#endif
+#endif /* defined(UNICODE) */
} else {
result = tor_strdup("<unformattable error>");
}
@@ -3287,7 +3309,7 @@ format_win32_error(DWORD err)
}
return result;
}
-#endif
+#endif /* defined(_WIN32) */
#if defined(HW_PHYSMEM64)
/* This appears to be an OpenBSD thing */
@@ -3295,7 +3317,7 @@ format_win32_error(DWORD err)
#elif defined(HW_MEMSIZE)
/* OSX defines this one */
#define INT64_HW_MEM HW_MEMSIZE
-#endif
+#endif /* defined(HW_PHYSMEM64) || ... */
/**
* Helper: try to detect the total system memory, and return it. On failure,
@@ -3328,8 +3350,8 @@ get_total_system_memory_impl(void)
tor_free(s);
return result * 1024;
- err:
/* LCOV_EXCL_START Can't reach this unless proc is broken. */
+ err:
tor_free(s);
close(fd);
return 0;
@@ -3369,7 +3391,7 @@ get_total_system_memory_impl(void)
#else
/* I have no clue. */
return 0;
-#endif
+#endif /* defined(__linux__) || ... */
}
/**
@@ -3402,7 +3424,7 @@ get_total_system_memory(size_t *mem_out)
* size_t. */
m = SIZE_MAX;
}
-#endif
+#endif /* SIZE_MAX != UINT64_MAX */
*mem_out = mem_cached = (size_t) m;
@@ -3483,7 +3505,7 @@ tor_getpass(const char *prompt, char *output, size_t buflen)
return r;
#else
#error "No implementation for tor_getpass found!"
-#endif
+#endif /* defined(HAVE_READPASSPHRASE) || ... */
}
/** Return the amount of free disk space we have permission to use, in
@@ -3523,6 +3545,6 @@ tor_get_avail_disk_space(const char *path)
(void)path;
errno = ENOSYS;
return -1;
-#endif
+#endif /* defined(HAVE_STATVFS) || ... */
}
diff --git a/src/common/compat.h b/src/common/compat.h
index 473ad2b957..fee9e6587d 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -50,8 +50,8 @@
* clang rejects because it is off the end of a less-than-3. Clang hates this,
* even though those references never actually happen. */
# undef strcmp
-# endif
-#endif
+#endif /* __has_feature(address_sanitizer) */
+#endif /* defined(__has_feature) */
#include <stdio.h>
#include <errno.h>
@@ -76,13 +76,13 @@
__attribute__ ((format(printf, formatIdx, firstArg)))
#else
#define CHECK_PRINTF(formatIdx, firstArg)
-#endif
+#endif /* defined(__GNUC__) */
#ifdef __GNUC__
#define CHECK_SCANF(formatIdx, firstArg) \
__attribute__ ((format(scanf, formatIdx, firstArg)))
#else
#define CHECK_SCANF(formatIdx, firstArg)
-#endif
+#endif /* defined(__GNUC__) */
/* What GCC do we have? */
#ifdef __GNUC__
@@ -109,18 +109,18 @@
PRAGMA_DIAGNOSTIC_(ignored PRAGMA_JOIN_STRINGIFY_(-W,warningopt))
# define ENABLE_GCC_WARNING(warningopt) \
PRAGMA_DIAGNOSTIC_(pop)
-# else
+#else /* !(defined(__clang__) || GCC_VERSION >= 406) */
/* older version of gcc: no push/pop support. */
# define DISABLE_GCC_WARNING(warningopt) \
PRAGMA_DIAGNOSTIC_(ignored PRAGMA_JOIN_STRINGIFY_(-W,warningopt))
# define ENABLE_GCC_WARNING(warningopt) \
PRAGMA_DIAGNOSTIC_(warning PRAGMA_JOIN_STRINGIFY_(-W,warningopt))
-# endif
-#else /* ifdef __GNUC__ */
+#endif /* defined(__clang__) || GCC_VERSION >= 406 */
+#else /* !(defined(__GNUC__)) */
/* not gcc at all */
# define DISABLE_GCC_WARNING(warning)
# define ENABLE_GCC_WARNING(warning)
-#endif
+#endif /* defined(__GNUC__) */
/* inline is __inline on windows. */
#ifdef _WIN32
@@ -142,9 +142,9 @@
#define __func__ __FUNC__
#else
#define __func__ "???"
-#endif
-#endif /* ifndef MAVE_MACRO__func__ */
-#endif /* if not windows */
+#endif /* defined(HAVE_MACRO__FUNCTION__) || ... */
+#endif /* !defined(HAVE_MACRO__func__) */
+#endif /* defined(_MSC_VER) */
#define U64_TO_DBL(x) ((double) (x))
#define DBL_TO_U64(x) ((uint64_t) (x))
@@ -157,7 +157,7 @@
* problems), but if enumerated types are unsigned, we must use unsigned,
* so that the loss of precision doesn't make large values negative. */
#define ENUM_BF(t) t
-#endif
+#endif /* defined(ENUM_VALS_ARE_SIGNED) */
/* GCC has several useful attributes. */
#if defined(__GNUC__) && __GNUC__ >= 3
@@ -194,7 +194,7 @@
* taken. This can generate slightly better code with some CPUs.
*/
#define PREDICT_UNLIKELY(exp) __builtin_expect(!!(exp), 0)
-#else
+#else /* !(defined(__GNUC__) && __GNUC__ >= 3) */
#define ATTR_NORETURN
#define ATTR_CONST
#define ATTR_MALLOC
@@ -204,7 +204,7 @@
#define ATTR_WUR
#define PREDICT_LIKELY(exp) (exp)
#define PREDICT_UNLIKELY(exp) (exp)
-#endif
+#endif /* defined(__GNUC__) && __GNUC__ >= 3 */
/** Expands to a syntactically valid empty statement. */
#define STMT_NIL (void)0
@@ -224,7 +224,7 @@
#else
#define STMT_BEGIN do {
#define STMT_END } while (0)
-#endif
+#endif /* defined(__GNUC__) || ... */
/* Some tools (like coccinelle) don't like to see operators as macro
* arguments. */
@@ -251,7 +251,7 @@
*/
#undef strlcat
#undef strlcpy
-#endif
+#endif /* defined __APPLE__ */
#ifndef HAVE_STRLCAT
size_t strlcat(char *dst, const char *src, size_t siz) ATTR_NONNULL((1,2));
@@ -272,24 +272,28 @@ size_t strlcpy(char *dst, const char *src, size_t siz) ATTR_NONNULL((1,2));
#define I64_PRINTF_ARG(a) (a)
#define I64_SCANF_ARG(a) (a)
#define I64_LITERAL(n) (n ## i64)
-#else
+#else /* !(defined(_MSC_VER)) */
#define U64_PRINTF_ARG(a) ((long long unsigned int)(a))
#define U64_SCANF_ARG(a) ((long long unsigned int*)(a))
#define U64_LITERAL(n) (n ## llu)
#define I64_PRINTF_ARG(a) ((long long signed int)(a))
#define I64_SCANF_ARG(a) ((long long signed int*)(a))
#define I64_LITERAL(n) (n ## ll)
+#endif /* defined(_MSC_VER) */
+
+#if defined(__MINGW32__) || defined(__MINGW64__)
+#define MINGW_ANY
#endif
-#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
+#if defined(_MSC_VER) || defined(MINGW_ANY)
/** The formatting string used to put a uint64_t value in a printf() or
* scanf() function. See also U64_PRINTF_ARG and U64_SCANF_ARG. */
#define U64_FORMAT "%I64u"
#define I64_FORMAT "%I64d"
-#else
+#else /* !(defined(_MSC_VER) || defined(MINGW_ANY)) */
#define U64_FORMAT "%llu"
#define I64_FORMAT "%lld"
-#endif
+#endif /* defined(_MSC_VER) || defined(MINGW_ANY) */
#if (SIZEOF_INTPTR_T == SIZEOF_INT)
#define INTPTR_T_FORMAT "%d"
@@ -302,7 +306,7 @@ size_t strlcpy(char *dst, const char *src, size_t siz) ATTR_NONNULL((1,2));
#define INTPTR_PRINTF_ARG(x) I64_PRINTF_ARG(x)
#else
#error Unknown: SIZEOF_INTPTR_T
-#endif
+#endif /* (SIZEOF_INTPTR_T == SIZEOF_INT) || ... */
/** Represents an mmaped file. Allocated via tor_mmap_file; freed with
* tor_munmap_file. */
@@ -316,7 +320,7 @@ typedef struct tor_mmap_t {
* size, rounded up to the nearest page.) */
#elif defined _WIN32
HANDLE mmap_handle;
-#endif
+#endif /* defined(HAVE_SYS_MMAN_H) || ... */
} tor_mmap_t;
@@ -378,7 +382,7 @@ const char *tor_fix_source_file(const char *fname);
#else
#define SHORT_FILE__ (__FILE__)
#define tor_fix_source_file(s) (s)
-#endif
+#endif /* defined(_WIN32) */
/* ===== Time compatibility */
@@ -397,7 +401,7 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
(tvout)->tv_sec++; \
} \
} while (0)
-#endif
+#endif /* !defined(timeradd) */
#ifndef timersub
/** Replacement for timersub on platforms that do not have it: sets tvout to
@@ -411,7 +415,7 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
(tvout)->tv_sec--; \
} \
} while (0)
-#endif
+#endif /* !defined(timersub) */
#ifndef timercmp
/** Replacement for timercmp on platforms that do not have it: returns true
@@ -425,7 +429,7 @@ struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
(((tv1)->tv_sec == (tv2)->tv_sec) ? \
((tv1)->tv_usec op (tv2)->tv_usec) : \
((tv1)->tv_sec op (tv2)->tv_sec))
-#endif
+#endif /* !defined(timercmp) */
/* ===== File compatibility */
int tor_open_cloexec(const char *path, int flags, unsigned mode);
@@ -467,7 +471,7 @@ typedef int socklen_t;
#define TOR_SOCKET_T_FORMAT INTPTR_T_FORMAT
#define SOCKET_OK(s) ((SOCKET)(s) != INVALID_SOCKET)
#define TOR_INVALID_SOCKET INVALID_SOCKET
-#else
+#else /* !(defined(_WIN32)) */
/** Type used for a network socket. */
#define tor_socket_t int
#define TOR_SOCKET_T_FORMAT "%d"
@@ -475,7 +479,7 @@ typedef int socklen_t;
#define SOCKET_OK(s) ((s) >= 0)
/** Error/uninitialized value for a tor_socket_t. */
#define TOR_INVALID_SOCKET (-1)
-#endif
+#endif /* defined(_WIN32) */
int tor_close_socket_simple(tor_socket_t s);
MOCK_DECL(int, tor_close_socket, (tor_socket_t s));
@@ -522,19 +526,19 @@ struct in6_addr
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
};
-#endif
+#endif /* !defined(HAVE_STRUCT_IN6_ADDR) */
/** @{ */
/** Many BSD variants seem not to define these. */
-#if defined(__APPLE__) || defined(__darwin__) || defined(__FreeBSD__) \
- || defined(__NetBSD__) || defined(__OpenBSD__)
+#if defined(__APPLE__) || defined(__darwin__) || \
+ defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#ifndef s6_addr16
#define s6_addr16 __u6_addr.__u6_addr16
#endif
#ifndef s6_addr32
#define s6_addr32 __u6_addr.__u6_addr32
#endif
-#endif
+#endif /* defined(__APPLE__) || defined(__darwin__) || ... */
/** @} */
#ifndef HAVE_SA_FAMILY_T
@@ -566,7 +570,7 @@ struct sockaddr_in6 {
struct in6_addr sin6_addr;
// uint32_t sin6_scope_id;
};
-#endif
+#endif /* !defined(HAVE_STRUCT_SOCKADDR_IN6) */
MOCK_DECL(int,tor_gethostname,(char *name, size_t namelen));
int tor_inet_aton(const char *cp, struct in_addr *addr) ATTR_NONNULL((1,2));
@@ -607,14 +611,14 @@ int network_init(void);
#define ERRNO_IS_EINTR(e) ((e) == WSAEINTR || 0)
int tor_socket_errno(tor_socket_t sock);
const char *tor_socket_strerror(int e);
-#else
+#else /* !(defined(_WIN32)) */
#define SOCK_ERRNO(e) e
#if EAGAIN == EWOULDBLOCK
/* || 0 is for -Wparentheses-equality (-Wall?) appeasement under clang */
#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || 0)
#else
#define ERRNO_IS_EAGAIN(e) ((e) == EAGAIN || (e) == EWOULDBLOCK)
-#endif
+#endif /* EAGAIN == EWOULDBLOCK */
#define ERRNO_IS_EINTR(e) ((e) == EINTR || 0)
#define ERRNO_IS_EINPROGRESS(e) ((e) == EINPROGRESS || 0)
#define ERRNO_IS_CONN_EINPROGRESS(e) ((e) == EINPROGRESS || 0)
@@ -625,7 +629,7 @@ const char *tor_socket_strerror(int e);
#define ERRNO_IS_EADDRINUSE(e) (((e) == EADDRINUSE) || 0)
#define tor_socket_errno(sock) (errno)
#define tor_socket_strerror(e) strerror(e)
-#endif
+#endif /* defined(_WIN32) */
/** Specified SOCKS5 status codes. */
typedef enum {
@@ -728,7 +732,7 @@ char *format_win32_error(DWORD err);
#define VER_SUITE_SINGLEUSERTS 0x00000100
#endif
-#endif
+#endif /* defined(_WIN32) */
#ifdef COMPAT_PRIVATE
#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
@@ -736,12 +740,12 @@ char *format_win32_error(DWORD err);
STATIC int tor_ersatz_socketpair(int family, int type, int protocol,
tor_socket_t fd[2]);
#endif
-#endif
+#endif /* defined(COMPAT_PRIVATE) */
ssize_t tor_getpass(const char *prompt, char *output, size_t buflen);
/* This needs some of the declarations above so we include it here. */
#include "compat_threads.h"
-#endif
+#endif /* !defined(TOR_COMPAT_H) */
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 31eb4ac496..740cc2a11d 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -88,8 +88,8 @@ static struct event_base *the_event_base = NULL;
(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1040)
#else
#define MACOSX_KQUEUE_IS_BROKEN 0
-#endif
-#endif
+#endif /* defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) */
+#endif /* defined(__APPLE__) */
/** Initialize the Libevent library and set up the event base. */
void
@@ -237,8 +237,9 @@ tor_init_libevent_rng(void)
return rv;
}
-#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,1,1) \
- && !defined(TOR_UNIT_TESTS)
+#if defined(LIBEVENT_VERSION_NUMBER) && \
+ LIBEVENT_VERSION_NUMBER >= V(2,1,1) && \
+ !defined(TOR_UNIT_TESTS)
void
tor_gettimeofday_cached(struct timeval *tv)
{
@@ -249,7 +250,7 @@ tor_gettimeofday_cache_clear(void)
{
event_base_update_cache_time(the_event_base);
}
-#else
+#else /* !(defined(LIBEVENT_VERSION_NUMBER) && ...) */
/** Cache the current hi-res time; the cache gets reset when libevent
* calls us. */
static struct timeval cached_time_hires = {0, 0};
@@ -289,6 +290,6 @@ tor_libevent_postfork(void)
int r = event_reinit(tor_libevent_get_base());
tor_assert(r == 0);
}
-#endif
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
+#endif /* defined(LIBEVENT_VERSION_NUMBER) && ... */
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 904938415c..834354c405 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -70,7 +70,7 @@ void tor_libevent_postfork(void);
STATIC void
libevent_logging_callback(int severity, const char *msg);
-#endif
+#endif /* defined(COMPAT_LIBEVENT_PRIVATE) */
-#endif
+#endif /* !defined(TOR_COMPAT_LIBEVENT_H) */
diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h
index 2b94fe5b4e..c695f1e9df 100644
--- a/src/common/compat_openssl.h
+++ b/src/common/compat_openssl.h
@@ -25,7 +25,7 @@
/* We define this macro if we're trying to build with the majorly refactored
* API in OpenSSL 1.1 */
#define OPENSSL_1_1_API
-#endif
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) && ... */
#ifndef OPENSSL_1_1_API
#define OPENSSL_VERSION SSLEAY_VERSION
@@ -37,11 +37,11 @@
((st) == SSL3_ST_SW_SRVR_HELLO_B))
#define OSSL_HANDSHAKE_STATE int
#define CONST_IF_OPENSSL_1_1_API
-#else
+#else /* !(!defined(OPENSSL_1_1_API)) */
#define STATE_IS_SW_SERVER_HELLO(st) \
((st) == TLS_ST_SW_SRVR_HELLO)
#define CONST_IF_OPENSSL_1_1_API const
-#endif
+#endif /* !defined(OPENSSL_1_1_API) */
-#endif
+#endif /* !defined(TOR_COMPAT_OPENSSL_H) */
diff --git a/src/common/compat_pthreads.c b/src/common/compat_pthreads.c
index 8e4b833573..002274c469 100644
--- a/src/common/compat_pthreads.c
+++ b/src/common/compat_pthreads.c
@@ -201,20 +201,21 @@ tor_cond_init(tor_cond_t *cond)
}
#if defined(HAVE_CLOCK_GETTIME)
-#if defined(CLOCK_MONOTONIC) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
+#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
+ defined(CLOCK_MONOTONIC)
/* Use monotonic time so when we timedwait() on it, any clock adjustment
* won't affect the timeout value. */
if (pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC)) {
return -1;
}
#define USE_COND_CLOCK CLOCK_MONOTONIC
-#else /* !defined HAVE_PTHREAD_CONDATTR_SETCLOCK */
+#else /* !(defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && ...) */
/* On OSX Sierra, there is no pthread_condattr_setclock, so we are stuck
* with the realtime clock.
*/
#define USE_COND_CLOCK CLOCK_REALTIME
-#endif /* which clock to use */
-#endif /* HAVE_CLOCK_GETTIME */
+#endif /* defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && ... */
+#endif /* defined(HAVE_CLOCK_GETTIME) */
if (pthread_cond_init(&cond->cond, &condattr)) {
return -1;
}
@@ -266,11 +267,11 @@ tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex, const struct timeval *tv)
tvnow.tv_sec = ts.tv_sec;
tvnow.tv_usec = (int)(ts.tv_nsec / 1000);
timeradd(tv, &tvnow, &tvsum);
-#else
+#else /* !(defined(HAVE_CLOCK_GETTIME) && defined(USE_COND_CLOCK)) */
if (gettimeofday(&tvnow, NULL) < 0)
return -1;
timeradd(tv, &tvnow, &tvsum);
-#endif /* HAVE_CLOCK_GETTIME, CLOCK_MONOTONIC */
+#endif /* defined(HAVE_CLOCK_GETTIME) && defined(USE_COND_CLOCK) */
ts.tv_sec = tvsum.tv_sec;
ts.tv_nsec = tvsum.tv_usec * 1000;
diff --git a/src/common/compat_rust.h b/src/common/compat_rust.h
index 752a29b56c..72fde39296 100644
--- a/src/common/compat_rust.h
+++ b/src/common/compat_rust.h
@@ -24,5 +24,5 @@ const char *rust_str_get(const rust_str_t);
rust_str_t rust_welcome_string(void);
-#endif
+#endif /* !defined(TOR_RUST_COMPAT_H) */
diff --git a/src/common/compat_threads.c b/src/common/compat_threads.c
index c593e9af8d..208d3138d9 100644
--- a/src/common/compat_threads.c
+++ b/src/common/compat_threads.c
@@ -126,7 +126,7 @@ read_ni(int fd, void *buf, size_t n)
}
return r;
}
-#endif
+#endif /* defined(HAVE_EVENTFD) || defined(HAVE_PIPE) */
/** As send(), but retry on EINTR, and return the negative error code on
* error. */
@@ -186,7 +186,7 @@ eventfd_drain(int fd)
return r;
return 0;
}
-#endif
+#endif /* defined(HAVE_EVENTFD) */
#ifdef HAVE_PIPE
/** Send a byte over a pipe. Return 0 on success or EAGAIN; -1 on error */
@@ -214,7 +214,7 @@ pipe_drain(int fd)
/* A value of r = 0 means EOF on the fd so successfully drained. */
return 0;
}
-#endif
+#endif /* defined(HAVE_PIPE) */
/** Send a byte on socket <b>fd</b>t. Return 0 on success or EAGAIN,
* -1 on error. */
@@ -276,7 +276,7 @@ alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
socks_out->drain_fn = eventfd_drain;
return 0;
}
-#endif
+#endif /* defined(HAVE_EVENTFD) */
#ifdef HAVE_PIPE2
/* Now we're going to try pipes. First type the pipe2() syscall, if we
@@ -289,7 +289,7 @@ alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
socks_out->drain_fn = pipe_drain;
return 0;
}
-#endif
+#endif /* defined(HAVE_PIPE2) */
#ifdef HAVE_PIPE
/* Now try the regular pipe() syscall. Pipes have a bit lower overhead than
@@ -313,7 +313,7 @@ alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
socks_out->drain_fn = pipe_drain;
return 0;
}
-#endif
+#endif /* defined(HAVE_PIPE) */
/* If nothing else worked, fall back on socketpair(). */
if (!(flags & ASOCKS_NOSOCKETPAIR) &&
diff --git a/src/common/compat_threads.h b/src/common/compat_threads.h
index 9fa3d0d0b7..42f14eab2a 100644
--- a/src/common/compat_threads.h
+++ b/src/common/compat_threads.h
@@ -20,7 +20,7 @@
#define USE_PTHREADS
#else
#error "No threading system was found"
-#endif
+#endif /* defined(_WIN32) || ... */
int spawn_func(void (*func)(void *), void *data);
void spawn_exit(void) ATTR_NORETURN;
@@ -41,7 +41,7 @@ typedef struct tor_mutex_t {
#else
/** No-threads only: Dummy variable so that tor_mutex_t takes up space. */
int _unused;
-#endif
+#endif /* defined(USE_WIN32_THREADS) || ... */
} tor_mutex_t;
tor_mutex_t *tor_mutex_new(void);
@@ -73,7 +73,7 @@ typedef struct tor_cond_t {
int generation;
#else
#error no known condition implementation.
-#endif
+#endif /* defined(USE_PTHREADS) || ... */
} tor_cond_t;
tor_cond_t *tor_cond_new(void);
@@ -161,5 +161,5 @@ void atomic_counter_add(atomic_counter_t *counter, size_t add);
void atomic_counter_sub(atomic_counter_t *counter, size_t sub);
size_t atomic_counter_get(atomic_counter_t *counter);
-#endif
+#endif /* !defined(TOR_COMPAT_THREADS_H) */
diff --git a/src/common/compat_time.c b/src/common/compat_time.c
index 2ccaa36e49..1ce6f5ce4e 100644
--- a/src/common/compat_time.c
+++ b/src/common/compat_time.c
@@ -28,7 +28,7 @@
/* as fallback implementation for tor_sleep_msec */
#include <sys/select.h>
#endif
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
#ifdef __APPLE__
#include <mach/mach_time.h>
@@ -64,9 +64,9 @@ tor_sleep_msec(int msec)
select(0, NULL, NULL, NULL, &tv);
#else
sleep(CEIL_DIV(msec, 1000));
-#endif
+#endif /* defined(_WIN32) || ... */
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Set *timeval to the current time of day. On error, log and terminate.
* (Same as gettimeofday(timeval,NULL), but never returns -1.)
@@ -112,7 +112,7 @@ tor_gettimeofday(struct timeval *timeval)
timeval->tv_usec = tb.millitm * 1000;
#else
#error "No way to get time."
-#endif
+#endif /* defined(_WIN32) || ... */
return;
}
@@ -187,8 +187,8 @@ monotime_coarse_set_mock_time_nsec(int64_t nsec)
tor_assert_nonfatal(monotime_mocking_enabled == 1);
mock_time_nsec_coarse = nsec;
}
-#endif
-#endif
+#endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
+#endif /* defined(TOR_UNIT_TESTS) */
/* "ratchet" functions for monotonic time. */
@@ -235,7 +235,7 @@ ratchet_coarse_performance_counter(const int64_t count_raw)
last_tick_count = count;
return count;
}
-#endif
+#endif /* defined(_WIN32) || defined(TOR_UNIT_TESTS) */
#if defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS)
static struct timeval last_timeofday = { 0, 0 };
@@ -251,7 +251,7 @@ ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out)
{
/* must hold lock */
timeradd(timeval_raw, &timeofday_offset, out);
- if (PREDICT_UNLIKELY(timercmp(out, &last_timeofday, <))) {
+ if (PREDICT_UNLIKELY(timercmp(out, &last_timeofday, OP_LT))) {
/* time ran backwards. Instead, declare that no time occurred. */
timersub(&last_timeofday, timeval_raw, &timeofday_offset);
memcpy(out, &last_timeofday, sizeof(struct timeval));
@@ -259,7 +259,7 @@ ratchet_timeval(const struct timeval *timeval_raw, struct timeval *out)
memcpy(&last_timeofday, out, sizeof(struct timeval));
}
}
-#endif
+#endif /* defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS) */
#ifdef TOR_UNIT_TESTS
/** For testing: reset all the ratchets */
@@ -271,7 +271,7 @@ monotime_reset_ratchets_for_testing(void)
memset(&last_timeofday, 0, sizeof(struct timeval));
memset(&timeofday_offset, 0, sizeof(struct timeval));
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
#ifdef __APPLE__
@@ -301,7 +301,7 @@ monotime_get(monotime_t *out)
/ mach_time_info.numer;
return;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
out->abstime_ = mach_absolute_time();
}
@@ -332,7 +332,7 @@ monotime_diff_nsec(const monotime_t *start,
* an old Linux kernel. In that case, we will fall back to CLOCK_MONOTONIC.
*/
static int clock_monotonic_coarse = CLOCK_MONOTONIC_COARSE;
-#endif
+#endif /* defined(CLOCK_MONOTONIC_COARSE) */
static void
monotime_init_internal(void)
@@ -344,7 +344,7 @@ monotime_init_internal(void)
"falling back to CLOCK_MONOTONIC.", strerror(errno));
clock_monotonic_coarse = CLOCK_MONOTONIC;
}
-#endif
+#endif /* defined(CLOCK_MONOTONIC_COARSE) */
}
void
@@ -356,7 +356,7 @@ monotime_get(monotime_t *out)
out->ts_.tv_nsec = (int) (mock_time_nsec % ONE_BILLION);
return;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
int r = clock_gettime(CLOCK_MONOTONIC, &out->ts_);
tor_assert(r == 0);
}
@@ -371,7 +371,7 @@ monotime_coarse_get(monotime_coarse_t *out)
out->ts_.tv_nsec = (int) (mock_time_nsec_coarse % ONE_BILLION);
return;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
int r = clock_gettime(clock_monotonic_coarse, &out->ts_);
if (PREDICT_UNLIKELY(r < 0) &&
errno == EINVAL &&
@@ -386,7 +386,7 @@ monotime_coarse_get(monotime_coarse_t *out)
tor_assert(r == 0);
}
-#endif
+#endif /* defined(CLOCK_MONOTONIC_COARSE) */
int64_t
monotime_diff_nsec(const monotime_t *start,
@@ -462,7 +462,7 @@ monotime_get(monotime_t *out)
/ nsec_per_tick_numer;
return;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/* Alas, QueryPerformanceCounter is not always monotonic: see bug list at
@@ -486,7 +486,7 @@ monotime_coarse_get(monotime_coarse_t *out)
out->tick_count_ = mock_time_nsec_coarse / ONE_MILLION;
return;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
if (GetTickCount64_fn) {
out->tick_count_ = (int64_t)GetTickCount64_fn();
@@ -570,7 +570,7 @@ monotime_diff_nsec(const monotime_t *start,
/* end of "MONOTIME_USING_GETTIMEOFDAY" */
#else
#error "No way to implement monotonic timers."
-#endif
+#endif /* defined(__APPLE__) || ... */
/**
* Initialize the monotonic timer subsystem. Must be called before any
@@ -653,5 +653,5 @@ monotime_coarse_absolute_msec(void)
{
return monotime_coarse_absolute_nsec() / ONE_MILLION;
}
-#endif
+#endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
diff --git a/src/common/compat_time.h b/src/common/compat_time.h
index 90194c5ebc..5ea4aae42b 100644
--- a/src/common/compat_time.h
+++ b/src/common/compat_time.h
@@ -28,13 +28,13 @@
#include <time.h>
#endif
-#if !defined(HAVE_GETTIMEOFDAY) && !defined(HAVE_STRUCT_TIMEVAL_TV_SEC)
+#if !defined(HAVE_STRUCT_TIMEVAL_TV_SEC)
/** Implementation of timeval for platforms that don't have it. */
struct timeval {
time_t tv_sec;
unsigned int tv_usec;
};
-#endif
+#endif /* !defined(HAVE_STRUCT_TIMEVAL_TV_SEC) */
/** Represents a monotonic timer in a platform-dependent way. */
typedef struct monotime_t {
@@ -51,10 +51,11 @@ typedef struct monotime_t {
#define MONOTIME_USING_GETTIMEOFDAY
/* Otherwise, we will be stuck using gettimeofday. */
struct timeval tv_;
-#endif
+#endif /* defined(__APPLE__) || ... */
} monotime_t;
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC_COARSE)
+#if defined(CLOCK_MONOTONIC_COARSE) && \
+ defined(HAVE_CLOCK_GETTIME)
#define MONOTIME_COARSE_FN_IS_DIFFERENT
#define monotime_coarse_t monotime_t
#elif defined(_WIN32)
@@ -66,7 +67,7 @@ typedef struct monotime_coarse_t {
} monotime_coarse_t;
#else
#define monotime_coarse_t monotime_t
-#endif
+#endif /* defined(CLOCK_MONOTONIC_COARSE) && ... || ... */
/**
* Initialize the timing subsystem. This function is idempotent.
@@ -109,12 +110,12 @@ void monotime_coarse_get(monotime_coarse_t *out);
uint64_t monotime_coarse_absolute_nsec(void);
uint64_t monotime_coarse_absolute_usec(void);
uint64_t monotime_coarse_absolute_msec(void);
-#else
+#else /* !(defined(MONOTIME_COARSE_FN_IS_DIFFERENT)) */
#define monotime_coarse_get monotime_get
#define monotime_coarse_absolute_nsec monotime_absolute_nsec
#define monotime_coarse_absolute_usec monotime_absolute_usec
#define monotime_coarse_absolute_msec monotime_absolute_msec
-#endif
+#endif /* defined(MONOTIME_COARSE_FN_IS_DIFFERENT) */
#if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)
int64_t monotime_coarse_diff_nsec(const monotime_coarse_t *start,
@@ -123,11 +124,11 @@ int64_t monotime_coarse_diff_usec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
int64_t monotime_coarse_diff_msec(const monotime_coarse_t *start,
const monotime_coarse_t *end);
-#else
+#else /* !(defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)) */
#define monotime_coarse_diff_nsec monotime_diff_nsec
#define monotime_coarse_diff_usec monotime_diff_usec
#define monotime_coarse_diff_msec monotime_diff_msec
-#endif
+#endif /* defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT) */
void tor_gettimeofday(struct timeval *timeval);
@@ -142,7 +143,7 @@ void monotime_coarse_set_mock_time_nsec(int64_t);
#else
#define monotime_coarse_set_mock_time_nsec monotime_set_mock_time_nsec
#endif
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
#ifdef COMPAT_TIME_PRIVATE
#if defined(_WIN32) || defined(TOR_UNIT_TESTS)
@@ -156,7 +157,7 @@ STATIC void ratchet_timeval(const struct timeval *timeval_raw,
#ifdef TOR_UNIT_TESTS
void monotime_reset_ratchets_for_testing(void);
#endif
-#endif
+#endif /* defined(COMPAT_TIME_PRIVATE) */
-#endif
+#endif /* !defined(TOR_COMPAT_TIME_H) */
diff --git a/src/common/compat_winthreads.c b/src/common/compat_winthreads.c
index 915368b94f..50a3c498ca 100644
--- a/src/common/compat_winthreads.c
+++ b/src/common/compat_winthreads.c
@@ -246,5 +246,5 @@ tor_threads_init(void)
set_main_thread();
}
-#endif
+#endif /* defined(_WIN32) */
diff --git a/src/common/compress.c b/src/common/compress.c
index beeff5fcb8..bc12a58ad6 100644
--- a/src/common/compress.c
+++ b/src/common/compress.c
@@ -51,8 +51,8 @@ static atomic_counter_t total_compress_allocation;
/** Return true if uncompressing an input of size <b>in_size</b> to an input of
* size at least <b>size_out</b> looks like a compression bomb. */
-int
-tor_compress_is_compression_bomb(size_t size_in, size_t size_out)
+MOCK_IMPL(int,
+tor_compress_is_compression_bomb,(size_t size_in, size_t size_out))
{
if (size_in == 0 || size_out < CHECK_FOR_COMPRESSION_BOMB_AFTER)
return 0;
@@ -164,7 +164,7 @@ tor_compress_impl(int compress,
goto err;
}
if (out_alloc >= SIZE_T_CEILING / 2) {
- log_warn(LD_GENERAL, "While %scompresing data: ran out of space.",
+ log_warn(LD_GENERAL, "While %scompressing data: ran out of space.",
compress?"":"un");
goto err;
}
@@ -185,11 +185,12 @@ tor_compress_impl(int compress,
}
case TOR_COMPRESS_ERROR:
log_fn(protocol_warn_level, LD_GENERAL,
- "Error while %scompresing data: bad input?",
+ "Error while %scompressing data: bad input?",
compress?"":"un");
goto err; // bad data.
- default:
+
// LCOV_EXCL_START
+ default:
tor_assert_nonfatal_unreached();
goto err;
// LCOV_EXCL_STOP
@@ -561,9 +562,9 @@ tor_compress_process(tor_compress_state_t *state,
finish);
break;
case LZMA_METHOD:
- rv =tor_lzma_compress_process(state->u.lzma_state,
- out, out_len, in, in_len,
- finish);
+ rv = tor_lzma_compress_process(state->u.lzma_state,
+ out, out_len, in, in_len,
+ finish);
break;
case ZSTD_METHOD:
rv = tor_zstd_compress_process(state->u.zstd_state,
@@ -581,6 +582,12 @@ tor_compress_process(tor_compress_state_t *state,
if (BUG((rv == TOR_COMPRESS_OK) &&
*in_len == in_len_orig &&
*out_len == out_len_orig)) {
+ log_warn(LD_GENERAL,
+ "More info on the bug: method == %s, finish == %d, "
+ " *in_len == in_len_orig == %lu, "
+ "*out_len == out_len_orig == %lu",
+ compression_method_get_human_name(state->method), finish,
+ (unsigned long)in_len_orig, (unsigned long)out_len_orig);
return TOR_COMPRESS_ERROR;
}
diff --git a/src/common/compress.h b/src/common/compress.h
index 59c8b7b9b5..23a9817479 100644
--- a/src/common/compress.h
+++ b/src/common/compress.h
@@ -45,7 +45,8 @@ int tor_uncompress(char **out, size_t *out_len,
compress_method_t detect_compression_method(const char *in, size_t in_len);
-int tor_compress_is_compression_bomb(size_t size_in, size_t size_out);
+MOCK_DECL(int,tor_compress_is_compression_bomb,(size_t size_in,
+ size_t size_out));
int tor_compress_supports_method(compress_method_t method);
unsigned tor_compress_get_supported_method_bitmask(void);
@@ -85,5 +86,5 @@ size_t tor_compress_state_size(const tor_compress_state_t *state);
void tor_compress_init(void);
-#endif // TOR_COMPRESS_H.
+#endif /* !defined(TOR_COMPRESS_H) */
diff --git a/src/common/compress_lzma.c b/src/common/compress_lzma.c
index d453d9f718..6426ede4fd 100644
--- a/src/common/compress_lzma.c
+++ b/src/common/compress_lzma.c
@@ -75,7 +75,7 @@ lzma_error_str(lzma_ret error)
return "Unknown LZMA error";
}
}
-#endif // HAVE_LZMA.
+#endif /* defined(HAVE_LZMA) */
/** Return 1 if LZMA compression is supported; otherwise 0. */
int
@@ -158,10 +158,12 @@ tor_lzma_state_size_precalc(int compress, compression_level_t level)
return (size_t)memory_usage;
+ // LCOV_EXCL_START
err:
- return 0; // LCOV_EXCL_LINE
+ return 0;
+ // LCOV_EXCL_STOP
}
-#endif // HAVE_LZMA.
+#endif /* defined(HAVE_LZMA) */
/** Construct and return a tor_lzma_compress_state_t object using
* <b>method</b>. If <b>compress</b>, it's for compression; otherwise it's for
@@ -212,16 +214,18 @@ tor_lzma_compress_new(int compress,
atomic_counter_add(&total_lzma_allocation, result->allocation);
return result;
+ /* LCOV_EXCL_START */
err:
- tor_free(result); // LCOV_EXCL_LINE
+ tor_free(result);
return NULL;
-#else // HAVE_LZMA.
+ /* LCOV_EXCL_STOP */
+#else /* !(defined(HAVE_LZMA)) */
(void)compress;
(void)method;
(void)level;
return NULL;
-#endif // HAVE_LZMA.
+#endif /* defined(HAVE_LZMA) */
}
/** Compress/decompress some bytes using <b>state</b>. Read up to
@@ -306,7 +310,7 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state,
lzma_error_str(retval));
return TOR_COMPRESS_ERROR;
}
-#else // HAVE_LZMA.
+#else /* !(defined(HAVE_LZMA)) */
(void)state;
(void)out;
(void)out_len;
@@ -314,7 +318,7 @@ tor_lzma_compress_process(tor_lzma_compress_state_t *state,
(void)in_len;
(void)finish;
return TOR_COMPRESS_ERROR;
-#endif // HAVE_LZMA.
+#endif /* defined(HAVE_LZMA) */
}
/** Deallocate <b>state</b>. */
diff --git a/src/common/compress_lzma.h b/src/common/compress_lzma.h
index 1433c89f88..7639d98a70 100644
--- a/src/common/compress_lzma.h
+++ b/src/common/compress_lzma.h
@@ -39,5 +39,5 @@ size_t tor_lzma_get_total_allocation(void);
void tor_lzma_init(void);
-#endif // TOR_COMPRESS_LZMA_H.
+#endif /* !defined(TOR_COMPRESS_LZMA_H) */
diff --git a/src/common/compress_none.h b/src/common/compress_none.h
index d1ebb4b625..77c3cef47b 100644
--- a/src/common/compress_none.h
+++ b/src/common/compress_none.h
@@ -16,5 +16,5 @@ tor_cnone_compress_process(char **out, size_t *out_len,
const char **in, size_t *in_len,
int finish);
-#endif // TOR_COMPRESS_NONE_H.
+#endif /* !defined(TOR_COMPRESS_NONE_H) */
diff --git a/src/common/compress_zlib.h b/src/common/compress_zlib.h
index df5c196ac7..8ace467bf0 100644
--- a/src/common/compress_zlib.h
+++ b/src/common/compress_zlib.h
@@ -39,5 +39,5 @@ size_t tor_zlib_get_total_allocation(void);
void tor_zlib_init(void);
-#endif // TOR_COMPRESS_ZLIB_H.
+#endif /* !defined(TOR_COMPRESS_ZLIB_H) */
diff --git a/src/common/compress_zstd.c b/src/common/compress_zstd.c
index 11edfffa0e..baa7749f0a 100644
--- a/src/common/compress_zstd.c
+++ b/src/common/compress_zstd.c
@@ -40,7 +40,7 @@ memory_level(compression_level_t level)
case LOW_COMPRESSION: return 7;
}
}
-#endif // HAVE_ZSTD.
+#endif /* defined(HAVE_ZSTD) */
/** Return 1 if Zstandard compression is supported; otherwise 0. */
int
@@ -70,9 +70,9 @@ tor_zstd_get_version_str(void)
(int) version_number % 100);
return version_str;
-#else
+#else /* !(defined(HAVE_ZSTD)) */
return NULL;
-#endif
+#endif /* defined(HAVE_ZSTD) */
}
/** Return a string representation of the version of the version of libzstd
@@ -97,7 +97,7 @@ struct tor_zstd_compress_state_t {
/** Decompression stream. Used when <b>compress</b> is false. */
ZSTD_DStream *decompress_stream;
} u; /**< Zstandard stream objects. */
-#endif // HAVE_ZSTD.
+#endif /* defined(HAVE_ZSTD) */
int compress; /**< True if we are compressing; false if we are inflating */
int have_called_end; /**< True if we are compressing and we've called
@@ -173,7 +173,7 @@ tor_zstd_state_size_precalc(int compress, int preset)
return memory_usage;
}
-#endif // HAVE_ZSTD.
+#endif /* defined(HAVE_ZSTD) */
/** Construct and return a tor_zstd_compress_state_t object using
* <b>method</b>. If <b>compress</b>, it's for compression; otherwise it's for
@@ -250,13 +250,13 @@ tor_zstd_compress_new(int compress,
tor_free(result);
return NULL;
// LCOV_EXCL_STOP
-#else // HAVE_ZSTD.
+#else /* !(defined(HAVE_ZSTD)) */
(void)compress;
(void)method;
(void)level;
return NULL;
-#endif // HAVE_ZSTD.
+#endif /* defined(HAVE_ZSTD) */
}
/** Compress/decompress some bytes using <b>state</b>. Read up to
@@ -387,7 +387,7 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state,
return TOR_COMPRESS_OK;
}
-#else // HAVE_ZSTD.
+#else /* !(defined(HAVE_ZSTD)) */
(void)state;
(void)out;
(void)out_len;
@@ -396,7 +396,7 @@ tor_zstd_compress_process(tor_zstd_compress_state_t *state,
(void)finish;
return TOR_COMPRESS_ERROR;
-#endif // HAVE_ZSTD.
+#endif /* defined(HAVE_ZSTD) */
}
/** Deallocate <b>state</b>. */
@@ -414,7 +414,7 @@ tor_zstd_compress_free(tor_zstd_compress_state_t *state)
} else {
ZSTD_freeDStream(state->u.decompress_stream);
}
-#endif // HAVE_ZSTD.
+#endif /* defined(HAVE_ZSTD) */
tor_free(state);
}
diff --git a/src/common/compress_zstd.h b/src/common/compress_zstd.h
index d3e65c2f16..02466010ff 100644
--- a/src/common/compress_zstd.h
+++ b/src/common/compress_zstd.h
@@ -39,5 +39,5 @@ size_t tor_zstd_get_total_allocation(void);
void tor_zstd_init(void);
-#endif // TOR_COMPRESS_ZSTD_H.
+#endif /* !defined(TOR_COMPRESS_ZSTD_H) */
diff --git a/src/common/confline.c b/src/common/confline.c
index 15fd96bf38..04545bc2c3 100644
--- a/src/common/confline.c
+++ b/src/common/confline.c
@@ -288,7 +288,7 @@ config_process_include(const char *path, int recursion_level, int extended,
return -1;
}
tor_free(unquoted_path);
-#endif
+#endif /* 0 */
smartlist_t *config_files = config_get_file_list(path);
if (!config_files) {
return -1;
@@ -342,7 +342,8 @@ config_lines_dup(const config_line_t *inp)
}
/** Return a newly allocated deep copy of the lines in <b>inp</b>,
- * but only the ones that match <b>key</b>. */
+ * but only the ones whose keys begin with <b>key</b> (case-insensitive).
+ * If <b>key</b> is NULL, do not filter. */
config_line_t *
config_lines_dup_and_filter(const config_line_t *inp,
const char *key)
diff --git a/src/common/confline.h b/src/common/confline.h
index eb863e8fd8..8256326f2d 100644
--- a/src/common/confline.h
+++ b/src/common/confline.h
@@ -49,5 +49,5 @@ void config_free_lines(config_line_t *front);
const char *parse_config_line_from_str_verbose(const char *line,
char **key_out, char **value_out,
const char **err_out);
-#endif
+#endif /* !defined(TOR_CONFLINE_H) */
diff --git a/src/common/container.c b/src/common/container.c
index 689e7e55e9..8645cb4826 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -843,13 +843,13 @@ smartlist_sort_pointers(smartlist_t *sl)
* }
*
* void timer_heap_insert(smartlist_t *heap, timer_t *timer) {
- * smartlist_pqueue_add(heap, compare, STRUCT_OFFSET(timer_t, heap_index),
+ * smartlist_pqueue_add(heap, compare, offsetof(timer_t, heap_index),
* timer);
* }
*
* void timer_heap_pop(smartlist_t *heap) {
* return smartlist_pqueue_pop(heap, compare,
- * STRUCT_OFFSET(timer_t, heap_index));
+ * offsetof(timer_t, heap_index));
* }
*/
diff --git a/src/common/container.h b/src/common/container.h
index db68d892f5..f6affd3bc6 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -74,11 +74,11 @@ static inline void smartlist_set(smartlist_t *sl, int idx, void *val) {
tor_assert(sl->num_used > idx);
sl->list[idx] = val;
}
-#else
+#else /* !(defined(DEBUG_SMARTLIST)) */
#define smartlist_len(sl) ((sl)->num_used)
#define smartlist_get(sl, idx) ((sl)->list[idx])
#define smartlist_set(sl, idx, val) ((sl)->list[idx] = (val))
-#endif
+#endif /* defined(DEBUG_SMARTLIST) */
/** Exchange the elements at indices <b>idx1</b> and <b>idx2</b> of the
* smartlist <b>sl</b>. */
@@ -580,7 +580,7 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
#define BITARRAY_SHIFT 6
#else
#error "int is neither 4 nor 8 bytes. I can't deal with that."
-#endif
+#endif /* SIZEOF_INT == 4 || ... */
#define BITARRAY_MASK ((1u<<BITARRAY_SHIFT)-1)
/** A random-access array of one-bit-wide elements. */
@@ -723,5 +723,5 @@ third_quartile_uint32(uint32_t *array, int n_elements)
return find_nth_uint32(array, n_elements, (n_elements*3)/4);
}
-#endif
+#endif /* !defined(TOR_CONTAINER_H) */
diff --git a/src/common/crypto.c b/src/common/crypto.c
index d2801e0d45..16536f3716 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -20,7 +20,7 @@
/* Windows defines this; so does OpenSSL 0.9.8h and later. We don't actually
* use either definition. */
#undef OCSP_RESPONSE
-#endif
+#endif /* defined(_WIN32) */
#define CRYPTO_PRIVATE
#include "crypto.h"
@@ -50,7 +50,7 @@ ENABLE_GCC_WARNING(redundant-decls)
#else
#pragma GCC diagnostic warning "-Wredundant-decls"
#endif
-#endif
+#endif /* __GNUC__ && GCC_VERSION >= 402 */
#ifdef HAVE_CTYPE_H
#include <ctype.h>
@@ -101,7 +101,7 @@ ENABLE_GCC_WARNING(redundant-decls)
* pointless, so let's not.
*/
#define NEW_THREAD_API
-#endif
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) && ... */
/** Longest recognized */
#define MAX_DNS_LABEL_SIZE 63
@@ -114,7 +114,7 @@ ENABLE_GCC_WARNING(redundant-decls)
static tor_mutex_t **openssl_mutexes_ = NULL;
/** How many mutexes have we allocated for use by OpenSSL? */
static int n_openssl_mutexes_ = 0;
-#endif
+#endif /* !defined(NEW_THREAD_API) */
/** A public key, or a public/private key-pair. */
struct crypto_pk_t
@@ -198,7 +198,7 @@ log_engine(const char *fn, ENGINE *e)
log_info(LD_CRYPTO, "Using default implementation for %s", fn);
}
}
-#endif
+#endif /* !defined(DISABLE_ENGINES) */
#ifndef DISABLE_ENGINES
/** Try to load an engine in a shared library via fully qualified path.
@@ -218,7 +218,7 @@ try_load_engine(const char *path, const char *engine)
}
return e;
}
-#endif
+#endif /* !defined(DISABLE_ENGINES) */
/* Returns a trimmed and human-readable version of an openssl version string
* <b>raw_version</b>. They are usually in the form of 'OpenSSL 1.0.0b 10
@@ -394,7 +394,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
#else
log_engine("ECDH", ENGINE_get_default_ECDH());
log_engine("ECDSA", ENGINE_get_default_ECDSA());
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
log_engine("RAND", ENGINE_get_default_RAND());
log_engine("RAND (which we will not use)", ENGINE_get_default_RAND());
log_engine("SHA1", ENGINE_get_digest_engine(NID_sha1));
@@ -412,7 +412,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
log_engine("AES-256-GCM", ENGINE_get_cipher_engine(NID_aes_256_gcm));
#endif
-#endif
+#endif /* defined(DISABLE_ENGINES) */
} else {
log_info(LD_CRYPTO, "NOT using OpenSSL engine support.");
}
@@ -450,9 +450,9 @@ crypto_pk_private_ok(const crypto_pk_t *k)
const BIGNUM *p, *q;
RSA_get0_factors(k->key, &p, &q);
return p != NULL; /* XXX/yawning: Should we check q? */
-#else
+#else /* !(defined(OPENSSL_1_1_API)) */
return k && k->key && k->key->p;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
}
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */
@@ -899,7 +899,7 @@ crypto_pk_public_exponent_ok(crypto_pk_t *env)
RSA_get0_key(env->key, &n, &e, &d);
#else
e = env->key->e;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
return BN_is_word(e, 65537);
}
@@ -933,7 +933,7 @@ crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
a_e = a->key->e;
b_n = b->key->n;
b_e = b->key->e;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
tor_assert(a_n != NULL && a_e != NULL);
tor_assert(b_n != NULL && b_e != NULL);
@@ -982,10 +982,10 @@ crypto_pk_num_bits(crypto_pk_t *env)
tor_assert(n != NULL);
return RSA_bits(env->key);
-#else
+#else /* !(defined(OPENSSL_1_1_API)) */
tor_assert(env->key->n);
return BN_num_bits(env->key->n);
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
}
/** Increase the reference count of <b>env</b>, and return it.
@@ -1012,7 +1012,7 @@ crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src)
RSA_free(dest->key);
dest->key = RSAPrivateKey_dup(src->key);
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
* Returns NULL on failure. */
@@ -1253,9 +1253,12 @@ crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen,
* - The beginning of the source data prefixed with a 16-byte symmetric key,
* padded and encrypted with the public key; followed by the rest of
* the source data encrypted in AES-CTR mode with the symmetric key.
+ *
+ * NOTE that this format does not authenticate the symmetrically encrypted
+ * part of the data, and SHOULD NOT BE USED for new protocols.
*/
int
-crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
+crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env,
char *to, size_t tolen,
const char *from,
size_t fromlen,
@@ -1317,10 +1320,14 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
return -1;
}
-/** Invert crypto_pk_public_hybrid_encrypt. Returns the number of bytes
- * written on success, -1 on failure. */
+/** Invert crypto_pk_obsolete_public_hybrid_encrypt. Returns the number of
+ * bytes written on success, -1 on failure.
+ *
+ * NOTE that this format does not authenticate the symmetrically encrypted
+ * part of the data, and SHOULD NOT BE USED for new protocols.
+ */
int
-crypto_pk_private_hybrid_decrypt(crypto_pk_t *env,
+crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env,
char *to,
size_t tolen,
const char *from,
@@ -1806,8 +1813,8 @@ crypto_digest_algorithm_get_name(digest_algorithm_t alg)
return "sha3-256";
case DIGEST_SHA3_512:
return "sha3-512";
- default:
// LCOV_EXCL_START
+ default:
tor_fragile_assert();
return "??unknown_digest??";
// LCOV_EXCL_STOP
@@ -1869,6 +1876,18 @@ struct crypto_digest_t {
} d;
};
+#ifdef TOR_UNIT_TESTS
+
+digest_algorithm_t
+crypto_digest_get_algorithm(crypto_digest_t *digest)
+{
+ tor_assert(digest);
+
+ return digest->algorithm;
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
/**
* Return the number of bytes we need to malloc in order to get a
* crypto_digest_t for <b>alg</b>, or the number of bytes we need to wipe
@@ -1880,7 +1899,7 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg)
/* Helper: returns the number of bytes in the 'f' field of 'st' */
#define STRUCT_FIELD_SIZE(st, f) (sizeof( ((st*)0)->f ))
/* Gives the length of crypto_digest_t through the end of the field 'd' */
-#define END_OF_FIELD(f) (STRUCT_OFFSET(crypto_digest_t, f) + \
+#define END_OF_FIELD(f) (offsetof(crypto_digest_t, f) + \
STRUCT_FIELD_SIZE(crypto_digest_t, f))
switch (alg) {
case DIGEST_SHA1:
@@ -2251,12 +2270,12 @@ crypto_validate_dh_params(const BIGNUM *p, const BIGNUM *g)
goto out;
if (!DH_set0_pqg(dh, dh_p, NULL, dh_g))
goto out;
-#else
+#else /* !(defined(OPENSSL_1_1_API)) */
if (!(dh->p = BN_dup(p)))
goto out;
if (!(dh->g = BN_dup(g)))
goto out;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
/* Perform the validation. */
int codes = 0;
@@ -2427,7 +2446,7 @@ crypto_dh_new(int dh_type)
if (!DH_set_length(res->dh, DH_PRIVATE_KEY_BITS))
goto err;
-#else
+#else /* !(defined(OPENSSL_1_1_API)) */
if (dh_type == DH_TYPE_TLS) {
if (!(res->dh->p = BN_dup(dh_param_p_tls)))
goto err;
@@ -2440,12 +2459,13 @@ crypto_dh_new(int dh_type)
goto err;
res->dh->length = DH_PRIVATE_KEY_BITS;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
return res;
- err:
+
/* LCOV_EXCL_START
* This error condition is only reached when an allocation fails */
+ err:
crypto_log_errors(LOG_WARN, "creating DH object");
if (res->dh) DH_free(res->dh); /* frees p and g too */
tor_free(res);
@@ -2502,7 +2522,7 @@ crypto_dh_generate_public(crypto_dh_t *dh)
"the-universe chances really do happen. Treating as a failure.");
return -1;
}
-#else
+#else /* !(defined(OPENSSL_1_1_API)) */
if (tor_check_dh_key(LOG_WARN, dh->dh->pub_key)<0) {
/* LCOV_EXCL_START
* If this happens, then openssl's DH implementation is busted. */
@@ -2515,7 +2535,7 @@ crypto_dh_generate_public(crypto_dh_t *dh)
goto again;
/* LCOV_EXCL_STOP */
}
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
return 0;
}
@@ -2536,7 +2556,7 @@ crypto_dh_get_public(crypto_dh_t *dh, char *pubkey, size_t pubkey_len)
DH_get0_key(dh->dh, &dh_pub, &dh_priv);
#else
dh_pub = dh->dh->pub_key;
-#endif
+#endif /* defined(OPENSSL_1_1_API) */
if (!dh_pub) {
if (crypto_dh_generate_public(dh)<0)
@@ -2860,8 +2880,17 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
tor_assert(errno != EAGAIN);
tor_assert(errno != EINTR);
- /* Probably ENOSYS. */
- log_warn(LD_CRYPTO, "Can't get entropy from getrandom().");
+ /* Useful log message for errno. */
+ if (errno == ENOSYS) {
+ log_warn(LD_CRYPTO, "Can't get entropy from getrandom()."
+ " You are running a version of Tor built to support"
+ " getrandom(), but the kernel doesn't implement this"
+ " function--probably because it is too old?");
+ } else {
+ log_warn(LD_CRYPTO, "Can't get entropy from getrandom(): %s.",
+ strerror(errno));
+ }
+
getrandom_works = 0; /* Don't bother trying again. */
return -1;
/* LCOV_EXCL_STOP */
@@ -2879,7 +2908,7 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
return getentropy(out, out_len);
#else
(void) out;
-#endif
+#endif /* defined(_WIN32) || ... */
/* This platform doesn't have a supported syscall based random. */
return -1;
@@ -2903,7 +2932,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
(void)out;
(void)out_len;
return -1;
-#else
+#else /* !(defined(_WIN32)) */
static const char *filenames[] = {
"/dev/srandom", "/dev/urandom", "/dev/random", NULL
};
@@ -2931,7 +2960,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
}
return -1;
-#endif
+#endif /* defined(_WIN32) */
}
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
@@ -3180,7 +3209,7 @@ crypto_rand_double(void)
#define UINT_MAX_AS_DOUBLE 1.8446744073709552e+19
#else
#error SIZEOF_INT is neither 4 nor 8
-#endif
+#endif /* SIZEOF_INT == 4 || ... */
return ((double)u) / UINT_MAX_AS_DOUBLE;
}
@@ -3309,7 +3338,7 @@ memwipe(void *mem, uint8_t byte, size_t sz)
**/
OPENSSL_cleanse(mem, sz);
-#endif
+#endif /* defined(SecureZeroMemory) || defined(HAVE_SECUREZEROMEMORY) || ... */
/* Just in case some caller of memwipe() is relying on getting a buffer
* filled with a particular value, fill the buffer.
@@ -3351,7 +3380,7 @@ tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
{
CRYPTO_THREADID_set_numeric(threadid, tor_get_thread_id());
}
-#endif
+#endif /* !defined(NEW_THREAD_API) */
#if 0
/* This code is disabled, because OpenSSL never actually uses these callbacks.
@@ -3401,7 +3430,7 @@ openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v,
tor_mutex_free(v->lock);
tor_free(v);
}
-#endif
+#endif /* 0 */
/** @{ */
/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
@@ -3418,7 +3447,7 @@ setup_openssl_threading(void)
openssl_mutexes_[i] = tor_mutex_new();
CRYPTO_set_locking_callback(openssl_locking_cb_);
CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id);
-#endif
+#endif /* !defined(NEW_THREAD_API) */
#if 0
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_);
CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_);
@@ -3465,7 +3494,7 @@ crypto_global_cleanup(void)
}
tor_free(ms);
}
-#endif
+#endif /* !defined(NEW_THREAD_API) */
tor_free(crypto_openssl_version_str);
tor_free(crypto_openssl_header_version_str);
@@ -3484,5 +3513,5 @@ crypto_use_tor_alloc_functions(void)
int r = CRYPTO_set_mem_ex_functions(tor_malloc_, tor_realloc_, tor_free_);
return r ? 0 : -1;
}
-#endif
+#endif /* defined(USE_DMALLOC) */
diff --git a/src/common/crypto.h b/src/common/crypto.h
index c70d91c262..f9aeeee2c0 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -20,6 +20,9 @@
#include "testsupport.h"
#include "compat.h"
+#include <openssl/engine.h>
+#include "keccak-tiny/keccak-tiny.h"
+
/*
Macro to create an arbitrary OpenSSL version number as used by
OPENSSL_VERSION_NUMBER or SSLeay(), since the actual numbers are a bit hard
@@ -70,6 +73,9 @@
/** Length of our DH keys. */
#define DH_BYTES (1024/8)
+/** Length of a sha1 message digest when encoded in base32 with trailing =
+ * signs removed. */
+#define BASE32_DIGEST_LEN 32
/** Length of a sha1 message digest when encoded in base64 with trailing =
* signs removed. */
#define BASE64_DIGEST_LEN 27
@@ -194,11 +200,11 @@ int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen);
int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen);
-int crypto_pk_public_hybrid_encrypt(crypto_pk_t *env, char *to,
+int crypto_pk_obsolete_public_hybrid_encrypt(crypto_pk_t *env, char *to,
size_t tolen,
const char *from, size_t fromlen,
int padding, int force);
-int crypto_pk_private_hybrid_decrypt(crypto_pk_t *env, char *to,
+int crypto_pk_obsolete_private_hybrid_decrypt(crypto_pk_t *env, char *to,
size_t tolen,
const char *from, size_t fromlen,
int padding, int warnOnFailure);
@@ -335,6 +341,7 @@ struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
#ifdef CRYPTO_PRIVATE
+
STATIC int crypto_force_rand_ssleay(void);
STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len);
@@ -342,11 +349,12 @@ STATIC int crypto_strongest_rand_raw(uint8_t *out, size_t out_len);
extern int break_strongest_rng_syscall;
extern int break_strongest_rng_fallback;
#endif
-#endif
+#endif /* defined(CRYPTO_PRIVATE) */
#ifdef TOR_UNIT_TESTS
void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
+digest_algorithm_t crypto_digest_get_algorithm(crypto_digest_t *digest);
#endif
-#endif
+#endif /* !defined(TOR_CRYPTO_H) */
diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c
index b99f13a93b..8793fa6274 100644
--- a/src/common/crypto_curve25519.c
+++ b/src/common/crypto_curve25519.c
@@ -43,7 +43,7 @@ int curve25519_donna(uint8_t *mypublic,
#elif defined(HAVE_NACL_CRYPTO_SCALARMULT_CURVE25519_H)
#include <nacl/crypto_scalarmult_curve25519.h>
#endif
-#endif
+#endif /* defined(USE_CURVE25519_NACL) */
static void pick_curve25519_basepoint_impl(void);
@@ -72,7 +72,7 @@ curve25519_impl(uint8_t *output, const uint8_t *secret,
r = crypto_scalarmult_curve25519(output, secret, bp);
#else
#error "No implementation of curve25519 is available."
-#endif
+#endif /* defined(USE_CURVE25519_DONNA) || ... */
memwipe(bp, 0, sizeof(bp));
return r;
}
@@ -318,8 +318,11 @@ curve25519_basepoint_spot_check(void)
}
goto end;
+ // LCOV_EXCL_START -- we can only hit this code if there is a bug in our
+ // curve25519-basepoint implementation.
fail:
r = -1;
+ // LCOV_EXCL_STOP
end:
curve25519_use_ed = save_use_ed;
return r;
diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h
index e7790edac0..d024ab79f5 100644
--- a/src/common/crypto_curve25519.h
+++ b/src/common/crypto_curve25519.h
@@ -71,7 +71,7 @@ STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
const uint8_t *basepoint);
STATIC int curve25519_basepoint_impl(uint8_t *output, const uint8_t *secret);
-#endif
+#endif /* defined(CRYPTO_CURVE25519_PRIVATE) */
#define CURVE25519_BASE64_PADDED_LEN 44
@@ -83,5 +83,5 @@ int curve25519_public_to_base64(char *output,
void curve25519_set_impl_params(int use_ed);
void curve25519_init(void);
-#endif
+#endif /* !defined(TOR_CRYPTO_CURVE25519_H) */
diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c
index 188e18c710..94b23e31b9 100644
--- a/src/common/crypto_ed25519.c
+++ b/src/common/crypto_ed25519.c
@@ -28,6 +28,7 @@
#include "crypto_format.h"
#include "torlog.h"
#include "util.h"
+#include "util_format.h"
#include "ed25519/ref10/ed25519_ref10.h"
#include "ed25519/donna/ed25519_donna_tor.h"
@@ -57,6 +58,9 @@ typedef struct {
int (*pubkey_from_curve25519_pubkey)(unsigned char *, const unsigned char *,
int);
+
+ int (*ed25519_scalarmult_with_group_order)(unsigned char *,
+ const unsigned char *);
} ed25519_impl_t;
/** The Ref10 Ed25519 implementation. This one is pure C and lightly
@@ -77,6 +81,7 @@ static const ed25519_impl_t impl_ref10 = {
ed25519_ref10_blind_public_key,
ed25519_ref10_pubkey_from_curve25519_pubkey,
+ ed25519_ref10_scalarmult_with_group_order,
};
/** The Ref10 Ed25519 implementation. This one is heavily optimized, but still
@@ -97,6 +102,7 @@ static const ed25519_impl_t impl_donna = {
ed25519_donna_blind_public_key,
ed25519_donna_pubkey_from_curve25519_pubkey,
+ ed25519_donna_scalarmult_with_group_order,
};
/** Which Ed25519 implementation are we using? NULL if we haven't decided
@@ -145,7 +151,7 @@ crypto_ed25519_testing_restore_impl(void)
ed25519_impl = saved_ed25519_impl;
saved_ed25519_impl = NULL;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/**
* Initialize a new ed25519 secret key in <b>seckey_out</b>. If
@@ -287,9 +293,12 @@ ed25519_sign_prefixed,(ed25519_signature_t *signature_out,
prefixed_msg = get_prefixed_msg(msg, msg_len, prefix_str,
&prefixed_msg_len);
- if (!prefixed_msg) {
+ if (BUG(!prefixed_msg)) {
+ /* LCOV_EXCL_START -- only possible when the message and prefix are
+ * ridiculously huge */
log_warn(LD_GENERAL, "Failed to get prefixed msg.");
return -1;
+ /* LCOV_EXCL_STOP */
}
retval = ed25519_sign(signature_out,
@@ -332,9 +341,12 @@ ed25519_checksig_prefixed(const ed25519_signature_t *signature,
prefixed_msg = get_prefixed_msg(msg, msg_len, prefix_str,
&prefixed_msg_len);
- if (!prefixed_msg) {
+ if (BUG(!prefixed_msg)) {
+ /* LCOV_EXCL_START -- only possible when the message and prefix are
+ * ridiculously huge */
log_warn(LD_GENERAL, "Failed to get prefixed msg.");
return -1;
+ /* LCOV_EXCL_STOP */
}
retval = ed25519_checksig(signature,
@@ -462,7 +474,6 @@ ed25519_keypair_from_curve25519_keypair(ed25519_keypair_t *out,
tor_assert(fast_memeq(pubkey_check.pubkey, out->pubkey.pubkey, 32));
memwipe(&pubkey_check, 0, sizeof(pubkey_check));
- memwipe(&ctx, 0, sizeof(ctx));
memwipe(sha512_output, 0, sizeof(sha512_output));
return 0;
@@ -491,7 +502,8 @@ ed25519_public_key_from_curve25519_public_key(ed25519_public_key_t *pubkey,
* service descriptors are encrypted with a key derived from the service's
* long-term public key, and then signed with (and stored at a position
* indexed by) a short-term key derived by blinding the long-term keys.
- */
+ *
+ * Return 0 if blinding was successful, else return -1. */
int
ed25519_keypair_blind(ed25519_keypair_t *out,
const ed25519_keypair_t *inp,
@@ -502,7 +514,9 @@ ed25519_keypair_blind(ed25519_keypair_t *out,
get_ed_impl()->blind_secret_key(out->seckey.seckey,
inp->seckey.seckey, param);
- ed25519_public_blind(&pubkey_check, &inp->pubkey, param);
+ if (ed25519_public_blind(&pubkey_check, &inp->pubkey, param) < 0) {
+ return -1;
+ }
ed25519_public_key_generate(&out->pubkey, &out->seckey);
tor_assert(fast_memeq(pubkey_check.pubkey, out->pubkey.pubkey, 32));
@@ -522,8 +536,7 @@ ed25519_public_blind(ed25519_public_key_t *out,
const ed25519_public_key_t *inp,
const uint8_t *param)
{
- get_ed_impl()->blind_public_key(out->pubkey, inp->pubkey, param);
- return 0;
+ return get_ed_impl()->blind_public_key(out->pubkey, inp->pubkey, param);
}
/**
@@ -711,8 +724,11 @@ ed25519_impl_spot_check,(void))
*/
goto end;
+ // LCOV_EXCL_START -- We can only reach this if our ed25519 implementation is
+ // broken.
fail:
r = -1;
+ // LCOV_EXCL_STOP
end:
return r;
}
@@ -754,3 +770,47 @@ ed25519_init(void)
pick_ed25519_impl();
}
+/* Return true if <b>point</b> is the identity element of the ed25519 group. */
+static int
+ed25519_point_is_identity_element(const uint8_t *point)
+{
+ /* The identity element in ed25159 is the point with coordinates (0,1). */
+ static const uint8_t ed25519_identity[32] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ tor_assert(sizeof(ed25519_identity) == ED25519_PUBKEY_LEN);
+ return tor_memeq(point, ed25519_identity, sizeof(ed25519_identity));
+}
+
+/** Validate <b>pubkey</b> to ensure that it has no torsion component.
+ * Return 0 if <b>pubkey</b> is valid, else return -1. */
+int
+ed25519_validate_pubkey(const ed25519_public_key_t *pubkey)
+{
+ uint8_t result[32] = {9};
+
+ /* First check that we were not given the identity element */
+ if (ed25519_point_is_identity_element(pubkey->pubkey)) {
+ log_warn(LD_CRYPTO, "ed25519 pubkey is the identity");
+ return -1;
+ }
+
+ /* For any point on the curve, doing l*point should give the identity element
+ * (where l is the group order). Do the computation and check that the
+ * identity element is returned. */
+ if (get_ed_impl()->ed25519_scalarmult_with_group_order(result,
+ pubkey->pubkey) < 0) {
+ log_warn(LD_CRYPTO, "ed25519 group order scalarmult failed");
+ return -1;
+ }
+
+ if (!ed25519_point_is_identity_element(result)) {
+ log_warn(LD_CRYPTO, "ed25519 validation failed");
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h
index 77a3313adc..8d13a487d6 100644
--- a/src/common/crypto_ed25519.h
+++ b/src/common/crypto_ed25519.h
@@ -127,6 +127,8 @@ void ed25519_pubkey_copy(ed25519_public_key_t *dest,
void ed25519_set_impl_params(int use_donna);
void ed25519_init(void);
+int ed25519_validate_pubkey(const ed25519_public_key_t *pubkey);
+
#ifdef TOR_UNIT_TESTS
void crypto_ed25519_testing_force_impl(const char *name);
void crypto_ed25519_testing_restore_impl(void);
@@ -136,5 +138,5 @@ void crypto_ed25519_testing_restore_impl(void);
MOCK_DECL(STATIC int, ed25519_impl_spot_check, (void));
#endif
-#endif
+#endif /* !defined(TOR_CRYPTO_ED25519_H) */
diff --git a/src/common/crypto_format.h b/src/common/crypto_format.h
index 390916cf04..bbd85dc720 100644
--- a/src/common/crypto_format.h
+++ b/src/common/crypto_format.h
@@ -43,5 +43,5 @@ int digest_from_base64(char *digest, const char *d64);
int digest256_to_base64(char *d64, const char *digest);
int digest256_from_base64(char *digest, const char *d64);
-#endif
+#endif /* !defined(TOR_CRYPTO_FORMAT_H) */
diff --git a/src/common/crypto_pwbox.c b/src/common/crypto_pwbox.c
index db8892e376..12acc9331c 100644
--- a/src/common/crypto_pwbox.c
+++ b/src/common/crypto_pwbox.c
@@ -107,7 +107,6 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
rv = 0;
goto out;
- err:
/* LCOV_EXCL_START
This error case is often unreachable if we're correctly coded, unless
@@ -123,6 +122,7 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
- pwbox_encoded_encode can't fail unless we're using trunnel wrong,
or it's buggy.
*/
+ err:
tor_free(result);
rv = -1;
/* LCOV_EXCL_STOP */
diff --git a/src/common/crypto_pwbox.h b/src/common/crypto_pwbox.h
index aadd477078..cee8653587 100644
--- a/src/common/crypto_pwbox.h
+++ b/src/common/crypto_pwbox.h
@@ -16,5 +16,5 @@ int crypto_unpwbox(uint8_t **out, size_t *outlen_out,
const uint8_t *inp, size_t input_len,
const char *secret, size_t secret_len);
-#endif
+#endif /* !defined(CRYPTO_PWBOX_H_INCLUDED_) */
diff --git a/src/common/crypto_s2k.c b/src/common/crypto_s2k.c
index 076df815a9..b2fcca54c4 100644
--- a/src/common/crypto_s2k.c
+++ b/src/common/crypto_s2k.c
@@ -86,9 +86,11 @@ secret_to_key_key_len(uint8_t type)
return DIGEST_LEN;
case S2K_TYPE_SCRYPT:
return DIGEST256_LEN;
+ // LCOV_EXCL_START
default:
- tor_fragile_assert(); // LCOV_EXCL_LINE
- return -1; // LCOV_EXCL_LINE
+ tor_fragile_assert();
+ return -1;
+ // LCOV_EXCL_STOP
}
}
@@ -169,9 +171,11 @@ make_specifier(uint8_t *spec_out, uint8_t type, unsigned flags)
/* r = 8; p = 2. */
spec_out[SCRYPT_SPEC_LEN-1] = (3u << 4) | (1u << 0);
break;
+ // LCOV_EXCL_START - we should have returned above.
default:
- tor_fragile_assert(); // LCOV_EXCL_LINE - we should have returned above.
+ tor_fragile_assert();
return S2K_BAD_ALGORITHM;
+ // LCOV_EXCL_STOP
}
return speclen;
@@ -290,9 +294,9 @@ secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
if (rv != 0)
return S2K_FAILED;
return (int)key_out_len;
-#else
+#else /* !(defined(HAVE_SCRYPT)) */
return S2K_NO_SCRYPT_SUPPORT;
-#endif
+#endif /* defined(HAVE_SCRYPT) */
}
default:
return S2K_BAD_ALGORITHM;
diff --git a/src/common/crypto_s2k.h b/src/common/crypto_s2k.h
index 04212b868a..849ff59ce8 100644
--- a/src/common/crypto_s2k.h
+++ b/src/common/crypto_s2k.h
@@ -67,7 +67,7 @@ STATIC int secret_to_key_compute_key(uint8_t *key_out, size_t key_out_len,
const uint8_t *spec, size_t spec_len,
const char *secret, size_t secret_len,
int type);
-#endif
+#endif /* defined(CRYPTO_S2K_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CRYPTO_S2K_H_INCLUDED) */
diff --git a/src/common/di_ops.c b/src/common/di_ops.c
index e47998107d..7c0b4e7630 100644
--- a/src/common/di_ops.c
+++ b/src/common/di_ops.c
@@ -86,7 +86,7 @@ tor_memcmp(const void *a, const void *b, size_t len)
}
return retval;
-#endif /* timingsafe_memcmp */
+#endif /* defined(HAVE_TIMINGSAFE_MEMCMP) */
}
/**
@@ -238,7 +238,7 @@ gt_i64_timei(uint64_t a, uint64_t b)
int res = diff >> 63;
return res & 1;
}
-#endif
+#endif /* SIZEOF_VOID_P == 8 */
/**
* Given an array of list of <b>n_entries</b> uint64_t values, whose sum is
diff --git a/src/common/di_ops.h b/src/common/di_ops.h
index e174fcc4e4..e79973ba52 100644
--- a/src/common/di_ops.h
+++ b/src/common/di_ops.h
@@ -46,5 +46,5 @@ int select_array_member_cumulative_timei(const uint64_t *entries,
int n_entries,
uint64_t total, uint64_t rand_val);
-#endif
+#endif /* !defined(TOR_DI_OPS_H) */
diff --git a/src/common/handles.h b/src/common/handles.h
index 6d7262ab80..a610753a1c 100644
--- a/src/common/handles.h
+++ b/src/common/handles.h
@@ -149,5 +149,5 @@
} \
}
-#endif /* TOR_HANDLE_H */
+#endif /* !defined(TOR_HANDLE_H) */
diff --git a/src/common/include.am b/src/common/include.am
index d12895b107..715ec0264c 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -82,6 +82,7 @@ LIBOR_A_SRC = \
src/common/address.c \
src/common/address_set.c \
src/common/backtrace.c \
+ src/common/buffers.c \
src/common/compat.c \
src/common/compat_threads.c \
src/common/compat_time.c \
@@ -111,6 +112,7 @@ src/common/src_common_libor_testing_a-log.$(OBJEXT) \
LIBOR_CRYPTO_A_SRC = \
src/common/aes.c \
+ src/common/buffers_tls.c \
src/common/compress.c \
src/common/compress_lzma.c \
src/common/compress_none.c \
@@ -149,6 +151,8 @@ COMMONHEADERS = \
src/common/address.h \
src/common/address_set.h \
src/common/backtrace.h \
+ src/common/buffers.h \
+ src/common/buffers_tls.h \
src/common/aes.h \
src/common/ciphers.inc \
src/common/compat.h \
diff --git a/src/common/log.c b/src/common/log.c
index 87c260799d..e4d5cd8fd8 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -444,11 +444,11 @@ logfile_deliver(logfile_t *lf, const char *buf, size_t msg_len,
if (m != msg_after_prefix) {
tor_free(m);
}
-#else
+#else /* !(defined(MAXLINE)) */
/* We have syslog but not MAXLINE. That's promising! */
syslog(severity, "%s", msg_after_prefix);
-#endif
-#endif
+#endif /* defined(MAXLINE) */
+#endif /* defined(HAVE_SYSLOG_H) */
} else if (lf->callback) {
if (domain & LD_NOCB) {
if (!*callbacks_deferred && pending_cb_messages) {
@@ -807,7 +807,7 @@ close_log(logfile_t *victim)
/* There are no other syslogs; close the logging facility. */
closelog();
}
-#endif
+#endif /* defined(HAVE_SYSLOG_H) */
}
}
@@ -1144,7 +1144,7 @@ add_syslog_log(const log_severity_list_t *severity,
UNLOCK_LOGS();
return 0;
}
-#endif
+#endif /* defined(HAVE_SYSLOG_H) */
/** If <b>level</b> is a valid log severity, return the corresponding
* numeric value. Otherwise, return -1. */
diff --git a/src/common/memarea.c b/src/common/memarea.c
index 659d1edf54..b059987e0e 100644
--- a/src/common/memarea.c
+++ b/src/common/memarea.c
@@ -7,6 +7,7 @@
*/
#include "orconfig.h"
+#include <stddef.h>
#include <stdlib.h>
#include "memarea.h"
#include "util.h"
@@ -32,7 +33,7 @@
#define MEMAREA_ALIGN_MASK ((uintptr_t)7)
#else
#error "void* is neither 4 nor 8 bytes long. I don't know how to align stuff."
-#endif
+#endif /* MEMAREA_ALIGN == 4 || ... */
#if defined(__GNUC__) && defined(FLEXIBLE_ARRAY_MEMBER)
#define USE_ALIGNED_ATTRIBUTE
@@ -40,7 +41,7 @@
#define U_MEM mem
#else
#define U_MEM u.mem
-#endif
+#endif /* defined(__GNUC__) && defined(FLEXIBLE_ARRAY_MEMBER) */
#ifdef USE_SENTINELS
/** Magic value that we stick at the end of a memarea so we can make sure
@@ -60,11 +61,11 @@
uint32_t sent_val = get_uint32(&(chunk)->U_MEM[chunk->mem_size]); \
tor_assert(sent_val == SENTINEL_VAL); \
STMT_END
-#else
+#else /* !(defined(USE_SENTINELS)) */
#define SENTINEL_LEN 0
#define SET_SENTINEL(chunk) STMT_NIL
#define CHECK_SENTINEL(chunk) STMT_NIL
-#endif
+#endif /* defined(USE_SENTINELS) */
/** Increment <b>ptr</b> until it is aligned to MEMAREA_ALIGN. */
static inline void *
@@ -96,12 +97,12 @@ typedef struct memarea_chunk_t {
void *void_for_alignment_; /**< Dummy; used to make sure mem is aligned. */
} u; /**< Union used to enforce alignment when we don't have support for
* doing it right. */
-#endif
+#endif /* defined(USE_ALIGNED_ATTRIBUTE) */
} memarea_chunk_t;
/** How many bytes are needed for overhead before we get to the memory part
* of a chunk? */
-#define CHUNK_HEADER_SIZE STRUCT_OFFSET(memarea_chunk_t, U_MEM)
+#define CHUNK_HEADER_SIZE offsetof(memarea_chunk_t, U_MEM)
/** What's the smallest that we'll allocate a chunk? */
#define CHUNK_SIZE 4096
@@ -307,7 +308,7 @@ memarea_assert_ok(memarea_t *area)
}
}
-#else
+#else /* !(!defined(DISABLE_MEMORY_SENTINELS)) */
struct memarea_t {
smartlist_t *pieces;
@@ -393,5 +394,5 @@ memarea_assert_ok(memarea_t *area)
(void)area;
}
-#endif
+#endif /* !defined(DISABLE_MEMORY_SENTINELS) */
diff --git a/src/common/memarea.h b/src/common/memarea.h
index 85012c1c34..c3d954e1ce 100644
--- a/src/common/memarea.h
+++ b/src/common/memarea.h
@@ -20,5 +20,5 @@ void memarea_get_stats(memarea_t *area,
size_t *allocated_out, size_t *used_out);
void memarea_assert_ok(memarea_t *area);
-#endif
+#endif /* !defined(TOR_MEMAREA_H) */
diff --git a/src/common/procmon.c b/src/common/procmon.c
index d49e7f18f5..26c11823e8 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -36,7 +36,7 @@ typedef int pid_t;
#define PID_T_FORMAT I64_FORMAT
#else
#error Unknown: SIZEOF_PID_T
-#endif
+#endif /* (0 == SIZEOF_PID_T) && defined(_WIN32) || ... */
/* Define to 1 if process-termination monitors on this OS and Libevent
version must poll for process termination themselves. */
@@ -71,7 +71,7 @@ parse_process_specifier(const char *process_spec,
/* If we're lucky, long will turn out to be large enough to hold a
* PID everywhere that Tor runs. */
- pid_l = tor_parse_long(process_spec, 0, 1, LONG_MAX, &pid_ok, &pspec_next);
+ pid_l = tor_parse_long(process_spec, 10, 1, LONG_MAX, &pid_ok, &pspec_next);
/* Reserve room in the ‘process specifier’ for additional
* (platform-specific) identifying information beyond the PID, to
@@ -114,7 +114,7 @@ struct tor_process_monitor_t {
HANDLE hproc;
/* XXXX We should have Libevent watch hproc for us,
* if/when some version of Libevent can be told to do so. */
-#endif
+#endif /* defined(_WIN32) */
/* XXXX On Linux, we can and should receive the 22nd
* (space-delimited) field (‘starttime’) of /proc/$PID/stat from the
@@ -219,7 +219,7 @@ tor_process_monitor_new(struct event_base *base,
"try again later.",
procmon->pid);
}
-#endif
+#endif /* defined(_WIN32) */
procmon->cb = cb;
procmon->cb_arg = cb_arg;
@@ -232,9 +232,9 @@ tor_process_monitor_new(struct event_base *base,
* tor_evtimer_new never returns NULL. */
evtimer_add(procmon->e, &poll_interval_tv);
-#else
+#else /* !(defined(PROCMON_POLLS)) */
#error OOPS?
-#endif
+#endif /* defined(PROCMON_POLLS) */
return procmon;
err:
@@ -306,11 +306,11 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
tor_free(errmsg);
}
}
-#else
+#else /* !(defined(_WIN32)) */
/* Unix makes this part easy, if a bit racy. */
its_dead_jim = kill(procmon->pid, 0);
its_dead_jim = its_dead_jim && (errno == ESRCH);
-#endif
+#endif /* defined(_WIN32) */
tor_log(its_dead_jim ? LOG_NOTICE : LOG_INFO,
procmon->log_domain, "Monitored process "PID_T_FORMAT" is %s.",
@@ -321,7 +321,7 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
procmon->cb(procmon->cb_arg);
}
}
-#endif
+#endif /* defined(PROCMON_POLLS) */
/** Free the process-termination monitor <b>procmon</b>. */
void
diff --git a/src/common/procmon.h b/src/common/procmon.h
index b07cff2c4a..10ead11ba8 100644
--- a/src/common/procmon.h
+++ b/src/common/procmon.h
@@ -29,5 +29,5 @@ tor_process_monitor_t *tor_process_monitor_new(struct event_base *base,
const char **msg);
void tor_process_monitor_free(tor_process_monitor_t *procmon);
-#endif
+#endif /* !defined(TOR_PROCMON_H) */
diff --git a/src/common/pubsub.h b/src/common/pubsub.h
index 6f4ce08754..2bee3af085 100644
--- a/src/common/pubsub.h
+++ b/src/common/pubsub.h
@@ -175,5 +175,5 @@ int pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn,
pubsub_clear_(&name##_topic_); \
}
-#endif /* TOR_PUBSUB_H */
+#endif /* !defined(TOR_PUBSUB_H) */
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index 312cf37722..97acf894f3 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -17,7 +17,7 @@
* with the libevent fix.
*/
#define _LARGEFILE64_SOURCE
-#endif
+#endif /* !defined(_LARGEFILE64_SOURCE) */
/** Malloc mprotect limit in bytes.
*
@@ -80,7 +80,7 @@
#define USE_BACKTRACE
#define EXPOSE_CLEAN_BACKTRACE
#include "backtrace.h"
-#endif
+#endif /* defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && ... */
#ifdef USE_BACKTRACE
#include <execinfo.h>
@@ -106,7 +106,12 @@
#define M_SYSCALL arm_r7
-#endif
+#elif defined(__aarch64__) && defined(__LP64__)
+
+#define REG_SYSCALL 8
+#define M_SYSCALL regs[REG_SYSCALL]
+
+#endif /* defined(__i386__) || ... */
/**Determines if at least one sandbox is active.*/
static int sandbox_active = 0;
@@ -299,37 +304,6 @@ sb_rt_sigaction(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return rc;
}
-#if 0
-/**
- * Function responsible for setting up the execve syscall for
- * the seccomp filter sandbox.
- */
-static int
-sb_execve(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
-{
- int rc;
- sandbox_cfg_t *elem = NULL;
-
- // for each dynamic parameter filters
- for (elem = filter; elem != NULL; elem = elem->next) {
- smp_param_t *param = elem->param;
-
- if (param != NULL && param->prot == 1 && param->syscall
- == SCMP_SYS(execve)) {
- rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve),
- SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
- if (rc != 0) {
- log_err(LD_BUG,"(Sandbox) failed to add execve syscall, received "
- "libseccomp error %d", rc);
- return rc;
- }
- }
- }
-
- return 0;
-}
-#endif
-
/**
* Function responsible for setting up the time syscall for
* the seccomp filter sandbox.
@@ -343,7 +317,7 @@ sb_time(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(0, SCMP_CMP_EQ, 0));
#else
return 0;
-#endif
+#endif /* defined(__NR_time) */
}
/**
@@ -362,7 +336,7 @@ sb_accept4(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
if (rc) {
return rc;
}
-#endif
+#endif /* defined(__i386__) */
rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(accept4),
SCMP_CMP_MASKED(3, SOCK_CLOEXEC|SOCK_NONBLOCK, 0));
@@ -435,7 +409,7 @@ sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return 0;
}
-#endif
+#endif /* defined(__NR_mmap2) */
#ifdef HAVE_GNU_LIBC_VERSION_H
#ifdef HAVE_GNU_GET_LIBC_VERSION
@@ -546,7 +520,7 @@ sb_chmod(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chmod),
SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
if (rc != 0) {
- log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
+ log_err(LD_BUG,"(Sandbox) failed to add chmod syscall, received "
"libseccomp error %d", rc);
return rc;
}
@@ -571,7 +545,7 @@ sb_chown(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(chown),
SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
if (rc != 0) {
- log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
+ log_err(LD_BUG,"(Sandbox) failed to add chown syscall, received "
"libseccomp error %d", rc);
return rc;
}
@@ -750,6 +724,25 @@ sb_socketpair(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return 0;
}
+#ifdef HAVE_KIST_SUPPORT
+
+#include <linux/sockios.h>
+
+static int
+sb_ioctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
+{
+ int rc;
+ (void) filter;
+
+ rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl),
+ SCMP_CMP(1, SCMP_CMP_EQ, SIOCOUTQNSD));
+ if (rc)
+ return rc;
+ return 0;
+}
+
+#endif /* defined(HAVE_KIST_SUPPORT) */
+
/**
* Function responsible for setting up the setsockopt syscall for
* the seccomp filter sandbox.
@@ -790,7 +783,7 @@ sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUFFORCE));
if (rc)
return rc;
-#endif
+#endif /* defined(HAVE_SYSTEMD) */
#ifdef IP_TRANSPARENT
rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt),
@@ -798,7 +791,7 @@ sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(2, SCMP_CMP_EQ, IP_TRANSPARENT));
if (rc)
return rc;
-#endif
+#endif /* defined(IP_TRANSPARENT) */
#ifdef IPV6_V6ONLY
rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt),
@@ -806,7 +799,7 @@ sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(2, SCMP_CMP_EQ, IPV6_V6ONLY));
if (rc)
return rc;
-#endif
+#endif /* defined(IPV6_V6ONLY) */
return 0;
}
@@ -839,7 +832,7 @@ sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(2, SCMP_CMP_EQ, SO_SNDBUF));
if (rc)
return rc;
-#endif
+#endif /* defined(HAVE_SYSTEMD) */
#ifdef HAVE_LINUX_NETFILTER_IPV4_H
rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt),
@@ -847,7 +840,7 @@ sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(2, SCMP_CMP_EQ, SO_ORIGINAL_DST));
if (rc)
return rc;
-#endif
+#endif /* defined(HAVE_LINUX_NETFILTER_IPV4_H) */
#ifdef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H
rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt),
@@ -855,7 +848,16 @@ sb_getsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(2, SCMP_CMP_EQ, IP6T_SO_ORIGINAL_DST));
if (rc)
return rc;
-#endif
+#endif /* defined(HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H) */
+
+#ifdef HAVE_KIST_SUPPORT
+#include <netinet/tcp.h>
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getsockopt),
+ SCMP_CMP(1, SCMP_CMP_EQ, SOL_TCP),
+ SCMP_CMP(2, SCMP_CMP_EQ, TCP_INFO));
+ if (rc)
+ return rc;
+#endif /* defined(HAVE_KIST_SUPPORT) */
return 0;
}
@@ -895,7 +897,7 @@ sb_fcntl64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return 0;
}
-#endif
+#endif /* defined(__NR_fcntl64) */
/**
* Function responsible for setting up the epoll_ctl syscall for
@@ -1092,8 +1094,8 @@ sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat64),
SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value));
if (rc != 0) {
- log_err(LD_BUG,"(Sandbox) failed to add open syscall, received "
- "libseccomp error %d", rc);
+ log_err(LD_BUG,"(Sandbox) failed to add stat64 syscall, received "
+ "libseccomp error %d", rc);
return rc;
}
}
@@ -1101,7 +1103,7 @@ sb_stat64(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return 0;
}
-#endif
+#endif /* defined(__NR_stat64) */
static int
sb_kill(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
@@ -1123,9 +1125,6 @@ sb_kill(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
static sandbox_filter_func_t filter_func[] = {
sb_rt_sigaction,
sb_rt_sigprocmask,
-#if 0
- sb_execve,
-#endif
sb_time,
sb_accept4,
#ifdef __NR_mmap2
@@ -1154,6 +1153,9 @@ static sandbox_filter_func_t filter_func[] = {
sb_setsockopt,
sb_getsockopt,
sb_socketpair,
+#ifdef HAVE_KIST_SUPPORT
+ sb_ioctl,
+#endif
sb_kill
};
@@ -1380,10 +1382,6 @@ sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file)
sandbox_cfg_t *elem = NULL;
elem = new_element(SCMP_stat, file);
- if (!elem) {
- log_err(LD_BUG,"(Sandbox) failed to register parameter!");
- return -1;
- }
elem->next = *cfg;
*cfg = elem;
@@ -1397,10 +1395,6 @@ sandbox_cfg_allow_open_filename(sandbox_cfg_t **cfg, char *file)
sandbox_cfg_t *elem = NULL;
elem = new_element(SCMP_SYS(open), file);
- if (!elem) {
- log_err(LD_BUG,"(Sandbox) failed to register parameter!");
- return -1;
- }
elem->next = *cfg;
*cfg = elem;
@@ -1414,10 +1408,6 @@ sandbox_cfg_allow_chmod_filename(sandbox_cfg_t **cfg, char *file)
sandbox_cfg_t *elem = NULL;
elem = new_element(SCMP_SYS(chmod), file);
- if (!elem) {
- log_err(LD_BUG,"(Sandbox) failed to register parameter!");
- return -1;
- }
elem->next = *cfg;
*cfg = elem;
@@ -1431,10 +1421,6 @@ sandbox_cfg_allow_chown_filename(sandbox_cfg_t **cfg, char *file)
sandbox_cfg_t *elem = NULL;
elem = new_element(SCMP_SYS(chown), file);
- if (!elem) {
- log_err(LD_BUG,"(Sandbox) failed to register parameter!");
- return -1;
- }
elem->next = *cfg;
*cfg = elem;
@@ -1449,11 +1435,6 @@ sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2)
elem = new_element2(SCMP_SYS(rename), file1, file2);
- if (!elem) {
- log_err(LD_BUG,"(Sandbox) failed to register parameter!");
- return -1;
- }
-
elem->next = *cfg;
*cfg = elem;
@@ -1466,10 +1447,6 @@ sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file)
sandbox_cfg_t *elem = NULL;
elem = new_element(SCMP_SYS(openat), file);
- if (!elem) {
- log_err(LD_BUG,"(Sandbox) failed to register parameter!");
- return -1;
- }
elem->next = *cfg;
*cfg = elem;
@@ -1477,26 +1454,6 @@ sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file)
return 0;
}
-#if 0
-int
-sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
-{
- sandbox_cfg_t *elem = NULL;
-
- elem = new_element(SCMP_SYS(execve), com);
- if (!elem) {
- log_err(LD_BUG,"(Sandbox) failed to register parameter!");
- return -1;
- }
-
- elem->next = *cfg;
- *cfg = elem;
-
- return 0;
-}
-
-#endif
-
/** Cache entry for getaddrinfo results; used when sandboxing is implemented
* so that we can consult the cache when the sandbox prevents us from doing
* getaddrinfo.
@@ -1753,7 +1710,9 @@ install_syscall_filter(sandbox_cfg_t* cfg)
// loading the seccomp2 filter
if ((rc = seccomp_load(ctx))) {
- log_err(LD_BUG, "(Sandbox) failed to load: %d (%s)!", rc,
+ log_err(LD_BUG, "(Sandbox) failed to load: %d (%s)! "
+ "Are you sure that your kernel has seccomp2 support? The "
+ "sandbox won't work without it.", rc,
strerror(-rc));
goto end;
}
@@ -1821,7 +1780,7 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context)
/* Clean up the top stack frame so we get the real function
* name for the most recently failing function. */
clean_backtrace(syscall_cb_buf, depth, ctx);
-#endif
+#endif /* defined(USE_BACKTRACE) */
syscall_name = get_syscall_name(syscall);
@@ -1895,7 +1854,7 @@ register_cfg(sandbox_cfg_t* cfg)
return 0;
}
-#endif // USE_LIBSECCOMP
+#endif /* defined(USE_LIBSECCOMP) */
#ifdef USE_LIBSECCOMP
/**
@@ -1925,7 +1884,7 @@ sandbox_is_active(void)
{
return sandbox_active != 0;
}
-#endif // USE_LIBSECCOMP
+#endif /* defined(USE_LIBSECCOMP) */
sandbox_cfg_t*
sandbox_cfg_new(void)
@@ -1953,7 +1912,7 @@ sandbox_init(sandbox_cfg_t *cfg)
"Currently, sandboxing is only implemented on Linux. The feature "
"is disabled on your platform.");
return 0;
-#endif
+#endif /* defined(USE_LIBSECCOMP) || ... */
}
#ifndef USE_LIBSECCOMP
@@ -1971,15 +1930,6 @@ sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file)
return 0;
}
-#if 0
-int
-sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com)
-{
- (void)cfg; (void)com;
- return 0;
-}
-#endif
-
int
sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file)
{
@@ -2018,5 +1968,5 @@ void
sandbox_disable_getaddrinfo_cache(void)
{
}
-#endif
+#endif /* !defined(USE_LIBSECCOMP) */
diff --git a/src/common/sandbox.h b/src/common/sandbox.h
index a6b83153af..d0f85570f4 100644
--- a/src/common/sandbox.h
+++ b/src/common/sandbox.h
@@ -23,7 +23,7 @@
*/
#define SYS_SECCOMP 1
-#endif
+#endif /* !defined(SYS_SECCOMP) */
#if defined(HAVE_SECCOMP_H) && defined(__linux__)
#define USE_LIBSECCOMP
@@ -101,7 +101,7 @@ typedef struct {
sandbox_cfg_t *filter_dynamic;
} sandbox_t;
-#endif // USE_LIBSECCOMP
+#endif /* defined(USE_LIBSECCOMP) */
#ifdef USE_LIBSECCOMP
/** Pre-calls getaddrinfo in order to pre-record result. */
@@ -114,7 +114,7 @@ int sandbox_getaddrinfo(const char *name, const char *servname,
struct addrinfo **res);
void sandbox_freeaddrinfo(struct addrinfo *addrinfo);
void sandbox_free_getaddrinfo_cache(void);
-#else
+#else /* !(defined(USE_LIBSECCOMP)) */
#define sandbox_getaddrinfo(name, servname, hints, res) \
getaddrinfo((name),(servname), (hints),(res))
#define sandbox_add_addrinfo(name) \
@@ -122,16 +122,16 @@ void sandbox_free_getaddrinfo_cache(void);
#define sandbox_freeaddrinfo(addrinfo) \
freeaddrinfo((addrinfo))
#define sandbox_free_getaddrinfo_cache()
-#endif
+#endif /* defined(USE_LIBSECCOMP) */
#ifdef USE_LIBSECCOMP
/** Returns a registered protected string used with the sandbox, given that
* it matches the parameter.
*/
const char* sandbox_intern_string(const char *param);
-#else
+#else /* !(defined(USE_LIBSECCOMP)) */
#define sandbox_intern_string(s) (s)
-#endif
+#endif /* defined(USE_LIBSECCOMP) */
/** Creates an empty sandbox configuration file.*/
sandbox_cfg_t * sandbox_cfg_new(void);
@@ -156,14 +156,6 @@ int sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2);
*/
int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file);
-#if 0
-/**
- * Function used to add a execve allowed filename to a supplied configuration.
- * The (char*) specifies the path to the allowed file; that pointer is stolen.
- */
-int sandbox_cfg_allow_execve(sandbox_cfg_t **cfg, const char *com);
-#endif
-
/**
* Function used to add a stat/stat64 allowed filename to a configuration.
* The (char*) specifies the path to the allowed file; that pointer is stolen.
@@ -178,5 +170,5 @@ int sandbox_is_active(void);
void sandbox_disable_getaddrinfo_cache(void);
-#endif /* SANDBOX_H_ */
+#endif /* !defined(SANDBOX_H_) */
diff --git a/src/common/storagedir.h b/src/common/storagedir.h
index db25057e65..3de0afc361 100644
--- a/src/common/storagedir.h
+++ b/src/common/storagedir.h
@@ -47,5 +47,5 @@ int storage_dir_shrink(storage_dir_t *d,
int storage_dir_remove_all(storage_dir_t *d);
int storage_dir_get_max_files(storage_dir_t *d);
-#endif
+#endif /* !defined(TOR_STORAGEDIR_H) */
diff --git a/src/common/testsupport.h b/src/common/testsupport.h
index 9fc96199b4..a3f2ff91ed 100644
--- a/src/common/testsupport.h
+++ b/src/common/testsupport.h
@@ -10,7 +10,7 @@
#else
#define STATIC static
#define EXTERN(type, name)
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Quick and dirty macros to implement test mocking.
*
@@ -76,15 +76,15 @@
do { \
func = func ##__real; \
} while (0)
-#else
+#else /* !(defined(TOR_UNIT_TESTS)) */
#define MOCK_DECL(rv, funcname, arglist) \
rv funcname arglist
#define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \
rv funcname arglist attr
#define MOCK_IMPL(rv, funcname, arglist) \
rv funcname arglist
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** @} */
-#endif
+#endif /* !defined(TOR_TESTSUPPORT_H) */
diff --git a/src/common/timers.c b/src/common/timers.c
index c43c49c083..c8e09414f4 100644
--- a/src/common/timers.c
+++ b/src/common/timers.c
@@ -53,7 +53,7 @@ struct timeout_cb {
#else
/* We're not exposing any of the functions outside this file. */
#define TIMEOUT_PUBLIC static
-#endif
+#endif /* defined(__GNUC__) */
/* We're not using periodic events. */
#define TIMEOUT_DISABLE_INTERVALS
/* We always know the global_timeouts object, so we don't need each timeout
@@ -191,7 +191,7 @@ timers_initialize(void)
if (BUG(global_timeouts))
return; // LCOV_EXCL_LINE
- timeout_error_t err;
+ timeout_error_t err = 0;
global_timeouts = timeouts_open(0, &err);
if (!global_timeouts) {
// LCOV_EXCL_START -- this can only fail on malloc failure.
diff --git a/src/common/timers.h b/src/common/timers.h
index d9602cd2ae..d4d4fb00a9 100644
--- a/src/common/timers.h
+++ b/src/common/timers.h
@@ -26,5 +26,5 @@ void timers_shutdown(void);
STATIC void timers_run_pending(void);
#endif
-#endif
+#endif /* !defined(TOR_TIMERS_H) */
diff --git a/src/common/torint.h b/src/common/torint.h
index ee31459e94..bc81c114f8 100644
--- a/src/common/torint.h
+++ b/src/common/torint.h
@@ -34,8 +34,8 @@
does the same thing (but doesn't defined __FreeBSD__).
*/
#include <machine/limits.h>
-#endif
-#endif
+#endif /* !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) */
+#endif /* defined(HAVE_MACHINE_LIMITS_H) */
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
@@ -80,7 +80,7 @@ typedef signed char int8_t;
typedef unsigned char uint8_t;
#define HAVE_UINT8_T
#endif
-#endif
+#endif /* (SIZEOF_CHAR == 1) */
#if (SIZEOF_SHORT == 2)
#ifndef HAVE_INT16_T
@@ -91,7 +91,7 @@ typedef signed short int16_t;
typedef unsigned short uint16_t;
#define HAVE_UINT16_T
#endif
-#endif
+#endif /* (SIZEOF_SHORT == 2) */
#if (SIZEOF_INT == 2)
#ifndef HAVE_INT16_T
@@ -129,7 +129,7 @@ typedef unsigned int uint32_t;
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
-#endif
+#endif /* (SIZEOF_INT == 2) || ... */
#if (SIZEOF_LONG == 4)
#ifndef HAVE_INT32_T
@@ -142,7 +142,7 @@ typedef unsigned long uint32_t;
#ifndef UINT32_MAX
#define UINT32_MAX 0xfffffffful
#endif
-#endif
+#endif /* !defined(HAVE_UINT32_T) */
#elif (SIZEOF_LONG == 8)
#ifndef HAVE_INT64_T
typedef signed long int64_t;
@@ -155,7 +155,7 @@ typedef unsigned long uint64_t;
#ifndef UINT64_MAX
#define UINT64_MAX 0xfffffffffffffffful
#endif
-#endif
+#endif /* (SIZEOF_LONG == 4) || ... */
#if (SIZEOF_LONG_LONG == 8)
#ifndef HAVE_INT64_T
@@ -172,7 +172,7 @@ typedef unsigned long long uint64_t;
#ifndef INT64_MAX
#define INT64_MAX 0x7fffffffffffffffll
#endif
-#endif
+#endif /* (SIZEOF_LONG_LONG == 8) */
#if (SIZEOF___INT64 == 8)
#ifndef HAVE_INT64_T
@@ -189,7 +189,7 @@ typedef unsigned __int64 uint64_t;
#ifndef INT64_MAX
#define INT64_MAX 0x7fffffffffffffffi64
#endif
-#endif
+#endif /* (SIZEOF___INT64 == 8) */
#ifndef INT64_MIN
#define INT64_MIN ((- INT64_MAX) - 1)
@@ -202,8 +202,8 @@ typedef unsigned __int64 uint64_t;
#define SIZE_MAX UINT32_MAX
#else
#error "Can't define SIZE_MAX"
-#endif
-#endif
+#endif /* SIZEOF_SIZE_T == 8 || ... */
+#endif /* !defined(SIZE_MAX) */
#ifndef HAVE_SSIZE_T
#if SIZEOF_SIZE_T == 8
@@ -212,8 +212,8 @@ typedef int64_t ssize_t;
typedef int32_t ssize_t;
#else
#error "Can't define ssize_t."
-#endif
-#endif
+#endif /* SIZEOF_SIZE_T == 8 || ... */
+#endif /* !defined(HAVE_SSIZE_T) */
#if (SIZEOF_VOID_P > 4 && SIZEOF_VOID_P <= 8)
#ifndef HAVE_INTPTR_T
@@ -235,7 +235,7 @@ typedef uint32_t uintptr_t;
#endif
#else
#error "void * is either >8 bytes or <= 2. In either case, I am confused."
-#endif
+#endif /* (SIZEOF_VOID_P > 4 && SIZEOF_VOID_P <= 8) || ... */
#ifndef HAVE_INT8_T
#error "Missing type int8_t"
@@ -275,8 +275,8 @@ typedef uint32_t uintptr_t;
#define LONG_MAX 0x7fffffffffffffffL
#else
#error "Can't define LONG_MAX"
-#endif
-#endif
+#endif /* (SIZEOF_LONG == 4) || ... */
+#endif /* !defined(LONG_MAX) */
#ifndef INT_MAX
#if (SIZEOF_INT == 4)
@@ -285,8 +285,8 @@ typedef uint32_t uintptr_t;
#define INT_MAX 0x7fffffffffffffffL
#else
#error "Can't define INT_MAX"
-#endif
-#endif
+#endif /* (SIZEOF_INT == 4) || ... */
+#endif /* !defined(INT_MAX) */
#ifndef UINT_MAX
#if (SIZEOF_INT == 2)
@@ -297,8 +297,8 @@ typedef uint32_t uintptr_t;
#define UINT_MAX 0xffffffffffffffffu
#else
#error "Can't define UINT_MAX"
-#endif
-#endif
+#endif /* (SIZEOF_INT == 2) || ... */
+#endif /* !defined(UINT_MAX) */
#ifndef SHORT_MAX
#if (SIZEOF_SHORT == 2)
@@ -307,8 +307,8 @@ typedef uint32_t uintptr_t;
#define SHORT_MAX 0x7fffffff
#else
#error "Can't define SHORT_MAX"
-#endif
-#endif
+#endif /* (SIZEOF_SHORT == 2) || ... */
+#endif /* !defined(SHORT_MAX) */
#ifndef TIME_MAX
@@ -320,9 +320,9 @@ typedef uint32_t uintptr_t;
#define TIME_MAX ((time_t)INT64_MAX)
#else
#error "Can't define TIME_MAX"
-#endif
+#endif /* (SIZEOF_TIME_T == SIZEOF_INT) || ... */
-#endif /* ifndef(TIME_MAX) */
+#endif /* !defined(TIME_MAX) */
#ifndef TIME_MIN
@@ -334,9 +334,9 @@ typedef uint32_t uintptr_t;
#define TIME_MIN ((time_t)INT64_MIN)
#else
#error "Can't define TIME_MIN"
-#endif
+#endif /* (SIZEOF_TIME_T == SIZEOF_INT) || ... */
-#endif /* ifndef(TIME_MIN) */
+#endif /* !defined(TIME_MIN) */
#ifndef SIZE_MAX
#if (SIZEOF_SIZE_T == 4)
@@ -345,8 +345,8 @@ typedef uint32_t uintptr_t;
#define SIZE_MAX UINT64_MAX
#else
#error "Can't define SIZE_MAX"
-#endif
-#endif
+#endif /* (SIZEOF_SIZE_T == 4) || ... */
+#endif /* !defined(SIZE_MAX) */
#ifndef SSIZE_MAX
#if (SIZEOF_SIZE_T == 4)
@@ -355,13 +355,13 @@ typedef uint32_t uintptr_t;
#define SSIZE_MAX INT64_MAX
#else
#error "Can't define SSIZE_MAX"
-#endif
-#endif
+#endif /* (SIZEOF_SIZE_T == 4) || ... */
+#endif /* !defined(SSIZE_MAX) */
/** Any ssize_t larger than this amount is likely to be an underflow. */
#define SSIZE_T_CEILING ((ssize_t)(SSIZE_MAX-16))
/** Any size_t larger than this amount is likely to be an underflow. */
#define SIZE_T_CEILING ((size_t)(SSIZE_MAX-16))
-#endif /* __TORINT_H */
+#endif /* !defined(TOR_TORINT_H) */
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 0149ce9a5b..be24b2b908 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -22,7 +22,7 @@
#error "Your syslog.h thinks high numbers are more important. " \
"We aren't prepared to deal with that."
#endif
-#else
+#else /* !(defined(HAVE_SYSLOG_H)) */
/* Note: Syslog's logging code refers to priorities, with 0 being the most
* important. Thus, all our comparisons needed to be reversed when we added
* syslog support.
@@ -48,7 +48,7 @@
/** Error-level severity: for messages that only appear when something has gone
* very wrong, and the Tor process can no longer proceed. */
#define LOG_ERR 3
-#endif
+#endif /* defined(HAVE_SYSLOG_H) */
/* Logging domains */
@@ -215,7 +215,7 @@ void log_fn_ratelim_(struct ratelim_t *ratelim, int severity,
#define log_err(domain, args...) \
log_fn_(LOG_ERR, domain, __FUNCTION__, args)
-#else /* ! defined(__GNUC__) */
+#else /* !(defined(__GNUC__) && __GNUC__ <= 3) */
/* Here are the c99 variadic macros, to work with non-GCC compilers */
@@ -242,7 +242,7 @@ void log_fn_ratelim_(struct ratelim_t *ratelim, int severity,
#define log_fn_ratelim(ratelim, severity, domain, args,...) \
log_fn_ratelim_(ratelim, severity, domain, __FUNCTION__, \
args, ##__VA_ARGS__)
-#endif
+#endif /* defined(__GNUC__) && __GNUC__ <= 3 */
#ifdef LOG_PRIVATE
MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain,
@@ -251,5 +251,5 @@ MOCK_DECL(STATIC void, logv, (int severity, log_domain_mask_t domain,
#endif
# define TOR_TORLOG_H
-#endif
+#endif /* !defined(TOR_TORLOG_H) */
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 71de59896a..e8c51879bd 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -76,7 +76,7 @@ ENABLE_GCC_WARNING(redundant-decls)
* SSL3 safely at the same time.
*/
#define DISABLE_SSL3_HANDSHAKE
-#endif
+#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f') */
/* We redefine these so that we can run correctly even if the vendor gives us
* a version of OpenSSL that does not match its header files. (Apple: I am
@@ -390,7 +390,7 @@ tor_tls_init(void)
"when configuring it) would make ECDH much faster.");
}
/* LCOV_EXCL_STOP */
-#endif
+#endif /* (SIZEOF_VOID_P >= 8 && ... */
tor_tls_allocate_tor_tls_object_ex_data_index();
@@ -444,8 +444,9 @@ tor_x509_name_new(const char *cname)
goto error;
/* LCOV_EXCL_BR_STOP */
return name;
- error:
+
/* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
+ error:
X509_NAME_free(name);
return NULL;
/* LCOV_EXCL_STOP */
@@ -490,11 +491,14 @@ tor_tls_create_certificate,(crypto_pk_t *rsa,
* the past. */
const time_t min_real_lifetime = 24*3600;
const time_t start_granularity = 24*3600;
- time_t earliest_start_time = now - cert_lifetime + min_real_lifetime
- + start_granularity;
+ time_t earliest_start_time;
/* Don't actually start in the future! */
- if (earliest_start_time >= now)
+ if (cert_lifetime <= min_real_lifetime + start_granularity) {
earliest_start_time = now - 1;
+ } else {
+ earliest_start_time = now + min_real_lifetime + start_granularity
+ - cert_lifetime;
+ }
start_time = crypto_rand_time_range(earliest_start_time, now);
/* Round the start time back to the start of a day. */
start_time -= start_time % start_granularity;
@@ -1156,7 +1160,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
* with existing Tors. */
if (!(result->ctx = SSL_CTX_new(TLSv1_method())))
goto error;
-#endif
+#endif /* 0 */
/* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */
#ifdef HAVE_TLS_METHOD
@@ -1165,7 +1169,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
#else
if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
goto error;
-#endif
+#endif /* defined(HAVE_TLS_METHOD) */
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
@@ -1203,17 +1207,20 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_options(result->ctx,
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
+
+ /* Don't actually allow compression; it uses RAM and time, it makes TLS
+ * vulnerable to CRIME-style attacks, and most of the data we transmit over
+ * TLS is encrypted (and therefore uncompressible) anyway. */
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION);
#endif
#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
#ifndef OPENSSL_NO_COMP
- /* Don't actually allow compression; it uses ram and time, but the data
- * we transmit is all encrypted anyway. */
if (result->ctx->comp_methods)
result->ctx->comp_methods = NULL;
#endif
-#endif
+#endif /* OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0) */
+
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
@@ -1373,7 +1380,7 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher);
return c != NULL;
}
-#else
+#else /* !(defined(HAVE_SSL_CIPHER_FIND)) */
# if defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR)
if (m && m->get_cipher_by_char) {
@@ -1387,7 +1394,7 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
tor_assert((c->id & 0xffff) == cipher);
return c != NULL;
}
-# endif
+#endif /* defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR) */
# ifndef OPENSSL_1_1_API
if (m && m->get_cipher && m->num_ciphers) {
/* It would seem that some of the "let's-clean-up-openssl" forks have
@@ -1403,12 +1410,12 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
}
return 0;
}
-# endif
+#endif /* !defined(OPENSSL_1_1_API) */
(void) ssl;
(void) m;
(void) cipher;
return 1; /* No way to search */
-#endif
+#endif /* defined(HAVE_SSL_CIPHER_FIND) */
}
/** Remove from v2_cipher_list every cipher that we don't support, so that
@@ -1536,7 +1543,7 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
return CIPHERS_ERR;
}
ciphers = session->ciphers;
-#endif
+#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2;
}
@@ -1667,7 +1674,7 @@ tor_tls_new(int sock, int isServer)
SSL_set_tlsext_host_name(result->ssl, fake_hostname);
tor_free(fake_hostname);
}
-#endif
+#endif /* defined(SSL_set_tlsext_host_name) */
if (!SSL_set_cipher_list(result->ssl,
isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
@@ -1794,7 +1801,7 @@ tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
#else
(void) tls;
-#endif
+#endif /* defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) && ... */
}
/** Return whether this tls initiated the connect (client) or
@@ -2330,7 +2337,7 @@ tor_x509_cert_replace_expiration(const tor_x509_cert_t *inp,
EVP_PKEY_free(pk);
return tor_x509_cert_new(newc);
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Return the number of bytes available for reading from <b>tls</b>.
*/
@@ -2374,10 +2381,10 @@ tor_tls_get_n_raw_bytes(tor_tls_t *tls, size_t *n_read, size_t *n_written)
if (BIO_method_type(wbio) == BIO_TYPE_BUFFER &&
(tmpbio = BIO_next(wbio)) != NULL)
wbio = tmpbio;
-#else
+#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5)) */
if (wbio->method == BIO_f_buffer() && (tmpbio = BIO_next(wbio)) != NULL)
wbio = tmpbio;
-#endif
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) */
w = (unsigned long) BIO_number_written(wbio);
/* We are ok with letting these unsigned ints go "negative" here:
@@ -2456,7 +2463,7 @@ SSL_get_client_random(SSL *s, uint8_t *out, size_t len)
memcpy(out, s->s3->client_random, len);
return len;
}
-#endif
+#endif /* !defined(HAVE_SSL_GET_CLIENT_RANDOM) */
#ifndef HAVE_SSL_GET_SERVER_RANDOM
static size_t
@@ -2469,7 +2476,7 @@ SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
memcpy(out, s->s3->server_random, len);
return len;
}
-#endif
+#endif /* !defined(HAVE_SSL_GET_SERVER_RANDOM) */
#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
STATIC size_t
@@ -2483,7 +2490,7 @@ SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
memcpy(out, s->master_key, len);
return len;
}
-#endif
+#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */
/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in
* the v3 handshake to prove that the client knows the TLS secrets for the
@@ -2592,7 +2599,7 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
(void)wbuf_bytes;
return -1;
-#else
+#else /* !(OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)) */
if (tls->ssl->s3->rbuf.buf)
*rbuf_capacity = tls->ssl->s3->rbuf.len;
else
@@ -2604,7 +2611,7 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
*rbuf_bytes = tls->ssl->s3->rbuf.left;
*wbuf_bytes = tls->ssl->s3->wbuf.left;
return 0;
-#endif
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
}
/** Check whether the ECC group requested is supported by the current OpenSSL
diff --git a/src/common/tortls.h b/src/common/tortls.h
index f430aff70b..6145f7dbc9 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -161,7 +161,7 @@ STATIC int tor_tls_session_secret_cb(struct ssl_st *ssl, void *secret,
void *arg);
STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
uint16_t cipher);
-#endif
+#endif /* defined(TORTLS_OPENSSL_PRIVATE) */
MOCK_DECL(STATIC struct x509_st *, tor_tls_create_certificate,
(crypto_pk_t *rsa,
crypto_pk_t *rsa_sign,
@@ -192,9 +192,9 @@ STATIC tor_x509_cert_t *tor_x509_cert_replace_expiration(
const tor_x509_cert_t *inp,
time_t new_expiration_time,
crypto_pk_t *signing_key);
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* endif TORTLS_PRIVATE */
+#endif /* defined(TORTLS_PRIVATE) */
tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
const char *tor_tls_err_to_string(int err);
@@ -288,5 +288,5 @@ const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls);
int evaluate_ecgroup_for_tls(const char *ecgroup);
-#endif
+#endif /* !defined(TOR_TORTLS_H) */
diff --git a/src/common/util.c b/src/common/util.c
index 5b47028097..5ff7e104d6 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -31,11 +31,11 @@
#include <process.h>
#include <tchar.h>
#include <winbase.h>
-#else
+#else /* !(defined(_WIN32)) */
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
-#endif
+#endif /* defined(_WIN32) */
/* math.h needs this on Linux */
#ifndef _USE_ISOC99_
@@ -84,8 +84,8 @@
* scold us for being so stupid as to autodetect its presence. To be fair,
* they've done this since 1996, when autoconf was only 5 years old. */
#include <malloc.h>
-#endif
-#endif
+#endif /* !defined(OpenBSD) && !defined(__FreeBSD__) */
+#endif /* defined(HAVE_MALLOC_H) */
#ifdef HAVE_MALLOC_NP_H
#include <malloc_np.h>
#endif
@@ -116,12 +116,12 @@
dmalloc_strndup(file, line, (string), -1, xalloc_b)
#else
#error "No dmalloc_strdup or equivalent"
- #endif
+#endif /* defined(HAVE_DMALLOC_STRDUP) || ... */
-#else /* not using dmalloc */
+#else /* !(defined(USE_DMALLOC)) */
#define DMALLOC_FN_ARGS
-#endif
+#endif /* defined(USE_DMALLOC) */
/** Allocate a chunk of <b>size</b> bytes of memory, and return a pointer to
* result. On error, log and terminate the process. (Same as malloc(size),
@@ -142,7 +142,7 @@ tor_malloc_(size_t size DMALLOC_PARAMS)
if (size==0) {
size=1;
}
-#endif
+#endif /* !defined(MALLOC_ZERO_WORKS) */
#ifdef USE_DMALLOC
result = dmalloc_malloc(file, line, size, DMALLOC_FUNC_MALLOC, 0, 0);
@@ -233,7 +233,7 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS)
if (size==0) {
size=1;
}
-#endif
+#endif /* !defined(MALLOC_ZERO_WORKS) */
#ifdef USE_DMALLOC
result = dmalloc_realloc(file, line, ptr, size, DMALLOC_FUNC_REALLOC, 0);
@@ -362,16 +362,16 @@ tor_log_mallinfo(int severity)
mi.arena, mi.ordblks, mi.smblks, mi.hblks,
mi.hblkhd, mi.usmblks, mi.fsmblks, mi.uordblks, mi.fordblks,
mi.keepcost);
-#else
+#else /* !(defined(HAVE_MALLINFO)) */
(void)severity;
-#endif
+#endif /* defined(HAVE_MALLINFO) */
#ifdef USE_DMALLOC
dmalloc_log_changed(0, /* Since the program started. */
1, /* Log info about non-freed pointers. */
0, /* Do not log info about freed pointers. */
0 /* Do not log individual pointers. */
);
-#endif
+#endif /* defined(USE_DMALLOC) */
}
ENABLE_GCC_WARNING(aggregate-return)
@@ -401,7 +401,7 @@ tor_lround(double d)
return (long)rint(d);
#else
return (long)(d > 0 ? d + 0.5 : ceil(d - 0.5));
-#endif
+#endif /* defined(HAVE_LROUND) || ... */
}
/** Return the 64-bit integer closest to d. We define this wrapper here so
@@ -416,7 +416,7 @@ tor_llround(double d)
return (int64_t)rint(d);
#else
return (int64_t)(d > 0 ? d + 0.5 : ceil(d - 0.5));
-#endif
+#endif /* defined(HAVE_LLROUND) || ... */
}
/** Returns floor(log2(u64)). If u64 is 0, (incorrectly) returns 0. */
@@ -445,7 +445,7 @@ tor_log2(uint64_t u64)
r += 2;
}
if (u64 >= (U64_LITERAL(1)<<1)) {
- u64 >>= 1;
+ // u64 >>= 1; // not using this any more.
r += 1;
}
return r;
@@ -477,7 +477,7 @@ round_to_power_of_2(uint64_t u64)
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
* <b>divisor</b> == 0. If no such x can be expressed as an unsigned, return
- * UINT_MAX */
+ * UINT_MAX. Asserts if divisor is zero. */
unsigned
round_to_next_multiple_of(unsigned number, unsigned divisor)
{
@@ -491,7 +491,7 @@ round_to_next_multiple_of(unsigned number, unsigned divisor)
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
* <b>divisor</b> == 0. If no such x can be expressed as a uint32_t, return
- * UINT32_MAX */
+ * UINT32_MAX. Asserts if divisor is zero. */
uint32_t
round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor)
{
@@ -506,7 +506,7 @@ round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor)
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
* <b>divisor</b> == 0. If no such x can be expressed as a uint64_t, return
- * UINT64_MAX */
+ * UINT64_MAX. Asserts if divisor is zero. */
uint64_t
round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
{
@@ -1172,7 +1172,7 @@ tor_parse_long(const char *s, int base, long min, long max,
char *endptr;
long r;
- if (base < 0) {
+ if (BUG(base < 0)) {
if (ok)
*ok = 0;
return 0;
@@ -1191,7 +1191,7 @@ tor_parse_ulong(const char *s, int base, unsigned long min,
char *endptr;
unsigned long r;
- if (base < 0) {
+ if (BUG(base < 0)) {
if (ok)
*ok = 0;
return 0;
@@ -1223,7 +1223,7 @@ tor_parse_uint64(const char *s, int base, uint64_t min,
char *endptr;
uint64_t r;
- if (base < 0) {
+ if (BUG(base < 0)) {
if (ok)
*ok = 0;
return 0;
@@ -1233,20 +1233,12 @@ tor_parse_uint64(const char *s, int base, uint64_t min,
#ifdef HAVE_STRTOULL
r = (uint64_t)strtoull(s, &endptr, base);
#elif defined(_WIN32)
-#if defined(_MSC_VER) && _MSC_VER < 1300
- tor_assert(base <= 10);
- r = (uint64_t)_atoi64(s);
- endptr = (char*)s;
- while (TOR_ISSPACE(*endptr)) endptr++;
- while (TOR_ISDIGIT(*endptr)) endptr++;
-#else
r = (uint64_t)_strtoui64(s, &endptr, base);
-#endif
#elif SIZEOF_LONG == 8
r = (uint64_t)strtoul(s, &endptr, base);
#else
#error "I don't know how to parse 64-bit numbers."
-#endif
+#endif /* defined(HAVE_STRTOULL) || ... */
CHECK_STRTOX_RESULT();
}
@@ -1644,7 +1636,7 @@ tor_timegm(const struct tm *tm, time_t *time_out)
log_warn(LD_BUG, "Result does not fit in tor_timegm");
return -1;
}
-#endif
+#endif /* SIZEOF_TIME_T < 8 */
*time_out = (time_t)seconds;
return 0;
}
@@ -2022,7 +2014,7 @@ update_approx_time(time_t now)
{
cached_approx_time = now;
}
-#endif
+#endif /* !defined(TIME_IS_FAST) */
/* =====
* Rate limiting
@@ -2151,9 +2143,9 @@ clean_name_for_stat(char *name)
return;
name[len-1]='\0';
}
-#else
+#else /* !(defined(_WIN32)) */
(void)name;
-#endif
+#endif /* defined(_WIN32) */
}
/** Wrapper for unlink() to make it mockable for the test suite; returns 0
@@ -2350,21 +2342,27 @@ check_private_dir,(const char *dirname, cpd_check_t check,
running_gid = getgid();
}
if (st.st_uid != running_uid) {
- const struct passwd *pw_uid = NULL;
- char *process_ownername = NULL;
+ char *process_ownername = NULL, *file_ownername = NULL;
- pw_uid = tor_getpwuid(running_uid);
- process_ownername = pw_uid ? tor_strdup(pw_uid->pw_name) :
- tor_strdup("<unknown>");
+ {
+ const struct passwd *pw_running = tor_getpwuid(running_uid);
+ process_ownername = pw_running ? tor_strdup(pw_running->pw_name) :
+ tor_strdup("<unknown>");
+ }
- pw_uid = tor_getpwuid(st.st_uid);
+ {
+ const struct passwd *pw_stat = tor_getpwuid(st.st_uid);
+ file_ownername = pw_stat ? tor_strdup(pw_stat->pw_name) :
+ tor_strdup("<unknown>");
+ }
log_warn(LD_FS, "%s is not owned by this user (%s, %d) but by "
"%s (%d). Perhaps you are running Tor as the wrong user?",
- dirname, process_ownername, (int)running_uid,
- pw ? pw->pw_name : "<unknown>", (int)st.st_uid);
+ dirname, process_ownername, (int)running_uid,
+ file_ownername, (int)st.st_uid);
tor_free(process_ownername);
+ tor_free(file_ownername);
close(fd);
return -1;
}
@@ -2421,7 +2419,7 @@ check_private_dir,(const char *dirname, cpd_check_t check,
}
}
close(fd);
-#else
+#else /* !(!defined(_WIN32)) */
/* Win32 case: we can't open() a directory. */
(void)effective_user;
@@ -2455,7 +2453,7 @@ check_private_dir,(const char *dirname, cpd_check_t check,
return -1;
}
-#endif
+#endif /* !defined(_WIN32) */
return 0;
}
@@ -2475,7 +2473,7 @@ write_str_to_file,(const char *fname, const char *str, int bin))
"We're writing a text string that already contains a CR to %s",
escaped(fname));
}
-#endif
+#endif /* defined(_WIN32) */
return write_bytes_to_file(fname, str, strlen(str), bin);
}
@@ -2875,7 +2873,7 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
errno = save_errno;
return string;
}
-#endif
+#endif /* !defined(_WIN32) */
if ((uint64_t)(statbuf.st_size)+1 >= SIZE_T_CEILING) {
close(fd);
@@ -2908,7 +2906,7 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
if (!bin) {
statbuf.st_size = (size_t) r;
} else
-#endif
+#endif /* defined(_WIN32) || defined(__CYGWIN__) */
if (r != statbuf.st_size) {
/* Unless we're using text mode on win32, we'd better have an exact
* match for size. */
@@ -2982,8 +2980,9 @@ unescape_string(const char *s, char **result, size_t *size_out)
*out = '\0';
if (size_out) *size_out = out - *result;
return cp+1;
- case '\0':
+
/* LCOV_EXCL_START -- we caught this in parse_config_from_line. */
+ case '\0':
tor_fragile_assert();
tor_free(*result);
return NULL;
@@ -3031,8 +3030,9 @@ unescape_string(const char *s, char **result, size_t *size_out)
*out++ = cp[1];
cp += 2;
break;
- default:
+
/* LCOV_EXCL_START */
+ default:
/* we caught this above in the initial loop. */
tor_assert_nonfatal_unreached();
tor_free(*result); return NULL;
@@ -3092,7 +3092,7 @@ expand_filename(const char *filename)
* Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
*/
return tor_strdup(filename);
-#else
+#else /* !(defined(_WIN32)) */
if (*filename == '~') {
char *home, *result=NULL;
const char *rest;
@@ -3122,10 +3122,10 @@ expand_filename(const char *filename)
}
tor_free(username);
rest = slash ? (slash+1) : "";
-#else
+#else /* !(defined(HAVE_PWD_H)) */
log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h");
return tor_strdup(filename);
-#endif
+#endif /* defined(HAVE_PWD_H) */
}
tor_assert(home);
/* Remove trailing slash. */
@@ -3138,7 +3138,7 @@ expand_filename(const char *filename)
} else {
return tor_strdup(filename);
}
-#endif
+#endif /* defined(_WIN32) */
}
#define MAX_SCANF_WIDTH 9999
@@ -3507,7 +3507,7 @@ tor_listdir, (const char *dirname))
name[sizeof(name)-1] = '\0';
#else
strlcpy(name,findData.cFileName,sizeof(name));
-#endif
+#endif /* defined(UNICODE) */
if (strcmp(name, ".") &&
strcmp(name, "..")) {
smartlist_add_strdup(result, name);
@@ -3524,7 +3524,7 @@ tor_listdir, (const char *dirname))
}
FindClose(handle);
tor_free(pattern);
-#else
+#else /* !(defined(_WIN32)) */
const char *prot_dname = sandbox_intern_string(dirname);
DIR *d;
struct dirent *de;
@@ -3539,7 +3539,7 @@ tor_listdir, (const char *dirname))
smartlist_add_strdup(result, de->d_name);
}
closedir(d);
-#endif
+#endif /* defined(_WIN32) */
return result;
}
@@ -3555,7 +3555,7 @@ path_is_relative(const char *filename)
else if (filename && strlen(filename)>3 && TOR_ISALPHA(filename[0]) &&
filename[1] == ':' && filename[2] == '\\')
return 0;
-#endif
+#endif /* defined(_WIN32) */
else
return 1;
}
@@ -3618,7 +3618,7 @@ start_daemon(void)
} else { /* Child */
close(daemon_filedes[0]); /* we only write */
- pid = setsid(); /* Detach from controlling terminal */
+ (void) setsid(); /* Detach from controlling terminal */
/*
* Fork one more time, so the parent (the session group leader) can exit.
* This means that we, as a non-session group leader, can never regain a
@@ -3685,7 +3685,7 @@ finish_daemon(const char *desired_cwd)
}
close(daemon_filedes[1]);
}
-#else
+#else /* !(!defined(_WIN32)) */
/* defined(_WIN32) */
void
start_daemon(void)
@@ -3696,11 +3696,12 @@ finish_daemon(const char *cp)
{
(void)cp;
}
-#endif
+#endif /* !defined(_WIN32) */
/** Write the current process ID, followed by NL, into <b>filename</b>.
+ * Return 0 on success, -1 on failure.
*/
-void
+int
write_pidfile(const char *filename)
{
FILE *pidfile;
@@ -3708,13 +3709,19 @@ write_pidfile(const char *filename)
if ((pidfile = fopen(filename, "w")) == NULL) {
log_warn(LD_FS, "Unable to open \"%s\" for writing: %s", filename,
strerror(errno));
+ return -1;
} else {
#ifdef _WIN32
- fprintf(pidfile, "%d\n", (int)_getpid());
+ int pid = (int)_getpid();
#else
- fprintf(pidfile, "%d\n", (int)getpid());
+ int pid = (int)getpid();
#endif
- fclose(pidfile);
+ int rv = 0;
+ if (fprintf(pidfile, "%d\n", pid) < 0)
+ rv = -1;
+ if (fclose(pidfile) < 0)
+ rv = -1;
+ return rv;
}
}
@@ -3731,7 +3738,7 @@ load_windows_system_library(const TCHAR *library_name)
_tcscat(path, library_name);
return LoadLibrary(path);
}
-#endif
+#endif /* defined(_WIN32) */
/** Format a single argument for being put on a Windows command line.
* Returns a newly allocated string */
@@ -4026,7 +4033,7 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
done:
return res;
}
-#endif
+#endif /* !defined(_WIN32) */
/* Maximum number of file descriptors, if we cannot get it via sysconf() */
#define DEFAULT_MAX_FD 256
@@ -4050,12 +4057,12 @@ tor_terminate_process(process_handle_t *process_handle)
else
return 0;
}
-#else /* Unix */
+#else /* !(defined(_WIN32)) */
if (process_handle->waitpid_cb) {
/* We haven't got a waitpid yet, so we can just kill off the process. */
return kill(process_handle->pid, SIGTERM);
}
-#endif
+#endif /* defined(_WIN32) */
return 0; /* We didn't need to kill the process, so report success */
}
@@ -4077,14 +4084,14 @@ tor_process_get_stdout_pipe(process_handle_t *process_handle)
{
return process_handle->stdout_pipe;
}
-#else
+#else /* !(defined(_WIN32)) */
/* DOCDOC tor_process_get_stdout_pipe */
int
tor_process_get_stdout_pipe(process_handle_t *process_handle)
{
return process_handle->stdout_pipe;
}
-#endif
+#endif /* defined(_WIN32) */
/* DOCDOC process_handle_new */
static process_handle_t *
@@ -4100,7 +4107,7 @@ process_handle_new(void)
out->stdin_pipe = -1;
out->stdout_pipe = -1;
out->stderr_pipe = -1;
-#endif
+#endif /* defined(_WIN32) */
return out;
}
@@ -4120,7 +4127,7 @@ process_handle_waitpid_cb(int status, void *arg)
process_handle->status = PROCESS_STATUS_NOTRUNNING;
process_handle->waitpid_cb = 0;
}
-#endif
+#endif /* !defined(_WIN32) */
/**
* @name child-process states
@@ -4142,6 +4149,20 @@ process_handle_waitpid_cb(int status, void *arg)
#define CHILD_STATE_EXEC 8
#define CHILD_STATE_FAILEXEC 9
/** @} */
+/**
+ * Boolean. If true, then Tor may call execve or CreateProcess via
+ * tor_spawn_background.
+ **/
+static int may_spawn_background_process = 1;
+/**
+ * Turn off may_spawn_background_process, so that all future calls to
+ * tor_spawn_background are guaranteed to fail.
+ **/
+void
+tor_disable_spawning_background_processes(void)
+{
+ may_spawn_background_process = 0;
+}
/** Start a program in the background. If <b>filename</b> contains a '/', then
* it will be treated as an absolute or relative path. Otherwise, on
* non-Windows systems, the system path will be searched for <b>filename</b>.
@@ -4166,6 +4187,12 @@ tor_spawn_background(const char *const filename, const char **argv,
process_environment_t *env,
process_handle_t **process_handle_out)
{
+ if (BUG(may_spawn_background_process == 0)) {
+ /* We should never reach this point if we're forbidden to spawn
+ * processes. Instead we should have caught the attempt earlier. */
+ return PROCESS_STATUS_ERROR;
+ }
+
#ifdef _WIN32
HANDLE stdout_pipe_read = NULL;
HANDLE stdout_pipe_write = NULL;
@@ -4283,13 +4310,12 @@ tor_spawn_background(const char *const filename, const char **argv,
/* TODO: Close pipes on exit */
*process_handle_out = process_handle;
return status;
-#else // _WIN32
+#else /* !(defined(_WIN32)) */
pid_t pid;
int stdout_pipe[2];
int stderr_pipe[2];
int stdin_pipe[2];
int fd, retval;
- ssize_t nbytes;
process_handle_t *process_handle;
int status;
@@ -4310,7 +4336,7 @@ tor_spawn_background(const char *const filename, const char **argv,
and we are not allowed to use unsafe functions between fork and exec */
error_message_length = strlen(error_message);
- child_state = CHILD_STATE_PIPE;
+ // child_state = CHILD_STATE_PIPE;
/* Set up pipe for redirecting stdout, stderr, and stdin of child */
retval = pipe(stdout_pipe);
@@ -4347,7 +4373,7 @@ tor_spawn_background(const char *const filename, const char **argv,
return status;
}
- child_state = CHILD_STATE_MAXFD;
+ // child_state = CHILD_STATE_MAXFD;
#ifdef _SC_OPEN_MAX
if (-1 == max_fd) {
@@ -4358,11 +4384,11 @@ tor_spawn_background(const char *const filename, const char **argv,
"Cannot find maximum file descriptor, assuming %d", max_fd);
}
}
-#else
+#else /* !(defined(_SC_OPEN_MAX)) */
max_fd = DEFAULT_MAX_FD;
-#endif
+#endif /* defined(_SC_OPEN_MAX) */
- child_state = CHILD_STATE_FORK;
+ // child_state = CHILD_STATE_FORK;
pid = fork();
if (0 == pid) {
@@ -4375,7 +4401,7 @@ tor_spawn_background(const char *const filename, const char **argv,
* than nothing.
*/
prctl(PR_SET_PDEATHSIG, SIGTERM);
-#endif
+#endif /* defined(HAVE_SYS_PRCTL_H) && defined(__linux__) */
child_state = CHILD_STATE_DUPOUT;
@@ -4398,7 +4424,7 @@ tor_spawn_background(const char *const filename, const char **argv,
if (-1 == retval)
goto error;
- child_state = CHILD_STATE_CLOSEFD;
+ // child_state = CHILD_STATE_CLOSEFD;
close(stderr_pipe[0]);
close(stderr_pipe[1]);
@@ -4414,7 +4440,7 @@ tor_spawn_background(const char *const filename, const char **argv,
close(fd);
}
- child_state = CHILD_STATE_EXEC;
+ // child_state = CHILD_STATE_EXEC;
/* Call the requested program. We need the cast because
execvp doesn't define argv as const, even though it
@@ -4433,7 +4459,8 @@ tor_spawn_background(const char *const filename, const char **argv,
error:
{
/* XXX: are we leaking fds from the pipe? */
- int n;
+ int n, err=0;
+ ssize_t nbytes;
n = format_helper_exit_status(child_state, errno, hex_errno);
@@ -4442,13 +4469,14 @@ tor_spawn_background(const char *const filename, const char **argv,
value, but there is nothing we can do if it fails */
/* TODO: Don't use STDOUT, use a pipe set up just for this purpose */
nbytes = write(STDOUT_FILENO, error_message, error_message_length);
+ err = (nbytes < 0);
nbytes = write(STDOUT_FILENO, hex_errno, n);
+ err += (nbytes < 0);
}
- }
- (void) nbytes;
+ _exit(err?254:255);
+ }
- _exit(255);
/* Never reached, but avoids compiler warning */
return status; // LCOV_EXCL_LINE
}
@@ -4515,8 +4543,8 @@ tor_spawn_background(const char *const filename, const char **argv,
}
*process_handle_out = process_handle;
- return process_handle->status;
-#endif // _WIN32
+ return status;
+#endif /* defined(_WIN32) */
}
/** Destroy all resources allocated by the process handle in
@@ -4558,13 +4586,13 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
if (process_handle->stdin_pipe)
CloseHandle(process_handle->stdin_pipe);
-#else
+#else /* !(defined(_WIN32)) */
close(process_handle->stdout_pipe);
close(process_handle->stderr_pipe);
close(process_handle->stdin_pipe);
clear_waitpid_callback(process_handle->waitpid_cb);
-#endif
+#endif /* defined(_WIN32) */
memset(process_handle, 0x0f, sizeof(process_handle_t));
tor_free(process_handle);
@@ -4617,7 +4645,7 @@ tor_get_exit_code(process_handle_t *process_handle,
return PROCESS_EXIT_ERROR;
}
}
-#else
+#else /* !(defined(_WIN32)) */
int stat_loc;
int retval;
@@ -4652,7 +4680,7 @@ tor_get_exit_code(process_handle_t *process_handle,
if (exit_code != NULL)
*exit_code = WEXITSTATUS(stat_loc);
-#endif // _WIN32
+#endif /* defined(_WIN32) */
return PROCESS_EXIT_EXITED;
}
@@ -4892,7 +4920,7 @@ tor_read_all_handle(HANDLE h, char *buf, size_t count,
}
return (ssize_t)numread;
}
-#else
+#else /* !(defined(_WIN32)) */
/** Read from a handle <b>fd</b> into <b>buf</b>, up to <b>count</b> bytes. If
* <b>process</b> is NULL, the function will return immediately if there is
* nothing more to read. Otherwise data will be read until end of file, or
@@ -4937,7 +4965,7 @@ tor_read_all_handle(int fd, char *buf, size_t count,
log_debug(LD_GENERAL, "read() read %d bytes from handle", (int)numread);
return (ssize_t)numread;
}
-#endif
+#endif /* defined(_WIN32) */
/** Read from stdout of a process until the process exits. */
ssize_t
@@ -4950,7 +4978,7 @@ tor_read_all_from_process_stdout(const process_handle_t *process_handle,
#else
return tor_read_all_handle(process_handle->stdout_pipe, buf, count,
process_handle, NULL);
-#endif
+#endif /* defined(_WIN32) */
}
/** Read from stdout of a process until the process exits. */
@@ -4964,7 +4992,7 @@ tor_read_all_from_process_stderr(const process_handle_t *process_handle,
#else
return tor_read_all_handle(process_handle->stderr_pipe, buf, count,
process_handle, NULL);
-#endif
+#endif /* defined(_WIN32) */
}
/** Split buf into lines, and add to smartlist. The buffer <b>buf</b> will be
@@ -5153,7 +5181,7 @@ log_from_handle(HANDLE *pipe, int severity)
return 0;
}
-#else
+#else /* !(defined(_WIN32)) */
/** Return a smartlist containing lines outputted from
* <b>fd</b>. Return NULL on error, and set
@@ -5218,7 +5246,7 @@ log_from_pipe(int fd, int severity, const char *executable,
/* We should never get here */
return -1;
}
-#endif
+#endif /* defined(_WIN32) */
/** Reads from <b>fd</b> and stores input in <b>buf_out</b> making
* sure it's below <b>count</b> bytes.
@@ -5470,7 +5498,7 @@ tor_check_port_forwarding(const char *filename,
status = tor_spawn_background(NULL, argv, NULL, &child_handle);
#else
status = tor_spawn_background(filename, argv, NULL, &child_handle);
-#endif
+#endif /* defined(_WIN32) */
tor_free_((void*)argv);
argv=NULL;
@@ -5496,7 +5524,7 @@ tor_check_port_forwarding(const char *filename,
#else
stderr_status = log_from_pipe(child_handle->stderr_pipe,
LOG_INFO, filename, &retval);
-#endif
+#endif /* defined(_WIN32) */
if (handle_fw_helper_output(filename, child_handle) < 0) {
log_warn(LD_GENERAL, "Failed to handle fw helper output.");
stdout_status = -1;
@@ -5521,13 +5549,13 @@ tor_check_port_forwarding(const char *filename,
* between log_from_handle and tor_get_exit_code? */
retval = 1;
}
-#else
+#else /* !(defined(_WIN32)) */
else if (1 == stdout_status || 1 == stderr_status)
/* stdout or stderr was closed, the process probably
* exited. It will be reaped by waitpid() in main.c */
/* TODO: Do something with the process return value */
retval = 1;
-#endif
+#endif /* defined(_WIN32) */
else
/* Both are fine */
retval = 0;
@@ -5598,7 +5626,7 @@ clamp_double_to_int64(double number)
{
int exponent;
-#if (defined(__MINGW32__) || defined(__MINGW64__)) && GCC_VERSION >= 409
+#if defined(MINGW_ANY) && GCC_VERSION >= 409
/*
Mingw's math.h uses gcc's __builtin_choose_expr() facility to declare
isnan, isfinite, and signbit. But as implemented in at least some
@@ -5607,7 +5635,7 @@ clamp_double_to_int64(double number)
*/
#define PROBLEMATIC_FLOAT_CONVERSION_WARNING
DISABLE_GCC_WARNING(float-conversion)
-#endif
+#endif /* defined(MINGW_ANY) && GCC_VERSION >= 409 */
/*
With clang 4.0 we apparently run into "double promotion" warnings here,
@@ -5618,7 +5646,7 @@ DISABLE_GCC_WARNING(float-conversion)
#define PROBLEMATIC_DOUBLE_PROMOTION_WARNING
DISABLE_GCC_WARNING(double-promotion)
#endif
-#endif
+#endif /* defined(__clang__) */
/* NaN is a special case that can't be used with the logic below. */
if (isnan(number)) {
@@ -5665,7 +5693,7 @@ tor_htonll(uint64_t a)
/* Little endian. The worst... */
return htonl((uint32_t)(a>>32)) |
(((uint64_t)htonl((uint32_t)a))<<32);
-#endif /* WORDS_BIGENDIAN */
+#endif /* defined(WORDS_BIGENDIAN) */
}
/** Return a uint64_t value from <b>a</b> in host byte order. */
diff --git a/src/common/util.h b/src/common/util.h
index d56abcee2e..6bc853da26 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -45,7 +45,7 @@
#else
#define DMALLOC_PARAMS
#define DMALLOC_ARGS
-#endif
+#endif /* defined(USE_DMALLOC) */
/* Memory management */
void *tor_malloc_(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
@@ -72,7 +72,7 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
(p)=NULL; \
} \
STMT_END
-#else
+#else /* !(defined(USE_DMALLOC)) */
/** Release memory allocated by tor_malloc, tor_realloc, tor_strdup, etc.
* Unlike the free() function, tor_free() will still work on NULL pointers,
* and it sets the pointer value to NULL after freeing it.
@@ -86,7 +86,7 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
(p)=NULL; \
} \
STMT_END
-#endif
+#endif /* defined(USE_DMALLOC) */
#define tor_malloc(size) tor_malloc_(size DMALLOC_ARGS)
#define tor_malloc_zero(size) tor_malloc_zero_(size DMALLOC_ARGS)
@@ -109,19 +109,11 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
void tor_log_mallinfo(int severity);
-/** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */
-#if defined(__GNUC__) && __GNUC__ > 3
-#define STRUCT_OFFSET(tp, member) __builtin_offsetof(tp, member)
-#else
- #define STRUCT_OFFSET(tp, member) \
- ((off_t) (((char*)&((tp*)0)->member)-(char*)0))
-#endif
-
/** Macro: yield a pointer to the field at position <b>off</b> within the
* structure <b>st</b>. Example:
* <pre>
* struct a { int foo; int bar; } x;
- * off_t bar_offset = STRUCT_OFFSET(struct a, bar);
+ * off_t bar_offset = offsetof(struct a, bar);
* int *bar_p = STRUCT_VAR_P(&x, bar_offset);
* *bar_p = 3;
* </pre>
@@ -138,7 +130,7 @@ void tor_log_mallinfo(int severity);
* </pre>
*/
#define SUBTYPE_P(p, subtype, basemember) \
- ((void*) ( ((char*)(p)) - STRUCT_OFFSET(subtype, basemember) ))
+ ((void*) ( ((char*)(p)) - offsetof(subtype, basemember) ))
/* Logic */
/** Macro: true if two values have the same boolean value. */
@@ -269,7 +261,7 @@ int format_time_interval(char *out, size_t out_len, long interval);
#else
time_t approx_time(void);
void update_approx_time(time_t now);
-#endif
+#endif /* defined(TIME_IS_FAST) */
/* Rate-limiter */
@@ -397,13 +389,15 @@ int path_is_relative(const char *filename);
/* Process helpers */
void start_daemon(void);
void finish_daemon(const char *desired_cwd);
-void write_pidfile(const char *filename);
+int write_pidfile(const char *filename);
/* Port forwarding */
void tor_check_port_forwarding(const char *filename,
struct smartlist_t *ports_to_forward,
time_t now);
+void tor_disable_spawning_background_processes(void);
+
typedef struct process_handle_t process_handle_t;
typedef struct process_environment_t process_environment_t;
int tor_spawn_background(const char *const filename, const char **argv,
@@ -457,7 +451,7 @@ struct process_handle_t {
HANDLE stdout_pipe;
HANDLE stderr_pipe;
PROCESS_INFORMATION pid;
-#else
+#else /* !(defined(_WIN32)) */
int stdin_pipe;
int stdout_pipe;
int stderr_pipe;
@@ -468,9 +462,9 @@ struct process_handle_t {
struct waitpid_callback_t *waitpid_cb;
/** The exit status reported by waitpid. */
int waitpid_exit_status;
-#endif // _WIN32
+#endif /* defined(_WIN32) */
};
-#endif
+#endif /* defined(UTIL_PRIVATE) */
/* Return values of tor_get_exit_code() */
#define PROCESS_EXIT_RUNNING 1
@@ -486,7 +480,7 @@ ssize_t tor_read_all_handle(HANDLE h, char *buf, size_t count,
ssize_t tor_read_all_handle(int fd, char *buf, size_t count,
const process_handle_t *process,
int *eof);
-#endif
+#endif /* defined(_WIN32) */
ssize_t tor_read_all_from_process_stdout(
const process_handle_t *process_handle, char *buf, size_t count);
ssize_t tor_read_all_from_process_stderr(
@@ -508,7 +502,7 @@ tor_get_lines_from_handle,(HANDLE *handle,
MOCK_DECL(struct smartlist_t *,
tor_get_lines_from_handle,(int fd,
enum stream_status *stream_status));
-#endif
+#endif /* defined(_WIN32) */
int
tor_terminate_process(process_handle_t *process_handle);
@@ -545,13 +539,13 @@ STATIC int format_helper_exit_status(unsigned char child_state,
leading minus) and newline (no null) */
#define HEX_ERRNO_SIZE (sizeof(char) * 2 + 1 + \
1 + sizeof(int) * 2 + 1)
-#endif
+#endif /* !defined(_WIN32) */
-#endif
+#endif /* defined(UTIL_PRIVATE) */
int size_mul_check(const size_t x, const size_t y);
#define ARRAY_LENGTH(x) ((sizeof(x)) / sizeof(x[0]))
-#endif
+#endif /* !defined(TOR_UTIL_H) */
diff --git a/src/common/util_bug.c b/src/common/util_bug.c
index 3d990e3700..126e843866 100644
--- a/src/common/util_bug.c
+++ b/src/common/util_bug.c
@@ -13,6 +13,10 @@
#include "backtrace.h"
#include "container.h"
+#ifdef __COVERITY__
+int bug_macro_deadcode_dummy__ = 0;
+#endif
+
#ifdef TOR_UNIT_TESTS
static void (*failed_assertion_cb)(void) = NULL;
static int n_bugs_to_capture = 0;
@@ -55,10 +59,10 @@ tor_set_failed_assertion_callback(void (*fn)(void))
{
failed_assertion_cb = fn;
}
-#else
+#else /* !(defined(TOR_UNIT_TESTS)) */
#define capturing_bugs() (0)
#define add_captured_bug(s) do { } while (0)
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Helper for tor_assert: report the assertion failure. */
void
diff --git a/src/common/util_bug.h b/src/common/util_bug.h
index ae7e7a37fd..be549fde07 100644
--- a/src/common/util_bug.h
+++ b/src/common/util_bug.h
@@ -5,6 +5,32 @@
/**
* \file util_bug.h
+ *
+ * \brief Macros to manage assertions, fatal and non-fatal.
+ *
+ * Guidelines: All the different kinds of assertion in this file are for
+ * bug-checking only. Don't write code that can assert based on bad inputs.
+ *
+ * We provide two kinds of assertion here: "fatal" and "nonfatal". Use
+ * nonfatal assertions for any bug you can reasonably recover from -- and
+ * please, try to recover! Many severe bugs in Tor have been caused by using
+ * a regular assertion when a nonfatal assertion would have been better.
+ *
+ * If you need to check a condition with a nonfatal assertion, AND recover
+ * from that same condition, consider using the BUG() macro inside a
+ * conditional. For example:
+ *
+ * <code>
+ * // wrong -- use tor_assert_nonfatal() if you just want an assertion.
+ * BUG(ptr == NULL);
+ *
+ * // okay, but needlessly verbose
+ * tor_assert_nonfatal(ptr != NULL);
+ * if (ptr == NULL) { ... }
+ *
+ * // this is how we do it:
+ * if (BUG(ptr == NULL)) { ... }
+ * </code>
**/
#ifndef TOR_UTIL_BUG_H
@@ -27,7 +53,7 @@
* security-critical properties.
*/
#error "Sorry; we don't support building with NDEBUG."
-#endif
+#endif /* defined(NDEBUG) */
/* Sometimes we don't want to use assertions during branch coverage tests; it
* leads to tons of unreached branches which in reality are only assertions we
@@ -44,7 +70,7 @@
tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
abort(); \
} STMT_END
-#endif
+#endif /* defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS) */
#define tor_assert_unreached() tor_assert(0)
@@ -59,11 +85,14 @@
*/
#ifdef __COVERITY__
+extern int bug_macro_deadcode_dummy__;
#undef BUG
// Coverity defines this in global headers; let's override it. This is a
// magic coverity-only preprocessor thing.
-#nodef BUG(x) ((x)?(__coverity_panic__(),1):0)
-#endif
+// We use this "deadcode_dummy__" trick to prevent coverity from
+// complaining about unreachable bug cases.
+#nodef BUG(x) ((x)?(__coverity_panic__(),1):(0+bug_macro_deadcode_dummy__))
+#endif /* defined(__COVERITY__) */
#if defined(__COVERITY__) || defined(__clang_analyzer__)
// We're running with a static analysis tool: let's treat even nonfatal
@@ -114,7 +143,7 @@
(PREDICT_UNLIKELY(cond) ? \
(tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",0), 1) \
: 0)
-#endif
+#endif /* defined(ALL_BUGS_ARE_FATAL) || ... */
#ifdef __GNUC__
#define IF_BUG_ONCE__(cond,var) \
@@ -127,7 +156,7 @@
"!("#cond")", 1); \
} \
PREDICT_UNLIKELY(bool_result); } ))
-#else
+#else /* !(defined(__GNUC__)) */
#define IF_BUG_ONCE__(cond,var) \
static int var = 0; \
if (PREDICT_UNLIKELY(cond) ? \
@@ -137,7 +166,7 @@
"!("#cond")", 1), \
1)) \
: 0)
-#endif
+#endif /* defined(__GNUC__) */
#define IF_BUG_ONCE_VARNAME_(a) \
warning_logged_on_ ## a ## __
#define IF_BUG_ONCE_VARNAME__(a) \
@@ -167,7 +196,7 @@ void tor_capture_bugs_(int n);
void tor_end_capture_bugs_(void);
const struct smartlist_t *tor_get_captured_bug_log_(void);
void tor_set_failed_assertion_callback(void (*fn)(void));
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
-#endif
+#endif /* !defined(TOR_UTIL_BUG_H) */
diff --git a/src/common/util_format.c b/src/common/util_format.c
index 1f7b8b03aa..e51757a4e8 100644
--- a/src/common/util_format.c
+++ b/src/common/util_format.c
@@ -266,10 +266,13 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
ENCODE_N(3);
ENCODE_PAD();
break;
+ // LCOV_EXCL_START -- we can't reach this point, because we enforce
+ // 0 <= ncov_idx < 3 in the loop above.
default:
/* Something went catastrophically wrong. */
- tor_fragile_assert(); // LCOV_EXCL_LINE
+ tor_fragile_assert();
return -1;
+ // LCOV_EXCL_STOP
}
#undef ENCODE_N
diff --git a/src/common/util_format.h b/src/common/util_format.h
index 4af8832bbe..0aefe3a44e 100644
--- a/src/common/util_format.h
+++ b/src/common/util_format.h
@@ -48,5 +48,5 @@ int hex_decode_digit(char c);
void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
int base16_decode(char *dest, size_t destlen, const char *src, size_t srclen);
-#endif
+#endif /* !defined(TOR_UTIL_FORMAT_H) */
diff --git a/src/common/util_process.c b/src/common/util_process.c
index 9e9679b099..c2826152e9 100644
--- a/src/common/util_process.c
+++ b/src/common/util_process.c
@@ -154,5 +154,5 @@ notify_pending_waitpid_callbacks(void)
}
}
-#endif
+#endif /* !defined(_WIN32) */
diff --git a/src/common/util_process.h b/src/common/util_process.h
index c3a63498b5..c9aa771b77 100644
--- a/src/common/util_process.h
+++ b/src/common/util_process.h
@@ -20,7 +20,7 @@ waitpid_callback_t *set_waitpid_callback(pid_t pid,
void (*fn)(int, void *), void *arg);
void clear_waitpid_callback(waitpid_callback_t *ent);
void notify_pending_waitpid_callbacks(void);
-#endif
+#endif /* !defined(_WIN32) */
-#endif
+#endif /* !defined(TOR_UTIL_PROCESS_H) */
diff --git a/src/common/workqueue.h b/src/common/workqueue.h
index d2508f5329..eb885e680d 100644
--- a/src/common/workqueue.h
+++ b/src/common/workqueue.h
@@ -59,5 +59,5 @@ replyqueue_t *replyqueue_new(uint32_t alertsocks_flags);
tor_socket_t replyqueue_get_socket(replyqueue_t *rq);
void replyqueue_process(replyqueue_t *queue);
-#endif
+#endif /* !defined(TOR_WORKQUEUE_H) */
diff --git a/src/config/torrc.minimal.in-staging b/src/config/torrc.minimal.in-staging
index c537c51f9b..90f91e5cb9 100644
--- a/src/config/torrc.minimal.in-staging
+++ b/src/config/torrc.minimal.in-staging
@@ -1,5 +1,5 @@
## Configuration file for a typical Tor user
-## Last updated 22 September 2015 for Tor 0.2.7.3-alpha.
+## Last updated 22 December 2017 for Tor 0.3.2.8-rc.
## (may or may not work for much older or much newer versions of Tor.)
##
## Lines that begin with "## " try to explain what's going on. Lines
@@ -132,6 +132,9 @@
## spammers might also collect them. You may want to obscure the fact that
## it's an email address and/or generate a new address for this purpose.
## Notice that "<" and ">" are recommended.
+##
+## If you are running multiple relays, you MUST set this option.
+##
#ContactInfo Random Person <nobody AT example dot com>
## You might also include your PGP or GPG fingerprint if you have one.
## Use the full fingerprint, not just a (short) KeyID: KeyIDs are easy
@@ -161,8 +164,15 @@
## See https://www.torproject.org/docs/faq#MultipleRelays
## However, you should never include a bridge's fingerprint here, as it would
## break its concealability and potentially reveal its IP/TCP address.
+##
+## If you are running multiple relays, you MUST set this option.
+##
#MyFamily $keyid,$keyid,...
+## Uncomment this if you want your relay to allow IPv6 exit traffic.
+## (Relays only allow IPv4 exit traffic by default.)
+#IPv6Exit 1
+
## A comma-separated list of exit policies. They're considered first
## to last, and the first match wins.
##
diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in
index 8f3597f3f6..4e183478eb 100644
--- a/src/config/torrc.sample.in
+++ b/src/config/torrc.sample.in
@@ -1,5 +1,5 @@
## Configuration file for a typical Tor user
-## Last updated 22 September 2015 for Tor 0.2.7.3-alpha.
+## Last updated 22 December 2017 for Tor 0.3.2.8-rc.
## (may or may not work for much older or much newer versions of Tor.)
##
## Lines that begin with "## " try to explain what's going on. Lines
@@ -96,7 +96,8 @@
## If you have multiple network interfaces, you can specify one for
## outgoing traffic to use.
## OutboundBindAddressExit will be used for all exit traffic, while
-## OutboundBindAddressOR will be used for all other connections.
+## OutboundBindAddressOR will be used for all OR and Dir connections
+## (DNS connections ignore OutboundBindAddress).
## If you do not wish to differentiate, use OutboundBindAddress to
## specify the same address for both in a single line.
#OutboundBindAddressExit 10.0.0.4
@@ -135,6 +136,9 @@
## descriptors containing these lines and that Google indexes them, so
## spammers might also collect them. You may want to obscure the fact that
## it's an email address and/or generate a new address for this purpose.
+##
+## If you are running multiple relays, you MUST set this option.
+##
#ContactInfo Random Person <nobody AT example dot com>
## You might also include your PGP or GPG fingerprint if you have one:
#ContactInfo 0xFFFFFFFF Random Person <nobody AT example dot com>
@@ -161,8 +165,19 @@
## https://www.torproject.org/docs/faq#MultipleRelays
## However, you should never include a bridge's fingerprint here, as it would
## break its concealability and potentially reveal its IP/TCP address.
+##
+## If you are running multiple relays, you MUST set this option.
+##
#MyFamily $keyid,$keyid,...
+## Uncomment this if you do *not* want your relay to allow any exit traffic.
+## (Relays allow exit traffic by default.)
+#ExitRelay 0
+
+## Uncomment this if you want your relay to allow IPv6 exit traffic.
+## (Relays only allow IPv4 exit traffic by default.)
+#IPv6Exit 1
+
## A comma-separated list of exit policies. They're considered first
## to last, and the first match wins.
##
diff --git a/src/ext/ed25519/donna/ed25519_donna_tor.h b/src/ext/ed25519/donna/ed25519_donna_tor.h
index d225407b1c..7d7b8c0625 100644
--- a/src/ext/ed25519/donna/ed25519_donna_tor.h
+++ b/src/ext/ed25519/donna/ed25519_donna_tor.h
@@ -30,4 +30,9 @@ int ed25519_donna_blind_public_key(unsigned char *out, const unsigned char *inp,
int ed25519_donna_pubkey_from_curve25519_pubkey(unsigned char *out,
const unsigned char *inp, int signbit);
+
+int
+ed25519_donna_scalarmult_with_group_order(unsigned char *out,
+ const unsigned char *pubkey);
+
#endif
diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c
index 9537ae66a1..44ec562f02 100644
--- a/src/ext/ed25519/donna/ed25519_tor.c
+++ b/src/ext/ed25519/donna/ed25519_tor.c
@@ -245,13 +245,7 @@ ed25519_donna_sign(unsigned char *sig, const unsigned char *m, size_t mlen,
static void
ed25519_donna_gettweak(unsigned char *out, const unsigned char *param)
{
- static const char str[] = "Derive temporary signing key";
- ed25519_hash_context ctx;
-
- ed25519_hash_init(&ctx);
- ed25519_hash_update(&ctx, (const unsigned char*)str, strlen(str));
- ed25519_hash_update(&ctx, param, 32);
- ed25519_hash_final(&ctx, out);
+ memcpy(out, param, 32);
out[0] &= 248; /* Is this necessary ? */
out[31] &= 63;
@@ -304,7 +298,9 @@ ed25519_donna_blind_public_key(unsigned char *out, const unsigned char *inp,
/* No "ge25519_unpack", negate the public key. */
memcpy(pkcopy, inp, 32);
pkcopy[31] ^= (1<<7);
- ge25519_unpack_negative_vartime(&A, pkcopy);
+ if (!ge25519_unpack_negative_vartime(&A, pkcopy)) {
+ return -1;
+ }
/* A' = [tweak] * A + [0] * basepoint. */
ge25519_double_scalarmult_vartime(&Aprime, &A, t, zero);
@@ -340,5 +336,32 @@ ed25519_donna_pubkey_from_curve25519_pubkey(unsigned char *out,
return 0;
}
+/* Do the scalar multiplication of <b>pubkey</b> with the group order
+ * <b>modm_m</b>. Place the result in <b>out</b> which must be at least 32
+ * bytes long. */
+int
+ed25519_donna_scalarmult_with_group_order(unsigned char *out,
+ const unsigned char *pubkey)
+{
+ static const bignum256modm ALIGN(16) zero = { 0 };
+ unsigned char pkcopy[32];
+ ge25519 ALIGN(16) Point, Result;
+
+ /* No "ge25519_unpack", negate the public key and unpack it back.
+ * See ed25519_donna_blind_public_key() */
+ memcpy(pkcopy, pubkey, 32);
+ pkcopy[31] ^= (1<<7);
+ if (!ge25519_unpack_negative_vartime(&Point, pkcopy)) {
+ return -1; /* error: bail out */
+ }
+
+ /* There is no regular scalarmult function so we have to do:
+ * Result = l*P + 0*B */
+ ge25519_double_scalarmult_vartime(&Result, &Point, modm_m, zero);
+ ge25519_pack(out, &Result);
+
+ return 0;
+}
+
#include "test-internals.c"
diff --git a/src/ext/ed25519/ref10/blinding.c b/src/ext/ed25519/ref10/blinding.c
index ee3e8666fa..a3b32fa80c 100644
--- a/src/ext/ed25519/ref10/blinding.c
+++ b/src/ext/ed25519/ref10/blinding.c
@@ -12,8 +12,8 @@
static void
ed25519_ref10_gettweak(unsigned char *out, const unsigned char *param)
{
- const char str[] = "Derive temporary signing key";
- crypto_hash_sha512_2(out, (const unsigned char*)str, strlen(str), param, 32);
+ memcpy(out, param, 32);
+
out[0] &= 248; /* Is this necessary necessary ? */
out[31] &= 63;
out[31] |= 64;
@@ -49,6 +49,7 @@ int ed25519_ref10_blind_public_key(unsigned char *out,
unsigned char pkcopy[32];
ge_p3 A;
ge_p2 Aprime;
+ int retval = -1;
ed25519_ref10_gettweak(tweak, param);
@@ -62,15 +63,57 @@ int ed25519_ref10_blind_public_key(unsigned char *out,
* "ge_frombytes", we'd use that, but there isn't. */
memcpy(pkcopy, inp, 32);
pkcopy[31] ^= (1<<7);
- ge_frombytes_negate_vartime(&A, pkcopy);
+ if (ge_frombytes_negate_vartime(&A, pkcopy) != 0) {
+ goto done;
+ }
/* There isn't a regular ge_scalarmult -- we have to do tweak*A + zero*B. */
ge_double_scalarmult_vartime(&Aprime, tweak, &A, zero);
ge_tobytes(out, &Aprime);
+ retval = 0;
+
+ done:
memwipe(tweak, 0, sizeof(tweak));
memwipe(&A, 0, sizeof(A));
memwipe(&Aprime, 0, sizeof(Aprime));
memwipe(pkcopy, 0, sizeof(pkcopy));
+ return retval;
+}
+
+/* This is the group order encoded in a format that
+ * ge_double_scalarmult_vartime() understands. The group order m is:
+ * m = 2^252 + 27742317777372353535851937790883648493 =
+ * 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed
+ */
+static const uint8_t modm_m[32] = {0xed,0xd3,0xf5,0x5c,0x1a,0x63,0x12,0x58,
+ 0xd6,0x9c,0xf7,0xa2,0xde,0xf9,0xde,0x14,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10};
+
+/* Do the scalar multiplication of <b>pubkey</b> with the group order
+ * <b>modm_m</b>. Place the result in <b>out</b> which must be at least 32
+ * bytes long. */
+int
+ed25519_ref10_scalarmult_with_group_order(unsigned char *out,
+ const unsigned char *pubkey)
+{
+ unsigned char pkcopy[32];
+ unsigned char zero[32] = {0};
+ ge_p3 Point;
+ ge_p2 Result;
+
+ /* All this is done to fit 'pubkey' in 'Point' so that it can be used by
+ * ed25519 ref code. Same thing as in blinding function */
+ memcpy(pkcopy, pubkey, 32);
+ pkcopy[31] ^= (1<<7);
+ if (ge_frombytes_negate_vartime(&Point, pkcopy) != 0) {
+ return -1; /* error: bail out */
+ }
+
+ /* There isn't a regular scalarmult -- we have to do r = l*P + 0*B */
+ ge_double_scalarmult_vartime(&Result, modm_m, &Point, zero);
+ ge_tobytes(out, &Result);
+
return 0;
}
diff --git a/src/ext/ed25519/ref10/ed25519_ref10.h b/src/ext/ed25519/ref10/ed25519_ref10.h
index af7e21a2ad..5965694977 100644
--- a/src/ext/ed25519/ref10/ed25519_ref10.h
+++ b/src/ext/ed25519/ref10/ed25519_ref10.h
@@ -27,4 +27,8 @@ int ed25519_ref10_blind_public_key(unsigned char *out,
const unsigned char *inp,
const unsigned char *param);
+int
+ed25519_ref10_scalarmult_with_group_order(unsigned char *out,
+ const unsigned char *pubkey);
+
#endif
diff --git a/src/ext/ht.h b/src/ext/ht.h
index caa420e9b5..99da773faf 100644
--- a/src/ext/ht.h
+++ b/src/ext/ht.h
@@ -150,6 +150,8 @@
#define HT_CLEAR(name, head) name##_HT_CLEAR(head)
#define HT_INIT(name, head) name##_HT_INIT(head)
#define HT_REP_IS_BAD_(name, head) name##_HT_REP_IS_BAD_(head)
+#define HT_FOREACH_FN(name, head, fn, data) \
+ name##_HT_FOREACH_FN((head), (fn), (data))
/* Helper: */
static inline unsigned
ht_improve_hash(unsigned h)
diff --git a/src/ext/timeouts/timeout-bitops.c b/src/ext/timeouts/timeout-bitops.c
index 45466f6cb3..68db817933 100644
--- a/src/ext/timeouts/timeout-bitops.c
+++ b/src/ext/timeouts/timeout-bitops.c
@@ -231,7 +231,8 @@ main(int c, char **v)
int result = 0;
for (i = 0; i <= 63; ++i) {
- uint64_t x = 1 << i;
+ uint64_t x = 1;
+ x <<= i;
if (!check(x))
result = 1;
--x;
diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h
index 85c847b3fd..b233cf7631 100644
--- a/src/ext/trunnel/trunnel-impl.h
+++ b/src/ext/trunnel/trunnel-impl.h
@@ -1,4 +1,4 @@
-/* trunnel-impl.h -- copied from Trunnel v1.5.1
+/* trunnel-impl.h -- copied from Trunnel v1.5.2
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c
index 6a42417241..b749d8136f 100644
--- a/src/ext/trunnel/trunnel.c
+++ b/src/ext/trunnel/trunnel.c
@@ -1,4 +1,4 @@
-/* trunnel.c -- copied from Trunnel v1.5.1
+/* trunnel.c -- copied from Trunnel v1.5.2
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h
index dd78553c77..32c80bac23 100644
--- a/src/ext/trunnel/trunnel.h
+++ b/src/ext/trunnel/trunnel.h
@@ -1,4 +1,4 @@
-/* trunnel.h -- copied from Trunnel v1.5.1
+/* trunnel.h -- copied from Trunnel v1.5.2
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/or/addressmap.c b/src/or/addressmap.c
index c92af38254..7e92633601 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -213,8 +213,8 @@ addressmap_clear_excluded_trackexithosts(const or_options_t *options)
while (dot > target && *dot != '.')
dot--;
if (*dot == '.') dot++;
- nodename = tor_strndup(dot, len-5-(dot-target));;
- node = node_get_by_nickname(nodename, 0);
+ nodename = tor_strndup(dot, len-5-(dot-target));
+ node = node_get_by_nickname(nodename, NNF_NO_WARN_UNNAMED);
tor_free(nodename);
if (!node ||
(allow_nodes && !routerset_contains_node(allow_nodes, node)) ||
@@ -814,7 +814,7 @@ parse_virtual_addr_network(const char *val, sa_family_t family,
ipv6?"IPv6":"");
return -1;
}
-#endif
+#endif /* 0 */
if (bits > max_prefix_bits) {
if (msg)
@@ -1044,7 +1044,7 @@ addressmap_register_virtual_address(int type, char *new_address)
safe_str_client(*addrp),
safe_str_client(new_address));
}
-#endif
+#endif /* 0 */
return *addrp;
}
diff --git a/src/or/addressmap.h b/src/or/addressmap.h
index 80f453b4c1..1544b76e10 100644
--- a/src/or/addressmap.h
+++ b/src/or/addressmap.h
@@ -59,7 +59,7 @@ typedef struct virtual_addr_conf_t {
STATIC void get_random_virtual_addr(const virtual_addr_conf_t *conf,
tor_addr_t *addr_out);
-#endif
+#endif /* defined(ADDRESSMAP_PRIVATE) */
-#endif
+#endif /* !defined(TOR_ADDRESSMAP_H) */
diff --git a/src/or/bridges.c b/src/or/bridges.c
index 0818fb0812..0b1bbbd158 100644
--- a/src/or/bridges.c
+++ b/src/or/bridges.c
@@ -54,6 +54,8 @@ struct bridge_info_t {
};
static void bridge_free(bridge_info_t *bridge);
+static void rewrite_node_address_for_bridge(const bridge_info_t *bridge,
+ node_t *node);
/** A list of configured bridges. Whenever we actually get a descriptor
* for one, we add it as an entry guard. Note that the order of bridges
@@ -310,7 +312,7 @@ learned_router_identity(const tor_addr_t *addr, uint16_t port,
memcpy(&bridge->ed25519_identity, ed_id, sizeof(*ed_id));
learned = 1;
}
-#endif
+#endif /* 0 */
if (learned) {
char *transport_info = NULL;
const char *transport_name =
@@ -454,6 +456,9 @@ bridge_add_from_config(bridge_line_t *bridge_line)
b->transport_name = bridge_line->transport_name;
b->fetch_status.schedule = DL_SCHED_BRIDGE;
b->fetch_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL;
+ b->fetch_status.increment_on = DL_SCHED_INCREMENT_ATTEMPT;
+ /* We can't reset the bridge's download status here, because UseBridges
+ * might be 0 now, and it might be changed to 1 much later. */
b->socks_args = bridge_line->socks_args;
if (!bridge_list)
bridge_list = smartlist_new();
@@ -571,6 +576,12 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
return;
}
+ /* If we already have a node_t for this bridge, rewrite its address now. */
+ node_t *node = node_get_mutable_by_id(bridge->identity);
+ if (node) {
+ rewrite_node_address_for_bridge(bridge, node);
+ }
+
tor_addr_port_t bridge_addrport;
memcpy(&bridge_addrport.addr, &bridge->addr, sizeof(tor_addr_t));
bridge_addrport.port = bridge->port;
@@ -622,6 +633,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge)
{
+ /* This resets the download status on first use */
if (!download_status_is_ready(&bridge->fetch_status, now,
IMPOSSIBLE_TO_DOWNLOAD))
continue; /* don't bother, no need to retry yet */
@@ -632,8 +644,13 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now)
continue;
}
- /* schedule another fetch as if this one will fail, in case it does */
- download_status_failed(&bridge->fetch_status, 0);
+ /* schedule the next attempt
+ * we can't increment after a failure, because sometimes we use the
+ * bridge authority, and sometimes we use the bridge direct */
+ download_status_increment_attempt(
+ &bridge->fetch_status,
+ safe_str_client(fmt_and_decorate_addr(&bridge->addr)),
+ now);
can_use_bridge_authority = !tor_digest_is_zero(bridge->identity) &&
num_bridge_auths;
@@ -779,7 +796,11 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
tor_assert(ri);
tor_assert(ri->purpose == ROUTER_PURPOSE_BRIDGE);
if (get_options()->UseBridges) {
- int first = num_bridges_usable() <= 1;
+ /* Retry directory downloads whenever we get a bridge descriptor:
+ * - when bootstrapping, and
+ * - when we aren't sure if any of our bridges are reachable.
+ * Keep on retrying until we have at least one reachable bridge. */
+ int first = num_bridges_usable(0) < 1;
bridge_info_t *bridge = get_configured_bridge_by_routerinfo(ri);
time_t now = time(NULL);
router_set_status(ri->cache_info.identity_digest, 1);
@@ -787,8 +808,12 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
if (bridge) { /* if we actually want to use this one */
node_t *node;
/* it's here; schedule its re-fetch for a long time from now. */
- if (!from_cache)
+ if (!from_cache) {
+ /* This schedules the re-fetch at a constant interval, which produces
+ * a pattern of bridge traffic. But it's better than trying all
+ * configured briges several times in the first few minutes. */
download_status_reset(&bridge->fetch_status);
+ }
node = node_get_mutable_by_id(ri->cache_info.identity_digest);
tor_assert(node);
@@ -805,8 +830,8 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname,
from_cache ? "cached" : "fresh", router_describe(ri));
- /* set entry->made_contact so if it goes down we don't drop it from
- * our entry node list */
+ /* If we didn't have a reachable bridge before this one, try directory
+ * documents again. */
if (first) {
routerlist_retry_directory_downloads(now);
}
@@ -814,32 +839,6 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache)
}
}
-/** Return the number of bridges that have descriptors that
- * are marked with purpose 'bridge' and are running.
- *
- * We use this function to decide if we're ready to start building
- * circuits through our bridges, or if we need to wait until the
- * directory "server/authority" requests finish. */
-int
-any_bridge_descriptors_known(void)
-{
- tor_assert(get_options()->UseBridges);
-
- if (!bridge_list)
- return 0;
-
- SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, bridge) {
- const node_t *node;
- if (!tor_digest_is_zero(bridge->identity) &&
- (node = node_get_by_id(bridge->identity)) != NULL &&
- node->ri) {
- return 1;
- }
- } SMARTLIST_FOREACH_END(bridge);
-
- return 0;
-}
-
/** Return a smartlist containing all bridge identity digests */
MOCK_IMPL(smartlist_t *,
list_bridge_identities, (void))
diff --git a/src/or/bridges.h b/src/or/bridges.h
index 3bfc782f9a..54a6250259 100644
--- a/src/or/bridges.h
+++ b/src/or/bridges.h
@@ -45,7 +45,6 @@ void bridge_add_from_config(struct bridge_line_t *bridge_line);
void retry_bridge_descriptor_fetch_directly(const char *digest);
void fetch_bridge_descriptors(const or_options_t *options, time_t now);
void learned_bridge_descriptor(routerinfo_t *ri, int from_cache);
-int any_bridge_descriptors_known(void);
const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr,
uint16_t port);
@@ -66,5 +65,5 @@ MOCK_DECL(download_status_t *, get_bridge_dl_status_by_id,
void bridges_free_all(void);
-#endif
+#endif /* !defined(TOR_BRIDGES_H) */
diff --git a/src/or/buffers.c b/src/or/buffers.c
deleted file mode 100644
index 12a6c0239b..0000000000
--- a/src/or/buffers.c
+++ /dev/null
@@ -1,2192 +0,0 @@
-/* Copyright (c) 2001 Matej Pfajfar.
- * Copyright (c) 2001-2004, Roger Dingledine.
- * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2017, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-/**
- * \file buffers.c
- * \brief Implements a generic buffer interface.
- *
- * A buf_t is a (fairly) opaque byte-oriented FIFO that can read to or flush
- * from memory, sockets, file descriptors, TLS connections, or another buf_t.
- * Buffers are implemented as linked lists of memory chunks.
- *
- * All socket-backed and TLS-based connection_t objects have a pair of
- * buffers: one for incoming data, and one for outcoming data. These are fed
- * and drained from functions in connection.c, trigged by events that are
- * monitored in main.c.
- *
- * This module has basic support for reading and writing on buf_t objects. It
- * also contains specialized functions for handling particular protocols
- * on a buf_t backend, including SOCKS (used in connection_edge.c), Tor cells
- * (used in connection_or.c and channeltls.c), HTTP (used in directory.c), and
- * line-oriented communication (used in control.c).
- **/
-#define BUFFERS_PRIVATE
-#include "or.h"
-#include "addressmap.h"
-#include "buffers.h"
-#include "config.h"
-#include "connection_edge.h"
-#include "connection_or.h"
-#include "control.h"
-#include "reasons.h"
-#include "ext_orport.h"
-#include "util.h"
-#include "torlog.h"
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-//#define PARANOIA
-
-#ifdef PARANOIA
-/** Helper: If PARANOIA is defined, assert that the buffer in local variable
- * <b>buf</b> is well-formed. */
-#define check() STMT_BEGIN assert_buf_ok(buf); STMT_END
-#else
-#define check() STMT_NIL
-#endif
-
-/* Implementation notes:
- *
- * After flirting with memmove, and dallying with ring-buffers, we're finally
- * getting up to speed with the 1970s and implementing buffers as a linked
- * list of small chunks. Each buffer has such a list; data is removed from
- * the head of the list, and added at the tail. The list is singly linked,
- * and the buffer keeps a pointer to the head and the tail.
- *
- * Every chunk, except the tail, contains at least one byte of data. Data in
- * each chunk is contiguous.
- *
- * When you need to treat the first N characters on a buffer as a contiguous
- * string, use the buf_pullup function to make them so. Don't do this more
- * than necessary.
- *
- * The major free Unix kernels have handled buffers like this since, like,
- * forever.
- */
-
-static void socks_request_set_socks5_error(socks_request_t *req,
- socks5_reply_status_t reason);
-
-static int parse_socks(const char *data, size_t datalen, socks_request_t *req,
- int log_sockstype, int safe_socks, ssize_t *drain_out,
- size_t *want_length_out);
-static int parse_socks_client(const uint8_t *data, size_t datalen,
- int state, char **reason,
- ssize_t *drain_out);
-
-/* Chunk manipulation functions */
-
-#define CHUNK_HEADER_LEN STRUCT_OFFSET(chunk_t, mem[0])
-
-/* We leave this many NUL bytes at the end of the buffer. */
-#ifdef DISABLE_MEMORY_SENTINELS
-#define SENTINEL_LEN 0
-#else
-#define SENTINEL_LEN 4
-#endif
-
-/* Header size plus NUL bytes at the end */
-#define CHUNK_OVERHEAD (CHUNK_HEADER_LEN + SENTINEL_LEN)
-
-/** Return the number of bytes needed to allocate a chunk to hold
- * <b>memlen</b> bytes. */
-#define CHUNK_ALLOC_SIZE(memlen) (CHUNK_OVERHEAD + (memlen))
-/** Return the number of usable bytes in a chunk allocated with
- * malloc(<b>memlen</b>). */
-#define CHUNK_SIZE_WITH_ALLOC(memlen) ((memlen) - CHUNK_OVERHEAD)
-
-#define DEBUG_SENTINEL
-
-#if defined(DEBUG_SENTINEL) && !defined(DISABLE_MEMORY_SENTINELS)
-#define DBG_S(s) s
-#else
-#define DBG_S(s) (void)0
-#endif
-
-#ifdef DISABLE_MEMORY_SENTINELS
-#define CHUNK_SET_SENTINEL(chunk, alloclen) STMT_NIL
-#else
-#define CHUNK_SET_SENTINEL(chunk, alloclen) do { \
- uint8_t *a = (uint8_t*) &(chunk)->mem[(chunk)->memlen]; \
- DBG_S(uint8_t *b = &((uint8_t*)(chunk))[(alloclen)-SENTINEL_LEN]); \
- DBG_S(tor_assert(a == b)); \
- memset(a,0,SENTINEL_LEN); \
- } while (0)
-#endif
-
-/** Return the next character in <b>chunk</b> onto which data can be appended.
- * If the chunk is full, this might be off the end of chunk->mem. */
-static inline char *
-CHUNK_WRITE_PTR(chunk_t *chunk)
-{
- return chunk->data + chunk->datalen;
-}
-
-/** Return the number of bytes that can be written onto <b>chunk</b> without
- * running out of space. */
-static inline size_t
-CHUNK_REMAINING_CAPACITY(const chunk_t *chunk)
-{
- return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen);
-}
-
-/** Move all bytes stored in <b>chunk</b> to the front of <b>chunk</b>->mem,
- * to free up space at the end. */
-static inline void
-chunk_repack(chunk_t *chunk)
-{
- if (chunk->datalen && chunk->data != &chunk->mem[0]) {
- memmove(chunk->mem, chunk->data, chunk->datalen);
- }
- chunk->data = &chunk->mem[0];
-}
-
-/** Keep track of total size of allocated chunks for consistency asserts */
-static size_t total_bytes_allocated_in_chunks = 0;
-static void
-buf_chunk_free_unchecked(chunk_t *chunk)
-{
- if (!chunk)
- return;
-#ifdef DEBUG_CHUNK_ALLOC
- tor_assert(CHUNK_ALLOC_SIZE(chunk->memlen) == chunk->DBG_alloc);
-#endif
- tor_assert(total_bytes_allocated_in_chunks >=
- CHUNK_ALLOC_SIZE(chunk->memlen));
- total_bytes_allocated_in_chunks -= CHUNK_ALLOC_SIZE(chunk->memlen);
- tor_free(chunk);
-}
-static inline chunk_t *
-chunk_new_with_alloc_size(size_t alloc)
-{
- chunk_t *ch;
- ch = tor_malloc(alloc);
- ch->next = NULL;
- ch->datalen = 0;
-#ifdef DEBUG_CHUNK_ALLOC
- ch->DBG_alloc = alloc;
-#endif
- ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc);
- total_bytes_allocated_in_chunks += alloc;
- ch->data = &ch->mem[0];
- CHUNK_SET_SENTINEL(ch, alloc);
- return ch;
-}
-
-/** Expand <b>chunk</b> until it can hold <b>sz</b> bytes, and return a
- * new pointer to <b>chunk</b>. Old pointers are no longer valid. */
-static inline chunk_t *
-chunk_grow(chunk_t *chunk, size_t sz)
-{
- off_t offset;
- const size_t memlen_orig = chunk->memlen;
- const size_t orig_alloc = CHUNK_ALLOC_SIZE(memlen_orig);
- const size_t new_alloc = CHUNK_ALLOC_SIZE(sz);
- tor_assert(sz > chunk->memlen);
- offset = chunk->data - chunk->mem;
- chunk = tor_realloc(chunk, new_alloc);
- chunk->memlen = sz;
- chunk->data = chunk->mem + offset;
-#ifdef DEBUG_CHUNK_ALLOC
- tor_assert(chunk->DBG_alloc == orig_alloc);
- chunk->DBG_alloc = new_alloc;
-#endif
- total_bytes_allocated_in_chunks += new_alloc - orig_alloc;
- CHUNK_SET_SENTINEL(chunk, new_alloc);
- return chunk;
-}
-
-/** If a read onto the end of a chunk would be smaller than this number, then
- * just start a new chunk. */
-#define MIN_READ_LEN 8
-/** Every chunk should take up at least this many bytes. */
-#define MIN_CHUNK_ALLOC 256
-/** No chunk should take up more than this many bytes. */
-#define MAX_CHUNK_ALLOC 65536
-
-/** Return the allocation size we'd like to use to hold <b>target</b>
- * bytes. */
-STATIC size_t
-preferred_chunk_size(size_t target)
-{
- tor_assert(target <= SIZE_T_CEILING - CHUNK_OVERHEAD);
- if (CHUNK_ALLOC_SIZE(target) >= MAX_CHUNK_ALLOC)
- return CHUNK_ALLOC_SIZE(target);
- size_t sz = MIN_CHUNK_ALLOC;
- while (CHUNK_SIZE_WITH_ALLOC(sz) < target) {
- sz <<= 1;
- }
- return sz;
-}
-
-/** Collapse data from the first N chunks from <b>buf</b> into buf->head,
- * growing it as necessary, until buf->head has the first <b>bytes</b> bytes
- * of data from the buffer, or until buf->head has all the data in <b>buf</b>.
- */
-STATIC void
-buf_pullup(buf_t *buf, size_t bytes)
-{
- chunk_t *dest, *src;
- size_t capacity;
- if (!buf->head)
- return;
-
- check();
- if (buf->datalen < bytes)
- bytes = buf->datalen;
-
- capacity = bytes;
- if (buf->head->datalen >= bytes)
- return;
-
- if (buf->head->memlen >= capacity) {
- /* We don't need to grow the first chunk, but we might need to repack it.*/
- size_t needed = capacity - buf->head->datalen;
- if (CHUNK_REMAINING_CAPACITY(buf->head) < needed)
- chunk_repack(buf->head);
- tor_assert(CHUNK_REMAINING_CAPACITY(buf->head) >= needed);
- } else {
- chunk_t *newhead;
- size_t newsize;
- /* We need to grow the chunk. */
- chunk_repack(buf->head);
- newsize = CHUNK_SIZE_WITH_ALLOC(preferred_chunk_size(capacity));
- newhead = chunk_grow(buf->head, newsize);
- tor_assert(newhead->memlen >= capacity);
- if (newhead != buf->head) {
- if (buf->tail == buf->head)
- buf->tail = newhead;
- buf->head = newhead;
- }
- }
-
- dest = buf->head;
- while (dest->datalen < bytes) {
- size_t n = bytes - dest->datalen;
- src = dest->next;
- tor_assert(src);
- if (n >= src->datalen) {
- memcpy(CHUNK_WRITE_PTR(dest), src->data, src->datalen);
- dest->datalen += src->datalen;
- dest->next = src->next;
- if (buf->tail == src)
- buf->tail = dest;
- buf_chunk_free_unchecked(src);
- } else {
- memcpy(CHUNK_WRITE_PTR(dest), src->data, n);
- dest->datalen += n;
- src->data += n;
- src->datalen -= n;
- tor_assert(dest->datalen == bytes);
- }
- }
-
- check();
-}
-
-#ifdef TOR_UNIT_TESTS
-/* Return the data from the first chunk of buf in cp, and its length in sz. */
-void
-buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz)
-{
- if (!buf || !buf->head) {
- *cp = NULL;
- *sz = 0;
- } else {
- *cp = buf->head->data;
- *sz = buf->head->datalen;
- }
-}
-
-/* Write sz bytes from cp into a newly allocated buffer buf.
- * Returns NULL when passed a NULL cp or zero sz.
- * Asserts on failure: only for use in unit tests.
- * buf must be freed using buf_free(). */
-buf_t *
-buf_new_with_data(const char *cp, size_t sz)
-{
- /* Validate arguments */
- if (!cp || sz <= 0) {
- return NULL;
- }
-
- tor_assert(sz < SSIZE_T_CEILING);
-
- /* Allocate a buffer */
- buf_t *buf = buf_new_with_capacity(sz);
- tor_assert(buf);
- assert_buf_ok(buf);
- tor_assert(!buf->head);
-
- /* Allocate a chunk that is sz bytes long */
- buf->head = chunk_new_with_alloc_size(CHUNK_ALLOC_SIZE(sz));
- buf->tail = buf->head;
- tor_assert(buf->head);
- assert_buf_ok(buf);
- tor_assert(buf_allocation(buf) >= sz);
-
- /* Copy the data and size the buffers */
- tor_assert(sz <= buf_slack(buf));
- tor_assert(sz <= CHUNK_REMAINING_CAPACITY(buf->head));
- memcpy(&buf->head->mem[0], cp, sz);
- buf->datalen = sz;
- buf->head->datalen = sz;
- buf->head->data = &buf->head->mem[0];
- assert_buf_ok(buf);
-
- /* Make sure everything is large enough */
- tor_assert(buf_allocation(buf) >= sz);
- tor_assert(buf_allocation(buf) >= buf_datalen(buf) + buf_slack(buf));
- /* Does the buffer implementation allocate more than the requested size?
- * (for example, by rounding up). If so, these checks will fail. */
- tor_assert(buf_datalen(buf) == sz);
- tor_assert(buf_slack(buf) == 0);
-
- return buf;
-}
-#endif
-
-/** Remove the first <b>n</b> bytes from buf. */
-static inline void
-buf_remove_from_front(buf_t *buf, size_t n)
-{
- tor_assert(buf->datalen >= n);
- while (n) {
- tor_assert(buf->head);
- if (buf->head->datalen > n) {
- buf->head->datalen -= n;
- buf->head->data += n;
- buf->datalen -= n;
- return;
- } else {
- chunk_t *victim = buf->head;
- n -= victim->datalen;
- buf->datalen -= victim->datalen;
- buf->head = victim->next;
- if (buf->tail == victim)
- buf->tail = NULL;
- buf_chunk_free_unchecked(victim);
- }
- }
- check();
-}
-
-/** Create and return a new buf with default chunk capacity <b>size</b>.
- */
-buf_t *
-buf_new_with_capacity(size_t size)
-{
- buf_t *b = buf_new();
- b->default_chunk_size = preferred_chunk_size(size);
- return b;
-}
-
-/** Allocate and return a new buffer with default capacity. */
-buf_t *
-buf_new(void)
-{
- buf_t *buf = tor_malloc_zero(sizeof(buf_t));
- buf->magic = BUFFER_MAGIC;
- buf->default_chunk_size = 4096;
- return buf;
-}
-
-size_t
-buf_get_default_chunk_size(const buf_t *buf)
-{
- return buf->default_chunk_size;
-}
-
-/** Remove all data from <b>buf</b>. */
-void
-buf_clear(buf_t *buf)
-{
- chunk_t *chunk, *next;
- buf->datalen = 0;
- for (chunk = buf->head; chunk; chunk = next) {
- next = chunk->next;
- buf_chunk_free_unchecked(chunk);
- }
- buf->head = buf->tail = NULL;
-}
-
-/** Return the number of bytes stored in <b>buf</b> */
-MOCK_IMPL(size_t,
-buf_datalen, (const buf_t *buf))
-{
- return buf->datalen;
-}
-
-/** Return the total length of all chunks used in <b>buf</b>. */
-size_t
-buf_allocation(const buf_t *buf)
-{
- size_t total = 0;
- const chunk_t *chunk;
- for (chunk = buf->head; chunk; chunk = chunk->next) {
- total += CHUNK_ALLOC_SIZE(chunk->memlen);
- }
- return total;
-}
-
-/** Return the number of bytes that can be added to <b>buf</b> without
- * performing any additional allocation. */
-size_t
-buf_slack(const buf_t *buf)
-{
- if (!buf->tail)
- return 0;
- else
- return CHUNK_REMAINING_CAPACITY(buf->tail);
-}
-
-/** Release storage held by <b>buf</b>. */
-void
-buf_free(buf_t *buf)
-{
- if (!buf)
- return;
-
- buf_clear(buf);
- buf->magic = 0xdeadbeef;
- tor_free(buf);
-}
-
-/** Return a new copy of <b>in_chunk</b> */
-static chunk_t *
-chunk_copy(const chunk_t *in_chunk)
-{
- chunk_t *newch = tor_memdup(in_chunk, CHUNK_ALLOC_SIZE(in_chunk->memlen));
- total_bytes_allocated_in_chunks += CHUNK_ALLOC_SIZE(in_chunk->memlen);
-#ifdef DEBUG_CHUNK_ALLOC
- newch->DBG_alloc = CHUNK_ALLOC_SIZE(in_chunk->memlen);
-#endif
- newch->next = NULL;
- if (in_chunk->data) {
- off_t offset = in_chunk->data - in_chunk->mem;
- newch->data = newch->mem + offset;
- }
- return newch;
-}
-
-/** Return a new copy of <b>buf</b> */
-buf_t *
-buf_copy(const buf_t *buf)
-{
- chunk_t *ch;
- buf_t *out = buf_new();
- out->default_chunk_size = buf->default_chunk_size;
- for (ch = buf->head; ch; ch = ch->next) {
- chunk_t *newch = chunk_copy(ch);
- if (out->tail) {
- out->tail->next = newch;
- out->tail = newch;
- } else {
- out->head = out->tail = newch;
- }
- }
- out->datalen = buf->datalen;
- return out;
-}
-
-/** Append a new chunk with enough capacity to hold <b>capacity</b> bytes to
- * the tail of <b>buf</b>. If <b>capped</b>, don't allocate a chunk bigger
- * than MAX_CHUNK_ALLOC. */
-static chunk_t *
-buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
-{
- chunk_t *chunk;
-
- if (CHUNK_ALLOC_SIZE(capacity) < buf->default_chunk_size) {
- chunk = chunk_new_with_alloc_size(buf->default_chunk_size);
- } else if (capped && CHUNK_ALLOC_SIZE(capacity) > MAX_CHUNK_ALLOC) {
- chunk = chunk_new_with_alloc_size(MAX_CHUNK_ALLOC);
- } else {
- chunk = chunk_new_with_alloc_size(preferred_chunk_size(capacity));
- }
-
- chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec();
-
- if (buf->tail) {
- tor_assert(buf->head);
- buf->tail->next = chunk;
- buf->tail = chunk;
- } else {
- tor_assert(!buf->head);
- buf->head = buf->tail = chunk;
- }
- check();
- return chunk;
-}
-
-/** Return the age of the oldest chunk in the buffer <b>buf</b>, in
- * milliseconds. Requires the current monotonic time, in truncated msec,
- * as its input <b>now</b>.
- */
-uint32_t
-buf_get_oldest_chunk_timestamp(const buf_t *buf, uint32_t now)
-{
- if (buf->head) {
- return now - buf->head->inserted_time;
- } else {
- return 0;
- }
-}
-
-size_t
-buf_get_total_allocation(void)
-{
- return total_bytes_allocated_in_chunks;
-}
-
-/** Read up to <b>at_most</b> bytes from the socket <b>fd</b> into
- * <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set
- * *<b>reached_eof</b> to 1. Return -1 on error, 0 on eof or blocking,
- * and the number of bytes read otherwise. */
-static inline int
-read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most,
- int *reached_eof, int *socket_error)
-{
- ssize_t read_result;
- if (at_most > CHUNK_REMAINING_CAPACITY(chunk))
- at_most = CHUNK_REMAINING_CAPACITY(chunk);
- read_result = tor_socket_recv(fd, CHUNK_WRITE_PTR(chunk), at_most, 0);
-
- if (read_result < 0) {
- int e = tor_socket_errno(fd);
- if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
-#ifdef _WIN32
- if (e == WSAENOBUFS)
- log_warn(LD_NET,"recv() failed: WSAENOBUFS. Not enough ram?");
-#endif
- *socket_error = e;
- return -1;
- }
- return 0; /* would block. */
- } else if (read_result == 0) {
- log_debug(LD_NET,"Encountered eof on fd %d", (int)fd);
- *reached_eof = 1;
- return 0;
- } else { /* actually got bytes. */
- buf->datalen += read_result;
- chunk->datalen += read_result;
- log_debug(LD_NET,"Read %ld bytes. %d on inbuf.", (long)read_result,
- (int)buf->datalen);
- tor_assert(read_result < INT_MAX);
- return (int)read_result;
- }
-}
-
-/** As read_to_chunk(), but return (negative) error code on error, blocking,
- * or TLS, and the number of bytes read otherwise. */
-static inline int
-read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
- size_t at_most)
-{
- int read_result;
-
- tor_assert(CHUNK_REMAINING_CAPACITY(chunk) >= at_most);
- read_result = tor_tls_read(tls, CHUNK_WRITE_PTR(chunk), at_most);
- if (read_result < 0)
- return read_result;
- buf->datalen += read_result;
- chunk->datalen += read_result;
- return read_result;
-}
-
-/** Read from socket <b>s</b>, writing onto end of <b>buf</b>. Read at most
- * <b>at_most</b> bytes, growing the buffer as necessary. If recv() returns 0
- * (because of EOF), set *<b>reached_eof</b> to 1 and return 0. Return -1 on
- * error; else return the number of bytes read.
- */
-/* XXXX indicate "read blocked" somehow? */
-int
-read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof,
- int *socket_error)
-{
- /* XXXX It's stupid to overload the return values for these functions:
- * "error status" and "number of bytes read" are not mutually exclusive.
- */
- int r = 0;
- size_t total_read = 0;
-
- check();
- tor_assert(reached_eof);
- tor_assert(SOCKET_OK(s));
-
- if (BUG(buf->datalen >= INT_MAX))
- return -1;
- if (BUG(buf->datalen >= INT_MAX - at_most))
- return -1;
-
- while (at_most > total_read) {
- size_t readlen = at_most - total_read;
- chunk_t *chunk;
- if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
- chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
- if (readlen > chunk->memlen)
- readlen = chunk->memlen;
- } else {
- size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
- chunk = buf->tail;
- if (cap < readlen)
- readlen = cap;
- }
-
- r = read_to_chunk(buf, chunk, s, readlen, reached_eof, socket_error);
- check();
- if (r < 0)
- return r; /* Error */
- tor_assert(total_read+r < INT_MAX);
- total_read += r;
- if ((size_t)r < readlen) { /* eof, block, or no more to read. */
- break;
- }
- }
- return (int)total_read;
-}
-
-/** As read_to_buf, but reads from a TLS connection, and returns a TLS
- * status value rather than the number of bytes read.
- *
- * Using TLS on OR connections complicates matters in two ways.
- *
- * First, a TLS stream has its own read buffer independent of the
- * connection's read buffer. (TLS needs to read an entire frame from
- * the network before it can decrypt any data. Thus, trying to read 1
- * byte from TLS can require that several KB be read from the network
- * and decrypted. The extra data is stored in TLS's decrypt buffer.)
- * Because the data hasn't been read by Tor (it's still inside the TLS),
- * this means that sometimes a connection "has stuff to read" even when
- * poll() didn't return POLLIN. The tor_tls_get_pending_bytes function is
- * used in connection.c to detect TLS objects with non-empty internal
- * buffers and read from them again.
- *
- * Second, the TLS stream's events do not correspond directly to network
- * events: sometimes, before a TLS stream can read, the network must be
- * ready to write -- or vice versa.
- */
-int
-read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf)
-{
- int r = 0;
- size_t total_read = 0;
-
- check_no_tls_errors();
-
- check();
-
- if (BUG(buf->datalen >= INT_MAX))
- return -1;
- if (BUG(buf->datalen >= INT_MAX - at_most))
- return -1;
-
- while (at_most > total_read) {
- size_t readlen = at_most - total_read;
- chunk_t *chunk;
- if (!buf->tail || CHUNK_REMAINING_CAPACITY(buf->tail) < MIN_READ_LEN) {
- chunk = buf_add_chunk_with_capacity(buf, at_most, 1);
- if (readlen > chunk->memlen)
- readlen = chunk->memlen;
- } else {
- size_t cap = CHUNK_REMAINING_CAPACITY(buf->tail);
- chunk = buf->tail;
- if (cap < readlen)
- readlen = cap;
- }
-
- r = read_to_chunk_tls(buf, chunk, tls, readlen);
- check();
- if (r < 0)
- return r; /* Error */
- tor_assert(total_read+r < INT_MAX);
- total_read += r;
- if ((size_t)r < readlen) /* eof, block, or no more to read. */
- break;
- }
- return (int)total_read;
-}
-
-/** Helper for flush_buf(): try to write <b>sz</b> bytes from chunk
- * <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>. On success, deduct
- * the bytes written from *<b>buf_flushlen</b>. Return the number of bytes
- * written on success, 0 on blocking, -1 on failure.
- */
-static inline int
-flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz,
- size_t *buf_flushlen)
-{
- ssize_t write_result;
-
- if (sz > chunk->datalen)
- sz = chunk->datalen;
- write_result = tor_socket_send(s, chunk->data, sz, 0);
-
- if (write_result < 0) {
- int e = tor_socket_errno(s);
- if (!ERRNO_IS_EAGAIN(e)) { /* it's a real error */
-#ifdef _WIN32
- if (e == WSAENOBUFS)
- log_warn(LD_NET,"write() failed: WSAENOBUFS. Not enough ram?");
-#endif
- return -1;
- }
- log_debug(LD_NET,"write() would block, returning.");
- return 0;
- } else {
- *buf_flushlen -= write_result;
- buf_remove_from_front(buf, write_result);
- tor_assert(write_result < INT_MAX);
- return (int)write_result;
- }
-}
-
-/** Helper for flush_buf_tls(): try to write <b>sz</b> bytes from chunk
- * <b>chunk</b> of buffer <b>buf</b> onto socket <b>s</b>. (Tries to write
- * more if there is a forced pending write size.) On success, deduct the
- * bytes written from *<b>buf_flushlen</b>. Return the number of bytes
- * written on success, and a TOR_TLS error code on failure or blocking.
- */
-static inline int
-flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk,
- size_t sz, size_t *buf_flushlen)
-{
- int r;
- size_t forced;
- char *data;
-
- forced = tor_tls_get_forced_write_size(tls);
- if (forced > sz)
- sz = forced;
- if (chunk) {
- data = chunk->data;
- tor_assert(sz <= chunk->datalen);
- } else {
- data = NULL;
- tor_assert(sz == 0);
- }
- r = tor_tls_write(tls, data, sz);
- if (r < 0)
- return r;
- if (*buf_flushlen > (size_t)r)
- *buf_flushlen -= r;
- else
- *buf_flushlen = 0;
- buf_remove_from_front(buf, r);
- log_debug(LD_NET,"flushed %d bytes, %d ready to flush, %d remain.",
- r,(int)*buf_flushlen,(int)buf->datalen);
- return r;
-}
-
-/** Write data from <b>buf</b> to the socket <b>s</b>. Write at most
- * <b>sz</b> bytes, decrement *<b>buf_flushlen</b> by
- * the number of bytes actually written, and remove the written bytes
- * from the buffer. Return the number of bytes written on success,
- * -1 on failure. Return 0 if write() would block.
- */
-int
-flush_buf(tor_socket_t s, buf_t *buf, size_t sz, size_t *buf_flushlen)
-{
- /* XXXX It's stupid to overload the return values for these functions:
- * "error status" and "number of bytes flushed" are not mutually exclusive.
- */
- int r;
- size_t flushed = 0;
- tor_assert(buf_flushlen);
- tor_assert(SOCKET_OK(s));
- tor_assert(*buf_flushlen <= buf->datalen);
- tor_assert(sz <= *buf_flushlen);
-
- check();
- while (sz) {
- size_t flushlen0;
- tor_assert(buf->head);
- if (buf->head->datalen >= sz)
- flushlen0 = sz;
- else
- flushlen0 = buf->head->datalen;
-
- r = flush_chunk(s, buf, buf->head, flushlen0, buf_flushlen);
- check();
- if (r < 0)
- return r;
- flushed += r;
- sz -= r;
- if (r == 0 || (size_t)r < flushlen0) /* can't flush any more now. */
- break;
- }
- tor_assert(flushed < INT_MAX);
- return (int)flushed;
-}
-
-/** As flush_buf(), but writes data to a TLS connection. Can write more than
- * <b>flushlen</b> bytes.
- */
-int
-flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t flushlen,
- size_t *buf_flushlen)
-{
- int r;
- size_t flushed = 0;
- ssize_t sz;
- tor_assert(buf_flushlen);
- tor_assert(*buf_flushlen <= buf->datalen);
- tor_assert(flushlen <= *buf_flushlen);
- sz = (ssize_t) flushlen;
-
- /* we want to let tls write even if flushlen is zero, because it might
- * have a partial record pending */
- check_no_tls_errors();
-
- check();
- do {
- size_t flushlen0;
- if (buf->head) {
- if ((ssize_t)buf->head->datalen >= sz)
- flushlen0 = sz;
- else
- flushlen0 = buf->head->datalen;
- } else {
- flushlen0 = 0;
- }
-
- r = flush_chunk_tls(tls, buf, buf->head, flushlen0, buf_flushlen);
- check();
- if (r < 0)
- return r;
- flushed += r;
- sz -= r;
- if (r == 0) /* Can't flush any more now. */
- break;
- } while (sz > 0);
- tor_assert(flushed < INT_MAX);
- return (int)flushed;
-}
-
-/** Append <b>string_len</b> bytes from <b>string</b> to the end of
- * <b>buf</b>.
- *
- * Return the new length of the buffer on success, -1 on failure.
- */
-int
-write_to_buf(const char *string, size_t string_len, buf_t *buf)
-{
- if (!string_len)
- return (int)buf->datalen;
- check();
-
- if (BUG(buf->datalen >= INT_MAX))
- return -1;
- if (BUG(buf->datalen >= INT_MAX - string_len))
- return -1;
-
- while (string_len) {
- size_t copy;
- if (!buf->tail || !CHUNK_REMAINING_CAPACITY(buf->tail))
- buf_add_chunk_with_capacity(buf, string_len, 1);
-
- copy = CHUNK_REMAINING_CAPACITY(buf->tail);
- if (copy > string_len)
- copy = string_len;
- memcpy(CHUNK_WRITE_PTR(buf->tail), string, copy);
- string_len -= copy;
- string += copy;
- buf->datalen += copy;
- buf->tail->datalen += copy;
- }
-
- check();
- tor_assert(buf->datalen < INT_MAX);
- return (int)buf->datalen;
-}
-
-/** Helper: copy the first <b>string_len</b> bytes from <b>buf</b>
- * onto <b>string</b>.
- */
-static inline void
-peek_from_buf(char *string, size_t string_len, const buf_t *buf)
-{
- chunk_t *chunk;
-
- tor_assert(string);
- /* make sure we don't ask for too much */
- tor_assert(string_len <= buf->datalen);
- /* assert_buf_ok(buf); */
-
- chunk = buf->head;
- while (string_len) {
- size_t copy = string_len;
- tor_assert(chunk);
- if (chunk->datalen < copy)
- copy = chunk->datalen;
- memcpy(string, chunk->data, copy);
- string_len -= copy;
- string += copy;
- chunk = chunk->next;
- }
-}
-
-/** Remove <b>string_len</b> bytes from the front of <b>buf</b>, and store
- * them into <b>string</b>. Return the new buffer size. <b>string_len</b>
- * must be \<= the number of bytes on the buffer.
- */
-int
-fetch_from_buf(char *string, size_t string_len, buf_t *buf)
-{
- /* There must be string_len bytes in buf; write them onto string,
- * then memmove buf back (that is, remove them from buf).
- *
- * Return the number of bytes still on the buffer. */
-
- check();
- peek_from_buf(string, string_len, buf);
- buf_remove_from_front(buf, string_len);
- check();
- tor_assert(buf->datalen < INT_MAX);
- return (int)buf->datalen;
-}
-
-/** True iff the cell command <b>command</b> is one that implies a
- * variable-length cell in Tor link protocol <b>linkproto</b>. */
-static inline int
-cell_command_is_var_length(uint8_t command, int linkproto)
-{
- /* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells
- * work as implemented here. If it's 1, there are no variable-length cells.
- * Tor does not support other versions right now, and so can't negotiate
- * them.
- */
- switch (linkproto) {
- case 1:
- /* Link protocol version 1 has no variable-length cells. */
- return 0;
- case 2:
- /* In link protocol version 2, VERSIONS is the only variable-length cell */
- return command == CELL_VERSIONS;
- case 0:
- case 3:
- default:
- /* In link protocol version 3 and later, and in version "unknown",
- * commands 128 and higher indicate variable-length. VERSIONS is
- * grandfathered in. */
- return command == CELL_VERSIONS || command >= 128;
- }
-}
-
-/** Check <b>buf</b> for a variable-length cell according to the rules of link
- * protocol version <b>linkproto</b>. If one is found, pull it off the buffer
- * and assign a newly allocated var_cell_t to *<b>out</b>, and return 1.
- * Return 0 if whatever is on the start of buf_t is not a variable-length
- * cell. Return 1 and set *<b>out</b> to NULL if there seems to be the start
- * of a variable-length cell on <b>buf</b>, but the whole thing isn't there
- * yet. */
-int
-fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
-{
- char hdr[VAR_CELL_MAX_HEADER_SIZE];
- var_cell_t *result;
- uint8_t command;
- uint16_t length;
- const int wide_circ_ids = linkproto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS;
- const int circ_id_len = get_circ_id_size(wide_circ_ids);
- const unsigned header_len = get_var_cell_header_size(wide_circ_ids);
- check();
- *out = NULL;
- if (buf->datalen < header_len)
- return 0;
- peek_from_buf(hdr, header_len, buf);
-
- command = get_uint8(hdr + circ_id_len);
- if (!(cell_command_is_var_length(command, linkproto)))
- return 0;
-
- length = ntohs(get_uint16(hdr + circ_id_len + 1));
- if (buf->datalen < (size_t)(header_len+length))
- return 1;
- result = var_cell_new(length);
- result->command = command;
- if (wide_circ_ids)
- result->circ_id = ntohl(get_uint32(hdr));
- else
- result->circ_id = ntohs(get_uint16(hdr));
-
- buf_remove_from_front(buf, header_len);
- peek_from_buf((char*) result->payload, length, buf);
- buf_remove_from_front(buf, length);
- check();
-
- *out = result;
- return 1;
-}
-
-/** Move up to *<b>buf_flushlen</b> bytes from <b>buf_in</b> to
- * <b>buf_out</b>, and modify *<b>buf_flushlen</b> appropriately.
- * Return the number of bytes actually copied.
- */
-int
-move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen)
-{
- /* We can do way better here, but this doesn't turn up in any profiles. */
- char b[4096];
- size_t cp, len;
-
- if (BUG(buf_out->datalen >= INT_MAX))
- return -1;
- if (BUG(buf_out->datalen >= INT_MAX - *buf_flushlen))
- return -1;
-
- len = *buf_flushlen;
- if (len > buf_in->datalen)
- len = buf_in->datalen;
-
- cp = len; /* Remember the number of bytes we intend to copy. */
- tor_assert(cp < INT_MAX);
- while (len) {
- /* This isn't the most efficient implementation one could imagine, since
- * it does two copies instead of 1, but I kinda doubt that this will be
- * critical path. */
- size_t n = len > sizeof(b) ? sizeof(b) : len;
- fetch_from_buf(b, n, buf_in);
- write_to_buf(b, n, buf_out);
- len -= n;
- }
- *buf_flushlen -= cp;
- return (int)cp;
-}
-
-/** Internal structure: represents a position in a buffer. */
-typedef struct buf_pos_t {
- const chunk_t *chunk; /**< Which chunk are we pointing to? */
- int pos;/**< Which character inside the chunk's data are we pointing to? */
- size_t chunk_pos; /**< Total length of all previous chunks. */
-} buf_pos_t;
-
-/** Initialize <b>out</b> to point to the first character of <b>buf</b>.*/
-static void
-buf_pos_init(const buf_t *buf, buf_pos_t *out)
-{
- out->chunk = buf->head;
- out->pos = 0;
- out->chunk_pos = 0;
-}
-
-/** Advance <b>out</b> to the first appearance of <b>ch</b> at the current
- * position of <b>out</b>, or later. Return -1 if no instances are found;
- * otherwise returns the absolute position of the character. */
-static off_t
-buf_find_pos_of_char(char ch, buf_pos_t *out)
-{
- const chunk_t *chunk;
- int pos;
- tor_assert(out);
- if (out->chunk) {
- if (out->chunk->datalen) {
- tor_assert(out->pos < (off_t)out->chunk->datalen);
- } else {
- tor_assert(out->pos == 0);
- }
- }
- pos = out->pos;
- for (chunk = out->chunk; chunk; chunk = chunk->next) {
- char *cp = memchr(chunk->data+pos, ch, chunk->datalen - pos);
- if (cp) {
- out->chunk = chunk;
- tor_assert(cp - chunk->data < INT_MAX);
- out->pos = (int)(cp - chunk->data);
- return out->chunk_pos + out->pos;
- } else {
- out->chunk_pos += chunk->datalen;
- pos = 0;
- }
- }
- return -1;
-}
-
-/** Advance <b>pos</b> by a single character, if there are any more characters
- * in the buffer. Returns 0 on success, -1 on failure. */
-static inline int
-buf_pos_inc(buf_pos_t *pos)
-{
- ++pos->pos;
- if (pos->pos == (off_t)pos->chunk->datalen) {
- if (!pos->chunk->next)
- return -1;
- pos->chunk_pos += pos->chunk->datalen;
- pos->chunk = pos->chunk->next;
- pos->pos = 0;
- }
- return 0;
-}
-
-/** Return true iff the <b>n</b>-character string in <b>s</b> appears
- * (verbatim) at <b>pos</b>. */
-static int
-buf_matches_at_pos(const buf_pos_t *pos, const char *s, size_t n)
-{
- buf_pos_t p;
- if (!n)
- return 1;
-
- memcpy(&p, pos, sizeof(p));
-
- while (1) {
- char ch = p.chunk->data[p.pos];
- if (ch != *s)
- return 0;
- ++s;
- /* If we're out of characters that don't match, we match. Check this
- * _before_ we test incrementing pos, in case we're at the end of the
- * string. */
- if (--n == 0)
- return 1;
- if (buf_pos_inc(&p)<0)
- return 0;
- }
-}
-
-/** Return the first position in <b>buf</b> at which the <b>n</b>-character
- * string <b>s</b> occurs, or -1 if it does not occur. */
-STATIC int
-buf_find_string_offset(const buf_t *buf, const char *s, size_t n)
-{
- buf_pos_t pos;
- buf_pos_init(buf, &pos);
- while (buf_find_pos_of_char(*s, &pos) >= 0) {
- if (buf_matches_at_pos(&pos, s, n)) {
- tor_assert(pos.chunk_pos + pos.pos < INT_MAX);
- return (int)(pos.chunk_pos + pos.pos);
- } else {
- if (buf_pos_inc(&pos)<0)
- return -1;
- }
- }
- return -1;
-}
-
-/**
- * Scan the HTTP headers in the <b>headerlen</b>-byte memory range at
- * <b>headers</b>, looking for a "Content-Length" header. Try to set
- * *<b>result_out</b> to the numeric value of that header if possible.
- * Return -1 if the header was malformed, 0 if it was missing, and 1 if
- * it was present and well-formed.
- */
-STATIC int
-buf_http_find_content_length(const char *headers, size_t headerlen,
- size_t *result_out)
-{
- const char *p, *newline;
- char *len_str, *eos=NULL;
- size_t remaining, result;
- int ok;
- *result_out = 0; /* The caller shouldn't look at this unless the
- * return value is 1, but let's prevent confusion */
-
-#define CONTENT_LENGTH "\r\nContent-Length: "
- p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH);
- if (p == NULL)
- return 0;
-
- tor_assert(p >= headers && p < headers+headerlen);
- remaining = (headers+headerlen)-p;
- p += strlen(CONTENT_LENGTH);
- remaining -= strlen(CONTENT_LENGTH);
-
- newline = memchr(p, '\n', remaining);
- if (newline == NULL)
- return -1;
-
- len_str = tor_memdup_nulterm(p, newline-p);
- /* We limit the size to INT_MAX because other parts of the buffer.c
- * code don't like buffers to be any bigger than that. */
- result = (size_t) tor_parse_uint64(len_str, 10, 0, INT_MAX, &ok, &eos);
- if (eos && !tor_strisspace(eos)) {
- ok = 0;
- } else {
- *result_out = result;
- }
- tor_free(len_str);
-
- return ok ? 1 : -1;
-}
-
-/** There is a (possibly incomplete) http statement on <b>buf</b>, of the
- * form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain NULs.)
- * If a) the headers include a Content-Length field and all bytes in
- * the body are present, or b) there's no Content-Length field and
- * all headers are present, then:
- *
- * - strdup headers into <b>*headers_out</b>, and NUL-terminate it.
- * - memdup body into <b>*body_out</b>, and NUL-terminate it.
- * - Then remove them from <b>buf</b>, and return 1.
- *
- * - If headers or body is NULL, discard that part of the buf.
- * - If a headers or body doesn't fit in the arg, return -1.
- * (We ensure that the headers or body don't exceed max len,
- * _even if_ we're planning to discard them.)
- * - If force_complete is true, then succeed even if not all of the
- * content has arrived.
- *
- * Else, change nothing and return 0.
- */
-int
-fetch_from_buf_http(buf_t *buf,
- char **headers_out, size_t max_headerlen,
- char **body_out, size_t *body_used, size_t max_bodylen,
- int force_complete)
-{
- char *headers;
- size_t headerlen, bodylen, contentlen=0;
- int crlf_offset;
- int r;
-
- check();
- if (!buf->head)
- return 0;
-
- crlf_offset = buf_find_string_offset(buf, "\r\n\r\n", 4);
- if (crlf_offset > (int)max_headerlen ||
- (crlf_offset < 0 && buf->datalen > max_headerlen)) {
- log_debug(LD_HTTP,"headers too long.");
- return -1;
- } else if (crlf_offset < 0) {
- log_debug(LD_HTTP,"headers not all here yet.");
- return 0;
- }
- /* Okay, we have a full header. Make sure it all appears in the first
- * chunk. */
- if ((int)buf->head->datalen < crlf_offset + 4)
- buf_pullup(buf, crlf_offset+4);
- headerlen = crlf_offset + 4;
-
- headers = buf->head->data;
- bodylen = buf->datalen - headerlen;
- log_debug(LD_HTTP,"headerlen %d, bodylen %d.", (int)headerlen, (int)bodylen);
-
- if (max_headerlen <= headerlen) {
- log_warn(LD_HTTP,"headerlen %d larger than %d. Failing.",
- (int)headerlen, (int)max_headerlen-1);
- return -1;
- }
- if (max_bodylen <= bodylen) {
- log_warn(LD_HTTP,"bodylen %d larger than %d. Failing.",
- (int)bodylen, (int)max_bodylen-1);
- return -1;
- }
-
- r = buf_http_find_content_length(headers, headerlen, &contentlen);
- if (r == -1) {
- log_warn(LD_PROTOCOL, "Content-Length is bogus; maybe "
- "someone is trying to crash us.");
- return -1;
- } else if (r == 1) {
- /* if content-length is malformed, then our body length is 0. fine. */
- log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen);
- if (bodylen < contentlen) {
- if (!force_complete) {
- log_debug(LD_HTTP,"body not all here yet.");
- return 0; /* not all there yet */
- }
- }
- if (bodylen > contentlen) {
- bodylen = contentlen;
- log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen);
- }
- } else {
- tor_assert(r == 0);
- /* Leave bodylen alone */
- }
-
- /* all happy. copy into the appropriate places, and return 1 */
- if (headers_out) {
- *headers_out = tor_malloc(headerlen+1);
- fetch_from_buf(*headers_out, headerlen, buf);
- (*headers_out)[headerlen] = 0; /* NUL terminate it */
- }
- if (body_out) {
- tor_assert(body_used);
- *body_used = bodylen;
- *body_out = tor_malloc(bodylen+1);
- fetch_from_buf(*body_out, bodylen, buf);
- (*body_out)[bodylen] = 0; /* NUL terminate it */
- }
- check();
- return 1;
-}
-
-/**
- * Wait this many seconds before warning the user about using SOCKS unsafely
- * again. */
-#define SOCKS_WARN_INTERVAL 5
-
-/** Warn that the user application has made an unsafe socks request using
- * protocol <b>socks_protocol</b> on port <b>port</b>. Don't warn more than
- * once per SOCKS_WARN_INTERVAL, unless <b>safe_socks</b> is set. */
-static void
-log_unsafe_socks_warning(int socks_protocol, const char *address,
- uint16_t port, int safe_socks)
-{
- static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL);
-
- if (safe_socks) {
- log_fn_ratelim(&socks_ratelim, LOG_WARN, LD_APP,
- "Your application (using socks%d to port %d) is giving "
- "Tor only an IP address. Applications that do DNS resolves "
- "themselves may leak information. Consider using Socks4A "
- "(e.g. via privoxy or socat) instead. For more information, "
- "please see https://wiki.torproject.org/TheOnionRouter/"
- "TorFAQ#SOCKSAndDNS.%s",
- socks_protocol,
- (int)port,
- safe_socks ? " Rejecting." : "");
- }
- control_event_client_status(LOG_WARN,
- "DANGEROUS_SOCKS PROTOCOL=SOCKS%d ADDRESS=%s:%d",
- socks_protocol, address, (int)port);
-}
-
-/** Do not attempt to parse socks messages longer than this. This value is
- * actually significantly higher than the longest possible socks message. */
-#define MAX_SOCKS_MESSAGE_LEN 512
-
-/** Return a new socks_request_t. */
-socks_request_t *
-socks_request_new(void)
-{
- return tor_malloc_zero(sizeof(socks_request_t));
-}
-
-/** Free all storage held in the socks_request_t <b>req</b>. */
-void
-socks_request_free(socks_request_t *req)
-{
- if (!req)
- return;
- if (req->username) {
- memwipe(req->username, 0x10, req->usernamelen);
- tor_free(req->username);
- }
- if (req->password) {
- memwipe(req->password, 0x04, req->passwordlen);
- tor_free(req->password);
- }
- memwipe(req, 0xCC, sizeof(socks_request_t));
- tor_free(req);
-}
-
-/** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
- * of the forms
- * - socks4: "socksheader username\\0"
- * - socks4a: "socksheader username\\0 destaddr\\0"
- * - socks5 phase one: "version #methods methods"
- * - socks5 phase two: "version command 0 addresstype..."
- * If it's a complete and valid handshake, and destaddr fits in
- * MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
- * assign to <b>req</b>, and return 1.
- *
- * If it's invalid or too big, return -1.
- *
- * Else it's not all there yet, leave buf alone and return 0.
- *
- * If you want to specify the socks reply, write it into <b>req->reply</b>
- * and set <b>req->replylen</b>, else leave <b>req->replylen</b> alone.
- *
- * If <b>log_sockstype</b> is non-zero, then do a notice-level log of whether
- * the connection is possibly leaking DNS requests locally or not.
- *
- * If <b>safe_socks</b> is true, then reject unsafe socks protocols.
- *
- * If returning 0 or -1, <b>req->address</b> and <b>req->port</b> are
- * undefined.
- */
-int
-fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
- int log_sockstype, int safe_socks)
-{
- int res;
- ssize_t n_drain;
- size_t want_length = 128;
-
- if (buf->datalen < 2) /* version and another byte */
- return 0;
-
- do {
- n_drain = 0;
- buf_pullup(buf, want_length);
- tor_assert(buf->head && buf->head->datalen >= 2);
- want_length = 0;
-
- res = parse_socks(buf->head->data, buf->head->datalen, req, log_sockstype,
- safe_socks, &n_drain, &want_length);
-
- if (n_drain < 0)
- buf_clear(buf);
- else if (n_drain > 0)
- buf_remove_from_front(buf, n_drain);
-
- } while (res == 0 && buf->head && want_length < buf->datalen &&
- buf->datalen >= 2);
-
- return res;
-}
-
-/** The size of the header of an Extended ORPort message: 2 bytes for
- * COMMAND, 2 bytes for BODYLEN */
-#define EXT_OR_CMD_HEADER_SIZE 4
-
-/** Read <b>buf</b>, which should contain an Extended ORPort message
- * from a transport proxy. If well-formed, create and populate
- * <b>out</b> with the Extended ORport message. Return 0 if the
- * buffer was incomplete, 1 if it was well-formed and -1 if we
- * encountered an error while parsing it. */
-int
-fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out)
-{
- char hdr[EXT_OR_CMD_HEADER_SIZE];
- uint16_t len;
-
- check();
- if (buf->datalen < EXT_OR_CMD_HEADER_SIZE)
- return 0;
- peek_from_buf(hdr, sizeof(hdr), buf);
- len = ntohs(get_uint16(hdr+2));
- if (buf->datalen < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
- return 0;
- *out = ext_or_cmd_new(len);
- (*out)->cmd = ntohs(get_uint16(hdr));
- (*out)->len = len;
- buf_remove_from_front(buf, EXT_OR_CMD_HEADER_SIZE);
- fetch_from_buf((*out)->body, len, buf);
- return 1;
-}
-
-/** Create a SOCKS5 reply message with <b>reason</b> in its REP field and
- * have Tor send it as error response to <b>req</b>.
- */
-static void
-socks_request_set_socks5_error(socks_request_t *req,
- socks5_reply_status_t reason)
-{
- req->replylen = 10;
- memset(req->reply,0,10);
-
- req->reply[0] = 0x05; // VER field.
- req->reply[1] = reason; // REP field.
- req->reply[3] = 0x01; // ATYP field.
-}
-
-/** Implementation helper to implement fetch_from_*_socks. Instead of looking
- * at a buffer's contents, we look at the <b>datalen</b> bytes of data in
- * <b>data</b>. Instead of removing data from the buffer, we set
- * <b>drain_out</b> to the amount of data that should be removed (or -1 if the
- * buffer should be cleared). Instead of pulling more data into the first
- * chunk of the buffer, we set *<b>want_length_out</b> to the number of bytes
- * we'd like to see in the input buffer, if they're available. */
-static int
-parse_socks(const char *data, size_t datalen, socks_request_t *req,
- int log_sockstype, int safe_socks, ssize_t *drain_out,
- size_t *want_length_out)
-{
- unsigned int len;
- char tmpbuf[TOR_ADDR_BUF_LEN+1];
- tor_addr_t destaddr;
- uint32_t destip;
- uint8_t socksver;
- char *next, *startaddr;
- unsigned char usernamelen, passlen;
- struct in_addr in;
-
- if (datalen < 2) {
- /* We always need at least 2 bytes. */
- *want_length_out = 2;
- return 0;
- }
-
- if (req->socks_version == 5 && !req->got_auth) {
- /* See if we have received authentication. Strictly speaking, we should
- also check whether we actually negotiated username/password
- authentication. But some broken clients will send us authentication
- even if we negotiated SOCKS_NO_AUTH. */
- if (*data == 1) { /* username/pass version 1 */
- /* Format is: authversion [1 byte] == 1
- usernamelen [1 byte]
- username [usernamelen bytes]
- passlen [1 byte]
- password [passlen bytes] */
- usernamelen = (unsigned char)*(data + 1);
- if (datalen < 2u + usernamelen + 1u) {
- *want_length_out = 2u + usernamelen + 1u;
- return 0;
- }
- passlen = (unsigned char)*(data + 2u + usernamelen);
- if (datalen < 2u + usernamelen + 1u + passlen) {
- *want_length_out = 2u + usernamelen + 1u + passlen;
- return 0;
- }
- req->replylen = 2; /* 2 bytes of response */
- req->reply[0] = 1; /* authversion == 1 */
- req->reply[1] = 0; /* authentication successful */
- log_debug(LD_APP,
- "socks5: Accepted username/password without checking.");
- if (usernamelen) {
- req->username = tor_memdup(data+2u, usernamelen);
- req->usernamelen = usernamelen;
- }
- if (passlen) {
- req->password = tor_memdup(data+3u+usernamelen, passlen);
- req->passwordlen = passlen;
- }
- *drain_out = 2u + usernamelen + 1u + passlen;
- req->got_auth = 1;
- *want_length_out = 7; /* Minimal socks5 command. */
- return 0;
- } else if (req->auth_type == SOCKS_USER_PASS) {
- /* unknown version byte */
- log_warn(LD_APP, "Socks5 username/password version %d not recognized; "
- "rejecting.", (int)*data);
- return -1;
- }
- }
-
- socksver = *data;
-
- switch (socksver) { /* which version of socks? */
- case 5: /* socks5 */
-
- if (req->socks_version != 5) { /* we need to negotiate a method */
- unsigned char nummethods = (unsigned char)*(data+1);
- int have_user_pass, have_no_auth;
- int r=0;
- tor_assert(!req->socks_version);
- if (datalen < 2u+nummethods) {
- *want_length_out = 2u+nummethods;
- return 0;
- }
- if (!nummethods)
- return -1;
- req->replylen = 2; /* 2 bytes of response */
- req->reply[0] = 5; /* socks5 reply */
- have_user_pass = (memchr(data+2, SOCKS_USER_PASS, nummethods) !=NULL);
- have_no_auth = (memchr(data+2, SOCKS_NO_AUTH, nummethods) !=NULL);
- if (have_user_pass && !(have_no_auth && req->socks_prefer_no_auth)) {
- req->auth_type = SOCKS_USER_PASS;
- req->reply[1] = SOCKS_USER_PASS; /* tell client to use "user/pass"
- auth method */
- req->socks_version = 5; /* remember we've already negotiated auth */
- log_debug(LD_APP,"socks5: accepted method 2 (username/password)");
- r=0;
- } else if (have_no_auth) {
- req->reply[1] = SOCKS_NO_AUTH; /* tell client to use "none" auth
- method */
- req->socks_version = 5; /* remember we've already negotiated auth */
- log_debug(LD_APP,"socks5: accepted method 0 (no authentication)");
- r=0;
- } else {
- log_warn(LD_APP,
- "socks5: offered methods don't include 'no auth' or "
- "username/password. Rejecting.");
- req->reply[1] = '\xFF'; /* reject all methods */
- r=-1;
- }
- /* Remove packet from buf. Some SOCKS clients will have sent extra
- * junk at this point; let's hope it's an authentication message. */
- *drain_out = 2u + nummethods;
-
- return r;
- }
- if (req->auth_type != SOCKS_NO_AUTH && !req->got_auth) {
- log_warn(LD_APP,
- "socks5: negotiated authentication, but none provided");
- return -1;
- }
- /* we know the method; read in the request */
- log_debug(LD_APP,"socks5: checking request");
- if (datalen < 7) {/* basic info plus >=1 for addr plus 2 for port */
- *want_length_out = 7;
- return 0; /* not yet */
- }
- req->command = (unsigned char) *(data+1);
- if (req->command != SOCKS_COMMAND_CONNECT &&
- req->command != SOCKS_COMMAND_RESOLVE &&
- req->command != SOCKS_COMMAND_RESOLVE_PTR) {
- /* not a connect or resolve or a resolve_ptr? we don't support it. */
- socks_request_set_socks5_error(req,SOCKS5_COMMAND_NOT_SUPPORTED);
-
- log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
- req->command);
- return -1;
- }
- switch (*(data+3)) { /* address type */
- case 1: /* IPv4 address */
- case 4: /* IPv6 address */ {
- const int is_v6 = *(data+3) == 4;
- const unsigned addrlen = is_v6 ? 16 : 4;
- log_debug(LD_APP,"socks5: ipv4 address type");
- if (datalen < 6+addrlen) {/* ip/port there? */
- *want_length_out = 6+addrlen;
- return 0; /* not yet */
- }
-
- if (is_v6)
- tor_addr_from_ipv6_bytes(&destaddr, data+4);
- else
- tor_addr_from_ipv4n(&destaddr, get_uint32(data+4));
-
- tor_addr_to_str(tmpbuf, &destaddr, sizeof(tmpbuf), 1);
-
- if (strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
- socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
- log_warn(LD_APP,
- "socks5 IP takes %d bytes, which doesn't fit in %d. "
- "Rejecting.",
- (int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN);
- return -1;
- }
- strlcpy(req->address,tmpbuf,sizeof(req->address));
- req->port = ntohs(get_uint16(data+4+addrlen));
- *drain_out = 6+addrlen;
- if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
- !addressmap_have_mapping(req->address,0)) {
- log_unsafe_socks_warning(5, req->address, req->port, safe_socks);
- if (safe_socks) {
- socks_request_set_socks5_error(req, SOCKS5_NOT_ALLOWED);
- return -1;
- }
- }
- return 1;
- }
- case 3: /* fqdn */
- log_debug(LD_APP,"socks5: fqdn address type");
- if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {
- socks_request_set_socks5_error(req,
- SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED);
- log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
- "hostname type. Rejecting.");
- return -1;
- }
- len = (unsigned char)*(data+4);
- if (datalen < 7+len) { /* addr/port there? */
- *want_length_out = 7+len;
- return 0; /* not yet */
- }
- if (len+1 > MAX_SOCKS_ADDR_LEN) {
- socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
- log_warn(LD_APP,
- "socks5 hostname is %d bytes, which doesn't fit in "
- "%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
- return -1;
- }
- memcpy(req->address,data+5,len);
- req->address[len] = 0;
- req->port = ntohs(get_uint16(data+5+len));
- *drain_out = 5+len+2;
-
- if (string_is_valid_ipv4_address(req->address) ||
- string_is_valid_ipv6_address(req->address)) {
- log_unsafe_socks_warning(5,req->address,req->port,safe_socks);
-
- if (safe_socks) {
- socks_request_set_socks5_error(req, SOCKS5_NOT_ALLOWED);
- return -1;
- }
- } else if (!string_is_valid_hostname(req->address)) {
- socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
-
- log_warn(LD_PROTOCOL,
- "Your application (using socks5 to port %d) gave Tor "
- "a malformed hostname: %s. Rejecting the connection.",
- req->port, escaped_safe_str_client(req->address));
- return -1;
- }
- if (log_sockstype)
- log_notice(LD_APP,
- "Your application (using socks5 to port %d) instructed "
- "Tor to take care of the DNS resolution itself if "
- "necessary. This is good.", req->port);
- return 1;
- default: /* unsupported */
- socks_request_set_socks5_error(req,
- SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED);
- log_warn(LD_APP,"socks5: unsupported address type %d. Rejecting.",
- (int) *(data+3));
- return -1;
- }
- tor_assert(0);
- break;
- case 4: { /* socks4 */
- enum {socks4, socks4a} socks4_prot = socks4a;
- const char *authstart, *authend;
- /* http://ss5.sourceforge.net/socks4.protocol.txt */
- /* http://ss5.sourceforge.net/socks4A.protocol.txt */
-
- req->socks_version = 4;
- if (datalen < SOCKS4_NETWORK_LEN) {/* basic info available? */
- *want_length_out = SOCKS4_NETWORK_LEN;
- return 0; /* not yet */
- }
- // buf_pullup(buf, 1280);
- req->command = (unsigned char) *(data+1);
- if (req->command != SOCKS_COMMAND_CONNECT &&
- req->command != SOCKS_COMMAND_RESOLVE) {
- /* not a connect or resolve? we don't support it. (No resolve_ptr with
- * socks4.) */
- log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.",
- req->command);
- return -1;
- }
-
- req->port = ntohs(get_uint16(data+2));
- destip = ntohl(get_uint32(data+4));
- if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) {
- log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting.");
- return -1;
- }
- if (destip >> 8) {
- log_debug(LD_APP,"socks4: destip not in form 0.0.0.x.");
- in.s_addr = htonl(destip);
- tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
- if (strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN) {
- log_debug(LD_APP,"socks4 addr (%d bytes) too long. Rejecting.",
- (int)strlen(tmpbuf));
- return -1;
- }
- log_debug(LD_APP,
- "socks4: successfully read destip (%s)",
- safe_str_client(tmpbuf));
- socks4_prot = socks4;
- }
-
- authstart = data + SOCKS4_NETWORK_LEN;
- next = memchr(authstart, 0,
- datalen-SOCKS4_NETWORK_LEN);
- if (!next) {
- if (datalen >= 1024) {
- log_debug(LD_APP, "Socks4 user name too long; rejecting.");
- return -1;
- }
- log_debug(LD_APP,"socks4: Username not here yet.");
- *want_length_out = datalen+1024; /* More than we need, but safe */
- return 0;
- }
- authend = next;
- tor_assert(next < data+datalen);
-
- startaddr = NULL;
- if (socks4_prot != socks4a &&
- !addressmap_have_mapping(tmpbuf,0)) {
- log_unsafe_socks_warning(4, tmpbuf, req->port, safe_socks);
-
- if (safe_socks)
- return -1;
- }
- if (socks4_prot == socks4a) {
- if (next+1 == data+datalen) {
- log_debug(LD_APP,"socks4: No part of destaddr here yet.");
- *want_length_out = datalen + 1024; /* More than we need, but safe */
- return 0;
- }
- startaddr = next+1;
- next = memchr(startaddr, 0, data + datalen - startaddr);
- if (!next) {
- if (datalen >= 1024) {
- log_debug(LD_APP,"socks4: Destaddr too long.");
- return -1;
- }
- log_debug(LD_APP,"socks4: Destaddr not all here yet.");
- *want_length_out = datalen + 1024; /* More than we need, but safe */
- return 0;
- }
- if (MAX_SOCKS_ADDR_LEN <= next-startaddr) {
- log_warn(LD_APP,"socks4: Destaddr too long. Rejecting.");
- return -1;
- }
- // tor_assert(next < buf->cur+buf->datalen);
-
- if (log_sockstype)
- log_notice(LD_APP,
- "Your application (using socks4a to port %d) instructed "
- "Tor to take care of the DNS resolution itself if "
- "necessary. This is good.", req->port);
- }
- log_debug(LD_APP,"socks4: Everything is here. Success.");
- strlcpy(req->address, startaddr ? startaddr : tmpbuf,
- sizeof(req->address));
- if (!tor_strisprint(req->address) || strchr(req->address,'\"')) {
- log_warn(LD_PROTOCOL,
- "Your application (using socks4 to port %d) gave Tor "
- "a malformed hostname: %s. Rejecting the connection.",
- req->port, escaped_safe_str_client(req->address));
- return -1;
- }
- if (authend != authstart) {
- req->got_auth = 1;
- req->usernamelen = authend - authstart;
- req->username = tor_memdup(authstart, authend - authstart);
- }
- /* next points to the final \0 on inbuf */
- *drain_out = next - data + 1;
- return 1;
- }
- case 'G': /* get */
- case 'H': /* head */
- case 'P': /* put/post */
- case 'C': /* connect */
- strlcpy((char*)req->reply,
-"HTTP/1.0 501 Tor is not an HTTP Proxy\r\n"
-"Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
-"<html>\n"
-"<head>\n"
-"<title>Tor is not an HTTP Proxy</title>\n"
-"</head>\n"
-"<body>\n"
-"<h1>Tor is not an HTTP Proxy</h1>\n"
-"<p>\n"
-"It appears you have configured your web browser to use Tor as an HTTP proxy."
-"\n"
-"This is not correct: Tor is a SOCKS proxy, not an HTTP proxy.\n"
-"Please configure your client accordingly.\n"
-"</p>\n"
-"<p>\n"
-"See <a href=\"https://www.torproject.org/documentation.html\">"
- "https://www.torproject.org/documentation.html</a> for more "
- "information.\n"
-"<!-- Plus this comment, to make the body response more than 512 bytes, so "
-" IE will be willing to display it. Comment comment comment comment "
-" comment comment comment comment comment comment comment comment.-->\n"
-"</p>\n"
-"</body>\n"
-"</html>\n"
- , MAX_SOCKS_REPLY_LEN);
- req->replylen = strlen((char*)req->reply)+1;
- /* fall through */
- default: /* version is not socks4 or socks5 */
- log_warn(LD_APP,
- "Socks version %d not recognized. (Tor is not an http proxy.)",
- *(data));
- {
- /* Tell the controller the first 8 bytes. */
- char *tmp = tor_strndup(data, datalen < 8 ? datalen : 8);
- control_event_client_status(LOG_WARN,
- "SOCKS_UNKNOWN_PROTOCOL DATA=\"%s\"",
- escaped(tmp));
- tor_free(tmp);
- }
- return -1;
- }
-}
-
-/** Inspect a reply from SOCKS server stored in <b>buf</b> according
- * to <b>state</b>, removing the protocol data upon success. Return 0 on
- * incomplete response, 1 on success and -1 on error, in which case
- * <b>reason</b> is set to a descriptive message (free() when finished
- * with it).
- *
- * As a special case, 2 is returned when user/pass is required
- * during SOCKS5 handshake and user/pass is configured.
- */
-int
-fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
-{
- ssize_t drain = 0;
- int r;
- if (buf->datalen < 2)
- return 0;
-
- buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN);
- tor_assert(buf->head && buf->head->datalen >= 2);
-
- r = parse_socks_client((uint8_t*)buf->head->data, buf->head->datalen,
- state, reason, &drain);
- if (drain > 0)
- buf_remove_from_front(buf, drain);
- else if (drain < 0)
- buf_clear(buf);
-
- return r;
-}
-
-/** Implementation logic for fetch_from_*_socks_client. */
-static int
-parse_socks_client(const uint8_t *data, size_t datalen,
- int state, char **reason,
- ssize_t *drain_out)
-{
- unsigned int addrlen;
- *drain_out = 0;
- if (datalen < 2)
- return 0;
-
- switch (state) {
- case PROXY_SOCKS4_WANT_CONNECT_OK:
- /* Wait for the complete response */
- if (datalen < 8)
- return 0;
-
- if (data[1] != 0x5a) {
- *reason = tor_strdup(socks4_response_code_to_string(data[1]));
- return -1;
- }
-
- /* Success */
- *drain_out = 8;
- return 1;
-
- case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
- /* we don't have any credentials */
- if (data[1] != 0x00) {
- *reason = tor_strdup("server doesn't support any of our "
- "available authentication methods");
- return -1;
- }
-
- log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
- *drain_out = -1;
- return 1;
-
- case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
- /* we have a username and password. return 1 if we can proceed without
- * providing authentication, or 2 otherwise. */
- switch (data[1]) {
- case 0x00:
- log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
- "doesn't require authentication.");
- *drain_out = -1;
- return 1;
- case 0x02:
- log_info(LD_NET, "SOCKS 5 client: need authentication.");
- *drain_out = -1;
- return 2;
- /* fall through */
- }
-
- *reason = tor_strdup("server doesn't support any of our available "
- "authentication methods");
- return -1;
-
- case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
- /* handle server reply to rfc1929 authentication */
- if (data[1] != 0x00) {
- *reason = tor_strdup("authentication failed");
- return -1;
- }
-
- log_info(LD_NET, "SOCKS 5 client: authentication successful.");
- *drain_out = -1;
- return 1;
-
- case PROXY_SOCKS5_WANT_CONNECT_OK:
- /* response is variable length. BND.ADDR, etc, isn't needed
- * (don't bother with buf_pullup()), but make sure to eat all
- * the data used */
-
- /* wait for address type field to arrive */
- if (datalen < 4)
- return 0;
-
- switch (data[3]) {
- case 0x01: /* ip4 */
- addrlen = 4;
- break;
- case 0x04: /* ip6 */
- addrlen = 16;
- break;
- case 0x03: /* fqdn (can this happen here?) */
- if (datalen < 5)
- return 0;
- addrlen = 1 + data[4];
- break;
- default:
- *reason = tor_strdup("invalid response to connect request");
- return -1;
- }
-
- /* wait for address and port */
- if (datalen < 6 + addrlen)
- return 0;
-
- if (data[1] != 0x00) {
- *reason = tor_strdup(socks5_response_code_to_string(data[1]));
- return -1;
- }
-
- *drain_out = 6 + addrlen;
- return 1;
- }
-
- /* shouldn't get here... */
- tor_assert(0);
-
- return -1;
-}
-
-/** Return 1 iff buf looks more like it has an (obsolete) v0 controller
- * command on it than any valid v1 controller command. */
-int
-peek_buf_has_control0_command(buf_t *buf)
-{
- if (buf->datalen >= 4) {
- char header[4];
- uint16_t cmd;
- peek_from_buf(header, sizeof(header), buf);
- cmd = ntohs(get_uint16(header+2));
- if (cmd <= 0x14)
- return 1; /* This is definitely not a v1 control command. */
- }
- return 0;
-}
-
-/** Return the index within <b>buf</b> at which <b>ch</b> first appears,
- * or -1 if <b>ch</b> does not appear on buf. */
-static off_t
-buf_find_offset_of_char(buf_t *buf, char ch)
-{
- chunk_t *chunk;
- off_t offset = 0;
- for (chunk = buf->head; chunk; chunk = chunk->next) {
- char *cp = memchr(chunk->data, ch, chunk->datalen);
- if (cp)
- return offset + (cp - chunk->data);
- else
- offset += chunk->datalen;
- }
- return -1;
-}
-
-/** Try to read a single LF-terminated line from <b>buf</b>, and write it
- * (including the LF), NUL-terminated, into the *<b>data_len</b> byte buffer
- * at <b>data_out</b>. Set *<b>data_len</b> to the number of bytes in the
- * line, not counting the terminating NUL. Return 1 if we read a whole line,
- * return 0 if we don't have a whole line yet, and return -1 if the line
- * length exceeds *<b>data_len</b>.
- */
-int
-fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len)
-{
- size_t sz;
- off_t offset;
-
- if (!buf->head)
- return 0;
-
- offset = buf_find_offset_of_char(buf, '\n');
- if (offset < 0)
- return 0;
- sz = (size_t) offset;
- if (sz+2 > *data_len) {
- *data_len = sz + 2;
- return -1;
- }
- fetch_from_buf(data_out, sz+1, buf);
- data_out[sz+1] = '\0';
- *data_len = sz+1;
- return 1;
-}
-
-/** Compress on uncompress the <b>data_len</b> bytes in <b>data</b> using the
- * compression state <b>state</b>, appending the result to <b>buf</b>. If
- * <b>done</b> is true, flush the data in the state and finish the
- * compression/uncompression. Return -1 on failure, 0 on success. */
-int
-write_to_buf_compress(buf_t *buf, tor_compress_state_t *state,
- const char *data, size_t data_len,
- const int done)
-{
- char *next;
- size_t old_avail, avail;
- int over = 0;
-
- do {
- int need_new_chunk = 0;
- if (!buf->tail || ! CHUNK_REMAINING_CAPACITY(buf->tail)) {
- size_t cap = data_len / 4;
- buf_add_chunk_with_capacity(buf, cap, 1);
- }
- next = CHUNK_WRITE_PTR(buf->tail);
- avail = old_avail = CHUNK_REMAINING_CAPACITY(buf->tail);
- switch (tor_compress_process(state, &next, &avail,
- &data, &data_len, done)) {
- case TOR_COMPRESS_DONE:
- over = 1;
- break;
- case TOR_COMPRESS_ERROR:
- return -1;
- case TOR_COMPRESS_OK:
- if (data_len == 0) {
- tor_assert_nonfatal(!done);
- over = 1;
- }
- break;
- case TOR_COMPRESS_BUFFER_FULL:
- if (avail) {
- /* The compression module says we need more room
- * (TOR_COMPRESS_BUFFER_FULL). Start a new chunk automatically,
- * whether were going to or not. */
- need_new_chunk = 1;
- }
- if (data_len == 0 && !done) {
- /* We've consumed all the input data, though, so there's no
- * point in forging ahead right now. */
- over = 1;
- }
- break;
- }
- buf->datalen += old_avail - avail;
- buf->tail->datalen += old_avail - avail;
- if (need_new_chunk) {
- buf_add_chunk_with_capacity(buf, data_len/4, 1);
- }
-
- } while (!over);
- check();
- return 0;
-}
-
-/** Set *<b>output</b> to contain a copy of the data in *<b>input</b> */
-int
-buf_set_to_copy(buf_t **output,
- const buf_t *input)
-{
- if (*output)
- buf_free(*output);
- *output = buf_copy(input);
- return 0;
-}
-
-/** Log an error and exit if <b>buf</b> is corrupted.
- */
-void
-assert_buf_ok(buf_t *buf)
-{
- tor_assert(buf);
- tor_assert(buf->magic == BUFFER_MAGIC);
-
- if (! buf->head) {
- tor_assert(!buf->tail);
- tor_assert(buf->datalen == 0);
- } else {
- chunk_t *ch;
- size_t total = 0;
- tor_assert(buf->tail);
- for (ch = buf->head; ch; ch = ch->next) {
- total += ch->datalen;
- tor_assert(ch->datalen <= ch->memlen);
- tor_assert(ch->data >= &ch->mem[0]);
- tor_assert(ch->data <= &ch->mem[0]+ch->memlen);
- if (ch->data == &ch->mem[0]+ch->memlen) {
- static int warned = 0;
- if (! warned) {
- log_warn(LD_BUG, "Invariant violation in buf.c related to #15083");
- warned = 1;
- }
- }
- tor_assert(ch->data+ch->datalen <= &ch->mem[0] + ch->memlen);
- if (!ch->next)
- tor_assert(ch == buf->tail);
- }
- tor_assert(buf->datalen == total);
- }
-}
-
diff --git a/src/or/channel.c b/src/or/channel.c
index 9f652b5845..3c0025aff6 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -701,7 +701,7 @@ channel_remove_from_digest_map(channel_t *chan)
return;
}
-#endif
+#endif /* 0 */
/* Pull it out of its list, wherever that list is */
TOR_LIST_REMOVE(chan, next_with_same_id);
@@ -951,6 +951,9 @@ channel_init(channel_t *chan)
/* Scheduler state is idle */
chan->scheduler_state = SCHED_CHAN_IDLE;
+
+ /* Channel is not in the scheduler heap. */
+ chan->sched_heap_idx = -1;
}
/**
@@ -1832,7 +1835,7 @@ cell_queue_entry_is_padding(cell_queue_entry_t *q)
return 0;
}
-#endif
+#endif /* 0 */
/**
* Allocate a new cell queue entry for a fixed-size cell
@@ -2088,8 +2091,8 @@ channel_write_var_cell(channel_t *chan, var_cell_t *var_cell)
* are appropriate to the state transition in question.
*/
-void
-channel_change_state(channel_t *chan, channel_state_t to_state)
+static void
+channel_change_state_(channel_t *chan, channel_state_t to_state)
{
channel_state_t from_state;
unsigned char was_active, is_active;
@@ -2208,18 +2211,8 @@ channel_change_state(channel_t *chan, channel_state_t to_state)
estimated_total_queue_size += chan->bytes_in_queue;
}
- /* Tell circuits if we opened and stuff */
- if (to_state == CHANNEL_STATE_OPEN) {
- channel_do_open_actions(chan);
- chan->has_been_open = 1;
-
- /* Check for queued cells to process */
- if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
- channel_process_cells(chan);
- if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue))
- channel_flush_cells(chan);
- } else if (to_state == CHANNEL_STATE_CLOSED ||
- to_state == CHANNEL_STATE_ERROR) {
+ if (to_state == CHANNEL_STATE_CLOSED ||
+ to_state == CHANNEL_STATE_ERROR) {
/* Assert that all queues are empty */
tor_assert(TOR_SIMPLEQ_EMPTY(&chan->incoming_queue));
tor_assert(TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue));
@@ -2227,6 +2220,35 @@ channel_change_state(channel_t *chan, channel_state_t to_state)
}
/**
+ * As channel_change_state_, but change the state to any state but open.
+ */
+void
+channel_change_state(channel_t *chan, channel_state_t to_state)
+{
+ tor_assert(to_state != CHANNEL_STATE_OPEN);
+ channel_change_state_(chan, to_state);
+}
+
+/**
+ * As channel_change_state, but change the state to open.
+ */
+void
+channel_change_state_open(channel_t *chan)
+{
+ channel_change_state_(chan, CHANNEL_STATE_OPEN);
+
+ /* Tell circuits if we opened and stuff */
+ channel_do_open_actions(chan);
+ chan->has_been_open = 1;
+
+ /* Check for queued cells to process */
+ if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
+ channel_process_cells(chan);
+ if (! TOR_SIMPLEQ_EMPTY(&chan->outgoing_queue))
+ channel_flush_cells(chan);
+}
+
+/**
* Change channel listener state
*
* This internal and subclass use only function is used to change channel
@@ -2584,8 +2606,8 @@ channel_flush_cells(channel_t *chan)
* available.
*/
-int
-channel_more_to_flush(channel_t *chan)
+MOCK_IMPL(int,
+channel_more_to_flush, (channel_t *chan))
{
tor_assert(chan);
@@ -4076,7 +4098,7 @@ channel_mark_bad_for_new_circs(channel_t *chan)
*/
int
-channel_is_client(channel_t *chan)
+channel_is_client(const channel_t *chan)
{
tor_assert(chan);
@@ -4098,6 +4120,20 @@ channel_mark_client(channel_t *chan)
}
/**
+ * Clear the client flag
+ *
+ * Mark a channel as being _not_ from a client
+ */
+
+void
+channel_clear_client(channel_t *chan)
+{
+ tor_assert(chan);
+
+ chan->is_client = 0;
+}
+
+/**
* Get the canonical flag for a channel
*
* This returns the is_canonical for a channel; this flag is determined by
@@ -4827,8 +4863,6 @@ channel_update_xmit_queue_size(channel_t *chan)
U64_FORMAT ", new size is " U64_FORMAT,
U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier),
U64_PRINTF_ARG(estimated_total_queue_size));
- /* Tell the scheduler we're increasing the queue size */
- scheduler_adjust_queue_size(chan, 1, adj);
}
} else if (queued < chan->bytes_queued_for_xmit) {
adj = chan->bytes_queued_for_xmit - queued;
@@ -4851,8 +4885,6 @@ channel_update_xmit_queue_size(channel_t *chan)
U64_FORMAT ", new size is " U64_FORMAT,
U64_PRINTF_ARG(adj), U64_PRINTF_ARG(chan->global_identifier),
U64_PRINTF_ARG(estimated_total_queue_size));
- /* Tell the scheduler we're decreasing the queue size */
- scheduler_adjust_queue_size(chan, -1, adj);
}
}
}
diff --git a/src/or/channel.h b/src/or/channel.h
index 264743691f..074cc86fe7 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -487,7 +487,7 @@ STATIC void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off);
void channel_write_cell_generic_(channel_t *chan, const char *cell_type,
void *cell, cell_queue_entry_t *q);
-#endif
+#endif /* defined(CHANNEL_PRIVATE_) */
/* Channel operations for subclasses and internal use only */
@@ -522,6 +522,7 @@ void channel_listener_free(channel_listener_t *chan_l);
/* State/metadata setters */
void channel_change_state(channel_t *chan, channel_state_t to_state);
+void channel_change_state_open(channel_t *chan);
void channel_clear_identity_digest(channel_t *chan);
void channel_clear_remote_end(channel_t *chan);
void channel_mark_local(channel_t *chan);
@@ -567,7 +568,7 @@ MOCK_DECL(ssize_t, channel_flush_some_cells,
(channel_t *chan, ssize_t num_cells));
/* Query if data available on this channel */
-int channel_more_to_flush(channel_t *chan);
+MOCK_DECL(int, channel_more_to_flush, (channel_t *chan));
/* Notify flushed outgoing for dirreq handling */
void channel_notify_flushed(channel_t *chan);
@@ -579,7 +580,7 @@ void channel_do_open_actions(channel_t *chan);
extern uint64_t estimated_total_queue_size;
#endif
-#endif
+#endif /* defined(TOR_CHANNEL_INTERNAL_) */
/* Helper functions to perform operations on channels */
@@ -666,11 +667,12 @@ int channel_is_bad_for_new_circs(channel_t *chan);
void channel_mark_bad_for_new_circs(channel_t *chan);
int channel_is_canonical(channel_t *chan);
int channel_is_canonical_is_reliable(channel_t *chan);
-int channel_is_client(channel_t *chan);
+int channel_is_client(const channel_t *chan);
int channel_is_local(channel_t *chan);
int channel_is_incoming(channel_t *chan);
int channel_is_outgoing(channel_t *chan);
void channel_mark_client(channel_t *chan);
+void channel_clear_client(channel_t *chan);
int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info);
int channel_matches_target_addr_for_extend(channel_t *chan,
const tor_addr_t *target);
@@ -719,5 +721,5 @@ int packed_cell_is_destroy(channel_t *chan,
/* Declare the handle helpers */
HANDLE_DECL(channel, channel_s,)
-#endif
+#endif /* !defined(TOR_CHANNEL_H) */
diff --git a/src/or/channelpadding.c b/src/or/channelpadding.c
index ccaf5b4ec8..435436c45c 100644
--- a/src/or/channelpadding.c
+++ b/src/or/channelpadding.c
@@ -71,7 +71,7 @@ static int consensus_nf_pad_single_onion;
* its a client, use that. Then finally verify in the consensus).
*/
#define CHANNEL_IS_CLIENT(chan, options) \
- (!public_server_mode((options)) || (chan)->is_client || \
+ (!public_server_mode((options)) || channel_is_client(chan) || \
!connection_or_digest_is_known_relay((chan)->identity_digest))
/**
diff --git a/src/or/channelpadding.h b/src/or/channelpadding.h
index a227e27d5b..58bf741d5c 100644
--- a/src/or/channelpadding.h
+++ b/src/or/channelpadding.h
@@ -41,5 +41,5 @@ int channelpadding_get_circuits_available_timeout(void);
unsigned int channelpadding_get_channel_idle_timeout(const channel_t *, int);
void channelpadding_new_consensus_params(networkstatus_t *ns);
-#endif
+#endif /* !defined(TOR_CHANNELPADDING_H) */
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 8fb91d412f..0244871548 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -847,7 +847,7 @@ channel_tls_write_packed_cell_method(channel_t *chan,
tor_assert(packed_cell);
if (tlschan->conn) {
- connection_write_to_buf(packed_cell->body, cell_network_size,
+ connection_buf_add(packed_cell->body, cell_network_size,
TO_CONN(tlschan->conn));
/* This is where the cell is finished; used to be done from relay.c */
@@ -993,7 +993,7 @@ channel_tls_handle_state_change_on_orconn(channel_tls_t *chan,
* We can go to CHANNEL_STATE_OPEN from CHANNEL_STATE_OPENING or
* CHANNEL_STATE_MAINT on this.
*/
- channel_change_state(base_chan, CHANNEL_STATE_OPEN);
+ channel_change_state_open(base_chan);
/* We might have just become writeable; check and tell the scheduler */
if (connection_or_num_cells_writeable(conn) > 0) {
scheduler_channel_wants_writes(base_chan);
@@ -1044,7 +1044,7 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time,
*time += time_passed;
}
-#endif
+#endif /* defined(KEEP_TIMING_STATS) */
/**
* Handle an incoming cell on a channel_tls_t
@@ -1072,9 +1072,9 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn)
channel_tls_time_process_cell(cl, cn, & tp ## time , \
channel_tls_process_ ## tp ## _cell); \
} STMT_END
-#else
+#else /* !(defined(KEEP_TIMING_STATS)) */
#define PROCESS_CELL(tp, cl, cn) channel_tls_process_ ## tp ## _cell(cl, cn)
-#endif
+#endif /* defined(KEEP_TIMING_STATS) */
tor_assert(cell);
tor_assert(conn);
@@ -1204,7 +1204,7 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn)
/* remember which second it is, for next time */
current_second = now;
}
-#endif
+#endif /* defined(KEEP_TIMING_STATS) */
tor_assert(var_cell);
tor_assert(conn);
@@ -1579,7 +1579,7 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan)
connection_or_close_normally(chan->conn, 1);
return;
}
-#endif
+#endif /* defined(DISABLE_V3_LINKPROTO_SERVERSIDE) */
if (send_versions) {
if (connection_or_send_versions(chan->conn, 1) < 0) {
@@ -1680,6 +1680,8 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
long apparent_skew = 0;
tor_addr_t my_apparent_addr = TOR_ADDR_NULL;
+ int started_here = 0;
+ const char *identity_digest = NULL;
tor_assert(cell);
tor_assert(chan);
@@ -1699,10 +1701,12 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
}
tor_assert(chan->conn->handshake_state &&
chan->conn->handshake_state->received_versions);
+ started_here = connection_or_nonopen_was_started_here(chan->conn);
+ identity_digest = chan->conn->identity_digest;
if (chan->conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3) {
tor_assert(chan->conn->link_proto >= 3);
- if (chan->conn->handshake_state->started_here) {
+ if (started_here) {
if (!(chan->conn->handshake_state->authenticated)) {
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Got a NETINFO cell from server, "
@@ -1789,12 +1793,11 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
return;
}
/* A relay can connect from anywhere and be canonical, so
- * long as it tells you from where it came. This may be a bit
- * concerning.. Luckily we have another check in
- * channel_tls_matches_target_method() to ensure that extends
- * only go to the IP they ask for.
- *
- * XXX: Bleh. That check is not used if the connection is canonical.
+ * long as it tells you from where it came. This may sound a bit
+ * concerning... but that's what "canonical" means: that the
+ * address is one that the relay itself has claimed. The relay
+ * might be doing something funny, but nobody else is doing a MITM
+ * on the relay's TCP.
*/
if (tor_addr_eq(&addr, &(chan->conn->real_addr))) {
connection_or_set_canonical(chan->conn, 1);
@@ -1813,7 +1816,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
"they will not consider this connection canonical. They "
"think we are at %s, but we think its %s.",
safe_str(descr),
- safe_str(hex_str(chan->conn->identity_digest, DIGEST_LEN)),
+ safe_str(hex_str(identity_digest, DIGEST_LEN)),
safe_str(tor_addr_is_null(&my_apparent_addr) ?
"<none>" : fmt_and_decorate_addr(&my_apparent_addr)),
safe_str(fmt_addr32(me->addr)));
@@ -1823,7 +1826,8 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
/** Warn when we get a netinfo skew with at least this value. */
#define NETINFO_NOTICE_SKEW 3600
if (labs(apparent_skew) > NETINFO_NOTICE_SKEW &&
- connection_or_digest_is_known_relay(chan->conn->identity_digest)) {
+ (started_here ||
+ connection_or_digest_is_known_relay(chan->conn->identity_digest))) {
int trusted = router_digest_is_trusted_dir(chan->conn->identity_digest);
clock_skew_warning(TO_CONN(chan->conn), apparent_skew, trusted, LD_GENERAL,
"NETINFO cell", "OR");
@@ -1857,8 +1861,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
safe_str_client(chan->conn->base_.address),
chan->conn->base_.port,
(int)(chan->conn->link_proto),
- hex_str(TLS_CHAN_TO_BASE(chan)->identity_digest,
- DIGEST_LEN),
+ hex_str(identity_digest, DIGEST_LEN),
tor_addr_is_null(&my_apparent_addr) ?
"<none>" : fmt_and_decorate_addr(&my_apparent_addr));
}
@@ -1915,7 +1918,6 @@ certs_cell_typenum_to_cert_type(int typenum)
* of the connection, we then authenticate the server or mark the connection.
* If it's the server side, wait for an AUTHENTICATE cell.
*/
-
STATIC void
channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
{
@@ -1930,7 +1932,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
int n_certs, i;
certs_cell_t *cc = NULL;
- int send_netinfo = 0;
+ int send_netinfo = 0, started_here = 0;
memset(x509_certs, 0, sizeof(x509_certs));
memset(ed_certs, 0, sizeof(ed_certs));
@@ -1948,6 +1950,11 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
goto err; \
} while (0)
+ /* Can't use connection_or_nonopen_was_started_here(); its conn->tls
+ * check looks like it breaks
+ * test_link_handshake_recv_certs_ok_server(). */
+ started_here = chan->conn->handshake_state->started_here;
+
if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
ERR("We're not doing a v3 handshake!");
if (chan->conn->link_proto < 3)
@@ -2061,7 +2068,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
/* Note that this warns more loudly about time and validity if we were
* _trying_ to connect to an authority, not necessarily if we _did_ connect
* to one. */
- if (chan->conn->handshake_state->started_here &&
+ if (started_here &&
router_digest_is_trusted_dir(TLS_CHAN_TO_BASE(chan)->identity_digest))
severity = LOG_WARN;
else
@@ -2079,7 +2086,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
if (!checked_rsa_id)
ERR("Invalid certificate chain!");
- if (chan->conn->handshake_state->started_here) {
+ if (started_here) {
/* No more information is needed. */
chan->conn->handshake_state->authenticated = 1;
diff --git a/src/or/channeltls.h b/src/or/channeltls.h
index 1f9a39d8a4..d9c4239c3a 100644
--- a/src/or/channeltls.h
+++ b/src/or/channeltls.h
@@ -26,7 +26,7 @@ struct channel_tls_s {
or_connection_t *conn;
};
-#endif /* TOR_CHANNEL_INTERNAL_ */
+#endif /* defined(TOR_CHANNEL_INTERNAL_) */
channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port,
const char *id_digest,
@@ -69,7 +69,7 @@ STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell,
STATIC void channel_tls_common_init(channel_tls_t *tlschan);
STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell,
channel_tls_t *tlschan);
-#endif
+#endif /* defined(CHANNELTLS_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CHANNELTLS_H) */
diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c
index 4c0bd9e455..f4bd5ea2fa 100644
--- a/src/or/circpathbias.c
+++ b/src/or/circpathbias.c
@@ -292,7 +292,7 @@ pathbias_is_new_circ_attempt(origin_circuit_t *circ)
return circ->cpath &&
circ->cpath->next != circ->cpath &&
circ->cpath->next->state == CPATH_STATE_AWAITING_KEYS;
-#else
+#else /* !(defined(N2N_TAGGING_IS_POSSIBLE)) */
/* If tagging attacks are no longer possible, we probably want to
* count bias from the first hop. However, one could argue that
* timing-based tagging is still more useful than per-hop failure.
@@ -300,7 +300,7 @@ pathbias_is_new_circ_attempt(origin_circuit_t *circ)
*/
return circ->cpath &&
circ->cpath->state == CPATH_STATE_AWAITING_KEYS;
-#endif
+#endif /* defined(N2N_TAGGING_IS_POSSIBLE) */
}
/**
diff --git a/src/or/circpathbias.h b/src/or/circpathbias.h
index 2a4c316807..c9e572d2ae 100644
--- a/src/or/circpathbias.h
+++ b/src/or/circpathbias.h
@@ -25,5 +25,5 @@ void pathbias_mark_use_success(origin_circuit_t *circ);
void pathbias_mark_use_rollback(origin_circuit_t *circ);
const char *pathbias_state_to_string(path_state_t state);
-#endif
+#endif /* !defined(TOR_CIRCPATHBIAS_H) */
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 41ae51a3f2..8c52c58e6d 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -46,6 +46,7 @@
#include "crypto.h"
#include "directory.h"
#include "entrynodes.h"
+#include "hs_ntor.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -70,10 +71,15 @@ static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
static int circuit_deliver_create_cell(circuit_t *circ,
const create_cell_t *create_cell,
int relayed);
-static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit);
+static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit,
+ int is_hs_v3_rp_circuit);
static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
+static int circuit_send_first_onion_skin(origin_circuit_t *circ);
+static int circuit_build_no_more_hops(origin_circuit_t *circ);
+static int circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
+ crypt_path_t *hop);
/** This function tries to get a channel to the specified endpoint,
* and then calls command_setup_channel() to give it the right
@@ -284,14 +290,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names)
base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
}
} else { /* ! verbose_names */
- node = node_get_by_id(id);
- if (node && node_is_named(node)) {
- elt = tor_strdup(node_get_nickname(node));
- } else {
- elt = tor_malloc(HEX_DIGEST_LEN+2);
- elt[0] = '$';
- base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
- }
+ elt = tor_malloc(HEX_DIGEST_LEN+2);
+ elt[0] = '$';
+ base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN);
}
tor_assert(elt);
if (verbose) {
@@ -500,10 +501,15 @@ circuit_establish_circuit(uint8_t purpose, extend_info_t *exit_ei, int flags)
{
origin_circuit_t *circ;
int err_reason = 0;
+ int is_hs_v3_rp_circuit = 0;
+
+ if (flags & CIRCLAUNCH_IS_V3_RP) {
+ is_hs_v3_rp_circuit = 1;
+ }
circ = origin_circuit_init(purpose, flags);
- if (onion_pick_cpath_exit(circ, exit_ei) < 0 ||
+ if (onion_pick_cpath_exit(circ, exit_ei, is_hs_v3_rp_circuit) < 0 ||
onion_populate_cpath(circ) < 0) {
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOPATH);
return NULL;
@@ -912,234 +918,275 @@ circuit_purpose_may_omit_guard(int purpose)
* If circ's first hop is closed, then we need to build a create
* cell and send it forward.
*
- * Otherwise, we need to build a relay extend cell and send it
- * forward.
+ * Otherwise, if circ's cpath still has any non-open hops, we need to
+ * build a relay extend cell and send it forward to the next non-open hop.
+ *
+ * If all hops on the cpath are open, we're done building the circuit
+ * and we should do housekeeping for the newly opened circuit.
*
* Return -reason if we want to tear down circ, else return 0.
*/
int
circuit_send_next_onion_skin(origin_circuit_t *circ)
{
- crypt_path_t *hop;
- const node_t *node;
-
tor_assert(circ);
if (circ->cpath->state == CPATH_STATE_CLOSED) {
- /* This is the first hop. */
- create_cell_t cc;
- int fast;
- int len;
- log_debug(LD_CIRC,"First skin; sending create cell.");
- memset(&cc, 0, sizeof(cc));
- if (circ->build_state->onehop_tunnel)
- control_event_bootstrap(BOOTSTRAP_STATUS_ONEHOP_CREATE, 0);
- else {
- control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
-
- /* If this is not a one-hop tunnel, the channel is being used
- * for traffic that wants anonymity and protection from traffic
- * analysis (such as netflow record retention). That means we want
- * to pad it.
- */
- if (circ->base_.n_chan->channel_usage < CHANNEL_USED_FOR_FULL_CIRCS)
- circ->base_.n_chan->channel_usage = CHANNEL_USED_FOR_FULL_CIRCS;
- }
+ /* Case one: we're on the first hop. */
+ return circuit_send_first_onion_skin(circ);
+ }
- node = node_get_by_id(circ->base_.n_chan->identity_digest);
- fast = should_use_create_fast_for_circuit(circ);
- if (!fast) {
- /* We are an OR and we know the right onion key: we should
- * send a create cell.
- */
- circuit_pick_create_handshake(&cc.cell_type, &cc.handshake_type,
- circ->cpath->extend_info);
- } else {
- /* We are not an OR, and we're building the first hop of a circuit to a
- * new OR: we can be speedy and use CREATE_FAST to save an RSA operation
- * and a DH operation. */
- cc.cell_type = CELL_CREATE_FAST;
- cc.handshake_type = ONION_HANDSHAKE_TYPE_FAST;
- }
+ tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
+ tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING);
+ crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath);
- len = onion_skin_create(cc.handshake_type,
- circ->cpath->extend_info,
- &circ->cpath->handshake_state,
- cc.onionskin);
- if (len < 0) {
- log_warn(LD_CIRC,"onion_skin_create (first hop) failed.");
- return - END_CIRC_REASON_INTERNAL;
- }
- cc.handshake_len = len;
+ if (hop) {
+ /* Case two: we're on a hop after the first. */
+ return circuit_send_intermediate_onion_skin(circ, hop);
+ }
- if (circuit_deliver_create_cell(TO_CIRCUIT(circ), &cc, 0) < 0)
- return - END_CIRC_REASON_RESOURCELIMIT;
+ /* Case three: the circuit is finished. Do housekeeping tasks on it. */
+ return circuit_build_no_more_hops(circ);
+}
- circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
- circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
- log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'",
- fast ? "CREATE_FAST" : "CREATE",
- node ? node_describe(node) : "<unnamed>");
+/**
+ * Called from circuit_send_next_onion_skin() when we find ourselves connected
+ * to the first hop in <b>circ</b>: Send a CREATE or CREATE2 or CREATE_FAST
+ * cell to that hop. Return 0 on success; -reason on failure (if the circuit
+ * should be torn down).
+ */
+static int
+circuit_send_first_onion_skin(origin_circuit_t *circ)
+{
+ int fast;
+ int len;
+ const node_t *node;
+ create_cell_t cc;
+ memset(&cc, 0, sizeof(cc));
+
+ log_debug(LD_CIRC,"First skin; sending create cell.");
+
+ if (circ->build_state->onehop_tunnel) {
+ control_event_bootstrap(BOOTSTRAP_STATUS_ONEHOP_CREATE, 0);
} else {
- extend_cell_t ec;
- int len;
- tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
- tor_assert(circ->base_.state == CIRCUIT_STATE_BUILDING);
- log_debug(LD_CIRC,"starting to send subsequent skin.");
- hop = onion_next_hop_in_cpath(circ->cpath);
- memset(&ec, 0, sizeof(ec));
- if (!hop) {
- /* done building the circuit. whew. */
- guard_usable_t r;
- if (! circ->guard_state) {
- if (circuit_get_cpath_len(circ) != 1 &&
- ! circuit_purpose_may_omit_guard(circ->base_.purpose) &&
- get_options()->UseEntryGuards) {
- log_warn(LD_BUG, "%d-hop circuit %p with purpose %d has no "
- "guard state",
- circuit_get_cpath_len(circ), circ, circ->base_.purpose);
- }
- r = GUARD_USABLE_NOW;
- } else {
- r = entry_guard_succeeded(&circ->guard_state);
- }
- const int is_usable_for_streams = (r == GUARD_USABLE_NOW);
- if (r == GUARD_USABLE_NOW) {
- circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
- } else if (r == GUARD_MAYBE_USABLE_LATER) {
- // Wait till either a better guard succeeds, or till
- // all better guards fail.
- circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_GUARD_WAIT);
- } else {
- tor_assert_nonfatal(r == GUARD_USABLE_NEVER);
- return - END_CIRC_REASON_INTERNAL;
- }
+ control_event_bootstrap(BOOTSTRAP_STATUS_CIRCUIT_CREATE, 0);
- /* XXXX #21422 -- the rest of this branch needs careful thought!
- * Some of the things here need to happen when a circuit becomes
- * mechanically open; some need to happen when it is actually usable.
- * I think I got them right, but more checking would be wise. -NM
- */
+ /* If this is not a one-hop tunnel, the channel is being used
+ * for traffic that wants anonymity and protection from traffic
+ * analysis (such as netflow record retention). That means we want
+ * to pad it.
+ */
+ if (circ->base_.n_chan->channel_usage < CHANNEL_USED_FOR_FULL_CIRCS)
+ circ->base_.n_chan->channel_usage = CHANNEL_USED_FOR_FULL_CIRCS;
+ }
- if (circuit_timeout_want_to_count_circ(circ)) {
- struct timeval end;
- long timediff;
- tor_gettimeofday(&end);
- timediff = tv_mdiff(&circ->base_.timestamp_began, &end);
+ node = node_get_by_id(circ->base_.n_chan->identity_digest);
+ fast = should_use_create_fast_for_circuit(circ);
+ if (!fast) {
+ /* We know the right onion key: we should send a create cell. */
+ circuit_pick_create_handshake(&cc.cell_type, &cc.handshake_type,
+ circ->cpath->extend_info);
+ } else {
+ /* We don't know an onion key, so we need to fall back to CREATE_FAST. */
+ cc.cell_type = CELL_CREATE_FAST;
+ cc.handshake_type = ONION_HANDSHAKE_TYPE_FAST;
+ }
- /*
- * If the circuit build time is much greater than we would have cut
- * it off at, we probably had a suspend event along this codepath,
- * and we should discard the value.
- */
- if (timediff < 0 ||
- timediff > 2*get_circuit_build_close_time_ms()+1000) {
- log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. "
- "Assuming clock jump. Purpose %d (%s)", timediff,
- circ->base_.purpose,
- circuit_purpose_to_string(circ->base_.purpose));
- } else if (!circuit_build_times_disabled(get_options())) {
- /* Only count circuit times if the network is live */
- if (circuit_build_times_network_check_live(
- get_circuit_build_times())) {
- circuit_build_times_add_time(get_circuit_build_times_mutable(),
- (build_time_t)timediff);
- circuit_build_times_set_timeout(get_circuit_build_times_mutable());
- }
-
- if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- circuit_build_times_network_circ_success(
- get_circuit_build_times_mutable());
- }
- }
- }
- log_info(LD_CIRC,"circuit built!");
- circuit_reset_failure_count(0);
+ len = onion_skin_create(cc.handshake_type,
+ circ->cpath->extend_info,
+ &circ->cpath->handshake_state,
+ cc.onionskin);
+ if (len < 0) {
+ log_warn(LD_CIRC,"onion_skin_create (first hop) failed.");
+ return - END_CIRC_REASON_INTERNAL;
+ }
+ cc.handshake_len = len;
- if (circ->build_state->onehop_tunnel || circ->has_opened) {
- control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_STATUS, 0);
- }
+ if (circuit_deliver_create_cell(TO_CIRCUIT(circ), &cc, 0) < 0)
+ return - END_CIRC_REASON_RESOURCELIMIT;
- pathbias_count_build_success(circ);
- circuit_rep_hist_note_result(circ);
- if (is_usable_for_streams)
- circuit_has_opened(circ); /* do other actions as necessary */
-
- if (!have_completed_a_circuit() && !circ->build_state->onehop_tunnel) {
- const or_options_t *options = get_options();
- note_that_we_completed_a_circuit();
- /* FFFF Log a count of known routers here */
- log_notice(LD_GENERAL,
- "Tor has successfully opened a circuit. "
- "Looks like client functionality is working.");
- if (control_event_bootstrap(BOOTSTRAP_STATUS_DONE, 0) == 0) {
- log_notice(LD_GENERAL,
- "Tor has successfully opened a circuit. "
- "Looks like client functionality is working.");
- }
- control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED");
- clear_broken_connection_map(1);
- if (server_mode(options) && !check_whether_orport_reachable(options)) {
- inform_testing_reachability();
- consider_testing_reachability(1, 1);
- }
+ circ->cpath->state = CPATH_STATE_AWAITING_KEYS;
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
+ log_info(LD_CIRC,"First hop: finished sending %s cell to '%s'",
+ fast ? "CREATE_FAST" : "CREATE",
+ node ? node_describe(node) : "<unnamed>");
+ return 0;
+}
+
+/**
+ * Called from circuit_send_next_onion_skin() when we find that we have no
+ * more hops: mark the circuit as finished, and perform the necessary
+ * bookkeeping. Return 0 on success; -reason on failure (if the circuit
+ * should be torn down).
+ */
+static int
+circuit_build_no_more_hops(origin_circuit_t *circ)
+{
+ guard_usable_t r;
+ if (! circ->guard_state) {
+ if (circuit_get_cpath_len(circ) != 1 &&
+ ! circuit_purpose_may_omit_guard(circ->base_.purpose) &&
+ get_options()->UseEntryGuards) {
+ log_warn(LD_BUG, "%d-hop circuit %p with purpose %d has no "
+ "guard state",
+ circuit_get_cpath_len(circ), circ, circ->base_.purpose);
+ }
+ r = GUARD_USABLE_NOW;
+ } else {
+ r = entry_guard_succeeded(&circ->guard_state);
+ }
+ const int is_usable_for_streams = (r == GUARD_USABLE_NOW);
+ if (r == GUARD_USABLE_NOW) {
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_OPEN);
+ } else if (r == GUARD_MAYBE_USABLE_LATER) {
+ // Wait till either a better guard succeeds, or till
+ // all better guards fail.
+ circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_GUARD_WAIT);
+ } else {
+ tor_assert_nonfatal(r == GUARD_USABLE_NEVER);
+ return - END_CIRC_REASON_INTERNAL;
+ }
+
+ /* XXXX #21422 -- the rest of this branch needs careful thought!
+ * Some of the things here need to happen when a circuit becomes
+ * mechanically open; some need to happen when it is actually usable.
+ * I think I got them right, but more checking would be wise. -NM
+ */
+
+ if (circuit_timeout_want_to_count_circ(circ)) {
+ struct timeval end;
+ long timediff;
+ tor_gettimeofday(&end);
+ timediff = tv_mdiff(&circ->base_.timestamp_began, &end);
+
+ /*
+ * If the circuit build time is much greater than we would have cut
+ * it off at, we probably had a suspend event along this codepath,
+ * and we should discard the value.
+ */
+ if (timediff < 0 ||
+ timediff > 2*get_circuit_build_close_time_ms()+1000) {
+ log_notice(LD_CIRC, "Strange value for circuit build time: %ldmsec. "
+ "Assuming clock jump. Purpose %d (%s)", timediff,
+ circ->base_.purpose,
+ circuit_purpose_to_string(circ->base_.purpose));
+ } else if (!circuit_build_times_disabled(get_options())) {
+ /* Only count circuit times if the network is live */
+ if (circuit_build_times_network_check_live(
+ get_circuit_build_times())) {
+ circuit_build_times_add_time(get_circuit_build_times_mutable(),
+ (build_time_t)timediff);
+ circuit_build_times_set_timeout(get_circuit_build_times_mutable());
}
- /* We're done with measurement circuits here. Just close them */
- if (circ->base_.purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
+ if (circ->base_.purpose != CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ circuit_build_times_network_circ_success(
+ get_circuit_build_times_mutable());
}
- return 0;
}
-
- if (tor_addr_family(&hop->extend_info->addr) != AF_INET) {
- log_warn(LD_BUG, "Trying to extend to a non-IPv4 address.");
- return - END_CIRC_REASON_INTERNAL;
+ }
+ log_info(LD_CIRC,"circuit built!");
+ circuit_reset_failure_count(0);
+
+ if (circ->build_state->onehop_tunnel || circ->has_opened) {
+ control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_STATUS, 0);
+ }
+
+ pathbias_count_build_success(circ);
+ circuit_rep_hist_note_result(circ);
+ if (is_usable_for_streams)
+ circuit_has_opened(circ); /* do other actions as necessary */
+
+ if (!have_completed_a_circuit() && !circ->build_state->onehop_tunnel) {
+ const or_options_t *options = get_options();
+ note_that_we_completed_a_circuit();
+ /* FFFF Log a count of known routers here */
+ log_notice(LD_GENERAL,
+ "Tor has successfully opened a circuit. "
+ "Looks like client functionality is working.");
+ if (control_event_bootstrap(BOOTSTRAP_STATUS_DONE, 0) == 0) {
+ log_notice(LD_GENERAL,
+ "Tor has successfully opened a circuit. "
+ "Looks like client functionality is working.");
}
-
- circuit_pick_extend_handshake(&ec.cell_type,
- &ec.create_cell.cell_type,
- &ec.create_cell.handshake_type,
- hop->extend_info);
-
- tor_addr_copy(&ec.orport_ipv4.addr, &hop->extend_info->addr);
- ec.orport_ipv4.port = hop->extend_info->port;
- tor_addr_make_unspec(&ec.orport_ipv6.addr);
- memcpy(ec.node_id, hop->extend_info->identity_digest, DIGEST_LEN);
- /* Set the ED25519 identity too -- it will only get included
- * in the extend2 cell if we're configured to use it, though. */
- ed25519_pubkey_copy(&ec.ed_pubkey, &hop->extend_info->ed_identity);
-
- len = onion_skin_create(ec.create_cell.handshake_type,
- hop->extend_info,
- &hop->handshake_state,
- ec.create_cell.onionskin);
- if (len < 0) {
- log_warn(LD_CIRC,"onion_skin_create failed.");
- return - END_CIRC_REASON_INTERNAL;
+ control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED");
+ clear_broken_connection_map(1);
+ if (server_mode(options) && !check_whether_orport_reachable(options)) {
+ inform_testing_reachability();
+ consider_testing_reachability(1, 1);
}
- ec.create_cell.handshake_len = len;
+ }
- log_info(LD_CIRC,"Sending extend relay cell.");
- {
- uint8_t command = 0;
- uint16_t payload_len=0;
- uint8_t payload[RELAY_PAYLOAD_SIZE];
- if (extend_cell_format(&command, &payload_len, payload, &ec)<0) {
- log_warn(LD_CIRC,"Couldn't format extend cell");
- return -END_CIRC_REASON_INTERNAL;
- }
+ /* We're done with measurement circuits here. Just close them */
+ if (circ->base_.purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) {
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
+ }
+ return 0;
+}
- /* send it to hop->prev, because it will transfer
- * it to a create cell and then send to hop */
- if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
- command,
- (char*)payload, payload_len,
- hop->prev) < 0)
- return 0; /* circuit is closed */
+/**
+ * Called from circuit_send_next_onion_skin() when we find that we have a hop
+ * other than the first that we need to extend to: use <b>hop</b>'s
+ * information to extend the circuit another step. Return 0 on success;
+ * -reason on failure (if the circuit should be torn down).
+ */
+static int
+circuit_send_intermediate_onion_skin(origin_circuit_t *circ,
+ crypt_path_t *hop)
+{
+ int len;
+ extend_cell_t ec;
+ memset(&ec, 0, sizeof(ec));
+
+ log_debug(LD_CIRC,"starting to send subsequent skin.");
+
+ if (tor_addr_family(&hop->extend_info->addr) != AF_INET) {
+ log_warn(LD_BUG, "Trying to extend to a non-IPv4 address.");
+ return - END_CIRC_REASON_INTERNAL;
+ }
+
+ circuit_pick_extend_handshake(&ec.cell_type,
+ &ec.create_cell.cell_type,
+ &ec.create_cell.handshake_type,
+ hop->extend_info);
+
+ tor_addr_copy(&ec.orport_ipv4.addr, &hop->extend_info->addr);
+ ec.orport_ipv4.port = hop->extend_info->port;
+ tor_addr_make_unspec(&ec.orport_ipv6.addr);
+ memcpy(ec.node_id, hop->extend_info->identity_digest, DIGEST_LEN);
+ /* Set the ED25519 identity too -- it will only get included
+ * in the extend2 cell if we're configured to use it, though. */
+ ed25519_pubkey_copy(&ec.ed_pubkey, &hop->extend_info->ed_identity);
+
+ len = onion_skin_create(ec.create_cell.handshake_type,
+ hop->extend_info,
+ &hop->handshake_state,
+ ec.create_cell.onionskin);
+ if (len < 0) {
+ log_warn(LD_CIRC,"onion_skin_create failed.");
+ return - END_CIRC_REASON_INTERNAL;
+ }
+ ec.create_cell.handshake_len = len;
+
+ log_info(LD_CIRC,"Sending extend relay cell.");
+ {
+ uint8_t command = 0;
+ uint16_t payload_len=0;
+ uint8_t payload[RELAY_PAYLOAD_SIZE];
+ if (extend_cell_format(&command, &payload_len, payload, &ec)<0) {
+ log_warn(LD_CIRC,"Couldn't format extend cell");
+ return -END_CIRC_REASON_INTERNAL;
}
- hop->state = CPATH_STATE_AWAITING_KEYS;
+
+ /* send it to hop->prev, because that relay will transfer
+ * it to a create cell and then send to hop */
+ if (relay_send_command_from_edge(0, TO_CIRCUIT(circ),
+ command,
+ (char*)payload, payload_len,
+ hop->prev) < 0)
+ return 0; /* circuit is closed */
}
+ hop->state = CPATH_STATE_AWAITING_KEYS;
return 0;
}
@@ -1326,40 +1373,77 @@ circuit_extend(cell_t *cell, circuit_t *circ)
return 0;
}
-/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in
- * key_data. key_data must contain CPATH_KEY_MATERIAL bytes, which are
- * used as follows:
+/** Initialize cpath-\>{f|b}_{crypto|digest} from the key material in key_data.
+ *
+ * If <b>is_hs_v3</b> is set, this cpath will be used for next gen hidden
+ * service circuits and <b>key_data</b> must be at least
+ * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length.
+ *
+ * If <b>is_hs_v3</b> is not set, key_data must contain CPATH_KEY_MATERIAL_LEN
+ * bytes, which are used as follows:
* - 20 to initialize f_digest
* - 20 to initialize b_digest
* - 16 to key f_crypto
* - 16 to key b_crypto
*
* (If 'reverse' is true, then f_XX and b_XX are swapped.)
+ *
+ * Return 0 if init was successful, else -1 if it failed.
*/
int
-circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data,
- int reverse)
+circuit_init_cpath_crypto(crypt_path_t *cpath,
+ const char *key_data, size_t key_data_len,
+ int reverse, int is_hs_v3)
{
crypto_digest_t *tmp_digest;
crypto_cipher_t *tmp_crypto;
+ size_t digest_len = 0;
+ size_t cipher_key_len = 0;
tor_assert(cpath);
tor_assert(key_data);
tor_assert(!(cpath->f_crypto || cpath->b_crypto ||
cpath->f_digest || cpath->b_digest));
- cpath->f_digest = crypto_digest_new();
- crypto_digest_add_bytes(cpath->f_digest, key_data, DIGEST_LEN);
- cpath->b_digest = crypto_digest_new();
- crypto_digest_add_bytes(cpath->b_digest, key_data+DIGEST_LEN, DIGEST_LEN);
+ /* Basic key size validation */
+ if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
+ return -1;
+ } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) {
+ return -1;
+ }
- if (!(cpath->f_crypto =
- crypto_cipher_new(key_data+(2*DIGEST_LEN)))) {
+ /* If we are using this cpath for next gen onion services use SHA3-256,
+ otherwise use good ol' SHA1 */
+ if (is_hs_v3) {
+ digest_len = DIGEST256_LEN;
+ cipher_key_len = CIPHER256_KEY_LEN;
+ cpath->f_digest = crypto_digest256_new(DIGEST_SHA3_256);
+ cpath->b_digest = crypto_digest256_new(DIGEST_SHA3_256);
+ } else {
+ digest_len = DIGEST_LEN;
+ cipher_key_len = CIPHER_KEY_LEN;
+ cpath->f_digest = crypto_digest_new();
+ cpath->b_digest = crypto_digest_new();
+ }
+
+ tor_assert(digest_len != 0);
+ tor_assert(cipher_key_len != 0);
+ const int cipher_key_bits = (int) cipher_key_len * 8;
+
+ crypto_digest_add_bytes(cpath->f_digest, key_data, digest_len);
+ crypto_digest_add_bytes(cpath->b_digest, key_data+digest_len, digest_len);
+
+ cpath->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len),
+ cipher_key_bits);
+ if (!cpath->f_crypto) {
log_warn(LD_BUG,"Forward cipher initialization failed.");
return -1;
}
- if (!(cpath->b_crypto =
- crypto_cipher_new(key_data+(2*DIGEST_LEN)+CIPHER_KEY_LEN))) {
+
+ cpath->b_crypto = crypto_cipher_new_with_bits(
+ key_data+(2*digest_len)+cipher_key_len,
+ cipher_key_bits);
+ if (!cpath->b_crypto) {
log_warn(LD_BUG,"Backward cipher initialization failed.");
return -1;
}
@@ -1425,7 +1509,7 @@ circuit_finish_handshake(origin_circuit_t *circ,
onion_handshake_state_release(&hop->handshake_state);
- if (circuit_init_cpath_crypto(hop, keys, 0)<0) {
+ if (circuit_init_cpath_crypto(hop, keys, sizeof(keys), 0, 0)<0) {
return -END_CIRC_REASON_TORPROTOCOL;
}
@@ -1483,7 +1567,7 @@ circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason)
log_info(LD_CIRC, "finished");
return 0;
-#endif
+#endif /* 0 */
}
/** Given a response payload and keys, initialize, then send a created
@@ -1492,12 +1576,14 @@ circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer, int reason)
int
onionskin_answer(or_circuit_t *circ,
const created_cell_t *created_cell,
- const char *keys,
+ const char *keys, size_t keys_len,
const uint8_t *rend_circ_nonce)
{
cell_t cell;
crypt_path_t *tmp_cpath;
+ tor_assert(keys_len == CPATH_KEY_MATERIAL_LEN);
+
if (created_cell_format(&cell, created_cell) < 0) {
log_warn(LD_BUG,"couldn't format created cell (type=%d, len=%d)",
(int)created_cell->cell_type, (int)created_cell->handshake_len);
@@ -1513,7 +1599,7 @@ onionskin_answer(or_circuit_t *circ,
log_debug(LD_CIRC,"init digest forward 0x%.8x, backward 0x%.8x.",
(unsigned int)get_uint32(keys),
(unsigned int)get_uint32(keys+20));
- if (circuit_init_cpath_crypto(tmp_cpath, keys, 0)<0) {
+ if (circuit_init_cpath_crypto(tmp_cpath, keys, keys_len, 0, 0)<0) {
log_warn(LD_BUG,"Circuit initialization failed");
tor_free(tmp_cpath);
return -1;
@@ -1527,12 +1613,12 @@ onionskin_answer(or_circuit_t *circ,
memcpy(circ->rend_circ_nonce, rend_circ_nonce, DIGEST_LEN);
- circ->is_first_hop = (created_cell->cell_type == CELL_CREATED_FAST);
+ int used_create_fast = (created_cell->cell_type == CELL_CREATED_FAST);
append_cell_to_circuit_queue(TO_CIRCUIT(circ),
circ->p_chan, &cell, CELL_DIRECTION_IN, 0);
log_debug(LD_CIRC,"Finished sending '%s' cell.",
- circ->is_first_hop ? "created_fast" : "created");
+ used_create_fast ? "created_fast" : "created");
/* Ignore the local bit when ExtendAllowPrivateAddresses is set:
* it violates the assumption that private addresses are local.
@@ -1957,9 +2043,10 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
}
if (options->ExitNodes) {
log_warn(LD_CIRC,
- "No specified %sexit routers seem to be running: "
+ "No exits in ExitNodes%s seem to be running: "
"can't choose an exit.",
- options->ExcludeExitNodesUnion_ ? "non-excluded " : "");
+ options->ExcludeExitNodesUnion_ ?
+ ", except possibly those excluded by your configuration, " : "");
}
return NULL;
}
@@ -2018,7 +2105,7 @@ pick_tor2web_rendezvous_node(router_crn_flags_t flags,
return rp_node;
}
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS) */
/* Pick a Rendezvous Point for our HS circuits according to <b>flags</b>. */
static const node_t *
@@ -2054,7 +2141,7 @@ pick_rendezvous_node(router_crn_flags_t flags)
"Unable to find a random rendezvous point that is reachable via "
"a direct connection, falling back to a 3-hop path.");
}
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) */
return router_choose_random_node(NULL, options->ExcludeNodes, flags);
}
@@ -2071,7 +2158,8 @@ pick_rendezvous_node(router_crn_flags_t flags)
*/
static const node_t *
choose_good_exit_server(uint8_t purpose,
- int need_uptime, int need_capacity, int is_internal)
+ int need_uptime, int need_capacity, int is_internal,
+ int need_hs_v3)
{
const or_options_t *options = get_options();
router_crn_flags_t flags = CRN_NEED_DESC;
@@ -2079,6 +2167,8 @@ choose_good_exit_server(uint8_t purpose,
flags |= CRN_NEED_UPTIME;
if (need_capacity)
flags |= CRN_NEED_CAPACITY;
+ if (need_hs_v3)
+ flags |= CRN_RENDEZVOUS_V3;
switch (purpose) {
case CIRCUIT_PURPOSE_C_GENERAL:
@@ -2178,9 +2268,15 @@ warn_if_last_router_excluded(origin_circuit_t *circ,
/** Decide a suitable length for circ's cpath, and pick an exit
* router (or use <b>exit</b> if provided). Store these in the
- * cpath. Return 0 if ok, -1 if circuit should be closed. */
+ * cpath.
+ *
+ * If <b>is_hs_v3_rp_circuit</b> is set, then this exit should be suitable to
+ * be used as an HS v3 rendezvous point.
+ *
+ * Return 0 if ok, -1 if circuit should be closed. */
static int
-onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
+onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei,
+ int is_hs_v3_rp_circuit)
{
cpath_build_state_t *state = circ->build_state;
@@ -2204,7 +2300,8 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
} else { /* we have to decide one */
const node_t *node =
choose_good_exit_server(circ->base_.purpose, state->need_uptime,
- state->need_capacity, state->is_internal);
+ state->need_capacity, state->is_internal,
+ is_hs_v3_rp_circuit);
if (!node) {
log_warn(LD_CIRC,"Failed to choose an exit server");
return -1;
@@ -2312,6 +2409,30 @@ onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop)
}
}
+#ifdef TOR_UNIT_TESTS
+
+/** Unittest helper function: Count number of hops in cpath linked list. */
+unsigned int
+cpath_get_n_hops(crypt_path_t **head_ptr)
+{
+ unsigned int n_hops = 0;
+ crypt_path_t *tmp;
+
+ if (!*head_ptr) {
+ return 0;
+ }
+
+ tmp = *head_ptr;
+ do {
+ n_hops++;
+ tmp = tmp->next;
+ } while (tmp != *head_ptr);
+
+ return n_hops;
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
/** A helper function used by onion_extend_cpath(). Use <b>purpose</b>
* and <b>state</b> and the cpath <b>head</b> (currently populated only
* to length <b>cur_len</b> to decide a suitable middle hop for a
@@ -2333,8 +2454,8 @@ choose_good_middle_server(uint8_t purpose,
tor_assert(CIRCUIT_PURPOSE_MIN_ <= purpose &&
purpose <= CIRCUIT_PURPOSE_MAX_);
- log_debug(LD_CIRC, "Contemplating intermediate hop %d: random choice.",
- cur_len);
+ log_debug(LD_CIRC, "Contemplating intermediate hop #%d: random choice.",
+ cur_len+1);
excluded = smartlist_new();
if ((r = build_state_get_exit_node(state))) {
nodelist_add_node_and_family(excluded, r);
@@ -2359,9 +2480,6 @@ choose_good_middle_server(uint8_t purpose,
* router (if we're an OR), and respect firewall settings; if we're
* configured to use entry guards, return one.
*
- * If <b>state</b> is NULL, we're choosing a router to serve as an entry
- * guard, not for any particular circuit.
- *
* Set *<b>guard_state_out</b> to information about the guard that
* we're selecting, which we'll use later to remember whether the
* guard worked or not.
@@ -2379,6 +2497,11 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state,
CRN_DIRECT_CONN);
const node_t *node;
+ /* Once we used this function to select a node to be a guard. We had
+ * 'state == NULL' be the signal for that. But we don't do that any more.
+ */
+ tor_assert_nonfatal(state);
+
if (state && options->UseEntryGuards &&
(purpose != CIRCUIT_PURPOSE_TESTING || options->BridgeRelay)) {
/* This request is for an entry server to use for a regular circuit,
@@ -2468,12 +2591,12 @@ onion_extend_cpath(origin_circuit_t *circ)
}
if (!info) {
- log_warn(LD_CIRC,"Failed to find node for hop %d of our path. Discarding "
- "this circuit.", cur_len);
+ log_warn(LD_CIRC,"Failed to find node for hop #%d of our path. Discarding "
+ "this circuit.", cur_len+1);
return -1;
}
- log_debug(LD_CIRC,"Chose router %s for hop %d (exit is %s)",
+ log_debug(LD_CIRC,"Chose router %s for hop #%d (exit is %s)",
extend_info_describe(info),
cur_len+1, build_state_get_exit_nickname(state));
@@ -2581,7 +2704,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
ed_pubkey = node_get_ed25519_id(node);
} else if (node_get_ed25519_id(node)) {
log_info(LD_CIRC, "Not including the ed25519 ID for %s, since it won't "
- " be able to authenticate it.",
+ "be able to authenticate it.",
node_describe(node));
}
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 45d9b2fb75..b8a651e055 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -31,8 +31,9 @@ int circuit_timeout_want_to_count_circ(origin_circuit_t *circ);
int circuit_send_next_onion_skin(origin_circuit_t *circ);
void circuit_note_clock_jumped(int seconds_elapsed);
int circuit_extend(cell_t *cell, circuit_t *circ);
-int circuit_init_cpath_crypto(crypt_path_t *cpath, const char *key_data,
- int reverse);
+int circuit_init_cpath_crypto(crypt_path_t *cpath,
+ const char *key_data, size_t key_data_len,
+ int reverse, int is_hs_v3);
struct created_cell_t;
int circuit_finish_handshake(origin_circuit_t *circ,
const struct created_cell_t *created_cell);
@@ -40,7 +41,7 @@ int circuit_truncated(origin_circuit_t *circ, crypt_path_t *layer,
int reason);
int onionskin_answer(or_circuit_t *circ,
const struct created_cell_t *created_cell,
- const char *keys,
+ const char *keys, size_t keys_len,
const uint8_t *rend_circ_nonce);
MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now,
int *need_uptime,
@@ -83,9 +84,11 @@ MOCK_DECL(STATIC int, count_acceptable_nodes, (smartlist_t *nodes));
#if defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS)
STATIC const node_t *pick_tor2web_rendezvous_node(router_crn_flags_t flags,
const or_options_t *options);
-#endif
+unsigned int cpath_get_n_hops(crypt_path_t **head_ptr);
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) || defined(TOR_UNIT_TESTS) */
-#endif
+#endif /* defined(CIRCUITBUILD_PRIVATE) */
+
+#endif /* !defined(TOR_CIRCUITBUILD_H) */
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 6ffaabc16f..d442887c9e 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -65,8 +65,9 @@
#include "control.h"
#include "entrynodes.h"
#include "main.h"
+#include "hs_circuit.h"
#include "hs_circuitmap.h"
-#include "hs_common.h"
+#include "hs_ident.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "onion.h"
@@ -924,14 +925,24 @@ circuit_clear_testing_cell_stats(circuit_t *circ)
STATIC void
circuit_free(circuit_t *circ)
{
+ circid_t n_circ_id = 0;
void *mem;
size_t memlen;
int should_free = 1;
if (!circ)
return;
+ /* We keep a copy of this so we can log its value before it gets unset. */
+ n_circ_id = circ->n_circ_id;
+
circuit_clear_testing_cell_stats(circ);
+ /* Cleanup circuit from anything HS v3 related. We also do this when the
+ * circuit is closed. This is to avoid any code path that free registered
+ * circuits without closing them before. This needs to be done before the
+ * hs identifier is freed. */
+ hs_circ_cleanup(circ);
+
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
mem = ocirc;
@@ -958,6 +969,11 @@ circuit_free(circuit_t *circ)
crypto_pk_free(ocirc->intro_key);
rend_data_free(ocirc->rend_data);
+ /* Finally, free the identifier of the circuit and nullify it so multiple
+ * cleanup will work. */
+ hs_ident_circuit_free(ocirc->hs_ident);
+ ocirc->hs_ident = NULL;
+
tor_free(ocirc->dest_address);
if (ocirc->socks_username) {
memwipe(ocirc->socks_username, 0x12, ocirc->socks_username_len);
@@ -1015,15 +1031,15 @@ circuit_free(circuit_t *circ)
/* Remove from map. */
circuit_set_n_circid_chan(circ, 0, NULL);
- /* Clear HS circuitmap token from this circ (if any) */
- if (circ->hs_token) {
- hs_circuitmap_remove_circuit(circ);
- }
-
/* Clear cell queue _after_ removing it from the map. Otherwise our
* "active" checks will be violated. */
cell_queue_clear(&circ->n_chan_cells);
+ log_info(LD_CIRC, "Circuit %u (id: %" PRIu32 ") has been freed.",
+ n_circ_id,
+ CIRCUIT_IS_ORIGIN(circ) ?
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0);
+
if (should_free) {
memwipe(mem, 0xAA, memlen); /* poison memory */
tor_free(mem);
@@ -1431,7 +1447,7 @@ circuit_unlink_all_from_channel(channel_t *chan, int reason)
smartlist_free(detached_2);
}
}
-#endif
+#endif /* defined(DEBUG_CIRCUIT_UNLINK_ALL) */
SMARTLIST_FOREACH_BEGIN(detached, circuit_t *, circ) {
int mark = 0;
@@ -1530,6 +1546,41 @@ circuit_get_next_service_intro_circ(origin_circuit_t *start)
return NULL;
}
+/** Return the first service rendezvous circuit originating from the global
+ * circuit list after <b>start</b> or at the start of the list if <b>start</b>
+ * is NULL. Return NULL if no circuit is found.
+ *
+ * A service rendezvous point circuit has a purpose of either
+ * CIRCUIT_PURPOSE_S_CONNECT_REND or CIRCUIT_PURPOSE_S_REND_JOINED. This does
+ * not return a circuit marked for close and its state must be open. */
+origin_circuit_t *
+circuit_get_next_service_rp_circ(origin_circuit_t *start)
+{
+ int idx = 0;
+ smartlist_t *lst = circuit_get_global_list();
+
+ if (start) {
+ idx = TO_CIRCUIT(start)->global_circuitlist_idx + 1;
+ }
+
+ for ( ; idx < smartlist_len(lst); ++idx) {
+ circuit_t *circ = smartlist_get(lst, idx);
+
+ /* Ignore a marked for close circuit or purpose not matching a service
+ * intro point or if the state is not open. */
+ if (circ->marked_for_close || circ->state != CIRCUIT_STATE_OPEN ||
+ (circ->purpose != CIRCUIT_PURPOSE_S_CONNECT_REND &&
+ circ->purpose != CIRCUIT_PURPOSE_S_REND_JOINED)) {
+ continue;
+ }
+ /* The purposes we are looking for are only for origin circuits so the
+ * following is valid. */
+ return TO_ORIGIN_CIRCUIT(circ);
+ }
+ /* Not found. */
+ return NULL;
+}
+
/** Return the first circuit originating here in global_circuitlist after
* <b>start</b> whose purpose is <b>purpose</b>, and where <b>digest</b> (if
* set) matches the private key digest of the rend data associated with the
@@ -1571,6 +1622,30 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
return NULL;
}
+/** We might cannibalize this circuit: Return true if its last hop can be used
+ * as a v3 rendezvous point. */
+static int
+circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ)
+{
+ if (!circ->build_state) {
+ return 0;
+ }
+
+ extend_info_t *chosen_exit = circ->build_state->chosen_exit;
+ if (BUG(!chosen_exit)) {
+ return 0;
+ }
+
+ const node_t *rp_node = node_get_by_id(chosen_exit->identity_digest);
+ if (rp_node) {
+ if (node_supports_v3_rendezvous_point(rp_node)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL,
* has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_*
* flags in <b>flags</b>, and if info is defined, does not already use info
@@ -1653,6 +1728,14 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
hop = hop->next;
} while (hop != circ->cpath);
}
+
+ if ((flags & CIRCLAUNCH_IS_V3_RP) &&
+ !circuit_can_be_cannibalized_for_v3_rp(circ)) {
+ log_debug(LD_GENERAL, "Skipping uncannibalizable circuit for v3 "
+ "rendezvous point.");
+ goto next;
+ }
+
if (!best || (best->build_state->need_uptime && !need_uptime))
best = circ;
next: ;
@@ -1838,10 +1921,20 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line,
}
}
+ /* Notify the HS subsystem that this circuit is closing. */
+ hs_circ_cleanup(circ);
+
if (circuits_pending_close == NULL)
circuits_pending_close = smartlist_new();
smartlist_add(circuits_pending_close, circ);
+
+ log_info(LD_GENERAL, "Circuit %u (id: %" PRIu32 ") marked for close at "
+ "%s:%d (orig reason: %d, new reason: %d)",
+ circ->n_circ_id,
+ CIRCUIT_IS_ORIGIN(circ) ?
+ TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0,
+ file, line, orig_reason, reason);
}
/** Called immediately before freeing a marked circuit <b>circ</b> from
@@ -1916,8 +2009,8 @@ circuit_about_to_free(circuit_t *circ)
int timed_out = (reason == END_CIRC_REASON_TIMEOUT);
tor_assert(circ->state == CIRCUIT_STATE_OPEN);
tor_assert(ocirc->build_state->chosen_exit);
- tor_assert(ocirc->rend_data);
- if (orig_reason != END_CIRC_REASON_IP_NOW_REDUNDANT) {
+ if (orig_reason != END_CIRC_REASON_IP_NOW_REDUNDANT &&
+ ocirc->rend_data) {
/* treat this like getting a nack from it */
log_info(LD_REND, "Failed intro circ %s to %s (awaiting ack). %s",
safe_str_client(rend_data_get_address(ocirc->rend_data)),
@@ -1933,7 +2026,8 @@ circuit_about_to_free(circuit_t *circ)
reason != END_CIRC_REASON_TIMEOUT) {
origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
if (ocirc->build_state->chosen_exit && ocirc->rend_data) {
- if (orig_reason != END_CIRC_REASON_IP_NOW_REDUNDANT) {
+ if (orig_reason != END_CIRC_REASON_IP_NOW_REDUNDANT &&
+ ocirc->rend_data) {
log_info(LD_REND, "Failed intro circ %s to %s "
"(building circuit to intro point). "
"Marking intro point as possibly unreachable.",
@@ -2387,8 +2481,8 @@ assert_cpath_ok(const crypt_path_t *cp)
/** Verify that circuit <b>c</b> has all of its invariants
* correct. Trigger an assert if anything is invalid.
*/
-void
-assert_circuit_ok(const circuit_t *c)
+MOCK_IMPL(void,
+assert_circuit_ok,(const circuit_t *c))
{
edge_connection_t *conn;
const or_circuit_t *or_circ = NULL;
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index d647062f46..4190c1f82e 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -13,6 +13,7 @@
#define TOR_CIRCUITLIST_H
#include "testsupport.h"
+#include "hs_ident.h"
MOCK_DECL(smartlist_t *, circuit_get_global_list, (void));
smartlist_t *circuit_get_global_origin_circuit_list(void);
@@ -48,6 +49,8 @@ origin_circuit_t *circuit_get_ready_rend_circ_by_rend_data(
origin_circuit_t *circuit_get_next_by_pk_and_purpose(origin_circuit_t *start,
const uint8_t *digest, uint8_t purpose);
origin_circuit_t *circuit_get_next_service_intro_circ(origin_circuit_t *start);
+origin_circuit_t *circuit_get_next_service_rp_circ(origin_circuit_t *start);
+origin_circuit_t *circuit_get_next_service_hsdir_circ(origin_circuit_t *start);
origin_circuit_t *circuit_find_to_cannibalize(uint8_t purpose,
extend_info_t *info, int flags);
void circuit_mark_all_unused_circs(void);
@@ -65,7 +68,7 @@ int circuit_count_pending_on_channel(channel_t *chan);
circuit_mark_for_close_((c), (reason), __LINE__, SHORT_FILE__)
void assert_cpath_layer_ok(const crypt_path_t *cp);
-void assert_circuit_ok(const circuit_t *c);
+MOCK_DECL(void, assert_circuit_ok,(const circuit_t *c));
void circuit_free_all(void);
void circuits_handle_oom(size_t current_allocation);
@@ -83,7 +86,7 @@ STATIC size_t n_cells_in_circ_queues(const circuit_t *c);
STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now);
STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now);
STATIC uint32_t circuit_max_queued_item_age(const circuit_t *c, uint32_t now);
-#endif
+#endif /* defined(CIRCUITLIST_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CIRCUITLIST_H) */
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index 6c825011b6..2cc0e8fc44 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -185,7 +185,7 @@ struct chanid_circid_muxinfo_t {
circuitmux_assert_okay(cmux)
#else
#define circuitmux_assert_okay_paranoid(cmux)
-#endif
+#endif /* defined(CMUX_PARANOIA) */
/*
* Static function declarations
@@ -261,13 +261,11 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ,
if (circ->n_mux == cmux) {
next_p = &(circ->next_active_on_n_chan);
prev_p = &(circ->prev_active_on_n_chan);
- direction = CELL_DIRECTION_OUT;
} else {
or_circ = TO_OR_CIRCUIT(circ);
tor_assert(or_circ->p_mux == cmux);
next_p = &(or_circ->next_active_on_p_chan);
prev_p = &(or_circ->prev_active_on_p_chan);
- direction = CELL_DIRECTION_IN;
}
}
tor_assert(next_p);
@@ -1646,7 +1644,6 @@ circuitmux_assert_okay_pass_one(circuitmux_t *cmux)
circid_t circ_id;
circuit_t *circ;
or_circuit_t *or_circ;
- unsigned int circ_is_active;
circuit_t **next_p, **prev_p;
unsigned int n_circuits, n_active_circuits, n_cells;
@@ -1670,8 +1667,6 @@ circuitmux_assert_okay_pass_one(circuitmux_t *cmux)
tor_assert(chan);
circ = circuit_get_by_circid_channel_even_if_marked(circ_id, chan);
tor_assert(circ);
- /* Clear the circ_is_active bit to start */
- circ_is_active = 0;
/* Assert that we know which direction this is going */
tor_assert((*i)->muxinfo.direction == CELL_DIRECTION_OUT ||
@@ -1698,7 +1693,7 @@ circuitmux_assert_okay_pass_one(circuitmux_t *cmux)
* Should this circuit be active? I.e., does the mux know about > 0
* cells on it?
*/
- circ_is_active = ((*i)->muxinfo.cell_count > 0);
+ const int circ_is_active = ((*i)->muxinfo.cell_count > 0);
/* It should be in the linked list iff it's active */
if (circ_is_active) {
@@ -1750,7 +1745,6 @@ circuitmux_assert_okay_pass_two(circuitmux_t *cmux)
circuit_t **next_p, **prev_p;
channel_t *chan;
unsigned int n_active_circuits = 0;
- cell_direction_t direction;
chanid_circid_muxinfo_t search, *hashent = NULL;
tor_assert(cmux);
@@ -1769,7 +1763,7 @@ circuitmux_assert_okay_pass_two(circuitmux_t *cmux)
curr_or_circ = NULL;
next_circ = NULL;
next_p = prev_p = NULL;
- direction = 0;
+ cell_direction_t direction;
/* Figure out if this is n_mux or p_mux */
if (cmux == curr_circ->n_mux) {
diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h
index bf93bb8cbf..a95edb99d8 100644
--- a/src/or/circuitmux.h
+++ b/src/or/circuitmux.h
@@ -156,5 +156,5 @@ void circuitmux_mark_destroyed_circids_usable(circuitmux_t *cmux,
MOCK_DECL(int, circuitmux_compare_muxes,
(circuitmux_t *cmux_1, circuitmux_t *cmux_2));
-#endif /* TOR_CIRCUITMUX_H */
+#endif /* !defined(TOR_CIRCUITMUX_H) */
diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c
index c2440b13f0..fde2d22a89 100644
--- a/src/or/circuitmux_ewma.c
+++ b/src/or/circuitmux_ewma.c
@@ -731,7 +731,7 @@ add_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma)
smartlist_pqueue_add(pol->active_circuit_pqueue,
compare_cell_ewma_counts,
- STRUCT_OFFSET(cell_ewma_t, heap_index),
+ offsetof(cell_ewma_t, heap_index),
ewma);
}
@@ -746,7 +746,7 @@ remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma)
smartlist_pqueue_remove(pol->active_circuit_pqueue,
compare_cell_ewma_counts,
- STRUCT_OFFSET(cell_ewma_t, heap_index),
+ offsetof(cell_ewma_t, heap_index),
ewma);
}
@@ -760,6 +760,6 @@ pop_first_cell_ewma(ewma_policy_data_t *pol)
return smartlist_pqueue_pop(pol->active_circuit_pqueue,
compare_cell_ewma_counts,
- STRUCT_OFFSET(cell_ewma_t, heap_index));
+ offsetof(cell_ewma_t, heap_index));
}
diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h
index 1f04408789..8f4e57865e 100644
--- a/src/or/circuitmux_ewma.h
+++ b/src/or/circuitmux_ewma.h
@@ -20,5 +20,5 @@ unsigned int cell_ewma_get_tick(void);
void cell_ewma_set_scale_factor(const or_options_t *options,
const networkstatus_t *consensus);
-#endif /* TOR_CIRCUITMUX_EWMA_H */
+#endif /* !defined(TOR_CIRCUITMUX_EWMA_H) */
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 51d580a1a4..b8421a3c7e 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -60,7 +60,7 @@ static circuit_build_times_t circ_times;
static int unit_tests = 0;
#else
#define unit_tests 0
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Return a pointer to the data structure describing our current circuit
* build time history and computations. */
@@ -148,7 +148,7 @@ circuit_build_times_disabled_(const or_options_t *options,
"Consensus=%d, Config=%d, AuthDir=%d, StateFile=%d",
consensus_disabled, config_disabled, dirauth_disabled,
state_disabled);
-#endif
+#endif /* 0 */
return 1;
} else {
#if 0
@@ -157,7 +157,7 @@ circuit_build_times_disabled_(const or_options_t *options,
"Consensus=%d, Config=%d, AuthDir=%d, StateFile=%d",
consensus_disabled, config_disabled, dirauth_disabled,
state_disabled);
-#endif
+#endif /* 0 */
return 0;
}
}
@@ -431,9 +431,14 @@ circuit_build_times_new_consensus_params(circuit_build_times_t *cbt,
if (num > 0) {
if (num != cbt->liveness.num_recent_circs) {
int8_t *recent_circs;
- log_notice(LD_CIRC, "The Tor Directory Consensus has changed how many "
- "circuits we must track to detect network failures from %d "
- "to %d.", cbt->liveness.num_recent_circs, num);
+ if (cbt->liveness.num_recent_circs > 0) {
+ log_notice(LD_CIRC, "The Tor Directory Consensus has changed how "
+ "many circuits we must track to detect network failures "
+ "from %d to %d.", cbt->liveness.num_recent_circs, num);
+ } else {
+ log_notice(LD_CIRC, "Upon receiving a consensus directory, "
+ "re-enabling circuit-based network failure detection.");
+ }
tor_assert(cbt->liveness.timeouts_after_firsthop ||
cbt->liveness.num_recent_circs == 0);
@@ -608,7 +613,7 @@ circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n)
"Rewound history by %d places. Current index: %d. "
"Total: %d", n, cbt->build_times_idx, cbt->total_build_times);
}
-#endif
+#endif /* 0 */
/**
* Add a new build time value <b>time</b> to the set of build times. Time
@@ -676,7 +681,7 @@ circuit_build_times_min(circuit_build_times_t *cbt)
}
return min_build_time;
}
-#endif
+#endif /* 0 */
/**
* Calculate and return a histogram for the set of build times.
@@ -910,7 +915,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt,
int tot_values = 0;
uint32_t loaded_cnt = 0, N = 0;
config_line_t *line;
- unsigned int i;
+ int i;
build_time_t *loaded_times;
int err = 0;
circuit_build_times_init(cbt);
@@ -939,7 +944,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt,
uint32_t count, k;
build_time_t ms;
int ok;
- ms = (build_time_t)tor_parse_ulong(ms_str, 0, 0,
+ ms = (build_time_t)tor_parse_ulong(ms_str, 10, 0,
CBT_BUILD_TIME_MAX, &ok, NULL);
if (!ok) {
log_warn(LD_GENERAL, "Unable to parse circuit build times: "
@@ -949,7 +954,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt,
smartlist_free(args);
break;
}
- count = (uint32_t)tor_parse_ulong(count_str, 0, 0,
+ count = (uint32_t)tor_parse_ulong(count_str, 10, 0,
UINT32_MAX, &ok, NULL);
if (!ok) {
log_warn(LD_GENERAL, "Unable to parse circuit build times: "
@@ -960,8 +965,8 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt,
break;
}
- if (loaded_cnt+count+state->CircuitBuildAbandonedCount
- > state->TotalBuildTimes) {
+ if (loaded_cnt+count+ (unsigned)state->CircuitBuildAbandonedCount
+ > (unsigned) state->TotalBuildTimes) {
log_warn(LD_CIRC,
"Too many build times in state file. "
"Stopping short before %d",
@@ -986,7 +991,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt,
loaded_times[loaded_cnt++] = CBT_BUILD_ABANDONED;
}
- if (loaded_cnt != state->TotalBuildTimes) {
+ if (loaded_cnt != (unsigned)state->TotalBuildTimes) {
log_warn(LD_CIRC,
"Corrupt state file? Build times count mismatch. "
"Read %d times, but file says %d", loaded_cnt,
@@ -1165,7 +1170,7 @@ circuit_build_times_cdf(circuit_build_times_t *cbt, double x)
tor_assert(0 <= ret && ret <= 1.0);
return ret;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
#ifdef TOR_UNIT_TESTS
/**
@@ -1200,7 +1205,7 @@ circuit_build_times_generate_sample(circuit_build_times_t *cbt,
tor_assert(ret > 0);
return ret;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
#ifdef TOR_UNIT_TESTS
/**
@@ -1223,7 +1228,7 @@ circuit_build_times_initial_alpha(circuit_build_times_t *cbt,
(tor_mathlog(cbt->Xm)-tor_mathlog(timeout_ms));
tor_assert(cbt->alpha > 0);
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/**
* Returns true if we need circuits to be built
@@ -1682,7 +1687,7 @@ circuitbuild_running_unit_tests(void)
{
unit_tests = 1;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
void
circuit_build_times_update_last_circ(circuit_build_times_t *cbt)
diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h
index 8a1dec4bfd..92dc6405ba 100644
--- a/src/or/circuitstats.h
+++ b/src/or/circuitstats.h
@@ -54,7 +54,7 @@ STATIC void circuit_build_times_reset(circuit_build_times_t *cbt);
/* Network liveness functions */
STATIC int circuit_build_times_network_check_changed(
circuit_build_times_t *cbt);
-#endif
+#endif /* defined(CIRCUITSTATS_PRIVATE) */
#ifdef TOR_UNIT_TESTS
build_time_t circuit_build_times_generate_sample(circuit_build_times_t *cbt,
@@ -63,7 +63,7 @@ double circuit_build_times_cdf(circuit_build_times_t *cbt, double x);
void circuit_build_times_initial_alpha(circuit_build_times_t *cbt,
double quantile, double time_ms);
void circuitbuild_running_unit_tests(void);
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/* Network liveness functions */
void circuit_build_times_network_is_live(circuit_build_times_t *cbt);
@@ -95,7 +95,7 @@ struct circuit_build_times_s {
/** How long we wait before actually closing the circuit. */
double close_ms;
};
-#endif
+#endif /* defined(CIRCUITSTATS_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CIRCUITSTATS_H) */
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 9f9d3abf7c..7baa035696 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -42,6 +42,9 @@
#include "control.h"
#include "entrynodes.h"
#include "hs_common.h"
+#include "hs_client.h"
+#include "hs_circuit.h"
+#include "hs_ident.h"
#include "nodelist.h"
#include "networkstatus.h"
#include "policies.h"
@@ -55,6 +58,36 @@
static void circuit_expire_old_circuits_clientside(void);
static void circuit_increment_failure_count(void);
+/** Check whether the hidden service destination of the stream at
+ * <b>edge_conn</b> is the same as the destination of the circuit at
+ * <b>origin_circ</b>. */
+static int
+circuit_matches_with_rend_stream(const edge_connection_t *edge_conn,
+ const origin_circuit_t *origin_circ)
+{
+ /* Check if this is a v2 rendezvous circ/stream */
+ if ((edge_conn->rend_data && !origin_circ->rend_data) ||
+ (!edge_conn->rend_data && origin_circ->rend_data) ||
+ (edge_conn->rend_data && origin_circ->rend_data &&
+ rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data),
+ rend_data_get_address(origin_circ->rend_data)))) {
+ /* this circ is not for this conn */
+ return 0;
+ }
+
+ /* Check if this is a v3 rendezvous circ/stream */
+ if ((edge_conn->hs_ident && !origin_circ->hs_ident) ||
+ (!edge_conn->hs_ident && origin_circ->hs_ident) ||
+ (edge_conn->hs_ident && origin_circ->hs_ident &&
+ !ed25519_pubkey_eq(&edge_conn->hs_ident->identity_pk,
+ &origin_circ->hs_ident->identity_pk))) {
+ /* this circ is not for this conn */
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return 1 if <b>circ</b> could be returned by circuit_get_best().
* Else return 0.
*/
@@ -169,14 +202,9 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
/* can't exit from this router */
return 0;
}
- } else { /* not general */
+ } else { /* not general: this might be a rend circuit */
const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
- if ((edge_conn->rend_data && !origin_circ->rend_data) ||
- (!edge_conn->rend_data && origin_circ->rend_data) ||
- (edge_conn->rend_data && origin_circ->rend_data &&
- rend_cmp_service_ids(rend_data_get_address(edge_conn->rend_data),
- rend_data_get_address(origin_circ->rend_data)))) {
- /* this circ is not for this conn */
+ if (!circuit_matches_with_rend_stream(edge_conn, origin_circ)) {
return 0;
}
}
@@ -309,7 +337,8 @@ circuit_get_best(const entry_connection_t *conn,
/* Log an info message if we're going to launch a new intro circ in
* parallel */
if (purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT &&
- !must_be_open && origin_circ->hs_circ_has_timed_out) {
+ !must_be_open && origin_circ->hs_circ_has_timed_out &&
+ !circ->marked_for_close) {
intro_going_on_but_too_old = 1;
continue;
}
@@ -381,7 +410,7 @@ circuit_conforms_to_options(const origin_circuit_t *circ,
return 1;
}
-#endif
+#endif /* 0 */
/** Close all circuits that start at us, aren't open, and were born
* at least CircuitBuildTimeout seconds ago.
@@ -514,8 +543,7 @@ circuit_expire_building(void)
cutoff = begindir_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT)
cutoff = close_cutoff;
- else if (victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCING ||
- victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
+ else if (victim->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
cutoff = c_intro_cutoff;
else if (victim->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
cutoff = s_intro_cutoff;
@@ -605,7 +633,7 @@ circuit_expire_building(void)
victim->n_circ_id,
(int)(now - victim->timestamp_dirty));
}
-#endif
+#endif /* 0 */
/* if circ is !open, or if it's open but purpose is a non-finished
* intro or rend, then mark it for close */
@@ -622,6 +650,7 @@ circuit_expire_building(void)
* because that's set when they switch purposes
*/
if (TO_ORIGIN_CIRCUIT(victim)->rend_data ||
+ TO_ORIGIN_CIRCUIT(victim)->hs_ident ||
victim->timestamp_dirty > cutoff.tv_sec)
continue;
break;
@@ -631,12 +660,13 @@ circuit_expire_building(void)
TO_ORIGIN_CIRCUIT(victim)->path_state = PATH_STATE_USE_FAILED;
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
- /* We keep old introducing circuits around for
- * a while in parallel, and they can end up "opened".
- * We decide below if we're going to mark them timed
- * out and eventually close them.
- */
- break;
+ /* That purpose means that the intro point circuit has been opened
+ * succesfully but the INTRODUCE1 cell hasn't been sent yet because
+ * the client is waiting for the rendezvous point circuit to open.
+ * Keep this circuit open while waiting for the rendezvous circuit.
+ * We let the circuit idle timeout take care of cleaning this
+ * circuit if it never used. */
+ continue;
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
@@ -727,8 +757,6 @@ circuit_expire_building(void)
NULL)
break;
/* fallthrough! */
- case CIRCUIT_PURPOSE_C_INTRODUCING:
- /* connection_ap_handshake_attach_circuit() will relaunch for us */
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:
/* If we have reached this line, we want to spare the circ for now. */
@@ -755,7 +783,7 @@ circuit_expire_building(void)
victim->state, circuit_state_to_string(victim->state),
victim->purpose);
TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1;
- rend_service_relaunch_rendezvous(TO_ORIGIN_CIRCUIT(victim));
+ hs_circ_retry_service_rendezvous_point(TO_ORIGIN_CIRCUIT(victim));
continue;
}
@@ -973,7 +1001,7 @@ circuit_remove_handled_ports(smartlist_t *needed_ports)
tor_assert(*port);
if (circuit_stream_is_being_handled(NULL, *port,
MIN_CIRCUITS_HANDLING_STREAM)) {
-// log_debug(LD_CIRC,"Port %d is already being handled; removing.", port);
+ log_debug(LD_CIRC,"Port %d is already being handled; removing.", *port);
smartlist_del(needed_ports, i--);
tor_free(port);
} else {
@@ -1010,6 +1038,10 @@ circuit_stream_is_being_handled(entry_connection_t *conn,
continue;
if (origin_circ->unusable_for_new_conns)
continue;
+ if (origin_circ->isolation_values_set &&
+ (conn == NULL ||
+ !connection_edge_compatible_with_circuit(conn, origin_circ)))
+ continue;
exitnode = build_state_get_exit_node(build_state);
if (exitnode && (!need_uptime || build_state->need_uptime)) {
@@ -1086,11 +1118,32 @@ needs_exit_circuits(time_t now, int *needs_uptime, int *needs_capacity)
/* Return true if we need any more hidden service server circuits.
* HS servers only need an internal circuit. */
STATIC int
-needs_hs_server_circuits(int num_uptime_internal)
+needs_hs_server_circuits(time_t now, int num_uptime_internal)
{
- return (num_rend_services() &&
- num_uptime_internal < SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS &&
- router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN);
+ if (!rend_num_services() && !hs_service_get_num_services()) {
+ /* No services, we don't need anything. */
+ goto no_need;
+ }
+
+ if (num_uptime_internal >= SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS) {
+ /* We have sufficient amount of internal circuit. */
+ goto no_need;
+ }
+
+ if (router_have_consensus_path() == CONSENSUS_PATH_UNKNOWN) {
+ /* Consensus hasn't been checked or might be invalid so requesting
+ * internal circuits is not wise. */
+ goto no_need;
+ }
+
+ /* At this point, we need a certain amount of circuits and we will most
+ * likely use them for rendezvous so we note down the use of internal
+ * circuit for our prediction for circuit needing uptime and capacity. */
+ rep_hist_note_used_internal(now, 1, 1);
+
+ return 1;
+ no_need:
+ return 0;
}
/* We need at least this many internal circuits for hidden service clients */
@@ -1189,7 +1242,7 @@ circuit_predict_and_launch_new(void)
return;
}
- if (needs_hs_server_circuits(num_uptime_internal)) {
+ if (needs_hs_server_circuits(now, num_uptime_internal)) {
flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME |
CIRCLAUNCH_IS_INTERNAL);
@@ -1253,11 +1306,6 @@ circuit_build_needed_circs(time_t now)
if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN)
connection_ap_rescan_and_attach_pending();
- /* make sure any hidden services have enough intro points
- * HS intro point streams only require an internal circuit */
- if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN)
- rend_consider_services_intro_points();
-
circuit_expire_old_circs_as_needed(now);
if (!options->DisablePredictedCircuits)
@@ -1293,7 +1341,7 @@ circuit_expire_old_circs_as_needed(time_t now)
log_fn(LOG_INFO,"Creating a new testing circuit.");
circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, 0);
}
-#endif
+#endif /* 0 */
}
}
@@ -1339,8 +1387,7 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
* number of streams on the circuit associated with the rend service.
*/
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
- tor_assert(origin_circ->rend_data);
- origin_circ->rend_data->nr_streams--;
+ hs_dec_rdv_stream_counter(origin_circ);
}
return;
}
@@ -1469,7 +1516,7 @@ circuit_expire_old_circuits_clientside(void)
#define IDLE_ONE_HOP_CIRC_TIMEOUT 60
/** Find each non-origin circuit that has been unused for too long,
- * has no streams on it, used a create_fast, and ends here: mark it
+ * has no streams on it, came from a client, and ends here: mark it
* for close.
*/
void
@@ -1485,9 +1532,9 @@ circuit_expire_old_circuits_serverside(time_t now)
/* If the circuit has been idle for too long, and there are no streams
* on it, and it ends here, and it used a create_fast, mark it for close.
*/
- if (or_circ->is_first_hop && !circ->n_chan &&
+ if (or_circ->p_chan && channel_is_client(or_circ->p_chan) &&
+ !circ->n_chan &&
!or_circ->n_streams && !or_circ->resolving_streams &&
- or_circ->p_chan &&
channel_when_last_xmit(or_circ->p_chan) <= cutoff) {
log_info(LD_CIRC, "Closing circ_id %u (empty %d secs ago)",
(unsigned)or_circ->p_circ_id,
@@ -1593,7 +1640,7 @@ circuit_has_opened(origin_circuit_t *circ)
switch (TO_CIRCUIT(circ)->purpose) {
case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
- rend_client_rendcirc_has_opened(circ);
+ hs_client_circuit_has_opened(circ);
/* Start building an intro circ if we don't have one yet. */
connection_ap_attach_pending(1);
/* This isn't a call to circuit_try_attaching_streams because a
@@ -1605,7 +1652,7 @@ circuit_has_opened(origin_circuit_t *circ)
* state. */
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
- rend_client_introcirc_has_opened(circ);
+ hs_client_circuit_has_opened(circ);
break;
case CIRCUIT_PURPOSE_C_GENERAL:
/* Tell any AP connections that have been waiting for a new
@@ -1614,11 +1661,11 @@ circuit_has_opened(origin_circuit_t *circ)
break;
case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
/* at the service, waiting for introductions */
- rend_service_intro_has_opened(circ);
+ hs_service_circuit_has_opened(circ);
break;
case CIRCUIT_PURPOSE_S_CONNECT_REND:
/* at the service, connecting to rend point */
- rend_service_rendezvous_has_opened(circ);
+ hs_service_circuit_has_opened(circ);
break;
case CIRCUIT_PURPOSE_TESTING:
circuit_testing_opened(circ);
@@ -1707,13 +1754,17 @@ circuit_build_failed(origin_circuit_t *circ)
already_marked = 1;
}
log_info(LD_OR,
- "Our circuit failed to get a response from the first hop "
- "(%s). I'm going to try to rotate to a better connection.",
+ "Our circuit %u (id: %" PRIu32 ") failed to get a response "
+ "from the first hop (%s). I'm going to try to rotate to a "
+ "better connection.",
+ TO_CIRCUIT(circ)->n_circ_id, circ->global_identifier,
channel_get_canonical_remote_descr(n_chan));
n_chan->is_bad_for_new_circs = 1;
} else {
log_info(LD_OR,
- "Our circuit died before the first hop with no connection");
+ "Our circuit %u (id: %" PRIu32 ") died before the first hop "
+ "with no connection",
+ TO_CIRCUIT(circ)->n_circ_id, circ->global_identifier);
}
if (n_chan_id && !already_marked) {
/* New guard API: we failed. */
@@ -1768,7 +1819,7 @@ circuit_build_failed(origin_circuit_t *circ)
"(%s hop failed).",
escaped(build_state_get_exit_nickname(circ->build_state)),
failed_at_last_hop?"last":"non-last");
- rend_service_relaunch_rendezvous(circ);
+ hs_circ_retry_service_rendezvous_point(circ);
break;
/* default:
* This won't happen in normal operation, but might happen if the
@@ -1855,8 +1906,9 @@ circuit_launch_by_extend_info(uint8_t purpose,
uint8_t old_purpose = circ->base_.purpose;
struct timeval old_timestamp_began = circ->base_.timestamp_began;
- log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d (%s)",
- build_state_get_exit_nickname(circ->build_state), purpose,
+ log_info(LD_CIRC, "Cannibalizing circ %u (id: %" PRIu32 ") for "
+ "purpose %d (%s)",
+ TO_CIRCUIT(circ)->n_circ_id, circ->global_identifier, purpose,
circuit_purpose_to_string(purpose));
if ((purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
@@ -2027,8 +2079,12 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (!connection_get_by_type(CONN_TYPE_DIR)) {
int severity = LOG_NOTICE;
/* Retry some stuff that might help the connection work. */
- if (entry_list_is_constrained(options) &&
- guards_retry_optimistic(options)) {
+ /* If we are configured with EntryNodes or UseBridges */
+ if (entry_list_is_constrained(options)) {
+ /* Retry all our guards / bridges.
+ * guards_retry_optimistic() always returns true here. */
+ int rv = guards_retry_optimistic(options);
+ tor_assert_nonfatal_once(rv);
log_fn(severity, LD_APP|LD_DIR,
"Application request when we haven't %s. "
"Optimistically trying known %s again.",
@@ -2036,7 +2092,12 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
"used client functionality lately" :
"received a consensus with exits",
options->UseBridges ? "bridges" : "entrynodes");
- } else if (!options->UseBridges || any_bridge_descriptors_known()) {
+ } else {
+ /* Getting directory documents doesn't help much if we have a limited
+ * number of guards */
+ tor_assert_nonfatal(!options->UseBridges);
+ tor_assert_nonfatal(!options->EntryNodes);
+ /* Retry our directory fetches, so we have a fresh set of guard info */
log_fn(severity, LD_APP|LD_DIR,
"Application request when we haven't %s. "
"Optimistically trying directory fetches again.",
@@ -2076,7 +2137,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
} else {
/* XXXX Duplicates checks in connection_ap_handshake_attach_circuit:
* refactor into a single function. */
- const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 0);
int opt = conn->chosen_exit_optional;
if (node && !connection_ap_can_use_exit(conn, node)) {
log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP,
@@ -2131,22 +2192,25 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
/* If this is a hidden service trying to start an introduction point,
* handle that case. */
if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
+ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
/* need to pick an intro point */
- rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data;
- tor_assert(rend_data);
- extend_info = rend_client_get_random_intro(rend_data);
+ extend_info = hs_client_get_random_intro_from_edge(edge_conn);
if (!extend_info) {
- log_info(LD_REND,
- "No intro points for '%s': re-fetching service descriptor.",
- safe_str_client(rend_data_get_address(rend_data)));
- rend_client_refetch_v2_renddesc(rend_data);
+ log_info(LD_REND, "No intro points: re-fetching service descriptor.");
+ if (edge_conn->rend_data) {
+ rend_client_refetch_v2_renddesc(edge_conn->rend_data);
+ } else {
+ hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk);
+ }
connection_ap_mark_as_non_pending_circuit(conn);
ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT;
return 0;
}
log_info(LD_REND,"Chose %s as intro point for '%s'.",
extend_info_describe(extend_info),
- safe_str_client(rend_data_get_address(rend_data)));
+ (edge_conn->rend_data) ?
+ safe_str_client(rend_data_get_address(edge_conn->rend_data)) :
+ "service");
}
/* If we have specified a particular exit node for our
@@ -2156,7 +2220,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (conn->chosen_exit_name) {
const node_t *r;
int opt = conn->chosen_exit_optional;
- r = node_get_by_nickname(conn->chosen_exit_name, 1);
+ r = node_get_by_nickname(conn->chosen_exit_name, 0);
if (r && node_has_descriptor(r)) {
/* We might want to connect to an IPv6 bridge for loading
descriptors so we use the preferred address rather than
@@ -2234,7 +2298,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
new_circ_purpose == CIRCUIT_PURPOSE_C_INTRODUCING)) {
want_onehop = 1;
}
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) */
/* Determine what kind of a circuit to launch, and actually launch it. */
{
@@ -2242,6 +2306,16 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
if (need_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
if (need_internal) flags |= CIRCLAUNCH_IS_INTERNAL;
+
+ /* If we are about to pick a v3 RP right now, make sure we pick a
+ * rendezvous point that supports the v3 protocol! */
+ if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED &&
+ new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
+ ENTRY_TO_EDGE_CONN(conn)->hs_ident) {
+ flags |= CIRCLAUNCH_IS_V3_RP;
+ log_info(LD_GENERAL, "Getting rendezvous circuit to v3 service!");
+ }
+
circ = circuit_launch_by_extend_info(new_circ_purpose, extend_info,
flags);
}
@@ -2265,8 +2339,15 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
/* help predict this next time */
rep_hist_note_used_internal(time(NULL), need_uptime, 1);
if (circ) {
- /* write the service_id into circ */
- circ->rend_data = rend_data_dup(ENTRY_TO_EDGE_CONN(conn)->rend_data);
+ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ if (edge_conn->rend_data) {
+ /* write the service_id into circ */
+ circ->rend_data = rend_data_dup(edge_conn->rend_data);
+ } else if (edge_conn->hs_ident) {
+ circ->hs_ident =
+ hs_ident_circuit_new(&edge_conn->hs_ident->identity_pk,
+ HS_IDENT_CIRCUIT_INTRO);
+ }
if (circ->base_.purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
circ->base_.state == CIRCUIT_STATE_OPEN)
circuit_has_opened(circ);
@@ -2348,8 +2429,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ,
/* We are attaching a stream to a rendezvous circuit. That means
* that an attempt to connect to a hidden service just
* succeeded. Tell rendclient.c. */
- rend_client_note_connection_attempt_ended(
- ENTRY_TO_EDGE_CONN(apconn)->rend_data);
+ hs_client_note_connection_attempt_succeeded(ENTRY_TO_EDGE_CONN(apconn));
}
if (cpath) { /* we were given one; use it */
@@ -2556,7 +2636,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
* open to that exit. See what exit we meant, and whether we can use it.
*/
if (conn->chosen_exit_name) {
- const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 0);
int opt = conn->chosen_exit_optional;
if (!node && !want_onehop) {
/* We ran into this warning when trying to extend a circuit to a
@@ -2626,9 +2706,10 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
tor_assert(rendcirc);
/* one is already established, attach */
log_info(LD_REND,
- "rend joined circ %u already here. attaching. "
- "(stream %d sec old)",
- (unsigned)rendcirc->base_.n_circ_id, conn_age);
+ "rend joined circ %u (id: %" PRIu32 ") already here. "
+ "Attaching. (stream %d sec old)",
+ (unsigned) TO_CIRCUIT(rendcirc)->n_circ_id,
+ rendcirc->global_identifier, conn_age);
/* Mark rendezvous circuits as 'newly dirty' every time you use
* them, since the process of rebuilding a rendezvous circ is so
* expensive. There is a tradeoff between linkability and
@@ -2661,9 +2742,10 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
if (rendcirc && (rendcirc->base_.purpose ==
CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)) {
log_info(LD_REND,
- "pending-join circ %u already here, with intro ack. "
- "Stalling. (stream %d sec old)",
- (unsigned)rendcirc->base_.n_circ_id, conn_age);
+ "pending-join circ %u (id: %" PRIu32 ") already here, with "
+ "intro ack. Stalling. (stream %d sec old)",
+ (unsigned) TO_CIRCUIT(rendcirc)->n_circ_id,
+ rendcirc->global_identifier, conn_age);
return 0;
}
@@ -2675,10 +2757,13 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
if (retval > 0) {
/* one has already sent the intro. keep waiting. */
tor_assert(introcirc);
- log_info(LD_REND, "Intro circ %u present and awaiting ack (rend %u). "
- "Stalling. (stream %d sec old)",
- (unsigned)introcirc->base_.n_circ_id,
- rendcirc ? (unsigned)rendcirc->base_.n_circ_id : 0,
+ log_info(LD_REND, "Intro circ %u (id: %" PRIu32 ") present and "
+ "awaiting ACK. Rend circuit %u (id: %" PRIu32 "). "
+ "Stalling. (stream %d sec old)",
+ (unsigned) TO_CIRCUIT(introcirc)->n_circ_id,
+ introcirc->global_identifier,
+ rendcirc ? (unsigned) TO_CIRCUIT(rendcirc)->n_circ_id : 0,
+ rendcirc ? rendcirc->global_identifier : 0,
conn_age);
return 0;
}
@@ -2688,19 +2773,26 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
if (rendcirc && introcirc &&
rendcirc->base_.purpose == CIRCUIT_PURPOSE_C_REND_READY) {
log_info(LD_REND,
- "ready rend circ %u already here (no intro-ack yet on "
- "intro %u). (stream %d sec old)",
- (unsigned)rendcirc->base_.n_circ_id,
- (unsigned)introcirc->base_.n_circ_id, conn_age);
+ "ready rend circ %u (id: %" PRIu32 ") already here. No"
+ "intro-ack yet on intro %u (id: %" PRIu32 "). "
+ "(stream %d sec old)",
+ (unsigned) TO_CIRCUIT(rendcirc)->n_circ_id,
+ rendcirc->global_identifier,
+ (unsigned) TO_CIRCUIT(introcirc)->n_circ_id,
+ introcirc->global_identifier, conn_age);
tor_assert(introcirc->base_.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
if (introcirc->base_.state == CIRCUIT_STATE_OPEN) {
- log_info(LD_REND,"found open intro circ %u (rend %u); sending "
- "introduction. (stream %d sec old)",
- (unsigned)introcirc->base_.n_circ_id,
- (unsigned)rendcirc->base_.n_circ_id,
- conn_age);
- switch (rend_client_send_introduction(introcirc, rendcirc)) {
+ int ret;
+ log_info(LD_REND, "Found open intro circ %u (id: %" PRIu32 "). "
+ "Rend circuit %u (id: %" PRIu32 "); Sending "
+ "introduction. (stream %d sec old)",
+ (unsigned) TO_CIRCUIT(introcirc)->n_circ_id,
+ introcirc->global_identifier,
+ (unsigned) TO_CIRCUIT(rendcirc)->n_circ_id,
+ rendcirc->global_identifier, conn_age);
+ ret = hs_client_send_introduce1(introcirc, rendcirc);
+ switch (ret) {
case 0: /* success */
rendcirc->base_.timestamp_dirty = time(NULL);
introcirc->base_.timestamp_dirty = time(NULL);
@@ -2722,10 +2814,13 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
}
}
- log_info(LD_REND, "Intro (%u) and rend (%u) circs are not both ready. "
- "Stalling conn. (%d sec old)",
- introcirc ? (unsigned)introcirc->base_.n_circ_id : 0,
- rendcirc ? (unsigned)rendcirc->base_.n_circ_id : 0, conn_age);
+ log_info(LD_REND, "Intro %u (id: %" PRIu32 ") and rend circuit %u "
+ "(id: %" PRIu32 ") circuits are not both ready. "
+ "Stalling conn. (%d sec old)",
+ introcirc ? (unsigned) TO_CIRCUIT(introcirc)->n_circ_id : 0,
+ introcirc ? introcirc->global_identifier : 0,
+ rendcirc ? (unsigned) TO_CIRCUIT(rendcirc)->n_circ_id : 0,
+ rendcirc ? rendcirc->global_identifier : 0, conn_age);
return 0;
}
}
diff --git a/src/or/circuituse.h b/src/or/circuituse.h
index ad4c214a3b..2b0f983f1a 100644
--- a/src/or/circuituse.h
+++ b/src/or/circuituse.h
@@ -44,6 +44,9 @@ void circuit_build_failed(origin_circuit_t *circ);
/** Flag to set when the last hop of a circuit doesn't need to be an
* exit node. */
#define CIRCLAUNCH_IS_INTERNAL (1<<3)
+/** Flag to set when we are trying to launch a v3 rendezvous circuit. We need
+ * to apply some additional filters on the node picked. */
+#define CIRCLAUNCH_IS_V3_RP (1<<4)
origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
extend_info_t *info,
int flags);
@@ -68,7 +71,8 @@ STATIC int circuit_is_available_for_use(const circuit_t *circ);
STATIC int needs_exit_circuits(time_t now,
int *port_needs_uptime,
int *port_needs_capacity);
-STATIC int needs_hs_server_circuits(int num_uptime_internal);
+STATIC int needs_hs_server_circuits(time_t now,
+ int num_uptime_internal);
STATIC int needs_hs_client_circuits(time_t now,
int *needs_uptime,
@@ -78,7 +82,7 @@ STATIC int needs_hs_client_circuits(time_t now,
STATIC int needs_circuits_for_build(int num);
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
-#endif
+#endif /* !defined(TOR_CIRCUITUSE_H) */
diff --git a/src/or/command.c b/src/or/command.c
index be912dffa7..185596a65a 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -129,7 +129,7 @@ command_time_process_cell(cell_t *cell, channel_t *chan, int *time,
}
*time += time_passed;
}
-#endif
+#endif /* defined(KEEP_TIMING_STATS) */
/** Process a <b>cell</b> that was just received on <b>chan</b>. Keep internal
* statistics about how many of each cell we've processed so far
@@ -166,7 +166,7 @@ command_process_cell(channel_t *chan, cell_t *cell)
/* remember which second it is, for next time */
current_second = now;
}
-#endif
+#endif /* defined(KEEP_TIMING_STATS) */
#ifdef KEEP_TIMING_STATS
#define PROCESS_CELL(tp, cl, cn) STMT_BEGIN { \
@@ -174,9 +174,9 @@ command_process_cell(channel_t *chan, cell_t *cell)
command_time_process_cell(cl, cn, & tp ## time , \
command_process_ ## tp ## _cell); \
} STMT_END
-#else
+#else /* !(defined(KEEP_TIMING_STATS)) */
#define PROCESS_CELL(tp, cl, cn) command_process_ ## tp ## _cell(cl, cn)
-#endif
+#endif /* defined(KEEP_TIMING_STATS) */
switch (cell->command) {
case CELL_CREATE:
@@ -378,7 +378,8 @@ command_process_create_cell(cell_t *cell, channel_t *chan)
created_cell.handshake_len = len;
if (onionskin_answer(circ, &created_cell,
- (const char *)keys, rend_circ_nonce)<0) {
+ (const char *)keys, sizeof(keys),
+ rend_circ_nonce)<0) {
log_warn(LD_OR,"Failed to reply to CREATE_FAST cell. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
return;
diff --git a/src/or/command.h b/src/or/command.h
index 5079d42e75..c0d1996cbb 100644
--- a/src/or/command.h
+++ b/src/or/command.h
@@ -27,5 +27,5 @@ extern uint64_t stats_n_created_cells_processed;
extern uint64_t stats_n_relay_cells_processed;
extern uint64_t stats_n_destroy_cells_processed;
-#endif
+#endif /* !defined(TOR_COMMAND_H) */
diff --git a/src/or/config.c b/src/or/config.c
index cac4ce8125..79cfcb4111 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -92,6 +92,7 @@
#include "relay.h"
#include "rendclient.h"
#include "rendservice.h"
+#include "hs_config.h"
#include "rephist.h"
#include "router.h"
#include "sandbox.h"
@@ -114,9 +115,9 @@
* Coverity. Here's a kludge to unconfuse it.
*/
# define __INCLUDE_LEVEL__ 2
-# endif
+#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */
#include <systemd/sd-daemon.h>
-#endif
+#endif /* defined(HAVE_SYSTEMD) */
/* Prefix used to indicate a Unix socket in a FooPort configuration. */
static const char unix_socket_prefix[] = "unix:";
@@ -172,18 +173,26 @@ static config_abbrev_t option_abbrevs_[] = {
{ NULL, NULL, 0, 0},
};
+/** dummy instance of or_options_t, used for type-checking its
+ * members with CONF_CHECK_VAR_TYPE. */
+DUMMY_TYPECHECK_INSTANCE(or_options_t);
+
/** An entry for config_vars: "The option <b>name</b> has type
* CONFIG_TYPE_<b>conftype</b>, and corresponds to
* or_options_t.<b>member</b>"
*/
#define VAR(name,conftype,member,initvalue) \
- { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_options_t, member), \
- initvalue }
+ { name, CONFIG_TYPE_ ## conftype, offsetof(or_options_t, member), \
+ initvalue CONF_TEST_MEMBERS(or_options_t, conftype, member) }
/** As VAR, but the option name and member name are the same. */
#define V(member,conftype,initvalue) \
VAR(#member, conftype, member, initvalue)
/** An entry for config_vars: "The option <b>name</b> is obsolete." */
+#ifdef TOR_UNIT_TESTS
+#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL, {.INT=NULL} }
+#else
#define OBSOLETE(name) { name, CONFIG_TYPE_OBSOLETE, 0, NULL }
+#endif
/**
* Macro to declare *Port options. Each one comes in three entries.
@@ -206,7 +215,7 @@ static config_var_t option_vars_[] = {
VAR("AccountingRule", STRING, AccountingRule_option, "max"),
V(AccountingStart, STRING, NULL),
V(Address, STRING, NULL),
- V(AllowDotExit, BOOL, "0"),
+ OBSOLETE("AllowDotExit"),
OBSOLETE("AllowInvalidNodes"),
V(AllowNonRFC953Hostnames, BOOL, "0"),
OBSOLETE("AllowSingleHopCircuits"),
@@ -243,6 +252,7 @@ static config_var_t option_vars_[] = {
V(BridgePassword, STRING, NULL),
V(BridgeRecordUsageByCountry, BOOL, "1"),
V(BridgeRelay, BOOL, "0"),
+ V(BridgeDistribution, STRING, NULL),
V(CellStatistics, BOOL, "0"),
V(PaddingStatistics, BOOL, "1"),
V(LearnCircuitBuildTimeout, BOOL, "1"),
@@ -363,7 +373,7 @@ static config_var_t option_vars_[] = {
SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"),
V(GeoIPv6File, FILENAME,
SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip6"),
-#endif
+#endif /* defined(_WIN32) */
OBSOLETE("Group"),
V(GuardLifetime, INTERVAL, "0 minutes"),
V(HardwareAccel, BOOL, "0"),
@@ -392,6 +402,7 @@ static config_var_t option_vars_[] = {
V(HTTPProxyAuthenticator, STRING, NULL),
V(HTTPSProxy, STRING, NULL),
V(HTTPSProxyAuthenticator, STRING, NULL),
+ VPORT(HTTPTunnelPort),
V(IPv6Exit, BOOL, "0"),
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
V(ServerTransportListenAddr, LINELIST, NULL),
@@ -429,6 +440,7 @@ static config_var_t option_vars_[] = {
OBSOLETE("PredictedPortsRelevanceTime"),
OBSOLETE("WarnUnsafeSocks"),
VAR("NodeFamily", LINELIST, NodeFamilies, NULL),
+ V(NoExec, BOOL, "0"),
V(NumCPUs, UINT, "0"),
V(NumDirectoryGuards, UINT, "0"),
V(NumEntryGuards, UINT, "0"),
@@ -504,9 +516,12 @@ static config_var_t option_vars_[] = {
V(ServerDNSSearchDomains, BOOL, "0"),
V(ServerDNSTestAddresses, CSV,
"www.google.com,www.mit.edu,www.yahoo.com,www.slashdot.org"),
- V(SchedulerLowWaterMark__, MEMUNIT, "100 MB"),
- V(SchedulerHighWaterMark__, MEMUNIT, "101 MB"),
- V(SchedulerMaxFlushCells__, UINT, "1000"),
+ OBSOLETE("SchedulerLowWaterMark__"),
+ OBSOLETE("SchedulerHighWaterMark__"),
+ OBSOLETE("SchedulerMaxFlushCells__"),
+ V(KISTSchedRunInterval, MSEC_INTERVAL, "0 msec"),
+ V(KISTSockBufSizeFactor, DOUBLE, "1.0"),
+ V(Schedulers, CSV, "KIST,KISTLite,Vanilla"),
V(ShutdownWaitLength, INTERVAL, "30 seconds"),
OBSOLETE("SocksListenAddress"),
V(SocksPolicy, LINELIST, NULL),
@@ -605,7 +620,16 @@ static config_var_t option_vars_[] = {
* blackholed. Clients will try 3 directories simultaneously.
* (Relays never use simultaneous connections.) */
V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"),
- V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "1200, 900, 900, 3600"),
+ /* When a client has any running bridges, check each bridge occasionally,
+ * whether or not that bridge is actually up. */
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL,
+ "10800, 25200, 54000, 111600, 262800"),
+ /* When a client is just starting, or has no running bridges, check each
+ * bridge a few times quickly, and then try again later. These schedules
+ * are much longer than the other schedules, because we try each and every
+ * configured bridge with this schedule. */
+ V(TestingBridgeBootstrapDownloadSchedule, CSV_INTERVAL,
+ "0, 30, 90, 600, 3600, 10800, 25200, 54000, 111600, 262800"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
V(TestingConsensusMaxDownloadTries, UINT, "8"),
@@ -625,13 +649,12 @@ static config_var_t option_vars_[] = {
V(TestingDirAuthVoteHSDirIsStrict, BOOL, "0"),
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"),
- { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+ END_OF_CONFIG_VARS
};
/** Override default values with these if the user sets the TestingTorNetwork
* option. */
static const config_var_t testing_tor_network_defaults[] = {
- V(ServerDNSAllowBrokenConfig, BOOL, "1"),
V(DirAllowPrivateAddresses, BOOL, "1"),
V(EnforceDistinctSubnets, BOOL, "0"),
V(AssumeReachable, BOOL, "1"),
@@ -644,7 +667,7 @@ static const config_var_t testing_tor_network_defaults[] = {
"0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
V(ClientBootstrapConsensusMaxDownloadTries, UINT, "80"),
V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"),
- V(ClientDNSRejectInternalAddresses, BOOL,"0"), // deprecated in 0.2.9.2-alpha
+ V(ClientDNSRejectInternalAddresses, BOOL,"0"),
V(ClientRejectInternalAddresses, BOOL, "0"),
V(CountPrivateBandwidth, BOOL, "1"),
V(ExitPolicyRejectPrivate, BOOL, "0"),
@@ -655,7 +678,6 @@ static const config_var_t testing_tor_network_defaults[] = {
V(TestingV3AuthInitialVotingInterval, INTERVAL, "150 seconds"),
V(TestingV3AuthInitialVoteDelay, INTERVAL, "20 seconds"),
V(TestingV3AuthInitialDistDelay, INTERVAL, "20 seconds"),
- V(TestingV3AuthVotingStartOffset, INTERVAL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "0 minutes"),
V(TestingEstimatedDescriptorPropagationTime, INTERVAL, "0 minutes"),
V(MinUptimeHidServDirectoryV2, INTERVAL, "0 minutes"),
@@ -667,7 +689,9 @@ static const config_var_t testing_tor_network_defaults[] = {
"15, 20, 30, 60"),
V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
"15, 20, 30, 60"),
- V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "10, 30, 60"),
+ V(TestingBridgeBootstrapDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
+ "15, 20, 30, 60"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
V(TestingConsensusMaxDownloadTries, UINT, "80"),
@@ -680,7 +704,7 @@ static const config_var_t testing_tor_network_defaults[] = {
VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "1"),
V(RendPostPeriod, INTERVAL, "2 minutes"),
- { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+ END_OF_CONFIG_VARS
};
#undef VAR
@@ -688,12 +712,19 @@ static const config_var_t testing_tor_network_defaults[] = {
#undef OBSOLETE
static const config_deprecation_t option_deprecation_notes_[] = {
- /* Deprecated since 0.2.9.2-alpha... */
- { "AllowDotExit", "Unrestricted use of the .exit notation can be used for "
- "a wide variety of application-level attacks." },
- { "ClientDNSRejectInternalAddresses", "Turning this on makes your client "
- "easier to fingerprint, and may open you to esoteric attacks." },
- /* End of options deprecated since 0.2.9.2-alpha. */
+ /* Deprecated since 0.3.2.0-alpha. */
+ { "HTTPProxy", "It only applies to direct unencrypted HTTP connections "
+ "to your directory server, which your Tor probably wasn't using." },
+ { "HTTPProxyAuthenticator", "HTTPProxy is deprecated in favor of HTTPSProxy "
+ "which should be used with HTTPSProxyAuthenticator." },
+ /* End of options deprecated since 0.3.2.1-alpha */
+
+ /* Options deprecated since 0.3.2.2-alpha */
+ { "ReachableDirAddresses", "It has no effect on relays, and has had no "
+ "effect on clients since 0.2.8." },
+ { "ClientPreferIPv6DirPort", "It has no effect on relays, and has had no "
+ "effect on clients since 0.2.8." },
+ /* End of options deprecated since 0.3.2.2-alpha. */
{ NULL, NULL }
};
@@ -720,7 +751,6 @@ static int parse_ports(or_options_t *options, int validate_only,
static int check_server_ports(const smartlist_t *ports,
const or_options_t *options,
int *num_low_ports_out);
-
static int validate_data_directory(or_options_t *options);
static int write_configuration_file(const char *fname,
const or_options_t *options);
@@ -746,7 +776,7 @@ static uint64_t compute_real_max_mem_in_queues(const uint64_t val,
STATIC config_format_t options_format = {
sizeof(or_options_t),
OR_OPTIONS_MAGIC,
- STRUCT_OFFSET(or_options_t, magic_),
+ offsetof(or_options_t, magic_),
option_abbrevs_,
option_deprecation_notes_,
option_vars_,
@@ -777,6 +807,9 @@ static int have_parsed_cmdline = 0;
static char *global_dirfrontpagecontents = NULL;
/** List of port_cfg_t for all configured ports. */
static smartlist_t *configured_ports = NULL;
+/** True iff we're currently validating options, and any calls to
+ * get_options() are likely to be bugs. */
+static int in_option_validation = 0;
/** Return the contents of our frontpage string, or NULL if not configured. */
MOCK_IMPL(const char*,
@@ -790,6 +823,7 @@ MOCK_IMPL(or_options_t *,
get_options_mutable, (void))
{
tor_assert(global_options);
+ tor_assert_nonfatal(! in_option_validation);
return global_options;
}
@@ -916,6 +950,10 @@ or_options_free(or_options_t *options)
rs, routerset_free(rs));
smartlist_free(options->NodeFamilySets);
}
+ if (options->SchedulerTypes_) {
+ SMARTLIST_FOREACH(options->SchedulerTypes_, int *, i, tor_free(i));
+ smartlist_free(options->SchedulerTypes_);
+ }
tor_free(options->BridgePassword_AuthDigest_);
tor_free(options->command_arg);
tor_free(options->master_key_fname);
@@ -1011,6 +1049,23 @@ escaped_safe_str(const char *address)
return escaped(address);
}
+/**
+ * The severity level that should be used for warnings of severity
+ * LOG_PROTOCOL_WARN.
+ *
+ * We keep this outside the options, in case somebody needs to use
+ * LOG_PROTOCOL_WARN while an option transition is happening.
+ */
+static int protocol_warning_severity_level = LOG_WARN;
+
+/** Return the severity level that should be used for warnings of severity
+ * LOG_PROTOCOL_WARN. */
+int
+get_protocol_warning_severity_level(void)
+{
+ return protocol_warning_severity_level;
+}
+
/** List of default directory authorities */
static const char *default_authorities[] = {
@@ -1217,13 +1272,13 @@ options_act_reversible(const or_options_t *old_options, char **msg)
"on this OS/with this build.");
goto rollback;
}
-#else
+#else /* !(!defined(HAVE_SYS_UN_H)) */
if (options->ControlSocketsGroupWritable && !options->ControlSocket) {
*msg = tor_strdup("Setting ControlSocketGroupWritable without setting"
"a ControlSocket makes no sense.");
goto rollback;
}
-#endif
+#endif /* !defined(HAVE_SYS_UN_H) */
if (running_tor) {
int n_ports=0;
@@ -1300,7 +1355,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
goto rollback;
}
}
-#endif
+#endif /* defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) */
/* Attempt to lock all current and future memory with mlockall() only once */
if (options->DisableAllSwap) {
@@ -1352,7 +1407,7 @@ options_act_reversible(const or_options_t *old_options, char **msg)
options->DataDirectory, strerror(errno));
}
}
-#endif
+#endif /* !defined(_WIN32) */
/* Bail out at this point if we're not going to be a client or server:
* we don't run Tor itself. */
@@ -1579,6 +1634,10 @@ options_act(const or_options_t *old_options)
const int transition_affects_guards =
old_options && options_transition_affects_guards(old_options, options);
+ if (options->NoExec || options->Sandbox) {
+ tor_disable_spawning_background_processes();
+ }
+
/* disable ptrace and later, other basic debugging techniques */
{
/* Remember if we already disabled debugger attachment */
@@ -1613,6 +1672,11 @@ options_act(const or_options_t *old_options)
return -1;
}
+ if (options->ProtocolWarnings)
+ protocol_warning_severity_level = LOG_WARN;
+ else
+ protocol_warning_severity_level = LOG_INFO;
+
if (consider_adding_dir_servers(options, old_options) < 0)
return -1;
@@ -1630,7 +1694,7 @@ options_act(const or_options_t *old_options)
return -1;
}
/* LCOV_EXCL_STOP */
-#else
+#else /* !(defined(ENABLE_TOR2WEB_MODE)) */
if (options->Tor2webMode) {
log_err(LD_CONFIG, "This copy of Tor was not compiled to run in "
"'tor2web mode'. It cannot be run with the Tor2webMode torrc "
@@ -1638,7 +1702,7 @@ options_act(const or_options_t *old_options)
"--enable-tor2web-mode option.");
return -1;
}
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) */
/* If we are a bridge with a pluggable transport proxy but no
Extended ORPort, inform the user that they are missing out. */
@@ -1667,7 +1731,7 @@ options_act(const or_options_t *old_options)
sweep_bridge_list();
}
- if (running_tor && rend_config_services(options, 0)<0) {
+ if (running_tor && hs_config_service_all(options, 0)<0) {
log_warn(LD_BUG,
"Previously validated hidden services line could not be added!");
return -1;
@@ -1750,9 +1814,13 @@ options_act(const or_options_t *old_options)
}
/* Write our PID to the PID file. If we do not have write permissions we
- * will log a warning */
+ * will log a warning and exit. */
if (options->PidFile && !sandbox_is_active()) {
- write_pidfile(options->PidFile);
+ if (write_pidfile(options->PidFile) < 0) {
+ log_err(LD_CONFIG, "Unable to write PIDFile %s",
+ escaped(options->PidFile));
+ return -1;
+ }
}
/* Register addressmap directives */
@@ -1784,16 +1852,14 @@ options_act(const or_options_t *old_options)
monitor_owning_controller_process(options->OwningControllerProcess);
/* reload keys as needed for rendezvous services. */
- if (rend_service_load_all_keys(NULL)<0) {
+ if (hs_service_load_all_keys() < 0) {
log_warn(LD_GENERAL,"Error loading rendezvous service keys");
return -1;
}
- /* Set up scheduler thresholds */
- scheduler_set_watermarks((uint32_t)options->SchedulerLowWaterMark__,
- (uint32_t)options->SchedulerHighWaterMark__,
- (options->SchedulerMaxFlushCells__ > 0) ?
- options->SchedulerMaxFlushCells__ : 1000);
+ /* Inform the scheduler subsystem that a configuration changed happened. It
+ * might be a change of scheduler or parameter. */
+ scheduler_conf_changed();
/* Set up accounting */
if (accounting_parse_options(options, 0)<0) {
@@ -2139,6 +2205,7 @@ static const struct {
{ "--dump-config", ARGUMENT_OPTIONAL },
{ "--list-fingerprint", TAKES_NO_ARGUMENT },
{ "--keygen", TAKES_NO_ARGUMENT },
+ { "--key-expiration", ARGUMENT_OPTIONAL },
{ "--newpass", TAKES_NO_ARGUMENT },
{ "--no-passphrase", TAKES_NO_ARGUMENT },
{ "--passphrase-fd", ARGUMENT_NECESSARY },
@@ -2299,24 +2366,36 @@ options_trial_assign(config_line_t *list, unsigned flags, char **msg)
return r;
}
- if (options_validate(get_options_mutable(), trial_options,
+ setopt_err_t rv;
+ or_options_t *cur_options = get_options_mutable();
+
+ in_option_validation = 1;
+
+ if (options_validate(cur_options, trial_options,
global_default_options, 1, msg) < 0) {
or_options_free(trial_options);
- return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
+ rv = SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
+ goto done;
}
- if (options_transition_allowed(get_options(), trial_options, msg) < 0) {
+ if (options_transition_allowed(cur_options, trial_options, msg) < 0) {
or_options_free(trial_options);
- return SETOPT_ERR_TRANSITION;
+ rv = SETOPT_ERR_TRANSITION;
+ goto done;
}
+ in_option_validation = 0;
if (set_options(trial_options, msg)<0) {
or_options_free(trial_options);
- return SETOPT_ERR_SETTING;
+ rv = SETOPT_ERR_SETTING;
+ goto done;
}
/* we liked it. put it in place. */
- return SETOPT_OK;
+ rv = SETOPT_OK;
+ done:
+ in_option_validation = 0;
+ return rv;
}
/** Print a usage message for tor. */
@@ -2808,10 +2887,6 @@ compute_publishserverdescriptor(or_options_t *options)
* will generate too many circuits and potentially overload the network. */
#define MIN_CIRCUIT_STREAM_TIMEOUT 10
-/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might
- * expose more information than we're comfortable with. */
-#define MIN_HEARTBEAT_PERIOD (30*60)
-
/** Lowest recommended value for CircuitBuildTimeout; if it is set too low
* and LearnCircuitBuildTimeout is off, the failure rate for circuit
* construction may be very high. In that case, if it is set below this
@@ -2823,8 +2898,11 @@ static int
options_validate_cb(void *old_options, void *options, void *default_options,
int from_setconf, char **msg)
{
- return options_validate(old_options, options, default_options,
+ in_option_validation = 1;
+ int rv = options_validate(old_options, options, default_options,
from_setconf, msg);
+ in_option_validation = 0;
+ return rv;
}
#define REJECT(arg) \
@@ -2835,15 +2913,17 @@ options_validate_cb(void *old_options, void *options, void *default_options,
#else
#define COMPLAIN(args, ...) \
STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END
-#endif
+#endif /* defined(__GNUC__) && __GNUC__ <= 3 */
/** Log a warning message iff <b>filepath</b> is not absolute.
* Warning message must contain option name <b>option</b> and
* an absolute path that <b>filepath</b> will resolve to.
*
* In case <b>filepath</b> is absolute, do nothing.
+ *
+ * Return 1 if there were relative paths; 0 otherwise.
*/
-static void
+static int
warn_if_option_path_is_relative(const char *option,
char *filepath)
{
@@ -2852,39 +2932,100 @@ warn_if_option_path_is_relative(const char *option,
COMPLAIN("Path for %s (%s) is relative and will resolve to %s."
" Is this what you wanted?", option, filepath, abs_path);
tor_free(abs_path);
+ return 1;
}
+ return 0;
}
/** Scan <b>options</b> for occurances of relative file/directory
* path and log a warning whenever it is found.
+ *
+ * Return 1 if there were relative paths; 0 otherwise.
*/
-static void
+static int
warn_about_relative_paths(or_options_t *options)
{
tor_assert(options);
+ int n = 0;
- warn_if_option_path_is_relative("CookieAuthFile",
- options->CookieAuthFile);
- warn_if_option_path_is_relative("ExtORPortCookieAuthFile",
- options->ExtORPortCookieAuthFile);
- warn_if_option_path_is_relative("DirPortFrontPage",
- options->DirPortFrontPage);
- warn_if_option_path_is_relative("V3BandwidthsFile",
- options->V3BandwidthsFile);
- warn_if_option_path_is_relative("ControlPortWriteToFile",
- options->ControlPortWriteToFile);
- warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile);
- warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File);
- warn_if_option_path_is_relative("Log",options->DebugLogFile);
- warn_if_option_path_is_relative("AccelDir",options->AccelDir);
- warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
- warn_if_option_path_is_relative("PidFile",options->PidFile);
+ n += warn_if_option_path_is_relative("CookieAuthFile",
+ options->CookieAuthFile);
+ n += warn_if_option_path_is_relative("ExtORPortCookieAuthFile",
+ options->ExtORPortCookieAuthFile);
+ n += warn_if_option_path_is_relative("DirPortFrontPage",
+ options->DirPortFrontPage);
+ n += warn_if_option_path_is_relative("V3BandwidthsFile",
+ options->V3BandwidthsFile);
+ n += warn_if_option_path_is_relative("ControlPortWriteToFile",
+ options->ControlPortWriteToFile);
+ n += warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile);
+ n += warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File);
+ n += warn_if_option_path_is_relative("Log",options->DebugLogFile);
+ n += warn_if_option_path_is_relative("AccelDir",options->AccelDir);
+ n += warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
+ n += warn_if_option_path_is_relative("PidFile",options->PidFile);
for (config_line_t *hs_line = options->RendConfigLines; hs_line;
hs_line = hs_line->next) {
if (!strcasecmp(hs_line->key, "HiddenServiceDir"))
- warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value);
+ n += warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value);
+ }
+ return n != 0;
+}
+
+/* Validate options related to the scheduler. From the Schedulers list, the
+ * SchedulerTypes_ list is created with int values so once we select the
+ * scheduler, which can happen anytime at runtime, we don't have to parse
+ * strings and thus be quick.
+ *
+ * Return 0 on success else -1 and msg is set with an error message. */
+static int
+options_validate_scheduler(or_options_t *options, char **msg)
+{
+ tor_assert(options);
+ tor_assert(msg);
+
+ if (!options->Schedulers || smartlist_len(options->Schedulers) == 0) {
+ REJECT("Empty Schedulers list. Either remove the option so the defaults "
+ "can be used or set at least one value.");
+ }
+ /* Ok, we do have scheduler types, validate them. */
+ options->SchedulerTypes_ = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(options->Schedulers, const char *, type) {
+ int *sched_type;
+ if (!strcasecmp("KISTLite", type)) {
+ sched_type = tor_malloc_zero(sizeof(int));
+ *sched_type = SCHEDULER_KIST_LITE;
+ smartlist_add(options->SchedulerTypes_, sched_type);
+ } else if (!strcasecmp("KIST", type)) {
+ sched_type = tor_malloc_zero(sizeof(int));
+ *sched_type = SCHEDULER_KIST;
+ smartlist_add(options->SchedulerTypes_, sched_type);
+ } else if (!strcasecmp("Vanilla", type)) {
+ sched_type = tor_malloc_zero(sizeof(int));
+ *sched_type = SCHEDULER_VANILLA;
+ smartlist_add(options->SchedulerTypes_, sched_type);
+ } else {
+ tor_asprintf(msg, "Unknown type %s in option Schedulers. "
+ "Possible values are KIST, KISTLite and Vanilla.",
+ escaped(type));
+ return -1;
+ }
+ } SMARTLIST_FOREACH_END(type);
+
+ if (options->KISTSockBufSizeFactor < 0) {
+ REJECT("KISTSockBufSizeFactor must be at least 0");
+ }
+
+ /* Don't need to validate that the Interval is less than anything because
+ * zero is valid and all negative values are valid. */
+ if (options->KISTSchedRunInterval > KIST_SCHED_RUN_INTERVAL_MAX) {
+ tor_asprintf(msg, "KISTSchedRunInterval must not be more than %d (ms)",
+ KIST_SCHED_RUN_INTERVAL_MAX);
+ return -1;
}
+
+ return 0;
}
/* Validate options related to single onion services.
@@ -2915,7 +3056,8 @@ options_validate_single_onion(or_options_t *options, char **msg)
const int client_port_set = (options->SocksPort_set ||
options->TransPort_set ||
options->NATDPort_set ||
- options->DNSPort_set);
+ options->DNSPort_set ||
+ options->HTTPTunnelPort_set);
if (rend_service_non_anonymous_mode_enabled(options) && client_port_set &&
!options->Tor2webMode) {
REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as "
@@ -2987,7 +3129,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
* Always use the value of UseEntryGuards, not UseEntryGuards_option. */
options->UseEntryGuards = options->UseEntryGuards_option;
- warn_about_relative_paths(options);
+ if (warn_about_relative_paths(options) && options->RunAsDaemon) {
+ REJECT("You have specified at least one relative path (see above) "
+ "with the RunAsDaemon option. RunAsDaemon is not compatible "
+ "with relative paths.");
+ }
if (server_mode(options) &&
(!strcmpstart(uname, "Windows 95") ||
@@ -3070,7 +3216,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"and OS X/Darwin-specific feature.");
#else
options->TransProxyType_parsed = TPT_PF_DIVERT;
-#endif
+#endif /* !defined(OpenBSD) && !defined( DARWIN ) */
} else if (!strcasecmp(options->TransProxyType, "tproxy")) {
#if !defined(__linux__)
REJECT("TPROXY is a Linux-specific feature.");
@@ -3084,7 +3230,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"and OS X/Darwin-specific feature.");
#else
options->TransProxyType_parsed = TPT_IPFW;
-#endif
+#endif /* !defined(KERNEL_MAY_SUPPORT_IPFW) */
} else {
REJECT("Unrecognized value for TransProxyType");
}
@@ -3094,10 +3240,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Cannot use TransProxyType without any valid TransPort.");
}
}
-#else
+#else /* !(defined(USE_TRANSPARENT)) */
if (options->TransPort_set)
REJECT("TransPort is disabled in this build.");
-#endif
+#endif /* defined(USE_TRANSPARENT) */
if (options->TokenBucketRefillInterval <= 0
|| options->TokenBucketRefillInterval > 1000) {
@@ -3110,17 +3256,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeNodes);
}
- if (options->SchedulerLowWaterMark__ == 0 ||
- options->SchedulerLowWaterMark__ > UINT32_MAX) {
- log_warn(LD_GENERAL, "Bad SchedulerLowWaterMark__ option");
- return -1;
- } else if (options->SchedulerHighWaterMark__ <=
- options->SchedulerLowWaterMark__ ||
- options->SchedulerHighWaterMark__ > UINT32_MAX) {
- log_warn(LD_GENERAL, "Bad SchedulerHighWaterMark option");
- return -1;
- }
-
if (options->NodeFamilies) {
options->NodeFamilySets = smartlist_new();
for (cl = options->NodeFamilies; cl; cl = cl->next) {
@@ -3165,7 +3300,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"UseEntryGuards. Disabling.");
options->UseEntryGuards = 0;
}
- if (!options->DownloadExtraInfo && authdir_mode_any_main(options)) {
+ if (!options->DownloadExtraInfo && authdir_mode_v3(options)) {
log_info(LD_CONFIG, "Authoritative directories always try to download "
"extra-info documents. Setting DownloadExtraInfo.");
options->DownloadExtraInfo = 1;
@@ -3383,6 +3518,15 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Relays cannot set ReducedConnectionPadding. ");
}
+ if (options->BridgeDistribution) {
+ if (!options->BridgeRelay) {
+ REJECT("You set BridgeDistribution, but you didn't set BridgeRelay!");
+ }
+ if (check_bridge_distribution_setting(options->BridgeDistribution) < 0) {
+ REJECT("Invalid BridgeDistribution value.");
+ }
+ }
+
if (options->MinUptimeHidServDirectoryV2 < 0) {
log_warn(LD_CONFIG, "MinUptimeHidServDirectoryV2 option must be at "
"least 0 seconds. Changing to 0.");
@@ -3395,7 +3539,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->RendPostPeriod < min_rendpostperiod) {
log_warn(LD_CONFIG, "RendPostPeriod option is too short; "
"raising to %d seconds.", min_rendpostperiod);
- options->RendPostPeriod = min_rendpostperiod;;
+ options->RendPostPeriod = min_rendpostperiod;
}
if (options->RendPostPeriod > MAX_DIR_PERIOD) {
@@ -3429,7 +3573,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
"Tor2WebMode is enabled; disabling UseEntryGuards.");
options->UseEntryGuards = 0;
}
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) */
if (options->Tor2webRendezvousPoints && !options->Tor2webMode) {
REJECT("Tor2webRendezvousPoints cannot be set without Tor2webMode.");
@@ -3575,6 +3719,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("PortForwarding is not compatible with Sandbox; at most one can "
"be set");
}
+ if (options->PortForwarding && options->NoExec) {
+ COMPLAIN("Both PortForwarding and NoExec are set; PortForwarding will "
+ "be ignored.");
+ }
if (ensure_bandwidth_cap(&options->BandwidthRate,
"BandwidthRate", msg) < 0)
@@ -4013,7 +4161,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
COMPLAIN("V3AuthVotingInterval does not divide evenly into 24 hours.");
}
- if (rend_config_services(options, 1) < 0)
+ if (hs_config_service_all(options, 1) < 0)
REJECT("Failed to configure rendezvous options. See logs for details.");
/* Parse client-side authorization for hidden services. */
@@ -4057,6 +4205,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingServerConsensusDownloadSchedule);
CHECK_DEFAULT(TestingClientConsensusDownloadSchedule);
CHECK_DEFAULT(TestingBridgeDownloadSchedule);
+ CHECK_DEFAULT(TestingBridgeBootstrapDownloadSchedule);
CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
CHECK_DEFAULT(TestingDirConnectionMaxStall);
CHECK_DEFAULT(TestingConsensusMaxDownloadTries);
@@ -4070,6 +4219,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingLinkKeySlop);
#undef CHECK_DEFAULT
+ if (!options->ClientDNSRejectInternalAddresses &&
+ !(options->DirAuthorities ||
+ (options->AlternateDirAuthority && options->AlternateBridgeAuthority)))
+ REJECT("ClientDNSRejectInternalAddresses used for default network.");
if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2)
REJECT("SigningKeyLifetime is too short.");
if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2)
@@ -4233,6 +4386,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
"combination.");
+ if (options_validate_scheduler(options, msg) < 0) {
+ return -1;
+ }
+
return 0;
}
@@ -4267,7 +4424,7 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess)
#else
/* (presumably) 32-bit system. Let's hope for 1 GB. */
result = ONE_GIGABYTE;
-#endif
+#endif /* SIZEOF_VOID_P >= 8 */
} else {
/* We detected it, so let's pick 3/4 of the total RAM as our limit. */
const uint64_t avail = (ram / 4) * 3;
@@ -4452,6 +4609,12 @@ options_transition_allowed(const or_options_t *old,
return -1;
}
+ if (old->NoExec && !new_val->NoExec) {
+ *msg = tor_strdup("While Tor is running, disabling "
+ "NoExec is not allowed.");
+ return -1;
+ }
+
if (sandbox_is_active()) {
#define SB_NOCHANGE_STR(opt) \
do { \
@@ -4543,6 +4706,8 @@ options_transition_affects_descriptor(const or_options_t *old_options,
get_effective_bwburst(old_options) !=
get_effective_bwburst(new_options) ||
!opt_streq(old_options->ContactInfo, new_options->ContactInfo) ||
+ !opt_streq(old_options->BridgeDistribution,
+ new_options->BridgeDistribution) ||
!config_lines_eq(old_options->MyFamily, new_options->MyFamily) ||
!opt_streq(old_options->AccountingStart, new_options->AccountingStart) ||
old_options->AccountingMax != new_options->AccountingMax ||
@@ -4596,7 +4761,7 @@ get_windows_conf_root(void)
path[sizeof(path)-1] = '\0';
#else
strlcpy(path,tpath,sizeof(path));
-#endif
+#endif /* defined(UNICODE) */
/* Now we need to free the memory that the path-idl was stored in. In
* typical Windows fashion, we can't just call 'free()' on it. */
@@ -4612,7 +4777,7 @@ get_windows_conf_root(void)
is_set = 1;
return path;
}
-#endif
+#endif /* defined(_WIN32) */
/** Return the default location for our torrc file (if <b>defaults_file</b> is
* false), or for the torrc-defaults file (if <b>defaults_file</b> is true). */
@@ -4636,7 +4801,7 @@ get_default_conf_file(int defaults_file)
}
#else
return defaults_file ? CONFDIR "/torrc-defaults" : CONFDIR "/torrc";
-#endif
+#endif /* defined(DISABLE_SYSTEM_TORRC) || ... */
}
/** Verify whether lst is a list of strings containing valid-looking
@@ -4787,9 +4952,9 @@ find_torrc_filename(config_line_t *cmd_arg,
} else {
fname = dflt ? tor_strdup(dflt) : NULL;
}
-#else
+#else /* !(!defined(_WIN32)) */
fname = dflt ? tor_strdup(dflt) : NULL;
-#endif
+#endif /* !defined(_WIN32) */
}
}
return fname;
@@ -4940,6 +5105,9 @@ options_init_from_torrc(int argc, char **argv)
for (p_index = cmdline_only_options; p_index; p_index = p_index->next) {
if (!strcmp(p_index->key,"--keygen")) {
command = CMD_KEYGEN;
+ } else if (!strcmp(p_index->key, "--key-expiration")) {
+ command = CMD_KEY_EXPIRATION;
+ command_arg = p_index->value;
} else if (!strcmp(p_index->key,"--list-fingerprint")) {
command = CMD_LIST_FINGERPRINT;
} else if (!strcmp(p_index->key, "--hash-password")) {
@@ -5187,6 +5355,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
}
newoptions->IncludeUsed = cf_has_include;
+ in_option_validation = 1;
/* Validate newoptions */
if (options_validate(oldoptions, newoptions, newdefaultoptions,
@@ -5199,17 +5368,20 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err = SETOPT_ERR_TRANSITION;
goto err;
}
+ in_option_validation = 0;
if (set_options(newoptions, msg)) {
err = SETOPT_ERR_SETTING;
goto err; /* frees and replaces old options */
}
+
or_options_free(global_default_options);
global_default_options = newdefaultoptions;
return SETOPT_OK;
err:
+ in_option_validation = 0;
or_options_free(newoptions);
or_options_free(newdefaultoptions);
if (*msg) {
@@ -5413,7 +5585,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
}
#else
log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry.");
-#endif
+#endif /* defined(HAVE_SYSLOG_H) */
goto cleanup;
}
@@ -5730,6 +5902,15 @@ parse_transport_line(const or_options_t *options,
goto err;
}
+ if (is_managed && options->NoExec) {
+ log_warn(LD_CONFIG,
+ "Managed proxies are not compatible with NoExec mode; ignoring."
+ "(%sTransportPlugin line was %s)",
+ server ? "Server" : "Client", escaped(line));
+ r = 0;
+ goto done;
+ }
+
if (is_managed) {
/* managed */
@@ -6000,6 +6181,8 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
dirinfo_type_t type = 0;
double weight = 1.0;
+ memset(v3_digest, 0, sizeof(v3_digest));
+
items = smartlist_new();
smartlist_split_string(items, line, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
@@ -6249,7 +6432,6 @@ port_cfg_new(size_t namelen)
cfg->entry_cfg.ipv6_traffic = 1;
cfg->entry_cfg.dns_request = 1;
cfg->entry_cfg.onion_traffic = 1;
- cfg->entry_cfg.cache_ipv4_answers = 1;
cfg->entry_cfg.prefer_ipv6_virtaddr = 1;
return cfg;
}
@@ -6264,8 +6446,9 @@ port_cfg_free(port_cfg_t *port)
/** Warn for every port in <b>ports</b> of type <b>listener_type</b> that is
* on a publicly routable address. */
static void
-warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname,
- int listener_type)
+warn_nonlocal_client_ports(const smartlist_t *ports,
+ const char *portname,
+ const int listener_type)
{
SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
if (port->type != listener_type)
@@ -6422,6 +6605,55 @@ warn_client_dns_cache(const char *option, int disabling)
}
/**
+ * Validate the configured bridge distribution method from a BridgeDistribution
+ * config line.
+ *
+ * The input <b>bd</b>, is a string taken from the BridgeDistribution config
+ * line (if present). If the option wasn't set, return 0 immediately. The
+ * BridgeDistribution option is then validated. Currently valid, recognised
+ * options are:
+ *
+ * - "none"
+ * - "any"
+ * - "https"
+ * - "email"
+ * - "moat"
+ * - "hyphae"
+ *
+ * If the option string is unrecognised, a warning will be logged and 0 is
+ * returned. If the option string contains an invalid character, -1 is
+ * returned.
+ **/
+STATIC int
+check_bridge_distribution_setting(const char *bd)
+{
+ if (bd == NULL)
+ return 0;
+
+ const char *RECOGNIZED[] = {
+ "none", "any", "https", "email", "moat", "hyphae"
+ };
+ unsigned i;
+ for (i = 0; i < ARRAY_LENGTH(RECOGNIZED); ++i) {
+ if (!strcmp(bd, RECOGNIZED[i]))
+ return 0;
+ }
+
+ const char *cp = bd;
+ // Method = (KeywordChar | "_") +
+ while (TOR_ISALNUM(*cp) || *cp == '-' || *cp == '_')
+ ++cp;
+
+ if (*cp == 0) {
+ log_warn(LD_CONFIG, "Unrecognized BridgeDistribution value %s. I'll "
+ "assume you know what you are doing...", escaped(bd));
+ return 0; // we reached the end of the string; all is well
+ } else {
+ return -1; // we found a bad character in the string.
+ }
+}
+
+/**
* Parse port configuration for a single port type.
*
* Read entries of the "FooPort" type from the list <b>ports</b>. Syntax is
@@ -6515,7 +6747,7 @@ parse_port_config(smartlist_t *out,
bind_ipv4_only = 0, bind_ipv6_only = 0,
ipv4_traffic = 1, ipv6_traffic = 1, prefer_ipv6 = 0, dns_request = 1,
onion_traffic = 1,
- cache_ipv4 = 1, use_cached_ipv4 = 0,
+ cache_ipv4 = 0, use_cached_ipv4 = 0,
cache_ipv6 = 0, use_cached_ipv6 = 0,
prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0,
relax_dirmode_check = 0,
@@ -6614,7 +6846,7 @@ parse_port_config(smartlist_t *out,
} else if (!strcasecmp(elt, "AllAddrs")) {
all_addrs = 1;
-#endif
+#endif /* 0 */
} else if (!strcasecmp(elt, "IPv4Only")) {
bind_ipv4_only = 1;
} else if (!strcasecmp(elt, "IPv6Only")) {
@@ -6950,7 +7182,8 @@ parse_ports(or_options_t *options, int validate_only,
options->SocksPort_lines,
"Socks", CONN_TYPE_AP_LISTENER,
"127.0.0.1", 9050,
- CL_PORT_WARN_NONLOCAL|CL_PORT_TAKES_HOSTNAMES|gw_flag) < 0) {
+ ((validate_only ? 0 : CL_PORT_WARN_NONLOCAL)
+ | CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) {
*msg = tor_strdup("Invalid SocksPort configuration");
goto err;
}
@@ -6978,6 +7211,15 @@ parse_ports(or_options_t *options, int validate_only,
*msg = tor_strdup("Invalid NatdPort configuration");
goto err;
}
+ if (parse_port_config(ports,
+ options->HTTPTunnelPort_lines,
+ "HTTP Tunnel", CONN_TYPE_AP_HTTP_CONNECT_LISTENER,
+ "127.0.0.1", 0,
+ ((validate_only ? 0 : CL_PORT_WARN_NONLOCAL)
+ | CL_PORT_TAKES_HOSTNAMES | gw_flag)) < 0) {
+ *msg = tor_strdup("Invalid HTTPTunnelPort configuration");
+ goto err;
+ }
{
unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS |
CL_PORT_WARN_NONLOCAL;
@@ -7055,6 +7297,8 @@ parse_ports(or_options_t *options, int validate_only,
!! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1);
options->NATDPort_set =
!! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1);
+ options->HTTPTunnelPort_set =
+ !! count_real_listeners(ports, CONN_TYPE_AP_HTTP_CONNECT_LISTENER, 1);
/* Use options->ControlSocket to test if a control socket is set */
options->ControlPort_set =
!! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0);
@@ -7385,7 +7629,7 @@ normalize_data_directory(or_options_t *options)
strlcpy(p,get_windows_conf_root(),MAX_PATH);
options->DataDirectory = p;
return 0;
-#else
+#else /* !(defined(_WIN32)) */
const char *d = options->DataDirectory;
if (!d)
d = "~/.tor";
@@ -7411,7 +7655,7 @@ normalize_data_directory(or_options_t *options)
options->DataDirectory = fn;
}
return 0;
-#endif
+#endif /* defined(_WIN32) */
}
/** Check and normalize the value of options->DataDirectory; return 0 if it
@@ -7899,13 +8143,28 @@ parse_outbound_addresses(or_options_t *options, int validate_only, char **msg)
memset(&options->OutboundBindAddresses, 0,
sizeof(options->OutboundBindAddresses));
}
- parse_outbound_address_lines(options->OutboundBindAddress,
- OUTBOUND_ADDR_EXIT_AND_OR, options, validate_only, msg);
- parse_outbound_address_lines(options->OutboundBindAddressOR,
- OUTBOUND_ADDR_OR, options, validate_only, msg);
- parse_outbound_address_lines(options->OutboundBindAddressExit,
- OUTBOUND_ADDR_EXIT, options, validate_only, msg);
+
+ if (parse_outbound_address_lines(options->OutboundBindAddress,
+ OUTBOUND_ADDR_EXIT_AND_OR, options,
+ validate_only, msg) < 0) {
+ goto err;
+ }
+
+ if (parse_outbound_address_lines(options->OutboundBindAddressOR,
+ OUTBOUND_ADDR_OR, options, validate_only,
+ msg) < 0) {
+ goto err;
+ }
+
+ if (parse_outbound_address_lines(options->OutboundBindAddressExit,
+ OUTBOUND_ADDR_EXIT, options, validate_only,
+ msg) < 0) {
+ goto err;
+ }
+
return 0;
+ err:
+ return -1;
}
/** Load one of the geoip files, <a>family</a> determining which
@@ -7927,10 +8186,10 @@ config_load_geoip_file_(sa_family_t family,
}
geoip_load_file(family, fname);
tor_free(free_fname);
-#else
+#else /* !(defined(_WIN32)) */
(void)default_fname;
geoip_load_file(family, fname);
-#endif
+#endif /* defined(_WIN32) */
}
/** Load geoip files for IPv4 and IPv6 if <a>options</a> and
@@ -8005,9 +8264,9 @@ init_cookie_authentication(const char *fname, const char *header,
log_warn(LD_FS,"Unable to make %s group-readable.", escaped(fname));
}
}
-#else
+#else /* !(!defined(_WIN32)) */
(void) group_readable;
-#endif
+#endif /* !defined(_WIN32) */
/* Success! */
log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname));
diff --git a/src/or/config.h b/src/or/config.h
index 27aec7fe3d..efdd8c59b0 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -18,6 +18,10 @@
#define KERNEL_MAY_SUPPORT_IPFW
#endif
+/** Lowest allowable value for HeartbeatPeriod; if this is too low, we might
+ * expose more information than we're comfortable with. */
+#define MIN_HEARTBEAT_PERIOD (30*60)
+
MOCK_DECL(const char*, get_dirportfrontpage, (void));
MOCK_DECL(const or_options_t *, get_options, (void));
MOCK_DECL(or_options_t *, get_options_mutable, (void));
@@ -27,6 +31,7 @@ const char *safe_str_client(const char *address);
const char *safe_str(const char *address);
const char *escaped_safe_str_client(const char *address);
const char *escaped_safe_str(const char *address);
+int get_protocol_warning_severity_level(void);
const char *get_version(void);
const char *get_short_version(void);
setopt_err_t options_trial_assign(config_line_t *list, unsigned flags,
@@ -198,7 +203,9 @@ STATIC int parse_port_config(smartlist_t *out,
const char *defaultaddr,
int defaultport,
const unsigned flags);
-#endif
-#endif
+STATIC int check_bridge_distribution_setting(const char *bd);
+#endif /* defined(CONFIG_PRIVATE) */
+
+#endif /* !defined(TOR_CONFIG_H) */
diff --git a/src/or/confparse.h b/src/or/confparse.h
index 9c4205d07c..6f0b3b325c 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -40,6 +40,36 @@ typedef enum config_type_t {
CONFIG_TYPE_OBSOLETE, /**< Obsolete (ignored) option. */
} config_type_t;
+#ifdef TOR_UNIT_TESTS
+/**
+ * Union used when building in test mode typechecking the members of a type
+ * used with confparse.c. See CONF_CHECK_VAR_TYPE for a description of how
+ * it is used. */
+typedef union {
+ char **STRING;
+ char **FILENAME;
+ int *UINT; /* yes, really: Even though the confparse type is called
+ * "UINT", it still uses the C int type -- it just enforces that
+ * the values are in range [0,INT_MAX].
+ */
+ int *INT;
+ int *PORT;
+ int *INTERVAL;
+ int *MSEC_INTERVAL;
+ uint64_t *MEMUNIT;
+ double *DOUBLE;
+ int *BOOL;
+ int *AUTOBOOL;
+ time_t *ISOTIME;
+ smartlist_t **CSV;
+ smartlist_t **CSV_INTERVAL;
+ config_line_t **LINELIST;
+ config_line_t **LINELIST_S;
+ config_line_t **LINELIST_V;
+ routerset_t **ROUTERSET;
+} confparse_dummy_values_t;
+#endif
+
/** An abbreviation for a configuration option allowed on the command line. */
typedef struct config_abbrev_t {
const char *abbreviated;
@@ -64,8 +94,52 @@ typedef struct config_var_t {
* value. */
off_t var_offset; /**< Offset of the corresponding member of or_options_t. */
const char *initvalue; /**< String (or null) describing initial value. */
+
+#ifdef TOR_UNIT_TESTS
+ /** Used for compiler-magic to typecheck the corresponding field in the
+ * corresponding struct. Only used in unit test mode, at compile-time. */
+ confparse_dummy_values_t var_ptr_dummy;
+#endif
} config_var_t;
+/* Macros to define extra members inside config_var_t fields, and at the
+ * end of a list of them.
+ */
+#ifdef TOR_UNIT_TESTS
+/* This is a somewhat magic type-checking macro for users of confparse.c.
+ * It initializes a union member "confparse_dummy_values_t.conftype" with
+ * the address of a static member "tp_dummy.member". This
+ * will give a compiler warning unless the member field is of the correct
+ * type.
+ *
+ * (This warning is mandatory, because a type mismatch here violates the type
+ * compatibility constraint for simple assignment, and requires a diagnostic,
+ * according to the C spec.)
+ *
+ * For example, suppose you say:
+ * "CONF_CHECK_VAR_TYPE(or_options_t, STRING, Address)".
+ * Then this macro will evaluate to:
+ * { .STRING = &or_options_t_dummy.Address }
+ * And since confparse_dummy_values_t.STRING has type "char **", that
+ * expression will create a warning unless or_options_t.Address also
+ * has type "char *".
+ */
+#define CONF_CHECK_VAR_TYPE(tp, conftype, member) \
+ { . conftype = &tp ## _dummy . member }
+#define CONF_TEST_MEMBERS(tp, conftype, member) \
+ , CONF_CHECK_VAR_TYPE(tp, conftype, member)
+#define END_OF_CONFIG_VARS \
+ { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL, { .INT=NULL } }
+#define DUMMY_TYPECHECK_INSTANCE(tp) \
+ static tp tp ## _dummy
+#else
+#define CONF_TEST_MEMBERS(tp, conftype, member)
+#define END_OF_CONFIG_VARS { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+/* Repeatedly declarable incomplete struct to absorb redundant semicolons */
+#define DUMMY_TYPECHECK_INSTANCE(tp) \
+ struct tor_semicolon_eater
+#endif
+
/** Type of a callback to validate whether a given configuration is
* well-formed and consistent. See options_trial_assign() for documentation
* of arguments. */
@@ -129,5 +203,5 @@ const char *config_expand_abbrev(const config_format_t *fmt,
int command_line, int warn_obsolete);
void warn_deprecated_option(const char *what, const char *why);
-#endif
+#endif /* !defined(TOR_CONFPARSE_H) */
diff --git a/src/or/connection.c b/src/or/connection.c
index fc0646b885..ed8de05d78 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -37,7 +37,7 @@
* they call connection_stop_reading() or connection_stop_writing().
*
* To queue data to be written on a connection, call
- * connection_write_to_buf(). When data arrives, the
+ * connection_buf_add(). When data arrives, the
* connection_process_inbuf() callback is invoked, which dispatches to a
* type-specific function (such as connection_edge_process_inbuf() for
* example). Connection types that need notice of when data has been written
@@ -58,6 +58,7 @@
#include "or.h"
#include "bridges.h"
#include "buffers.h"
+#include "buffers_tls.h"
/*
* Define this so we get channel internal functions, since we're implementing
* part of a subclass (channel_tls_t).
@@ -85,7 +86,10 @@
#include "geoip.h"
#include "main.h"
#include "hs_common.h"
+#include "hs_ident.h"
#include "nodelist.h"
+#include "proto_http.h"
+#include "proto_socks.h"
#include "policies.h"
#include "reasons.h"
#include "relay.h"
@@ -124,8 +128,9 @@ static int connection_finished_flushing(connection_t *conn);
static int connection_flushed_some(connection_t *conn);
static int connection_finished_connecting(connection_t *conn);
static int connection_reached_eof(connection_t *conn);
-static int connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
- int *socket_error);
+static int connection_buf_read_from_socket(connection_t *conn,
+ ssize_t *max_to_read,
+ int *socket_error);
static int connection_process_inbuf(connection_t *conn, int package_partial);
static void client_check_address_changed(tor_socket_t sock);
static void set_constrained_socket_buffers(tor_socket_t sock, int size);
@@ -158,7 +163,8 @@ static smartlist_t *outgoing_addrs = NULL;
case CONN_TYPE_CONTROL_LISTENER: \
case CONN_TYPE_AP_TRANS_LISTENER: \
case CONN_TYPE_AP_NATD_LISTENER: \
- case CONN_TYPE_AP_DNS_LISTENER
+ case CONN_TYPE_AP_DNS_LISTENER: \
+ case CONN_TYPE_AP_HTTP_CONNECT_LISTENER
/**************************************************************/
@@ -185,6 +191,7 @@ conn_type_to_string(int type)
case CONN_TYPE_CONTROL: return "Control";
case CONN_TYPE_EXT_OR: return "Extended OR";
case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener";
+ case CONN_TYPE_AP_HTTP_CONNECT_LISTENER: return "HTTP tunnel listener";
default:
log_warn(LD_BUG, "unknown connection type %d", type);
tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
@@ -606,6 +613,7 @@ connection_free_(connection_t *conn)
}
if (CONN_IS_EDGE(conn)) {
rend_data_free(TO_EDGE_CONN(conn)->rend_data);
+ hs_ident_edge_conn_free(TO_EDGE_CONN(conn)->hs_ident);
}
if (conn->type == CONN_TYPE_CONTROL) {
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
@@ -637,6 +645,7 @@ connection_free_(connection_t *conn)
}
rend_data_free(dir_conn->rend_data);
+ hs_ident_dir_conn_free(dir_conn->hs_ident);
if (dir_conn->guard_state) {
/* Cancel before freeing, if it's still there. */
entry_guard_cancel(&dir_conn->guard_state);
@@ -696,7 +705,7 @@ connection_free,(connection_t *conn))
connection_ap_warn_and_unmark_if_pending_circ(TO_ENTRY_CONN(conn),
"connection_free");
}
-#endif
+#endif /* 1 */
/* Notify the circuit creation DoS mitigation subsystem that an OR client
* connection has been closed. And only do that if we track it. */
@@ -815,7 +824,7 @@ connection_mark_for_close_(connection_t *conn, int line, const char *file)
* CONN_TYPE_OR checks; this should be called when you either are sure that
* if this is an or_connection_t the controlling channel has been notified
* (e.g. with connection_or_notify_error()), or you actually are the
- * connection_or_close_for_error() or connection_or_close_normally function.
+ * connection_or_close_for_error() or connection_or_close_normally() function.
* For all other cases, use connection_mark_and_flush() instead, which
* checks for or_connection_t properly, instead. See below.
*/
@@ -931,7 +940,7 @@ create_unix_sockaddr(const char *listenaddress, char **readable_address,
*len_out = sizeof(struct sockaddr_un);
return sockaddr;
}
-#else
+#else /* !(defined(HAVE_SYS_UN_H) || defined(RUNNING_DOXYGEN)) */
static struct sockaddr *
create_unix_sockaddr(const char *listenaddress, char **readable_address,
socklen_t *len_out)
@@ -944,7 +953,7 @@ create_unix_sockaddr(const char *listenaddress, char **readable_address,
tor_fragile_assert();
return NULL;
}
-#endif /* HAVE_SYS_UN_H */
+#endif /* defined(HAVE_SYS_UN_H) || defined(RUNNING_DOXYGEN) */
/** Warn that an accept or a connect has failed because we're running out of
* TCP sockets we can use on current system. Rate-limit these warnings so
@@ -1059,7 +1068,7 @@ check_location_for_unix_socket(const or_options_t *options, const char *path,
tor_free(p);
return r;
}
-#endif
+#endif /* defined(HAVE_SYS_UN_H) */
/** Tell the TCP stack that it shouldn't wait for a long time after
* <b>sock</b> has closed before reusing its port. Return 0 on success,
@@ -1082,7 +1091,7 @@ make_socket_reuseable(tor_socket_t sock)
return -1;
}
return 0;
-#endif
+#endif /* defined(_WIN32) */
}
#ifdef _WIN32
@@ -1103,12 +1112,12 @@ make_win32_socket_exclusive(tor_socket_t sock)
return -1;
}
return 0;
-#else
+#else /* !(defined(SO_EXCLUSIVEADDRUSE)) */
(void) sock;
return 0;
-#endif
+#endif /* defined(SO_EXCLUSIVEADDRUSE) */
}
-#endif
+#endif /* defined(_WIN32) */
/** Max backlog to pass to listen. We start at */
static int listen_limit = INT_MAX;
@@ -1198,7 +1207,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
conn_type_to_string(type),
tor_socket_strerror(errno));
}
-#endif
+#endif /* defined(_WIN32) */
#if defined(USE_TRANSPARENT) && defined(IP_TRANSPARENT)
if (options->TransProxyType_parsed == TPT_TPROXY &&
@@ -1215,7 +1224,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
tor_socket_strerror(e), extra);
}
}
-#endif
+#endif /* defined(USE_TRANSPARENT) && defined(IP_TRANSPARENT) */
#ifdef IPV6_V6ONLY
if (listensockaddr->sa_family == AF_INET6) {
@@ -1230,7 +1239,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
/* Keep going; probably not harmful. */
}
}
-#endif
+#endif /* defined(IPV6_V6ONLY) */
if (bind(s,listensockaddr,socklen) < 0) {
const char *helpfulhint = "";
@@ -1333,7 +1342,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
goto err;
}
}
-#endif
+#endif /* defined(HAVE_PWD_H) */
{
unsigned mode;
@@ -1364,7 +1373,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
tor_socket_strerror(tor_socket_errno(s)));
goto err;
}
-#endif /* HAVE_SYS_UN_H */
+#endif /* defined(HAVE_SYS_UN_H) */
} else {
log_err(LD_BUG, "Got unexpected address family %d.",
listensockaddr->sa_family);
@@ -1719,6 +1728,8 @@ connection_init_accepted_conn(connection_t *conn,
TO_ENTRY_CONN(conn)->is_transparent_ap = 1;
conn->state = AP_CONN_STATE_NATD_WAIT;
break;
+ case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
+ conn->state = AP_CONN_STATE_HTTP_CONNECT_WAIT;
}
break;
case CONN_TYPE_DIR:
@@ -2157,7 +2168,7 @@ connection_proxy_connect(connection_t *conn, int type)
fmt_addrport(&conn->addr, conn->port));
}
- connection_write_to_buf(buf, strlen(buf), conn);
+ connection_buf_add(buf, strlen(buf), conn);
conn->proxy_state = PROXY_HTTPS_WANT_CONNECT_OK;
break;
}
@@ -2223,7 +2234,7 @@ connection_proxy_connect(connection_t *conn, int type)
buf[8] = 0; /* no userid */
}
- connection_write_to_buf((char *)buf, buf_size, conn);
+ connection_buf_add((char *)buf, buf_size, conn);
tor_free(buf);
conn->proxy_state = PROXY_SOCKS4_WANT_CONNECT_OK;
@@ -2254,7 +2265,7 @@ connection_proxy_connect(connection_t *conn, int type)
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_METHOD_NONE;
}
- connection_write_to_buf((char *)buf, 2 + buf[1], conn);
+ connection_buf_add((char *)buf, 2 + buf[1], conn);
break;
}
@@ -2360,7 +2371,7 @@ connection_send_socks5_connect(connection_t *conn)
memcpy(buf + 20, &port, 2);
}
- connection_write_to_buf((char *)buf, reqsize, conn);
+ connection_buf_add((char *)buf, reqsize, conn);
conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK;
}
@@ -2486,7 +2497,7 @@ connection_read_proxy_handshake(connection_t *conn)
if (socks_args_string)
tor_free(socks_args_string);
- connection_write_to_buf((char *)buf, reqsize, conn);
+ connection_buf_add((char *)buf, reqsize, conn);
conn->proxy_state = PROXY_SOCKS5_WANT_AUTH_RFC1929_OK;
ret = 0;
@@ -2635,7 +2646,7 @@ retry_listener_ports(smartlist_t *old_conns,
if (port->is_unix_addr && !geteuid() && (options->User) &&
strcmp(options->User, "root"))
continue;
-#endif
+#endif /* !defined(_WIN32) */
if (port->is_unix_addr) {
listensockaddr = (struct sockaddr *)
@@ -3071,9 +3082,11 @@ connection_buckets_decrement(connection_t *conn, time_t now,
(unsigned long)num_read, (unsigned long)num_written,
conn_type_to_string(conn->type),
conn_state_to_string(conn->type, conn->state));
- if (num_written >= INT_MAX) num_written = 1;
- if (num_read >= INT_MAX) num_read = 1;
- tor_fragile_assert();
+ tor_assert_nonfatal_unreached();
+ if (num_written >= INT_MAX)
+ num_written = 1;
+ if (num_read >= INT_MAX)
+ num_read = 1;
}
record_num_bytes_transferred_impl(conn, now, num_read, num_written);
@@ -3385,7 +3398,7 @@ connection_bucket_should_increase(int bucket, or_connection_t *conn)
/** Read bytes from conn-\>s and process them.
*
- * It calls connection_read_to_buf() to bring in any new bytes,
+ * It calls connection_buf_read_from_socket() to bring in any new bytes,
* and then calls connection_process_inbuf() to process them.
*
* Mark the connection and return -1 if you want to close it, else
@@ -3411,6 +3424,7 @@ connection_handle_read_impl(connection_t *conn)
case CONN_TYPE_AP_LISTENER:
case CONN_TYPE_AP_TRANS_LISTENER:
case CONN_TYPE_AP_NATD_LISTENER:
+ case CONN_TYPE_AP_HTTP_CONNECT_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_AP);
case CONN_TYPE_DIR_LISTENER:
return connection_handle_listener_read(conn, CONN_TYPE_DIR);
@@ -3427,7 +3441,7 @@ connection_handle_read_impl(connection_t *conn)
tor_assert(!conn->marked_for_close);
before = buf_datalen(conn->inbuf);
- if (connection_read_to_buf(conn, &max_to_read, &socket_error) < 0) {
+ if (connection_buf_read_from_socket(conn, &max_to_read, &socket_error) < 0) {
/* There's a read error; kill the connection.*/
if (conn->type == CONN_TYPE_OR) {
connection_or_notify_error(TO_OR_CONN(conn),
@@ -3524,7 +3538,7 @@ connection_handle_read(connection_t *conn)
* Return -1 if we want to break conn, else return 0.
*/
static int
-connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
+connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read,
int *socket_error)
{
int result;
@@ -3565,7 +3579,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
initial_size = buf_datalen(conn->inbuf);
/* else open, or closing */
- result = read_to_buf_tls(or_conn->tls, at_most, conn->inbuf);
+ result = buf_read_from_tls(conn->inbuf, or_conn->tls, at_most);
if (TOR_TLS_IS_ERROR(result) || result == TOR_TLS_CLOSE)
or_conn->tls_error = result;
else
@@ -3601,10 +3615,8 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
connection_start_reading(conn);
}
/* we're already reading, one hopes */
- result = 0;
break;
case TOR_TLS_DONE: /* no data read, so nothing to process */
- result = 0;
break; /* so we call bucket_decrement below */
default:
break;
@@ -3614,7 +3626,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
/* If we have any pending bytes, we read them now. This *can*
* take us over our read allotment, but really we shouldn't be
* believing that SSL bytes are the same as TCP bytes anyway. */
- int r2 = read_to_buf_tls(or_conn->tls, pending, conn->inbuf);
+ int r2 = buf_read_from_tls(conn->inbuf, or_conn->tls, pending);
if (BUG(r2<0)) {
log_warn(LD_BUG, "apparently, reading pending bytes can fail.");
return -1;
@@ -3626,7 +3638,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
result, (long)n_read, (long)n_written);
} else if (conn->linked) {
if (conn->linked_conn) {
- result = move_buf_to_buf(conn->inbuf, conn->linked_conn->outbuf,
+ result = buf_move_to_buf(conn->inbuf, conn->linked_conn->outbuf,
&conn->linked_conn->outbuf_flushlen);
} else {
result = 0;
@@ -3644,8 +3656,10 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
/* !connection_speaks_cells, !conn->linked_conn. */
int reached_eof = 0;
CONN_LOG_PROTECT(conn,
- result = read_to_buf(conn->s, at_most, conn->inbuf, &reached_eof,
- socket_error));
+ result = buf_read_from_socket(conn->inbuf, conn->s,
+ at_most,
+ &reached_eof,
+ socket_error));
if (reached_eof)
conn->inbuf_reached_eof = 1;
@@ -3714,17 +3728,17 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
/** A pass-through to fetch_from_buf. */
int
-connection_fetch_from_buf(char *string, size_t len, connection_t *conn)
+connection_buf_get_bytes(char *string, size_t len, connection_t *conn)
{
- return fetch_from_buf(string, len, conn->inbuf);
+ return buf_get_bytes(conn->inbuf, string, len);
}
-/** As fetch_from_buf_line(), but read from a connection's input buffer. */
+/** As buf_get_line(), but read from a connection's input buffer. */
int
-connection_fetch_from_buf_line(connection_t *conn, char *data,
+connection_buf_get_line(connection_t *conn, char *data,
size_t *data_len)
{
- return fetch_from_buf_line(conn->inbuf, data, data_len);
+ return buf_get_line(conn->inbuf, data, data_len);
}
/** As fetch_from_buf_http, but fetches from a connection's input buffer_t as
@@ -3761,7 +3775,7 @@ connection_outbuf_too_full(connection_t *conn)
*
* This function gets called either from conn_write_callback() in main.c
* when libevent tells us that conn wants to write, or below
- * from connection_write_to_buf() when an entire TLS record is ready.
+ * from connection_buf_add() when an entire TLS record is ready.
*
* Update <b>conn</b>-\>timestamp_lastwritten to now, and call flush_buf
* or flush_buf_tls appropriately. If it succeeds and there are no more
@@ -3872,7 +3886,7 @@ connection_handle_write_impl(connection_t *conn, int force)
/* else open, or closing */
initial_size = buf_datalen(conn->outbuf);
- result = flush_buf_tls(or_conn->tls, conn->outbuf,
+ result = buf_flush_to_tls(conn->outbuf, or_conn->tls,
max_to_write, &conn->outbuf_flushlen);
/* If we just flushed the last bytes, tell the channel on the
@@ -3935,8 +3949,8 @@ connection_handle_write_impl(connection_t *conn, int force)
result = (int)(initial_size-buf_datalen(conn->outbuf));
} else {
CONN_LOG_PROTECT(conn,
- result = flush_buf(conn->s, conn->outbuf,
- max_to_write, &conn->outbuf_flushlen));
+ result = buf_flush_to_socket(conn->outbuf, conn->s,
+ max_to_write, &conn->outbuf_flushlen));
if (result < 0) {
if (CONN_IS_EDGE(conn))
connection_edge_end_errno(TO_EDGE_CONN(conn));
@@ -4076,11 +4090,11 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
if (zlib) {
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
int done = zlib < 0;
- CONN_LOG_PROTECT(conn, r = write_to_buf_compress(conn->outbuf,
+ CONN_LOG_PROTECT(conn, r = buf_add_compress(conn->outbuf,
dir_conn->compress_state,
string, len, done));
} else {
- CONN_LOG_PROTECT(conn, r = write_to_buf(string, len, conn->outbuf));
+ CONN_LOG_PROTECT(conn, r = buf_add(conn->outbuf, string, len));
}
if (r < 0) {
if (CONN_IS_EDGE(conn)) {
@@ -4119,6 +4133,38 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
}
}
+#define CONN_GET_ALL_TEMPLATE(var, test) \
+ STMT_BEGIN \
+ smartlist_t *conns = get_connection_array(); \
+ smartlist_t *ret_conns = smartlist_new(); \
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, var) { \
+ if (var && (test) && !var->marked_for_close) \
+ smartlist_add(ret_conns, var); \
+ } SMARTLIST_FOREACH_END(var); \
+ return ret_conns; \
+ STMT_END
+
+/* Return a list of connections that aren't close and matches the given type
+ * and state. The returned list can be empty and must be freed using
+ * smartlist_free(). The caller does NOT have owernship of the objects in the
+ * list so it must not free them nor reference them as they can disappear. */
+smartlist_t *
+connection_list_by_type_state(int type, int state)
+{
+ CONN_GET_ALL_TEMPLATE(conn, (conn->type == type && conn->state == state));
+}
+
+/* Return a list of connections that aren't close and matches the given type
+ * and purpose. The returned list can be empty and must be freed using
+ * smartlist_free(). The caller does NOT have owernship of the objects in the
+ * list so it must not free them nor reference them as they can disappear. */
+smartlist_t *
+connection_list_by_type_purpose(int type, int purpose)
+{
+ CONN_GET_ALL_TEMPLATE(conn,
+ (conn->type == type && conn->purpose == purpose));
+}
+
/** Return a connection_t * from get_connection_array() that satisfies test on
* var, and that is not marked for close. */
#define CONN_GET_TEMPLATE(var, test) \
@@ -4303,6 +4349,7 @@ connection_is_listener(connection_t *conn)
conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
conn->type == CONN_TYPE_AP_DNS_LISTENER ||
conn->type == CONN_TYPE_AP_NATD_LISTENER ||
+ conn->type == CONN_TYPE_AP_HTTP_CONNECT_LISTENER ||
conn->type == CONN_TYPE_DIR_LISTENER ||
conn->type == CONN_TYPE_CONTROL_LISTENER)
return 1;
@@ -4970,9 +5017,9 @@ assert_connection_ok(connection_t *conn, time_t now)
/* buffers */
if (conn->inbuf)
- assert_buf_ok(conn->inbuf);
+ buf_assert_ok(conn->inbuf);
if (conn->outbuf)
- assert_buf_ok(conn->outbuf);
+ buf_assert_ok(conn->outbuf);
if (conn->type == CONN_TYPE_OR) {
or_connection_t *or_conn = TO_OR_CONN(conn);
@@ -5194,7 +5241,7 @@ clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted,
const char *source)
{
char dbuf[64];
- char *ext_source = NULL;
+ char *ext_source = NULL, *warn = NULL;
format_time_interval(dbuf, sizeof(dbuf), apparent_skew);
if (conn)
tor_asprintf(&ext_source, "%s:%s:%d", source, conn->address, conn->port);
@@ -5208,9 +5255,14 @@ clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted,
apparent_skew > 0 ? "ahead" : "behind", dbuf,
apparent_skew > 0 ? "behind" : "ahead",
(!conn || trusted) ? "" : ", or they are sending us the wrong time");
- if (trusted)
+ if (trusted) {
control_event_general_status(LOG_WARN, "CLOCK_SKEW SKEW=%ld SOURCE=%s",
apparent_skew, ext_source);
+ tor_asprintf(&warn, "Clock skew %ld in %s from %s", apparent_skew,
+ received, source);
+ control_event_bootstrap_problem(warn, "CLOCK_SKEW", conn, 1);
+ }
+ tor_free(warn);
tor_free(ext_source);
}
diff --git a/src/or/connection.h b/src/or/connection.h
index 36e45aef38..4a5bd6971b 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -123,8 +123,8 @@ void connection_bucket_refill(int seconds_elapsed, time_t now);
int connection_handle_read(connection_t *conn);
-int connection_fetch_from_buf(char *string, size_t len, connection_t *conn);
-int connection_fetch_from_buf_line(connection_t *conn, char *data,
+int connection_buf_get_bytes(char *string, size_t len, connection_t *conn);
+int connection_buf_get_line(connection_t *conn, char *data,
size_t *data_len);
int connection_fetch_from_buf_http(connection_t *conn,
char **headers_out, size_t max_headerlen,
@@ -139,18 +139,18 @@ int connection_flush(connection_t *conn);
MOCK_DECL(void, connection_write_to_buf_impl_,
(const char *string, size_t len, connection_t *conn, int zlib));
/* DOCDOC connection_write_to_buf */
-static void connection_write_to_buf(const char *string, size_t len,
+static void connection_buf_add(const char *string, size_t len,
connection_t *conn);
/* DOCDOC connection_write_to_buf_compress */
-static void connection_write_to_buf_compress(const char *string, size_t len,
+static void connection_buf_add_compress(const char *string, size_t len,
dir_connection_t *conn, int done);
static inline void
-connection_write_to_buf(const char *string, size_t len, connection_t *conn)
+connection_buf_add(const char *string, size_t len, connection_t *conn)
{
connection_write_to_buf_impl_(string, len, conn, 0);
}
static inline void
-connection_write_to_buf_compress(const char *string, size_t len,
+connection_buf_add_compress(const char *string, size_t len,
dir_connection_t *conn, int done)
{
connection_write_to_buf_impl_(string, len, TO_CONN(conn), done ? -1 : 1);
@@ -182,6 +182,8 @@ MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type,
connection_t *connection_get_by_type_state(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery);
+smartlist_t *connection_list_by_type_state(int type, int state);
+smartlist_t *connection_list_by_type_purpose(int type, int purpose);
smartlist_t *connection_dir_list_by_purpose_and_resource(
int purpose,
const char *resource);
@@ -284,7 +286,7 @@ MOCK_DECL(STATIC int,connection_connect_sockaddr,
MOCK_DECL(STATIC void, kill_conn_list_for_oos, (smartlist_t *conns));
MOCK_DECL(STATIC smartlist_t *, pick_oos_victims, (int n));
-#endif
+#endif /* defined(CONNECTION_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CONNECTION_H) */
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 8480a35458..f178917f0b 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -76,9 +76,15 @@
#include "dirserv.h"
#include "hibernate.h"
#include "hs_common.h"
+#include "hs_cache.h"
+#include "hs_client.h"
+#include "hs_circuit.h"
#include "main.h"
+#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "proto_http.h"
+#include "proto_socks.h"
#include "reasons.h"
#include "relay.h"
#include "rendclient.h"
@@ -109,7 +115,7 @@
#define TRANS_NETFILTER
#define TRANS_NETFILTER_IPV6
#endif
-#endif
+#endif /* defined(HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H) */
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
#include <net/if.h>
@@ -152,7 +158,9 @@ connection_mark_unattached_ap_,(entry_connection_t *conn, int endreason,
* but we should fix it someday anyway. */
if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) &&
connection_edge_is_rendezvous_stream(edge_conn)) {
- rend_client_note_connection_attempt_ended(edge_conn->rend_data);
+ if (edge_conn->rend_data) {
+ rend_client_note_connection_attempt_ended(edge_conn->rend_data);
+ }
}
if (base_conn->marked_for_close) {
@@ -236,6 +244,11 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
return -1;
}
return 0;
+ case AP_CONN_STATE_HTTP_CONNECT_WAIT:
+ if (connection_ap_process_http_connect(EDGE_TO_ENTRY_CONN(conn)) < 0) {
+ return -1;
+ }
+ return 0;
case AP_CONN_STATE_OPEN:
case EXIT_CONN_STATE_OPEN:
if (connection_edge_package_raw_inbuf(conn, package_partial, NULL) < 0) {
@@ -485,6 +498,7 @@ connection_edge_finished_flushing(edge_connection_t *conn)
case AP_CONN_STATE_CONNECT_WAIT:
case AP_CONN_STATE_CONTROLLER_WAIT:
case AP_CONN_STATE_RESOLVE_WAIT:
+ case AP_CONN_STATE_HTTP_CONNECT_WAIT:
return 0;
default:
log_warn(LD_BUG, "Called in unexpected state %d.",conn->base_.state);
@@ -647,7 +661,7 @@ connection_ap_about_to_close(entry_connection_t *entry_conn)
connection_ap_warn_and_unmark_if_pending_circ(entry_conn,
"about_to_close");
}
-#endif
+#endif /* 1 */
control_event_stream_bandwidth(edge_conn);
control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED,
@@ -857,9 +871,9 @@ connection_ap_rescan_and_attach_pending(void)
entry_conn->marked_pending_circ_line = 0; \
entry_conn->marked_pending_circ_file = 0; \
} while (0)
-#else
+#else /* !(defined(DEBUGGING_17659)) */
#define UNMARK() do { } while (0)
-#endif
+#endif /* defined(DEBUGGING_17659) */
/** Tell any AP streams that are listed as waiting for a new circuit to try
* again. If there is an available circuit for a stream, attach it. Otherwise,
@@ -965,7 +979,7 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn,
log_warn(LD_BUG, "(Previously called from %s:%d.)\n",
f2 ? f2 : "<NULL>",
entry_conn->marked_pending_circ_line);
-#endif
+#endif /* defined(DEBUGGING_17659) */
log_backtrace(LOG_WARN, LD_BUG, "To debug, this may help");
return;
}
@@ -1073,7 +1087,8 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info)
if (!entry_conn->chosen_exit_optional &&
!entry_conn->chosen_exit_retries)
continue;
- r1 = node_get_by_nickname(entry_conn->chosen_exit_name, 0);
+ r1 = node_get_by_nickname(entry_conn->chosen_exit_name,
+ NNF_NO_WARN_UNNAMED);
r2 = node_get_by_id(info->identity_digest);
if (!r1 || !r2 || r1 != r2)
continue;
@@ -1176,10 +1191,10 @@ consider_plaintext_ports(entry_connection_t *conn, uint16_t port)
* See connection_ap_handshake_rewrite_and_attach()'s
* documentation for arguments and return value.
*/
-int
-connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
- origin_circuit_t *circ,
- crypt_path_t *cpath)
+MOCK_IMPL(int,
+connection_ap_rewrite_and_attach_if_allowed,(entry_connection_t *conn,
+ origin_circuit_t *circ,
+ crypt_path_t *cpath))
{
const or_options_t *options = get_options();
@@ -1222,10 +1237,9 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
/* Check for whether this is a .exit address. By default, those are
* disallowed when they're coming straight from the client, but you're
* allowed to have them in MapAddress commands and so forth. */
- if (!strcmpend(socks->address, ".exit") && !options->AllowDotExit) {
+ if (!strcmpend(socks->address, ".exit")) {
log_warn(LD_APP, "The \".exit\" notation is disabled in Tor due to "
- "security risks. Set AllowDotExit in your torrc to enable "
- "it (at your own risk).");
+ "security risks.");
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
out->end_reason = END_STREAM_REASON_TORPROTOCOL;
@@ -1391,6 +1405,199 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
}
}
+/** We just received a SOCKS request in <b>conn</b> to an onion address of type
+ * <b>addresstype</b>. Start connecting to the onion service. */
+static int
+connection_ap_handle_onion(entry_connection_t *conn,
+ socks_request_t *socks,
+ origin_circuit_t *circ,
+ hostname_type_t addresstype)
+{
+ time_t now = approx_time();
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
+
+ /* If .onion address requests are disabled, refuse the request */
+ if (!conn->entry_cfg.onion_traffic) {
+ log_warn(LD_APP, "Onion address %s requested from a port with .onion "
+ "disabled", safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ }
+
+ /* Check whether it's RESOLVE or RESOLVE_PTR. We don't handle those
+ * for hidden service addresses. */
+ if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
+ /* if it's a resolve request, fail it right now, rather than
+ * building all the circuits and then realizing it won't work. */
+ log_warn(LD_APP,
+ "Resolve requests to hidden services not allowed. Failing.");
+ connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,
+ 0,NULL,-1,TIME_MAX);
+ connection_mark_unattached_ap(conn,
+ END_STREAM_REASON_SOCKSPROTOCOL |
+ END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+ return -1;
+ }
+
+ /* If we were passed a circuit, then we need to fail. .onion addresses
+ * only work when we launch our own circuits for now. */
+ if (circ) {
+ log_warn(LD_CONTROL, "Attachstream to a circuit is not "
+ "supported for .onion addresses currently. Failing.");
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ return -1;
+ }
+
+ /* Interface: Regardless of HS version after the block below we should have
+ set onion_address, rend_cache_lookup_result, and descriptor_is_usable. */
+ const char *onion_address = NULL;
+ int rend_cache_lookup_result = -ENOENT;
+ int descriptor_is_usable = 0;
+
+ if (addresstype == ONION_V2_HOSTNAME) { /* it's a v2 hidden service */
+ rend_cache_entry_t *entry = NULL;
+ /* Look up if we have client authorization configured for this hidden
+ * service. If we do, associate it with the rend_data. */
+ rend_service_authorization_t *client_auth =
+ rend_client_lookup_service_authorization(socks->address);
+
+ const uint8_t *cookie = NULL;
+ rend_auth_type_t auth_type = REND_NO_AUTH;
+ if (client_auth) {
+ log_info(LD_REND, "Using previously configured client authorization "
+ "for hidden service request.");
+ auth_type = client_auth->auth_type;
+ cookie = client_auth->descriptor_cookie;
+ }
+
+ /* Fill in the rend_data field so we can start doing a connection to
+ * a hidden service. */
+ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
+ rend_data_client_create(socks->address, NULL, (char *) cookie,
+ auth_type);
+ if (rend_data == NULL) {
+ return -1;
+ }
+ onion_address = rend_data_get_address(rend_data);
+ log_info(LD_REND,"Got a hidden service request for ID '%s'",
+ safe_str_client(onion_address));
+
+ rend_cache_lookup_result = rend_cache_lookup_entry(onion_address,-1,
+ &entry);
+ if (!rend_cache_lookup_result && entry) {
+ descriptor_is_usable = rend_client_any_intro_points_usable(entry);
+ }
+ } else { /* it's a v3 hidden service */
+ tor_assert(addresstype == ONION_V3_HOSTNAME);
+ const hs_descriptor_t *cached_desc = NULL;
+ int retval;
+ /* Create HS conn identifier with HS pubkey */
+ hs_ident_edge_conn_t *hs_conn_ident =
+ tor_malloc_zero(sizeof(hs_ident_edge_conn_t));
+
+ retval = hs_parse_address(socks->address, &hs_conn_ident->identity_pk,
+ NULL, NULL);
+ if (retval < 0) {
+ log_warn(LD_GENERAL, "failed to parse hs address");
+ tor_free(hs_conn_ident);
+ return -1;
+ }
+ ENTRY_TO_EDGE_CONN(conn)->hs_ident = hs_conn_ident;
+
+ onion_address = socks->address;
+
+ /* Check the v3 desc cache */
+ cached_desc = hs_cache_lookup_as_client(&hs_conn_ident->identity_pk);
+ if (cached_desc) {
+ rend_cache_lookup_result = 0;
+ descriptor_is_usable =
+ hs_client_any_intro_points_usable(&hs_conn_ident->identity_pk,
+ cached_desc);
+ log_info(LD_GENERAL, "Found %s descriptor in cache for %s. %s.",
+ (descriptor_is_usable) ? "usable" : "unusable",
+ safe_str_client(onion_address),
+ (descriptor_is_usable) ? "Not fetching." : "Refecting.");
+ } else {
+ rend_cache_lookup_result = -ENOENT;
+ }
+ }
+
+ /* Lookup the given onion address. If invalid, stop right now.
+ * Otherwise, we might have it in the cache or not. */
+ unsigned int refetch_desc = 0;
+ if (rend_cache_lookup_result < 0) {
+ switch (-rend_cache_lookup_result) {
+ case EINVAL:
+ /* We should already have rejected this address! */
+ log_warn(LD_BUG,"Invalid service name '%s'",
+ safe_str_client(onion_address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ return -1;
+ case ENOENT:
+ /* We didn't have this; we should look it up. */
+ log_info(LD_REND, "No descriptor found in our cache for %s. Fetching.",
+ safe_str_client(onion_address));
+ refetch_desc = 1;
+ break;
+ default:
+ log_warn(LD_BUG, "Unknown cache lookup error %d",
+ rend_cache_lookup_result);
+ return -1;
+ }
+ }
+
+ /* Help predict that we'll want to do hidden service circuits in the
+ * future. We're not sure if it will need a stable circuit yet, but
+ * we know we'll need *something*. */
+ rep_hist_note_used_internal(now, 0, 1);
+
+ /* Now we have a descriptor but is it usable or not? If not, refetch.
+ * Also, a fetch could have been requested if the onion address was not
+ * found in the cache previously. */
+ if (refetch_desc || !descriptor_is_usable) {
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
+ connection_ap_mark_as_non_pending_circuit(conn);
+ base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+ if (addresstype == ONION_V2_HOSTNAME) {
+ tor_assert(edge_conn->rend_data);
+ rend_client_refetch_v2_renddesc(edge_conn->rend_data);
+ /* Whatever the result of the refetch, we don't go further. */
+ return 0;
+ } else {
+ tor_assert(addresstype == ONION_V3_HOSTNAME);
+ tor_assert(edge_conn->hs_ident);
+ /* Attempt to fetch the hsv3 descriptor. Check the retval to see how it
+ * went and act accordingly. */
+ int ret = hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk);
+ switch (ret) {
+ case HS_CLIENT_FETCH_MISSING_INFO:
+ /* Keeping the connection in descriptor wait state is fine because
+ * once we get enough dirinfo or a new live consensus, the HS client
+ * subsystem is notified and every connection in that state will
+ * trigger a fetch for the service key. */
+ case HS_CLIENT_FETCH_LAUNCHED:
+ case HS_CLIENT_FETCH_PENDING:
+ case HS_CLIENT_FETCH_HAVE_DESC:
+ return 0;
+ case HS_CLIENT_FETCH_ERROR:
+ case HS_CLIENT_FETCH_NO_HSDIRS:
+ case HS_CLIENT_FETCH_NOT_ALLOWED:
+ /* Can't proceed further and better close the SOCKS request. */
+ return -1;
+ }
+ }
+ }
+
+ /* We have the descriptor! So launch a connection to the HS. */
+ log_info(LD_REND, "Descriptor is here. Great.");
+
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ /* We'll try to attach it at the next event loop, or whenever
+ * we call connection_ap_attach_pending() */
+ connection_ap_mark_as_pending_circuit(conn);
+ return 0;
+}
+
/** Connection <b>conn</b> just finished its socks handshake, or the
* controller asked us to take care of it. If <b>circ</b> is defined,
* then that's where we'll want to attach it. Otherwise we have to
@@ -1466,23 +1673,23 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
const node_t *node = NULL;
/* If this .exit was added by an AUTOMAP, then it came straight from
- * a user. Make sure that options->AllowDotExit permits that! */
- if (exit_source == ADDRMAPSRC_AUTOMAP && !options->AllowDotExit) {
- /* Whoops; this one is stale. It must have gotten added earlier,
- * when AllowDotExit was on. */
- log_warn(LD_APP,"Stale automapped address for '%s.exit', with "
- "AllowDotExit disabled. Refusing.",
+ * a user. That's not safe. */
+ if (exit_source == ADDRMAPSRC_AUTOMAP) {
+ /* Whoops; this one is stale. It must have gotten added earlier?
+ * (Probably this is not possible, since AllowDotExit no longer
+ * exists.) */
+ log_warn(LD_APP,"Stale automapped address for '%s.exit'. Refusing.",
safe_str_client(socks->address));
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ tor_assert_nonfatal_unreached();
return -1;
}
/* Double-check to make sure there are no .exits coming from
* impossible/weird sources. */
- if (exit_source == ADDRMAPSRC_DNS ||
- (exit_source == ADDRMAPSRC_NONE && !options->AllowDotExit)) {
+ if (exit_source == ADDRMAPSRC_DNS || exit_source == ADDRMAPSRC_NONE) {
/* It shouldn't be possible to get a .exit address from any of these
* sources. */
log_warn(LD_BUG,"Address '%s.exit', with impossible source for the "
@@ -1507,7 +1714,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
if (s[1] != '\0') {
/* Looks like a real .exit one. */
conn->chosen_exit_name = tor_strdup(s+1);
- node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 0);
if (exit_source == ADDRMAPSRC_TRACKEXIT) {
/* We 5 tries before it expires the addressmap */
@@ -1528,7 +1735,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
* form that means (foo's address).foo.exit. */
conn->chosen_exit_name = tor_strdup(socks->address);
- node = node_get_by_nickname(conn->chosen_exit_name, 1);
+ node = node_get_by_nickname(conn->chosen_exit_name, 0);
if (node) {
*socks->address = 0;
node_get_address_string(node, socks->address, sizeof(socks->address));
@@ -1557,7 +1764,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
/* Now, we handle everything that isn't a .onion address. */
- if (addresstype != ONION_HOSTNAME) {
+ if (addresstype != ONION_V2_HOSTNAME && addresstype != ONION_V3_HOSTNAME) {
/* Not a hidden-service request. It's either a hostname or an IP,
* possibly with a .exit that we stripped off. We're going to check
* if we're allowed to connect/resolve there, and then launch the
@@ -1584,7 +1791,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1;
}
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) */
/* socks->address is a non-onion hostname or IP address.
* If we can't do any non-onion requests, refuse the connection.
@@ -1835,116 +2042,10 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return 0;
} else {
/* If we get here, it's a request for a .onion address! */
+ tor_assert(addresstype == ONION_V2_HOSTNAME ||
+ addresstype == ONION_V3_HOSTNAME);
tor_assert(!automap);
-
- /* If .onion address requests are disabled, refuse the request */
- if (!conn->entry_cfg.onion_traffic) {
- log_warn(LD_APP, "Onion address %s requested from a port with .onion "
- "disabled", safe_str_client(socks->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
- return -1;
- }
-
- /* Check whether it's RESOLVE or RESOLVE_PTR. We don't handle those
- * for hidden service addresses. */
- if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
- /* if it's a resolve request, fail it right now, rather than
- * building all the circuits and then realizing it won't work. */
- log_warn(LD_APP,
- "Resolve requests to hidden services not allowed. Failing.");
- connection_ap_handshake_socks_resolved(conn,RESOLVED_TYPE_ERROR,
- 0,NULL,-1,TIME_MAX);
- connection_mark_unattached_ap(conn,
- END_STREAM_REASON_SOCKSPROTOCOL |
- END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
- return -1;
- }
-
- /* If we were passed a circuit, then we need to fail. .onion addresses
- * only work when we launch our own circuits for now. */
- if (circ) {
- log_warn(LD_CONTROL, "Attachstream to a circuit is not "
- "supported for .onion addresses currently. Failing.");
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
- return -1;
- }
-
- /* Look up if we have client authorization configured for this hidden
- * service. If we do, associate it with the rend_data. */
- rend_service_authorization_t *client_auth =
- rend_client_lookup_service_authorization(socks->address);
-
- const uint8_t *cookie = NULL;
- rend_auth_type_t auth_type = REND_NO_AUTH;
- if (client_auth) {
- log_info(LD_REND, "Using previously configured client authorization "
- "for hidden service request.");
- auth_type = client_auth->auth_type;
- cookie = client_auth->descriptor_cookie;
- }
-
- /* Fill in the rend_data field so we can start doing a connection to
- * a hidden service. */
- rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
- rend_data_client_create(socks->address, NULL, (char *) cookie,
- auth_type);
- if (rend_data == NULL) {
- return -1;
- }
- const char *onion_address = rend_data_get_address(rend_data);
- log_info(LD_REND,"Got a hidden service request for ID '%s'",
- safe_str_client(onion_address));
-
- /* Lookup the given onion address. If invalid, stop right now.
- * Otherwise, we might have it in the cache or not. */
- unsigned int refetch_desc = 0;
- rend_cache_entry_t *entry = NULL;
- const int rend_cache_lookup_result =
- rend_cache_lookup_entry(onion_address, -1, &entry);
- if (rend_cache_lookup_result < 0) {
- switch (-rend_cache_lookup_result) {
- case EINVAL:
- /* We should already have rejected this address! */
- log_warn(LD_BUG,"Invalid service name '%s'",
- safe_str_client(onion_address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
- return -1;
- case ENOENT:
- /* We didn't have this; we should look it up. */
- refetch_desc = 1;
- break;
- default:
- log_warn(LD_BUG, "Unknown cache lookup error %d",
- rend_cache_lookup_result);
- return -1;
- }
- }
-
- /* Help predict that we'll want to do hidden service circuits in the
- * future. We're not sure if it will need a stable circuit yet, but
- * we know we'll need *something*. */
- rep_hist_note_used_internal(now, 0, 1);
-
- /* Now we have a descriptor but is it usable or not? If not, refetch.
- * Also, a fetch could have been requested if the onion address was not
- * found in the cache previously. */
- if (refetch_desc || !rend_client_any_intro_points_usable(entry)) {
- connection_ap_mark_as_non_pending_circuit(conn);
- base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
- log_info(LD_REND, "Unknown descriptor %s. Fetching.",
- safe_str_client(onion_address));
- rend_client_refetch_v2_renddesc(rend_data);
- return 0;
- }
-
- /* We have the descriptor! So launch a connection to the HS. */
- base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- log_info(LD_REND, "Descriptor is here. Great.");
-
- /* We'll try to attach it at the next event loop, or whenever
- * we call connection_ap_attach_pending() */
- connection_ap_mark_as_pending_circuit(conn);
- return 0;
+ return connection_ap_handle_onion(conn, socks, circ, addresstype);
}
return 0; /* unreached but keeps the compiler happy */
@@ -1966,7 +2067,7 @@ get_pf_socket(void)
#else
/* works on NetBSD and FreeBSD */
pf = tor_open_cloexec("/dev/pf", O_RDWR, 0);
-#endif
+#endif /* defined(OpenBSD) */
if (pf < 0) {
log_warn(LD_NET, "open(\"/dev/pf\") failed: %s", strerror(errno));
@@ -1976,9 +2077,10 @@ get_pf_socket(void)
pf_socket = pf;
return pf_socket;
}
-#endif
+#endif /* defined(TRANS_PF) */
-#if defined(TRANS_NETFILTER) || defined(TRANS_PF) || defined(TRANS_TPROXY)
+#if defined(TRANS_NETFILTER) || defined(TRANS_PF) || \
+ defined(TRANS_TPROXY)
/** Try fill in the address of <b>req</b> from the socket configured
* with <b>conn</b>. */
static int
@@ -1998,7 +2100,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
}
goto done;
}
-#endif
+#endif /* defined(TRANS_TPROXY) */
#ifdef TRANS_NETFILTER
int rv = -1;
@@ -2008,13 +2110,13 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
rv = getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST,
(struct sockaddr*)&orig_dst, &orig_dst_len);
break;
-#endif
+#endif /* defined(TRANS_NETFILTER_IPV4) */
#ifdef TRANS_NETFILTER_IPV6
case AF_INET6:
rv = getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IPV6, IP6T_SO_ORIGINAL_DST,
(struct sockaddr*)&orig_dst, &orig_dst_len);
break;
-#endif
+#endif /* defined(TRANS_NETFILTER_IPV6) */
default:
log_warn(LD_BUG,
"Received transparent data from an unsuported socket family %d",
@@ -2040,7 +2142,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
(void)req;
log_warn(LD_BUG, "Unable to determine destination from socket.");
return -1;
-#endif
+#endif /* defined(TRANS_NETFILTER) || ... */
done:
tor_addr_from_sockaddr(&addr, (struct sockaddr*)&orig_dst, &req->port);
@@ -2048,7 +2150,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
return 0;
}
-#endif
+#endif /* defined(TRANS_NETFILTER) || defined(TRANS_PF) || ... */
#ifdef TRANS_PF
static int
@@ -2082,7 +2184,7 @@ destination_from_pf(entry_connection_t *conn, socks_request_t *req)
return 0;
}
-#endif
+#endif /* defined(__FreeBSD__) */
memset(&pnl, 0, sizeof(pnl));
pnl.proto = IPPROTO_TCP;
@@ -2131,7 +2233,7 @@ destination_from_pf(entry_connection_t *conn, socks_request_t *req)
return 0;
}
-#endif
+#endif /* defined(TRANS_PF) */
/** Fetch the original destination address and port from a
* system-specific interface and put them into a
@@ -2167,7 +2269,7 @@ connection_ap_get_original_destination(entry_connection_t *conn,
log_warn(LD_BUG, "Called connection_ap_get_original_destination, but no "
"transparent proxy method was configured.");
return -1;
-#endif
+#endif /* defined(TRANS_NETFILTER) || ... */
}
/** connection_edge_process_inbuf() found a conn in state
@@ -2202,7 +2304,7 @@ connection_ap_handshake_process_socks(entry_connection_t *conn)
if (socks->replylen) {
had_reply = 1;
- connection_write_to_buf((const char*)socks->reply, socks->replylen,
+ connection_buf_add((const char*)socks->reply, socks->replylen,
base_conn);
socks->replylen = 0;
if (sockshere == -1) {
@@ -2299,7 +2401,7 @@ connection_ap_process_natd(entry_connection_t *conn)
/* look for LF-terminated "[DEST ip_addr port]"
* where ip_addr is a dotted-quad and port is in string form */
- err = connection_fetch_from_buf_line(ENTRY_TO_CONN(conn), tmp_buf, &tlen);
+ err = connection_buf_get_line(ENTRY_TO_CONN(conn), tmp_buf, &tlen);
if (err == 0)
return 0;
if (err < 0) {
@@ -2348,6 +2450,108 @@ connection_ap_process_natd(entry_connection_t *conn)
return connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
}
+/** Called on an HTTP CONNECT entry connection when some bytes have arrived,
+ * but we have not yet received a full HTTP CONNECT request. Try to parse an
+ * HTTP CONNECT request from the connection's inbuf. On success, set up the
+ * connection's socks_request field and try to attach the connection. On
+ * failure, send an HTTP reply, and mark the connection.
+ */
+STATIC int
+connection_ap_process_http_connect(entry_connection_t *conn)
+{
+ if (BUG(ENTRY_TO_CONN(conn)->state != AP_CONN_STATE_HTTP_CONNECT_WAIT))
+ return -1;
+
+ char *headers = NULL, *body = NULL;
+ char *command = NULL, *addrport = NULL;
+ char *addr = NULL;
+ size_t bodylen = 0;
+
+ const char *errmsg = NULL;
+ int rv = 0;
+
+ const int http_status =
+ fetch_from_buf_http(ENTRY_TO_CONN(conn)->inbuf, &headers, 8192,
+ &body, &bodylen, 1024, 0);
+ if (http_status < 0) {
+ /* Bad http status */
+ errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
+ goto err;
+ } else if (http_status == 0) {
+ /* no HTTP request yet. */
+ goto done;
+ }
+
+ const int cmd_status = parse_http_command(headers, &command, &addrport);
+ if (cmd_status < 0) {
+ errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
+ goto err;
+ }
+ tor_assert(command);
+ tor_assert(addrport);
+ if (strcasecmp(command, "connect")) {
+ errmsg = "HTTP/1.0 405 Method Not Allowed\r\n\r\n";
+ goto err;
+ }
+
+ tor_assert(conn->socks_request);
+ socks_request_t *socks = conn->socks_request;
+ uint16_t port;
+ if (tor_addr_port_split(LOG_WARN, addrport, &addr, &port) < 0) {
+ errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
+ goto err;
+ }
+ if (strlen(addr) >= MAX_SOCKS_ADDR_LEN) {
+ errmsg = "HTTP/1.0 414 Request-URI Too Long\r\n\r\n";
+ goto err;
+ }
+
+ /* Abuse the 'username' and 'password' fields here. They are already an
+ * abuse. */
+ {
+ char *authorization = http_get_header(headers, "Proxy-Authorization: ");
+ if (authorization) {
+ socks->username = authorization; // steal reference
+ socks->usernamelen = strlen(authorization);
+ }
+ char *isolation = http_get_header(headers, "X-Tor-Stream-Isolation: ");
+ if (isolation) {
+ socks->password = isolation; // steal reference
+ socks->passwordlen = strlen(isolation);
+ }
+ }
+
+ socks->command = SOCKS_COMMAND_CONNECT;
+ socks->listener_type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER;
+ strlcpy(socks->address, addr, sizeof(socks->address));
+ socks->port = port;
+
+ control_event_stream_status(conn, STREAM_EVENT_NEW, 0);
+
+ rv = connection_ap_rewrite_and_attach_if_allowed(conn, NULL, NULL);
+
+ // XXXX send a "100 Continue" message?
+
+ goto done;
+
+ err:
+ if (BUG(errmsg == NULL))
+ errmsg = "HTTP/1.0 400 Bad Request\r\n\r\n";
+ log_warn(LD_EDGE, "Saying %s", escaped(errmsg));
+ connection_buf_add(errmsg, strlen(errmsg), ENTRY_TO_CONN(conn));
+ connection_mark_unattached_ap(conn,
+ END_STREAM_REASON_HTTPPROTOCOL|
+ END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
+
+ done:
+ tor_free(headers);
+ tor_free(body);
+ tor_free(command);
+ tor_free(addrport);
+ tor_free(addr);
+ return rv;
+}
+
/** Iterate over the two bytes of stream_id until we get one that is not
* already in use; return it. Return 0 if can't get a unique stream_id.
*/
@@ -2455,8 +2659,8 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn)
*
* If ap_conn is broken, mark it for close and return -1. Else return 0.
*/
-int
-connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
+MOCK_IMPL(int,
+connection_ap_handshake_send_begin,(entry_connection_t *ap_conn))
{
char payload[CELL_PAYLOAD_SIZE];
int payload_len;
@@ -2967,15 +3171,22 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
return;
}
if (replylen) { /* we already have a reply in mind */
- connection_write_to_buf(reply, replylen, ENTRY_TO_CONN(conn));
+ connection_buf_add(reply, replylen, ENTRY_TO_CONN(conn));
conn->socks_request->has_finished = 1;
return;
}
- if (conn->socks_request->socks_version == 4) {
+ if (conn->socks_request->listener_type ==
+ CONN_TYPE_AP_HTTP_CONNECT_LISTENER) {
+ const char *response = end_reason_to_http_connect_response_line(endreason);
+ if (!response) {
+ response = "HTTP/1.0 400 Bad Request\r\n\r\n";
+ }
+ connection_buf_add(response, strlen(response), ENTRY_TO_CONN(conn));
+ } else if (conn->socks_request->socks_version == 4) {
memset(buf,0,SOCKS4_NETWORK_LEN);
buf[1] = (status==SOCKS5_SUCCEEDED ? SOCKS4_GRANTED : SOCKS4_REJECT);
/* leave version, destport, destip zero */
- connection_write_to_buf(buf, SOCKS4_NETWORK_LEN, ENTRY_TO_CONN(conn));
+ connection_buf_add(buf, SOCKS4_NETWORK_LEN, ENTRY_TO_CONN(conn));
} else if (conn->socks_request->socks_version == 5) {
size_t buf_len;
memset(buf,0,sizeof(buf));
@@ -2994,7 +3205,7 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
/* 4 bytes for the header, 2 bytes for the port, 16 for the address. */
buf_len = 22;
}
- connection_write_to_buf(buf,buf_len,ENTRY_TO_CONN(conn));
+ connection_buf_add(buf,buf_len,ENTRY_TO_CONN(conn));
}
/* If socks_version isn't 4 or 5, don't send anything.
* This can happen in the case of AP bridges. */
@@ -3007,7 +3218,7 @@ connection_ap_handshake_socks_reply(entry_connection_t *conn, char *reply,
* <0 and set *<b>end_reason_out</b> to the end reason we should send back to
* the client.
*
- * Return -1 in the case where want to send a RELAY_END cell, and < -1 when
+ * Return -1 in the case where we want to send a RELAY_END cell, and < -1 when
* we don't.
**/
STATIC int
@@ -3066,6 +3277,88 @@ begin_cell_parse(const cell_t *cell, begin_cell_t *bcell,
return 0;
}
+/** For the given <b>circ</b> and the edge connection <b>conn</b>, setup the
+ * connection, attach it to the circ and connect it. Return 0 on success
+ * or END_CIRC_AT_ORIGIN if we can't find the requested hidden service port
+ * where the caller should close the circuit. */
+static int
+handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn)
+{
+ int ret;
+ origin_circuit_t *origin_circ;
+
+ assert_circuit_ok(circ);
+ tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
+ tor_assert(conn);
+
+ log_debug(LD_REND, "Connecting the hidden service rendezvous circuit "
+ "to the service destination.");
+
+ origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ conn->base_.address = tor_strdup("(rendezvous)");
+ conn->base_.state = EXIT_CONN_STATE_CONNECTING;
+
+ /* The circuit either has an hs identifier for v3+ or a rend_data for legacy
+ * service. */
+ if (origin_circ->rend_data) {
+ conn->rend_data = rend_data_dup(origin_circ->rend_data);
+ tor_assert(connection_edge_is_rendezvous_stream(conn));
+ ret = rend_service_set_connection_addr_port(conn, origin_circ);
+ } else if (origin_circ->hs_ident) {
+ /* Setup the identifier to be the one for the circuit service. */
+ conn->hs_ident =
+ hs_ident_edge_conn_new(&origin_circ->hs_ident->identity_pk);
+ tor_assert(connection_edge_is_rendezvous_stream(conn));
+ ret = hs_service_set_conn_addr_port(origin_circ, conn);
+ } else {
+ /* We should never get here if the circuit's purpose is rendezvous. */
+ tor_assert_nonfatal_unreached();
+ return -1;
+ }
+ if (ret < 0) {
+ log_info(LD_REND, "Didn't find rendezvous service (addr%s, port %d)",
+ fmt_addr(&TO_CONN(conn)->addr), TO_CONN(conn)->port);
+ /* Send back reason DONE because we want to make hidden service port
+ * scanning harder thus instead of returning that the exit policy
+ * didn't match, which makes it obvious that the port is closed,
+ * return DONE and kill the circuit. That way, a user (malicious or
+ * not) needs one circuit per bad port unless it matches the policy of
+ * the hidden service. */
+ relay_send_end_cell_from_edge(conn->stream_id, circ,
+ END_STREAM_REASON_DONE,
+ origin_circ->cpath->prev);
+ connection_free(TO_CONN(conn));
+
+ /* Drop the circuit here since it might be someone deliberately
+ * scanning the hidden service ports. Note that this mitigates port
+ * scanning by adding more work on the attacker side to successfully
+ * scan but does not fully solve it. */
+ if (ret < -1) {
+ return END_CIRC_AT_ORIGIN;
+ } else {
+ return 0;
+ }
+ }
+
+ /* Link the circuit and the connection crypt path. */
+ conn->cpath_layer = origin_circ->cpath->prev;
+
+ /* Add it into the linked list of p_streams on this circuit */
+ conn->next_stream = origin_circ->p_streams;
+ origin_circ->p_streams = conn;
+ conn->on_circuit = circ;
+ assert_circuit_ok(circ);
+
+ hs_inc_rdv_stream_counter(origin_circ);
+
+ /* Connect tor to the hidden service destination. */
+ connection_exit_connect(conn);
+
+ /* For path bias: This circuit was used successfully */
+ pathbias_mark_use_success(origin_circ);
+ return 0;
+}
+
/** A relay 'begin' or 'begin_dir' cell has arrived, and either we are
* an exit hop for the circuit, or we are the origin and it is a
* rendezvous begin.
@@ -3141,7 +3434,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
port = bcell.port;
if (or_circ && or_circ->p_chan) {
- if ((or_circ->is_first_hop ||
+ const int client_chan = channel_is_client(or_circ->p_chan);
+ if ((client_chan ||
(!connection_or_digest_is_known_relay(
or_circ->p_chan->identity_digest) &&
should_refuse_unknown_exits(options)))) {
@@ -3151,10 +3445,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Attempt by %s to open a stream %s. Closing.",
safe_str(channel_get_canonical_remote_descr(or_circ->p_chan)),
- or_circ->is_first_hop ? "on first hop of circuit" :
- "from unknown relay");
+ client_chan ? "on first hop of circuit" :
+ "from unknown relay");
relay_send_end_cell_from_edge(rh.stream_id, circ,
- or_circ->is_first_hop ?
+ client_chan ?
END_STREAM_REASON_TORPROTOCOL :
END_STREAM_REASON_MISC,
NULL);
@@ -3217,58 +3511,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->deliver_window = STREAMWINDOW_START;
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
- tor_assert(origin_circ);
- log_info(LD_REND,"begin is for rendezvous. configuring stream.");
- n_stream->base_.address = tor_strdup("(rendezvous)");
- n_stream->base_.state = EXIT_CONN_STATE_CONNECTING;
- n_stream->rend_data = rend_data_dup(origin_circ->rend_data);
- tor_assert(connection_edge_is_rendezvous_stream(n_stream));
- assert_circuit_ok(circ);
-
- const int r = rend_service_set_connection_addr_port(n_stream, origin_circ);
- if (r < 0) {
- log_info(LD_REND,"Didn't find rendezvous service (port %d)",
- n_stream->base_.port);
- /* Send back reason DONE because we want to make hidden service port
- * scanning harder thus instead of returning that the exit policy
- * didn't match, which makes it obvious that the port is closed,
- * return DONE and kill the circuit. That way, a user (malicious or
- * not) needs one circuit per bad port unless it matches the policy of
- * the hidden service. */
- relay_send_end_cell_from_edge(rh.stream_id, circ,
- END_STREAM_REASON_DONE,
- layer_hint);
- connection_free(TO_CONN(n_stream));
- tor_free(address);
-
- /* Drop the circuit here since it might be someone deliberately
- * scanning the hidden service ports. Note that this mitigates port
- * scanning by adding more work on the attacker side to successfully
- * scan but does not fully solve it. */
- if (r < -1)
- return END_CIRC_AT_ORIGIN;
- else
- return 0;
- }
- assert_circuit_ok(circ);
- log_debug(LD_REND,"Finished assigning addr/port");
- n_stream->cpath_layer = origin_circ->cpath->prev; /* link it */
-
- /* add it into the linked list of p_streams on this circuit */
- n_stream->next_stream = origin_circ->p_streams;
- n_stream->on_circuit = circ;
- origin_circ->p_streams = n_stream;
- assert_circuit_ok(circ);
-
- origin_circ->rend_data->nr_streams++;
-
- connection_exit_connect(n_stream);
-
- /* For path bias: This circuit was used successfully */
- pathbias_mark_use_success(origin_circ);
-
tor_free(address);
- return 0;
+ /* We handle this circuit and stream in this function for all supported
+ * hidden service version. */
+ return handle_hs_exit_conn(circ, n_stream);
}
tor_strlower(address);
n_stream->base_.address = address;
@@ -3566,8 +3812,12 @@ int
connection_edge_is_rendezvous_stream(const edge_connection_t *conn)
{
tor_assert(conn);
- if (conn->rend_data)
+ /* It should not be possible to set both of these structs */
+ tor_assert_nonfatal(!(conn->rend_data && conn->hs_ident));
+
+ if (conn->rend_data || conn->hs_ident) {
return 1;
+ }
return 0;
}
@@ -3591,7 +3841,7 @@ connection_ap_can_use_exit(const entry_connection_t *conn,
*/
if (conn->chosen_exit_name) {
const node_t *chosen_exit =
- node_get_by_nickname(conn->chosen_exit_name, 1);
+ node_get_by_nickname(conn->chosen_exit_name, 0);
if (!chosen_exit || tor_memneq(chosen_exit->identity,
exit_node->identity, DIGEST_LEN)) {
/* doesn't match */
@@ -3640,10 +3890,12 @@ connection_ap_can_use_exit(const entry_connection_t *conn,
}
/** If address is of the form "y.onion" with a well-formed handle y:
- * Put a NUL after y, lower-case it, and return ONION_HOSTNAME.
+ * Put a NUL after y, lower-case it, and return ONION_V2_HOSTNAME or
+ * ONION_V3_HOSTNAME depending on the HS version.
*
* If address is of the form "x.y.onion" with a well-formed handle x:
- * Drop "x.", put a NUL after y, lower-case it, and return ONION_HOSTNAME.
+ * Drop "x.", put a NUL after y, lower-case it, and return
+ * ONION_V2_HOSTNAME or ONION_V3_HOSTNAME depending on the HS version.
*
* If address is of the form "y.onion" with a badly-formed handle y:
* Return BAD_HOSTNAME and log a message.
@@ -3659,7 +3911,7 @@ parse_extended_hostname(char *address)
{
char *s;
char *q;
- char query[REND_SERVICE_ID_LEN_BASE32+1];
+ char query[HS_SERVICE_ADDR_LEN_BASE32+1];
s = strrchr(address,'.');
if (!s)
@@ -3679,14 +3931,17 @@ parse_extended_hostname(char *address)
goto failed; /* reject sub-domain, as DNS does */
}
q = (NULL == q) ? address : q + 1;
- if (strlcpy(query, q, REND_SERVICE_ID_LEN_BASE32+1) >=
- REND_SERVICE_ID_LEN_BASE32+1)
+ if (strlcpy(query, q, HS_SERVICE_ADDR_LEN_BASE32+1) >=
+ HS_SERVICE_ADDR_LEN_BASE32+1)
goto failed;
if (q != address) {
memmove(address, q, strlen(q) + 1 /* also get \0 */);
}
- if (rend_valid_service_id(query)) {
- return ONION_HOSTNAME; /* success */
+ if (rend_valid_v2_service_id(query)) {
+ return ONION_V2_HOSTNAME; /* success */
+ }
+ if (hs_address_is_valid(query)) {
+ return ONION_V3_HOSTNAME;
}
failed:
/* otherwise, return to previous state and return 0 */
diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h
index e4780b3c7d..c6583d3845 100644
--- a/src/or/connection_edge.h
+++ b/src/or/connection_edge.h
@@ -33,7 +33,8 @@ int connection_edge_finished_connecting(edge_connection_t *conn);
void connection_ap_about_to_close(entry_connection_t *edge_conn);
void connection_exit_about_to_close(edge_connection_t *edge_conn);
-int connection_ap_handshake_send_begin(entry_connection_t *ap_conn);
+MOCK_DECL(int,
+ connection_ap_handshake_send_begin,(entry_connection_t *ap_conn));
int connection_ap_handshake_send_resolve(entry_connection_t *ap_conn);
entry_connection_t *connection_ap_make_link(connection_t *partner,
@@ -88,16 +89,18 @@ int connection_ap_process_transparent(entry_connection_t *conn);
int address_is_invalid_destination(const char *address, int client);
-int connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
- origin_circuit_t *circ,
- crypt_path_t *cpath);
+MOCK_DECL(int, connection_ap_rewrite_and_attach_if_allowed,
+ (entry_connection_t *conn,
+ origin_circuit_t *circ,
+ crypt_path_t *cpath));
int connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
origin_circuit_t *circ,
crypt_path_t *cpath);
/** Possible return values for parse_extended_hostname. */
typedef enum hostname_type_t {
- NORMAL_HOSTNAME, ONION_HOSTNAME, EXIT_HOSTNAME, BAD_HOSTNAME
+ NORMAL_HOSTNAME, ONION_V2_HOSTNAME, ONION_V3_HOSTNAME,
+ EXIT_HOSTNAME, BAD_HOSTNAME
} hostname_type_t;
hostname_type_t parse_extended_hostname(char *address);
@@ -186,7 +189,9 @@ typedef struct {
STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn,
rewrite_result_t *out);
-#endif
-#endif
+STATIC int connection_ap_process_http_connect(entry_connection_t *conn);
+#endif /* defined(CONNECTION_EDGE_PRIVATE) */
+
+#endif /* !defined(TOR_CONNECTION_EDGE_H) */
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index fcd281da26..fd8c5fc7f2 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -46,6 +46,7 @@
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
+#include "proto_cell.h"
#include "reasons.h"
#include "relay.h"
#include "rephist.h"
@@ -472,7 +473,7 @@ var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids)
var_cell_t *
var_cell_new(uint16_t payload_len)
{
- size_t size = STRUCT_OFFSET(var_cell_t, payload) + payload_len;
+ size_t size = offsetof(var_cell_t, payload) + payload_len;
var_cell_t *cell = tor_malloc_zero(size);
cell->payload_len = payload_len;
cell->command = 0;
@@ -491,7 +492,7 @@ var_cell_copy(const var_cell_t *src)
size_t size = 0;
if (src != NULL) {
- size = STRUCT_OFFSET(var_cell_t, payload) + src->payload_len;
+ size = offsetof(var_cell_t, payload) + src->payload_len;
copy = tor_malloc_zero(size);
copy->payload_len = src->payload_len;
copy->command = src->command;
@@ -726,7 +727,7 @@ connection_or_about_to_close(or_connection_t *or_conn)
control_event_or_conn_status(or_conn, OR_CONN_EVENT_FAILED,
reason);
if (!authdir_mode_tests_reachability(options))
- control_event_bootstrap_problem(
+ control_event_bootstrap_prob_or(
orconn_end_reason_to_control_string(reason),
reason, or_conn);
}
@@ -1112,7 +1113,7 @@ connection_or_connect_failed(or_connection_t *conn,
{
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, reason);
if (!authdir_mode_tests_reachability(get_options()))
- control_event_bootstrap_problem(msg, reason, conn);
+ control_event_bootstrap_prob_or(msg, reason, conn);
}
/** <b>conn</b> got an error in connection_handle_read_impl() or
@@ -1235,7 +1236,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port),
transport_name, transport_name);
- control_event_bootstrap_problem(
+ control_event_bootstrap_prob_or(
"Can't connect to bridge",
END_OR_CONN_REASON_PT_MISSING,
conn);
@@ -1369,7 +1370,6 @@ connection_tls_start_handshake,(or_connection_t *conn, int receiving))
connection_start_reading(TO_CONN(conn));
log_debug(LD_HANDSHAKE,"starting TLS handshake on fd "TOR_SOCKET_T_FORMAT,
conn->base_.s);
- note_crypto_pk_op(receiving ? TLS_HANDSHAKE_S : TLS_HANDSHAKE_C);
if (connection_tls_continue_handshake(conn) < 0)
return -1;
@@ -1550,6 +1550,7 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn,
if (identity_rcvd) {
if (crypto_pk_get_digest(identity_rcvd, digest_rcvd_out) < 0) {
+ crypto_pk_free(identity_rcvd);
return -1;
}
} else {
@@ -1713,7 +1714,7 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED,
END_OR_CONN_REASON_OR_IDENTITY);
if (!authdir_mode_tests_reachability(options))
- control_event_bootstrap_problem(
+ control_event_bootstrap_prob_or(
"Unexpected identity in router certificate",
END_OR_CONN_REASON_OR_IDENTITY,
conn);
@@ -1742,8 +1743,9 @@ connection_or_client_learned_peer_id(or_connection_t *conn,
return 0;
}
-/** Return when a client used this, for connection.c, since client_used
- * is now one of the timestamps of channel_t */
+/** Return when we last used this channel for client activity (origin
+ * circuits). This is called from connection.c, since client_used is now one
+ * of the timestamps in channel_t */
time_t
connection_or_client_used(or_connection_t *conn)
@@ -1757,7 +1759,7 @@ connection_or_client_used(or_connection_t *conn)
/** The v1/v2 TLS handshake is finished.
*
- * Make sure we are happy with the person we just handshaked with.
+ * Make sure we are happy with the peer we just handshaked with.
*
* If they initiated the connection, make sure they're not already connected,
* then initialize conn from the information in router.
@@ -1984,7 +1986,7 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
if (cell->command == CELL_PADDING)
rep_hist_padding_count_write(PADDING_TYPE_CELL);
- connection_write_to_buf(networkcell.body, cell_network_size, TO_CONN(conn));
+ connection_buf_add(networkcell.body, cell_network_size, TO_CONN(conn));
/* Touch the channel's active timestamp if there is one */
if (conn->chan) {
@@ -2014,8 +2016,8 @@ connection_or_write_var_cell_to_buf,(const var_cell_t *cell,
tor_assert(cell);
tor_assert(conn);
n = var_cell_pack_header(cell, hdr, conn->wide_circ_ids);
- connection_write_to_buf(hdr, n, TO_CONN(conn));
- connection_write_to_buf((char*)cell->payload,
+ connection_buf_add(hdr, n, TO_CONN(conn));
+ connection_buf_add((char*)cell->payload,
cell->payload_len, TO_CONN(conn));
if (conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3)
or_handshake_state_record_var_cell(conn, conn->handshake_state, cell, 0);
@@ -2090,7 +2092,7 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn)
channel_timestamp_active(TLS_CHAN_TO_BASE(conn->chan));
circuit_build_times_network_is_live(get_circuit_build_times_mutable());
- connection_fetch_from_buf(buf, cell_network_size, TO_CONN(conn));
+ connection_buf_get_bytes(buf, cell_network_size, TO_CONN(conn));
/* retrieve cell info from buf (create the host-order struct from the
* network-order string) */
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index fe85a3f5fd..ee66b7c528 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -118,5 +118,5 @@ void connection_or_group_set_badness_(smartlist_t *group, int force);
extern int certs_cell_ed25519_disabled_for_testing;
#endif
-#endif
+#endif /* !defined(TOR_CONNECTION_OR_H) */
diff --git a/src/or/conscache.c b/src/or/conscache.c
index 33a5495974..1a15126f97 100644
--- a/src/or/conscache.c
+++ b/src/or/conscache.c
@@ -15,7 +15,7 @@
* changes throughout our logic.
*/
#define MUST_UNMAP_TO_UNLINK
-#endif
+#endif /* defined(_WIN32) */
/**
* A consensus_cache_entry_t is a reference-counted handle to an
@@ -90,11 +90,11 @@ consensus_cache_open(const char *subdir, int max_entries)
*/
#define VERY_LARGE_STORAGEDIR_LIMIT (1000*1000)
storagedir_max_entries = VERY_LARGE_STORAGEDIR_LIMIT;
-#else
+#else /* !(defined(MUST_UNMAP_TO_UNLINK)) */
/* Otherwise, we can just tell the storagedir to use the same limits
* as this cache. */
storagedir_max_entries = max_entries;
-#endif
+#endif /* defined(MUST_UNMAP_TO_UNLINK) */
cache->dir = storage_dir_new(directory, storagedir_max_entries);
tor_free(directory);
@@ -145,7 +145,7 @@ consensus_cache_register_with_sandbox(consensus_cache_t *cache,
* conditional.
*/
tor_assert_nonfatal_unreached();
-#endif
+#endif /* defined(MUST_UNMAP_TO_UNLINK) */
return storage_dir_register_with_sandbox(cache->dir, cfg);
}
@@ -474,7 +474,7 @@ consensus_cache_get_n_filenames_available(consensus_cache_t *cache)
return 0;
#else
tor_assert_nonfatal(max >= used);
-#endif
+#endif /* defined(MUST_UNMAP_TO_UNLINK) */
return max - used;
}
@@ -495,7 +495,7 @@ consensus_cache_delete_pending(consensus_cache_t *cache, int force)
if (ent->map) {
force_ent = 0;
}
-#endif
+#endif /* defined(MUST_UNMAP_TO_UNLINK) */
if (! force_ent) {
if (ent->refcnt > 1 || BUG(ent->in_cache == NULL)) {
/* Somebody is using this entry right now */
@@ -622,5 +622,5 @@ consensus_cache_entry_is_mapped(consensus_cache_entry_t *ent)
return 0;
}
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/conscache.h b/src/or/conscache.h
index a0d74c4e08..3c89dedf43 100644
--- a/src/or/conscache.h
+++ b/src/or/conscache.h
@@ -58,5 +58,5 @@ int consensus_cache_entry_get_body(const consensus_cache_entry_t *ent,
int consensus_cache_entry_is_mapped(consensus_cache_entry_t *ent);
#endif
-#endif
+#endif /* !defined(TOR_CONSCACHE_H) */
diff --git a/src/or/consdiff.h b/src/or/consdiff.h
index d05df74b75..eb772c0b2b 100644
--- a/src/or/consdiff.h
+++ b/src/or/consdiff.h
@@ -92,7 +92,7 @@ MOCK_DECL(STATIC int,
MOCK_DECL(STATIC int,
consensus_digest_eq,(const uint8_t *d1,
const uint8_t *d2));
-#endif
+#endif /* defined(CONSDIFF_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CONSDIFF_H) */
diff --git a/src/or/consdiffmgr.c b/src/or/consdiffmgr.c
index a4ff9f2dad..8349f257de 100644
--- a/src/or/consdiffmgr.c
+++ b/src/or/consdiffmgr.c
@@ -663,7 +663,7 @@ consdiffmgr_find_diff_from(consensus_cache_entry_t **entry_out,
smartlist_free(matches);
return result;
-#endif
+#endif /* 0 */
}
/**
diff --git a/src/or/consdiffmgr.h b/src/or/consdiffmgr.h
index 079f9fe2d2..df569c8e23 100644
--- a/src/or/consdiffmgr.h
+++ b/src/or/consdiffmgr.h
@@ -68,7 +68,7 @@ STATIC int cdm_entry_get_sha3_value(uint8_t *digest_out,
const char *label);
STATIC int uncompress_or_copy(char **out, size_t *outlen,
consensus_cache_entry_t *ent);
-#endif
+#endif /* defined(CONSDIFFMGR_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CONSDIFFMGR_H) */
diff --git a/src/or/control.c b/src/or/control.c
index 1837d49025..202366aaec 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -60,9 +60,12 @@
#include "hibernate.h"
#include "hs_common.h"
#include "main.h"
+#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "proto_control0.h"
+#include "proto_http.h"
#include "reasons.h"
#include "rendclient.h"
#include "rendcommon.h"
@@ -354,7 +357,7 @@ static inline void
connection_write_str_to_buf(const char *s, control_connection_t *conn)
{
size_t len = strlen(s);
- connection_write_to_buf(s, len, TO_CONN(conn));
+ connection_buf_add(s, len, TO_CONN(conn));
}
/** Given a <b>len</b>-character string in <b>data</b>, made of lines
@@ -367,16 +370,23 @@ connection_write_str_to_buf(const char *s, control_connection_t *conn)
STATIC size_t
write_escaped_data(const char *data, size_t len, char **out)
{
- size_t sz_out = len+8;
+ tor_assert(len < SIZE_MAX - 9);
+ size_t sz_out = len+8+1;
char *outp;
const char *start = data, *end;
- int i;
+ size_t i;
int start_of_line;
- for (i=0; i<(int)len; ++i) {
- if (data[i]== '\n')
+ for (i=0; i < len; ++i) {
+ if (data[i] == '\n') {
sz_out += 2; /* Maybe add a CR; maybe add a dot. */
+ if (sz_out >= SIZE_T_CEILING) {
+ log_warn(LD_BUG, "Input to write_escaped_data was too long");
+ *out = tor_strdup(".\r\n");
+ return 3;
+ }
+ }
}
- *out = outp = tor_malloc(sz_out+1);
+ *out = outp = tor_malloc(sz_out);
end = data+len;
start_of_line = 1;
while (data < end) {
@@ -402,7 +412,8 @@ write_escaped_data(const char *data, size_t len, char **out)
*outp++ = '\r';
*outp++ = '\n';
*outp = '\0'; /* NUL-terminate just in case. */
- tor_assert((outp - *out) <= (int)sz_out);
+ tor_assert(outp >= *out);
+ tor_assert((size_t)(outp - *out) <= sz_out);
return outp - *out;
}
@@ -556,7 +567,7 @@ connection_printf_to_buf(control_connection_t *conn, const char *format, ...)
tor_assert(0);
}
- connection_write_to_buf(buf, (size_t)len, TO_CONN(conn));
+ connection_buf_add(buf, (size_t)len, TO_CONN(conn));
tor_free(buf);
}
@@ -582,7 +593,7 @@ control_ports_write_to_file(void)
smartlist_add_asprintf(lines, "UNIX_PORT=%s\n", conn->address);
continue;
}
-#endif
+#endif /* defined(AF_UNIX) */
smartlist_add_asprintf(lines, "PORT=%s:%d\n", conn->address, conn->port);
} SMARTLIST_FOREACH_END(conn);
@@ -599,7 +610,7 @@ control_ports_write_to_file(void)
options->ControlPortWriteToFile);
}
}
-#endif
+#endif /* !defined(_WIN32) */
tor_free(joined);
SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
smartlist_free(lines);
@@ -781,7 +792,7 @@ queued_events_flush_all(int force)
SMARTLIST_FOREACH_BEGIN(controllers, control_connection_t *,
control_conn) {
if (control_conn->event_mask & bit) {
- connection_write_to_buf(ev->msg, msg_len, TO_CONN(control_conn));
+ connection_buf_add(ev->msg, msg_len, TO_CONN(control_conn));
}
} SMARTLIST_FOREACH_END(control_conn);
@@ -802,7 +813,7 @@ queued_events_flush_all(int force)
}
/** Libevent callback: Flushes pending events to controllers that are
- * interested in them */
+ * interested in them. */
static void
flush_queued_events_cb(evutil_socket_t fd, short what, void *arg)
{
@@ -1063,7 +1074,7 @@ handle_control_getconf(control_connection_t *conn, uint32_t body_len,
tor_assert(strlen(tmp)>4);
tmp[3] = ' ';
msg = smartlist_join_strings(answers, "", 0, &msg_len);
- connection_write_to_buf(msg, msg_len, TO_CONN(conn));
+ connection_buf_add(msg, msg_len, TO_CONN(conn));
} else {
connection_write_str_to_buf("250 OK\r\n", conn);
}
@@ -1145,7 +1156,6 @@ static const struct control_event_t control_event_table[] = {
{ EVENT_ERR_MSG, "ERR" },
{ EVENT_NEW_DESC, "NEWDESC" },
{ EVENT_ADDRMAP, "ADDRMAP" },
- { EVENT_AUTHDIR_NEWDESCS, "AUTHDIR_NEWDESCS" },
{ EVENT_DESCCHANGED, "DESCCHANGED" },
{ EVENT_NS, "NS" },
{ EVENT_STATUS_GENERAL, "STATUS_GENERAL" },
@@ -1185,7 +1195,10 @@ handle_control_setevents(control_connection_t *conn, uint32_t len,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(events, const char *, ev)
{
- if (!strcasecmp(ev, "EXTENDED")) {
+ if (!strcasecmp(ev, "EXTENDED") ||
+ !strcasecmp(ev, "AUTHDIR_NEWDESCS")) {
+ log_warn(LD_CONTROL, "The \"%s\" SETEVENTS argument is no longer "
+ "supported.", ev);
continue;
} else {
int i;
@@ -1645,12 +1658,12 @@ handle_control_mapaddress(control_connection_t *conn, uint32_t len,
if (smartlist_len(reply)) {
((char*)smartlist_get(reply,smartlist_len(reply)-1))[3] = ' ';
r = smartlist_join_strings(reply, "\r\n", 1, &sz);
- connection_write_to_buf(r, sz, TO_CONN(conn));
+ connection_buf_add(r, sz, TO_CONN(conn));
tor_free(r);
} else {
const char *response =
"512 syntax error: not enough arguments to mapaddress.\r\n";
- connection_write_to_buf(response, strlen(response), TO_CONN(conn));
+ connection_buf_add(response, strlen(response), TO_CONN(conn));
}
SMARTLIST_FOREACH(reply, char *, cp, tor_free(cp));
@@ -1736,7 +1749,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
#else
int myUid = geteuid();
tor_asprintf(answer, "%d", myUid);
- #endif
+#endif /* defined(_WIN32) */
} else if (!strcmp(question, "process/user")) {
#ifdef _WIN32
*answer = tor_strdup("");
@@ -1749,7 +1762,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
} else {
*answer = tor_strdup("");
}
- #endif
+#endif /* defined(_WIN32) */
} else if (!strcmp(question, "process/descriptor-limit")) {
int max_fds = get_max_sockets();
tor_asprintf(answer, "%d", max_fds);
@@ -1885,27 +1898,42 @@ getinfo_helper_dir(control_connection_t *control_conn,
(void) control_conn;
if (!strcmpstart(question, "desc/id/")) {
const routerinfo_t *ri = NULL;
- const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"));
+ const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"), 0);
if (node)
ri = node->ri;
if (ri) {
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
*answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
+ } else if (! we_fetch_router_descriptors(get_options())) {
+ /* Descriptors won't be available, provide proper error */
+ *errmsg = "We fetch microdescriptors, not router "
+ "descriptors. You'll need to use md/id/* "
+ "instead of desc/id/*.";
+ return 0;
}
} else if (!strcmpstart(question, "desc/name/")) {
const routerinfo_t *ri = NULL;
/* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
* warning goes to the user, not to the controller. */
const node_t *node =
- node_get_by_nickname(question+strlen("desc/name/"), 1);
+ node_get_by_nickname(question+strlen("desc/name/"), 0);
if (node)
ri = node->ri;
if (ri) {
const char *body = signed_descriptor_get_body(&ri->cache_info);
if (body)
*answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
+ } else if (! we_fetch_router_descriptors(get_options())) {
+ /* Descriptors won't be available, provide proper error */
+ *errmsg = "We fetch microdescriptors, not router "
+ "descriptors. You'll need to use md/name/* "
+ "instead of desc/name/*.";
+ return 0;
}
+ } else if (!strcmp(question, "desc/download-enabled")) {
+ int r = we_fetch_router_descriptors(get_options());
+ tor_asprintf(answer, "%d", !!r);
} else if (!strcmp(question, "desc/all-recent")) {
routerlist_t *routerlist = router_get_routerlist();
smartlist_t *sl = smartlist_new();
@@ -1975,7 +2003,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
return -1;
}
} else if (!strcmpstart(question, "md/id/")) {
- const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
+ const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0);
const microdesc_t *md = NULL;
if (node) md = node->md;
if (md && md->body) {
@@ -1984,17 +2012,20 @@ getinfo_helper_dir(control_connection_t *control_conn,
} else if (!strcmpstart(question, "md/name/")) {
/* XXX Setting 'warn_if_unnamed' here is a bit silly -- the
* warning goes to the user, not to the controller. */
- const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 1);
+ const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 0);
/* XXXX duplicated code */
const microdesc_t *md = NULL;
if (node) md = node->md;
if (md && md->body) {
*answer = tor_strndup(md->body, md->bodylen);
}
+ } else if (!strcmp(question, "md/download-enabled")) {
+ int r = we_fetch_microdescriptors(get_options());
+ tor_asprintf(answer, "%d", !!r);
} else if (!strcmpstart(question, "desc-annotations/id/")) {
const routerinfo_t *ri = NULL;
const node_t *node =
- node_get_by_hex_id(question+strlen("desc-annotations/id/"));
+ node_get_by_hex_id(question+strlen("desc-annotations/id/"), 0);
if (node)
ri = node->ri;
if (ri) {
@@ -2127,7 +2158,7 @@ download_status_to_string(const download_status_t *dl)
if (dl) {
/* Get some substrings of the eventual output ready */
- format_iso_time(tbuf, dl->next_attempt_at);
+ format_iso_time(tbuf, download_status_get_next_attempt_at(dl));
switch (dl->schedule) {
case DL_SCHED_GENERIC:
@@ -2907,7 +2938,8 @@ getinfo_helper_sr(control_connection_t *control_conn,
* *<b>a</b>. If an internal error occurs, return -1 and optionally set
* *<b>error_out</b> to point to an error message to be delivered to the
* controller. On success, _or if the key is not recognized_, return 0. Do not
- * set <b>a</b> if the key is not recognized.
+ * set <b>a</b> if the key is not recognized but you may set <b>error_out</b>
+ * to improve the error message.
*/
typedef int (*getinfo_helper_t)(control_connection_t *,
const char *q, char **a,
@@ -3012,9 +3044,13 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("desc/name/", dir, "Router descriptors by nickname."),
ITEM("desc/all-recent", dir,
"All non-expired, non-superseded router descriptors."),
+ ITEM("desc/download-enabled", dir,
+ "Do we try to download router descriptors?"),
ITEM("desc/all-recent-extrainfo-hack", dir, NULL), /* Hack. */
PREFIX("md/id/", dir, "Microdescriptors by ID"),
PREFIX("md/name/", dir, "Microdescriptors by name"),
+ ITEM("md/download-enabled", dir,
+ "Do we try to download microdescriptors?"),
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
PREFIX("hs/client/desc/id", dir,
"Hidden Service descriptor in client's cache by onion."),
@@ -3162,7 +3198,7 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
smartlist_t *questions = smartlist_new();
smartlist_t *answers = smartlist_new();
smartlist_t *unrecognized = smartlist_new();
- char *msg = NULL, *ans = NULL;
+ char *ans = NULL;
int i;
(void) len; /* body is NUL-terminated, so it's safe to ignore the length. */
@@ -3177,20 +3213,26 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
goto done;
}
if (!ans) {
- smartlist_add(unrecognized, (char*)q);
+ if (errmsg) /* use provided error message */
+ smartlist_add_strdup(unrecognized, errmsg);
+ else /* use default error message */
+ smartlist_add_asprintf(unrecognized, "Unrecognized key \"%s\"", q);
} else {
smartlist_add_strdup(answers, q);
smartlist_add(answers, ans);
}
} SMARTLIST_FOREACH_END(q);
+
if (smartlist_len(unrecognized)) {
+ /* control-spec section 2.3, mid-reply '-' or end of reply ' ' */
for (i=0; i < smartlist_len(unrecognized)-1; ++i)
connection_printf_to_buf(conn,
- "552-Unrecognized key \"%s\"\r\n",
- (char*)smartlist_get(unrecognized, i));
+ "552-%s\r\n",
+ (char *)smartlist_get(unrecognized, i));
+
connection_printf_to_buf(conn,
- "552 Unrecognized key \"%s\"\r\n",
- (char*)smartlist_get(unrecognized, i));
+ "552 %s\r\n",
+ (char *)smartlist_get(unrecognized, i));
goto done;
}
@@ -3206,7 +3248,7 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
size_t esc_len;
esc_len = write_escaped_data(v, strlen(v), &esc);
connection_printf_to_buf(conn, "250+%s=\r\n", k);
- connection_write_to_buf(esc, esc_len, TO_CONN(conn));
+ connection_buf_add(esc, esc_len, TO_CONN(conn));
tor_free(esc);
}
}
@@ -3217,8 +3259,8 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len,
smartlist_free(answers);
SMARTLIST_FOREACH(questions, char *, cp, tor_free(cp));
smartlist_free(questions);
+ SMARTLIST_FOREACH(unrecognized, char *, cp, tor_free(cp));
smartlist_free(unrecognized);
- tor_free(msg);
return 0;
}
@@ -3364,7 +3406,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len,
nodes = smartlist_new();
SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) {
- const node_t *node = node_get_by_nickname(n, 1);
+ const node_t *node = node_get_by_nickname(n, 0);
if (!node) {
connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n);
goto done;
@@ -4105,7 +4147,7 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len,
/* Extract the first argument (either HSAddress or DescID). */
arg1 = smartlist_get(args, 0);
/* Test if it's an HS address without the .onion part. */
- if (rend_valid_service_id(arg1)) {
+ if (rend_valid_v2_service_id(arg1)) {
hsaddress = arg1;
} else if (strcmpstart(arg1, v2_str) == 0 &&
rend_valid_descriptor_id(arg1 + v2_str_len) &&
@@ -4131,7 +4173,7 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len,
const char *server;
server = arg + strlen(opt_server);
- node = node_get_by_hex_id(server);
+ node = node_get_by_hex_id(server, 0);
if (!node) {
connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
server);
@@ -4217,7 +4259,7 @@ handle_control_hspost(control_connection_t *conn,
SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
if (!strcasecmpstart(arg, opt_server)) {
const char *server = arg + strlen(opt_server);
- const node_t *node = node_get_by_hex_id(server);
+ const node_t *node = node_get_by_hex_id(server, 0);
if (!node || !node->rs) {
connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
@@ -4749,7 +4791,7 @@ handle_control_del_onion(control_connection_t *conn,
return 0;
const char *service_id = smartlist_get(args, 0);
- if (!rend_valid_service_id(service_id)) {
+ if (!rend_valid_v2_service_id(service_id)) {
connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n");
goto out;
}
@@ -4890,6 +4932,38 @@ peek_connection_has_control0_command(connection_t *conn)
return peek_buf_has_control0_command(conn->inbuf);
}
+static int
+peek_connection_has_http_command(connection_t *conn)
+{
+ return peek_buf_has_http_command(conn->inbuf);
+}
+
+static const char CONTROLPORT_IS_NOT_AN_HTTP_PROXY_MSG[] =
+ "HTTP/1.0 501 Tor ControlPort is not an HTTP proxy"
+ "\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>Tor's ControlPort is not an HTTP proxy</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<h1>Tor's ControlPort is not an HTTP proxy</h1>\n"
+ "<p>\n"
+ "It appears you have configured your web browser to use Tor's control port"
+ " as an HTTP proxy.\n"
+ "This is not correct: Tor's default SOCKS proxy port is 9050.\n"
+ "Please configure your client accordingly.\n"
+ "</p>\n"
+ "<p>\n"
+ "See <a href=\"https://www.torproject.org/documentation.html\">"
+ "https://www.torproject.org/documentation.html</a> for more "
+ "information.\n"
+ "<!-- Plus this comment, to make the body response more than 512 bytes, so "
+ " IE will be willing to display it. Comment comment comment comment "
+ " comment comment comment comment comment comment comment comment.-->\n"
+ "</p>\n"
+ "</body>\n"
+ "</html>\n";
+
/** Called when data has arrived on a v1 control connection: Try to fetch
* commands from conn->inbuf, and execute them.
*/
@@ -4923,12 +4997,21 @@ connection_control_process_inbuf(control_connection_t *conn)
sizeof(buf)-6);
body_len = 2+strlen(buf+6)+2; /* code, msg, nul. */
set_uint16(buf+0, htons(body_len));
- connection_write_to_buf(buf, 4+body_len, TO_CONN(conn));
+ connection_buf_add(buf, 4+body_len, TO_CONN(conn));
connection_mark_and_flush(TO_CONN(conn));
return 0;
}
+ /* If the user has the HTTP proxy port and the control port confused. */
+ if (conn->base_.state == CONTROL_CONN_STATE_NEEDAUTH &&
+ peek_connection_has_http_command(TO_CONN(conn))) {
+ connection_write_str_to_buf(CONTROLPORT_IS_NOT_AN_HTTP_PROXY_MSG, conn);
+ log_notice(LD_CONTROL, "Received HTTP request on ControlPort");
+ connection_mark_and_flush(TO_CONN(conn));
+ return 0;
+ }
+
again:
while (1) {
size_t last_idx;
@@ -4936,7 +5019,7 @@ connection_control_process_inbuf(control_connection_t *conn)
/* First, fetch a line. */
do {
data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len;
- r = connection_fetch_from_buf_line(TO_CONN(conn),
+ r = connection_buf_get_line(TO_CONN(conn),
conn->incoming_cmd+conn->incoming_cmd_cur_len,
&data_len);
if (r == 0)
@@ -5504,15 +5587,20 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn)
{
circuit_t *circ;
origin_circuit_t *ocirc;
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
if (!edge_conn->n_read && !edge_conn->n_written)
return 0;
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
send_control_event(EVENT_STREAM_BANDWIDTH_USED,
- "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
+ "650 STREAM_BW "U64_FORMAT" %lu %lu %s\r\n",
U64_PRINTF_ARG(edge_conn->base_.global_identifier),
(unsigned long)edge_conn->n_read,
- (unsigned long)edge_conn->n_written);
+ (unsigned long)edge_conn->n_written,
+ tbuf);
circ = circuit_get_by_edge_conn(edge_conn);
if (circ && CIRCUIT_IS_ORIGIN(circ)) {
@@ -5534,6 +5622,8 @@ control_event_stream_bandwidth_used(void)
if (EVENT_IS_INTERESTING(EVENT_STREAM_BANDWIDTH_USED)) {
smartlist_t *conns = get_connection_array();
edge_connection_t *edge_conn;
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn)
{
@@ -5543,11 +5633,14 @@ control_event_stream_bandwidth_used(void)
if (!edge_conn->n_read && !edge_conn->n_written)
continue;
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
send_control_event(EVENT_STREAM_BANDWIDTH_USED,
- "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n",
+ "650 STREAM_BW "U64_FORMAT" %lu %lu %s\r\n",
U64_PRINTF_ARG(edge_conn->base_.global_identifier),
(unsigned long)edge_conn->n_read,
- (unsigned long)edge_conn->n_written);
+ (unsigned long)edge_conn->n_written,
+ tbuf);
edge_conn->n_written = edge_conn->n_read = 0;
}
@@ -5563,6 +5656,8 @@ int
control_event_circ_bandwidth_used(void)
{
origin_circuit_t *ocirc;
+ struct timeval now;
+ char tbuf[ISO_TIME_USEC_LEN+1];
if (!EVENT_IS_INTERESTING(EVENT_CIRC_BANDWIDTH_USED))
return 0;
@@ -5572,11 +5667,15 @@ control_event_circ_bandwidth_used(void)
ocirc = TO_ORIGIN_CIRCUIT(circ);
if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw)
continue;
+ tor_gettimeofday(&now);
+ format_iso_time_nospace_usec(tbuf, &now);
send_control_event(EVENT_CIRC_BANDWIDTH_USED,
- "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu\r\n",
+ "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu "
+ "TIME=%s\r\n",
ocirc->global_identifier,
(unsigned long)ocirc->n_read_circ_bw,
- (unsigned long)ocirc->n_written_circ_bw);
+ (unsigned long)ocirc->n_written_circ_bw,
+ tbuf);
ocirc->n_written_circ_bw = ocirc->n_read_circ_bw = 0;
}
SMARTLIST_FOREACH_END(circ);
@@ -5747,7 +5846,7 @@ control_event_circuit_cell_stats(void)
if (!get_options()->TestingEnableCellStatsEvent ||
!EVENT_IS_INTERESTING(EVENT_CELL_STATS))
return 0;
- cell_stats = tor_malloc(sizeof(cell_stats_t));;
+ cell_stats = tor_malloc(sizeof(cell_stats_t));
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
if (!circ->testing_cell_stats)
continue;
@@ -5982,47 +6081,6 @@ control_event_address_mapped(const char *from, const char *to, time_t expires,
return 0;
}
-/** The authoritative dirserver has received a new descriptor that
- * has passed basic syntax checks and is properly self-signed.
- *
- * Notify any interested party of the new descriptor and what has
- * been done with it, and also optionally give an explanation/reason. */
-int
-control_event_or_authdir_new_descriptor(const char *action,
- const char *desc, size_t desclen,
- const char *msg)
-{
- char firstline[1024];
- char *buf;
- size_t totallen;
- char *esc = NULL;
- size_t esclen;
-
- if (!EVENT_IS_INTERESTING(EVENT_AUTHDIR_NEWDESCS))
- return 0;
-
- tor_snprintf(firstline, sizeof(firstline),
- "650+AUTHDIR_NEWDESC=\r\n%s\r\n%s\r\n",
- action,
- msg ? msg : "");
-
- /* Escape the server descriptor properly */
- esclen = write_escaped_data(desc, desclen, &esc);
-
- totallen = strlen(firstline) + esclen + 1;
- buf = tor_malloc(totallen);
- strlcpy(buf, firstline, totallen);
- strlcpy(buf+strlen(firstline), esc, totallen);
- send_control_event_string(EVENT_AUTHDIR_NEWDESCS,
- buf);
- send_control_event_string(EVENT_AUTHDIR_NEWDESCS,
- "650 OK\r\n");
- tor_free(esc);
- tor_free(buf);
-
- return 0;
-}
-
/** Cached liveness for network liveness events and GETINFO
*/
@@ -6712,80 +6770,112 @@ control_event_bootstrap(bootstrap_status_t status, int progress)
}
/** Called when Tor has failed to make bootstrapping progress in a way
- * that indicates a problem. <b>warn</b> gives a hint as to why, and
- * <b>reason</b> provides an "or_conn_end_reason" tag. <b>or_conn</b>
- * is the connection that caused this problem.
+ * that indicates a problem. <b>warn</b> gives a human-readable hint
+ * as to why, and <b>reason</b> provides a controller-facing short
+ * tag. <b>conn</b> is the connection that caused this problem and
+ * can be NULL if a connection cannot be easily identified.
*/
-MOCK_IMPL(void,
-control_event_bootstrap_problem, (const char *warn, int reason,
- or_connection_t *or_conn))
+void
+control_event_bootstrap_problem(const char *warn, const char *reason,
+ const connection_t *conn, int dowarn)
{
int status = bootstrap_percent;
const char *tag = "", *summary = "";
char buf[BOOTSTRAP_MSG_LEN];
const char *recommendation = "ignore";
int severity;
+ char *or_id = NULL, *hostaddr = NULL;
+ or_connection_t *or_conn = NULL;
/* bootstrap_percent must not be in "undefined" state here. */
tor_assert(status >= 0);
- if (or_conn->have_noted_bootstrap_problem)
- return;
-
- or_conn->have_noted_bootstrap_problem = 1;
-
if (bootstrap_percent == 100)
return; /* already bootstrapped; nothing to be done here. */
bootstrap_problems++;
if (bootstrap_problems >= BOOTSTRAP_PROBLEM_THRESHOLD)
- recommendation = "warn";
-
- if (reason == END_OR_CONN_REASON_NO_ROUTE)
- recommendation = "warn";
-
- /* If we are using bridges and all our OR connections are now
- closed, it means that we totally failed to connect to our
- bridges. Throw a warning. */
- if (get_options()->UseBridges && !any_other_active_or_conns(or_conn))
- recommendation = "warn";
+ dowarn = 1;
if (we_are_hibernating())
- recommendation = "ignore";
+ dowarn = 0;
while (status>=0 && bootstrap_status_to_string(status, &tag, &summary) < 0)
status--; /* find a recognized status string based on current progress */
status = bootstrap_percent; /* set status back to the actual number */
- severity = !strcmp(recommendation, "warn") ? LOG_WARN : LOG_INFO;
+ severity = dowarn ? LOG_WARN : LOG_INFO;
+
+ if (dowarn)
+ recommendation = "warn";
+
+ if (conn && conn->type == CONN_TYPE_OR) {
+ /* XXX TO_OR_CONN can't deal with const */
+ or_conn = TO_OR_CONN((connection_t *)conn);
+ or_id = tor_strdup(hex_str(or_conn->identity_digest, DIGEST_LEN));
+ } else {
+ or_id = tor_strdup("?");
+ }
+
+ if (conn)
+ tor_asprintf(&hostaddr, "%s:%d", conn->address, (int)conn->port);
+ else
+ hostaddr = tor_strdup("?");
log_fn(severity,
LD_CONTROL, "Problem bootstrapping. Stuck at %d%%: %s. (%s; %s; "
- "count %d; recommendation %s; host %s at %s:%d)",
- status, summary, warn,
- orconn_end_reason_to_control_string(reason),
+ "count %d; recommendation %s; host %s at %s)",
+ status, summary, warn, reason,
bootstrap_problems, recommendation,
- hex_str(or_conn->identity_digest, DIGEST_LEN),
- or_conn->base_.address,
- or_conn->base_.port);
+ or_id, hostaddr);
connection_or_report_broken_states(severity, LD_HANDSHAKE);
tor_snprintf(buf, sizeof(buf),
"BOOTSTRAP PROGRESS=%d TAG=%s SUMMARY=\"%s\" WARNING=\"%s\" REASON=%s "
- "COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s:%d\"",
- bootstrap_percent, tag, summary, warn,
- orconn_end_reason_to_control_string(reason), bootstrap_problems,
+ "COUNT=%d RECOMMENDATION=%s HOSTID=\"%s\" HOSTADDR=\"%s\"",
+ bootstrap_percent, tag, summary, warn, reason, bootstrap_problems,
recommendation,
- hex_str(or_conn->identity_digest, DIGEST_LEN),
- or_conn->base_.address,
- (int)or_conn->base_.port);
+ or_id, hostaddr);
tor_snprintf(last_sent_bootstrap_message,
sizeof(last_sent_bootstrap_message),
"WARN %s", buf);
control_event_client_status(LOG_WARN, "%s", buf);
+
+ tor_free(hostaddr);
+ tor_free(or_id);
+}
+
+/** Called when Tor has failed to make bootstrapping progress in a way
+ * that indicates a problem. <b>warn</b> gives a hint as to why, and
+ * <b>reason</b> provides an "or_conn_end_reason" tag. <b>or_conn</b>
+ * is the connection that caused this problem.
+ */
+MOCK_IMPL(void,
+control_event_bootstrap_prob_or, (const char *warn, int reason,
+ or_connection_t *or_conn))
+{
+ int dowarn = 0;
+
+ if (or_conn->have_noted_bootstrap_problem)
+ return;
+
+ or_conn->have_noted_bootstrap_problem = 1;
+
+ if (reason == END_OR_CONN_REASON_NO_ROUTE)
+ dowarn = 1;
+
+ /* If we are using bridges and all our OR connections are now
+ closed, it means that we totally failed to connect to our
+ bridges. Throw a warning. */
+ if (get_options()->UseBridges && !any_other_active_or_conns(or_conn))
+ dowarn = 1;
+
+ control_event_bootstrap_problem(warn,
+ orconn_end_reason_to_control_string(reason),
+ TO_CONN(or_conn), dowarn);
}
/** We just generated a new summary of which countries we've seen clients
@@ -7194,7 +7284,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest,
id_digest);
return;
}
- control_event_hs_descriptor_upload_end("UPLOAD_FAILED", onion_address,
+ control_event_hs_descriptor_upload_end("FAILED", onion_address,
id_digest, reason);
}
@@ -7227,5 +7317,5 @@ control_testing_set_global_event_mask(uint64_t mask)
{
global_event_mask = mask;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/control.h b/src/or/control.h
index 41a194bfcb..e957b593a6 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -33,7 +33,6 @@ void connection_control_closed(control_connection_t *conn);
int connection_control_process_inbuf(control_connection_t *conn);
-#define EVENT_AUTHDIR_NEWDESCS 0x000D
#define EVENT_NS 0x000F
int control_event_is_interesting(int event);
@@ -64,10 +63,6 @@ int control_event_descriptors_changed(smartlist_t *routers);
int control_event_address_mapped(const char *from, const char *to,
time_t expires, const char *error,
const int cached);
-int control_event_or_authdir_new_descriptor(const char *action,
- const char *desc,
- size_t desclen,
- const char *msg);
int control_event_my_descriptor_changed(void);
int control_event_network_liveness_update(int liveness);
int control_event_networkstatus_changed(smartlist_t *statuses);
@@ -104,9 +99,11 @@ void enable_control_logging(void);
void monitor_owning_controller_process(const char *process_spec);
int control_event_bootstrap(bootstrap_status_t status, int progress);
-MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn,
+MOCK_DECL(void, control_event_bootstrap_prob_or,(const char *warn,
int reason,
or_connection_t *or_conn));
+void control_event_bootstrap_problem(const char *warn, const char *reason,
+ const connection_t *conn, int dowarn);
void control_event_clients_seen(const char *controller_str);
void control_event_transport_launched(const char *mode,
@@ -169,8 +166,8 @@ void control_free_all(void);
#define EVENT_WARN_MSG 0x000A
#define EVENT_ERR_MSG 0x000B
#define EVENT_ADDRMAP 0x000C
-/* Exposed above */
-// #define EVENT_AUTHDIR_NEWDESCS 0x000D
+/* There was an AUTHDIR_NEWDESCS event, but it no longer exists. We
+ can reclaim 0x000D. */
#define EVENT_DESCCHANGED 0x000E
/* Exposed above */
// #define EVENT_NS 0x000F
@@ -228,7 +225,7 @@ MOCK_DECL(STATIC void,
queue_control_event_string,(uint16_t event, char *msg));
void control_testing_set_global_event_mask(uint64_t mask);
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/** Helper structure: temporarily stores cell statistics for a circuit. */
typedef struct cell_stats_t {
@@ -295,7 +292,7 @@ STATIC int getinfo_helper_dir(
const char *question, char **answer,
const char **errmsg);
-#endif
+#endif /* defined(CONTROL_PRIVATE) */
-#endif
+#endif /* !defined(TOR_CONTROL_H) */
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index f5fff2b331..d9371b3446 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -382,7 +382,7 @@ cpuworker_onion_handshake_replyfn(void *work_)
if (onionskin_answer(circ,
&rpl.created_cell,
- (const char*)rpl.keys,
+ (const char*)rpl.keys, sizeof(rpl.keys),
rpl.rend_auth_material) < 0) {
log_warn(LD_OR,"onionskin_answer failed. Closing.");
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
@@ -484,7 +484,7 @@ queue_pending_tasks(void)
if (!circ)
return;
- if (assign_onionskin_to_cpuworker(circ, onionskin))
+ if (assign_onionskin_to_cpuworker(circ, onionskin) < 0)
log_info(LD_OR,"assign_to_cpuworker failed. Ignoring.");
}
}
diff --git a/src/or/cpuworker.h b/src/or/cpuworker.h
index 320de9532f..d39851325f 100644
--- a/src/or/cpuworker.h
+++ b/src/or/cpuworker.h
@@ -33,5 +33,5 @@ void cpuworker_log_onionskin_overhead(int severity, int onionskin_type,
const char *onionskin_type_name);
void cpuworker_cancel_circ_handshake(or_circuit_t *circ);
-#endif
+#endif /* !defined(TOR_CPUWORKER_H) */
diff --git a/src/or/dircollate.c b/src/or/dircollate.c
index 172364c5f5..d34ebe8af5 100644
--- a/src/or/dircollate.c
+++ b/src/or/dircollate.c
@@ -53,7 +53,7 @@ ddmap_entry_free(ddmap_entry_t *e)
static ddmap_entry_t *
ddmap_entry_new(int n_votes)
{
- return tor_malloc_zero(STRUCT_OFFSET(ddmap_entry_t, vrs_lst) +
+ return tor_malloc_zero(offsetof(ddmap_entry_t, vrs_lst) +
sizeof(vote_routerstatus_t *) * n_votes);
}
diff --git a/src/or/dircollate.h b/src/or/dircollate.h
index 52214282b9..7932e18998 100644
--- a/src/or/dircollate.h
+++ b/src/or/dircollate.h
@@ -62,7 +62,7 @@ struct dircollator_s {
* identity digests .*/
smartlist_t *all_rsa_sha1_lst;
};
-#endif
+#endif /* defined(DIRCOLLATE_PRIVATE) */
-#endif
+#endif /* !defined(TOR_DIRCOLLATE_H) */
diff --git a/src/or/directory.c b/src/or/directory.c
index 5ceea2fb32..47058352f0 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -18,7 +18,6 @@
#include "consdiffmgr.h"
#include "control.h"
#include "compat.h"
-#define DIRECTORY_PRIVATE
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
@@ -26,6 +25,7 @@
#include "geoip.h"
#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_client.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -104,7 +104,6 @@ static void directory_send_command(dir_connection_t *conn,
int direct,
const directory_request_t *request);
static int body_is_plausible(const char *body, size_t body_len, int purpose);
-static char *http_get_header(const char *headers, const char *which);
static void http_set_address_origin(const char *headers, connection_t *conn);
static void connection_dir_download_routerdesc_failed(dir_connection_t *conn);
static void connection_dir_bridge_routerdesc_failed(dir_connection_t *conn);
@@ -185,9 +184,12 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose,
case DIR_PURPOSE_FETCH_EXTRAINFO:
case DIR_PURPOSE_FETCH_MICRODESC:
return 0;
+ case DIR_PURPOSE_HAS_FETCHED_HSDESC:
case DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2:
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
case DIR_PURPOSE_FETCH_RENDDESC_V2:
+ case DIR_PURPOSE_FETCH_HSDESC:
+ case DIR_PURPOSE_UPLOAD_HSDESC:
return 1;
case DIR_PURPOSE_SERVER:
default:
@@ -246,6 +248,10 @@ dir_conn_purpose_to_string(int purpose)
return "hidden-service v2 descriptor fetch";
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
return "hidden-service v2 descriptor upload";
+ case DIR_PURPOSE_FETCH_HSDESC:
+ return "hidden-service descriptor fetch";
+ case DIR_PURPOSE_UPLOAD_HSDESC:
+ return "hidden-service descriptor upload";
case DIR_PURPOSE_FETCH_MICRODESC:
return "microdescriptor fetch";
}
@@ -1006,47 +1012,6 @@ directory_must_use_begindir(const or_options_t *options)
return !public_server_mode(options);
}
-struct directory_request_t {
- /**
- * These fields specify which directory we're contacting. Routerstatus,
- * if present, overrides the other fields.
- *
- * @{ */
- tor_addr_port_t or_addr_port;
- tor_addr_port_t dir_addr_port;
- char digest[DIGEST_LEN];
-
- const routerstatus_t *routerstatus;
- /** @} */
- /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what
- * kind of operation we'll be doing (upload/download), and of what kind
- * of document. */
- uint8_t dir_purpose;
- /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo
- * and extrainfo docs. */
- uint8_t router_purpose;
- /** Enum: determines whether to anonymize, and whether to use dirport or
- * orport. */
- dir_indirection_t indirection;
- /** Alias to the variable part of the URL for this request */
- const char *resource;
- /** Alias to the payload to upload (if any) */
- const char *payload;
- /** Number of bytes to upload from payload</b> */
- size_t payload_len;
- /** Value to send in an if-modified-since header, or 0 for none. */
- time_t if_modified_since;
- /** Hidden-service-specific information */
- const rend_data_t *rend_query;
- /** Extra headers to append to the request */
- config_line_t *additional_headers;
- /** */
- /** Used internally to directory.c: gets informed when the attempt to
- * connect to the directory succeeds or fails, if that attempt bears on the
- * directory's usability as a directory guard. */
- circuit_guard_state_t *guard_state;
-};
-
/** Evaluate the situation and decide if we should use an encrypted
* "begindir-style" connection for this directory request.
* 0) If there is no DirPort, yes.
@@ -1120,6 +1085,7 @@ directory_request_new(uint8_t dir_purpose)
tor_assert(dir_purpose <= DIR_PURPOSE_MAX_);
tor_assert(dir_purpose != DIR_PURPOSE_SERVER);
tor_assert(dir_purpose != DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2);
+ tor_assert(dir_purpose != DIR_PURPOSE_HAS_FETCHED_HSDESC);
directory_request_t *result = tor_malloc_zero(sizeof(*result));
tor_addr_make_null(&result->or_addr_port.addr, AF_INET);
@@ -1270,6 +1236,34 @@ directory_request_set_rend_query(directory_request_t *req,
}
req->rend_query = query;
}
+/**
+ * Set an object containing HS connection identifier to be associated with
+ * this request. Note that only an alias to <b>ident</b> is stored, so the
+ * <b>ident</b> object must outlive the request.
+ */
+void
+directory_request_upload_set_hs_ident(directory_request_t *req,
+ const hs_ident_dir_conn_t *ident)
+{
+ if (ident) {
+ tor_assert(req->dir_purpose == DIR_PURPOSE_UPLOAD_HSDESC);
+ }
+ req->hs_ident = ident;
+}
+/**
+ * Set an object containing HS connection identifier to be associated with
+ * this fetch request. Note that only an alias to <b>ident</b> is stored, so
+ * the <b>ident</b> object must outlive the request.
+ */
+void
+directory_request_fetch_set_hs_ident(directory_request_t *req,
+ const hs_ident_dir_conn_t *ident)
+{
+ if (ident) {
+ tor_assert(req->dir_purpose == DIR_PURPOSE_FETCH_HSDESC);
+ }
+ req->hs_ident = ident;
+}
/** Set a static circuit_guard_state_t object to affliate with the request in
* <b>req</b>. This object will receive notification when the attempt to
* connect to the guard either succeeds or fails. */
@@ -1391,6 +1385,7 @@ directory_initiate_request,(directory_request_t *request))
const dir_indirection_t indirection = request->indirection;
const char *resource = request->resource;
const rend_data_t *rend_query = request->rend_query;
+ const hs_ident_dir_conn_t *hs_ident = request->hs_ident;
circuit_guard_state_t *guard_state = request->guard_state;
tor_assert(or_addr_port->port || dir_addr_port->port);
@@ -1478,8 +1473,16 @@ directory_initiate_request,(directory_request_t *request))
conn->dirconn_direct = !anonymized_connection;
/* copy rendezvous data, if any */
- if (rend_query)
+ if (rend_query) {
+ /* We can't have both v2 and v3+ identifier. */
+ tor_assert_nonfatal(!hs_ident);
conn->rend_data = rend_data_dup(rend_query);
+ }
+ if (hs_ident) {
+ /* We can't have both v2 and v3+ identifier. */
+ tor_assert_nonfatal(!rend_query);
+ conn->hs_ident = hs_ident_dir_conn_dup(hs_ident);
+ }
if (!anonymized_connection && !use_begindir) {
/* then we want to connect to dirport directly */
@@ -1807,9 +1810,10 @@ directory_send_command(dir_connection_t *conn,
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/");
- if (why) {
- smartlist_add_asprintf(headers, "X-Desc-Gen-Reason: %s\r\n", why);
+ if (!why) {
+ why = "for no reason at all";
}
+ smartlist_add_asprintf(headers, "X-Desc-Gen-Reason: %s\r\n", why);
break;
}
case DIR_PURPOSE_UPLOAD_VOTE:
@@ -1831,12 +1835,25 @@ directory_send_command(dir_connection_t *conn,
httpcommand = "GET";
tor_asprintf(&url, "/tor/rendezvous2/%s", resource);
break;
+ case DIR_PURPOSE_FETCH_HSDESC:
+ tor_assert(resource);
+ tor_assert(strlen(resource) <= ED25519_BASE64_LEN);
+ tor_assert(!payload);
+ httpcommand = "GET";
+ tor_asprintf(&url, "/tor/hs/3/%s", resource);
+ break;
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
tor_assert(!resource);
tor_assert(payload);
httpcommand = "POST";
url = tor_strdup("/tor/rendezvous2/publish");
break;
+ case DIR_PURPOSE_UPLOAD_HSDESC:
+ tor_assert(resource);
+ tor_assert(payload);
+ httpcommand = "POST";
+ tor_asprintf(&url, "/tor/hs/%s/publish", resource);
+ break;
default:
tor_assert(0);
return;
@@ -1854,11 +1871,11 @@ directory_send_command(dir_connection_t *conn,
request_len = strlen(request);
total_request_len += request_len;
- connection_write_to_buf(request, request_len, TO_CONN(conn));
+ connection_buf_add(request, request_len, TO_CONN(conn));
url_len = strlen(url);
total_request_len += url_len;
- connection_write_to_buf(url, url_len, TO_CONN(conn));
+ connection_buf_add(url, url_len, TO_CONN(conn));
tor_free(url);
if (!strcmp(httpcommand, "POST") || payload) {
@@ -1875,11 +1892,11 @@ directory_send_command(dir_connection_t *conn,
request_len = strlen(request);
total_request_len += request_len;
- connection_write_to_buf(request, request_len, TO_CONN(conn));
+ connection_buf_add(request, request_len, TO_CONN(conn));
if (payload) {
/* then send the payload afterwards too */
- connection_write_to_buf(payload, payload_len, TO_CONN(conn));
+ connection_buf_add(payload, payload_len, TO_CONN(conn));
total_request_len += payload_len;
}
@@ -1908,15 +1925,41 @@ directory_send_command(dir_connection_t *conn,
STATIC int
parse_http_url(const char *headers, char **url)
{
+ char *command = NULL;
+ if (parse_http_command(headers, &command, url) < 0) {
+ return -1;
+ }
+ if (strcmpstart(*url, "/tor/")) {
+ char *new_url = NULL;
+ tor_asprintf(&new_url, "/tor%s%s",
+ *url[0] == '/' ? "" : "/",
+ *url);
+ tor_free(*url);
+ *url = new_url;
+ }
+ tor_free(command);
+ return 0;
+}
+
+/** Parse an HTTP request line at the start of a headers string. On failure,
+ * return -1. On success, set *<b>command_out</b> to a copy of the HTTP
+ * command ("get", "post", etc), set *<b>url_out</b> to a copy of the URL, and
+ * return 0. */
+int
+parse_http_command(const char *headers, char **command_out, char **url_out)
+{
+ const char *command, *end_of_command;
char *s, *start, *tmp;
s = (char *)eat_whitespace_no_nl(headers);
if (!*s) return -1;
+ command = s;
s = (char *)find_whitespace(s); /* get past GET/POST */
if (!*s) return -1;
+ end_of_command = s;
s = (char *)eat_whitespace_no_nl(s);
if (!*s) return -1;
- start = s; /* this is it, assuming it's valid */
+ start = s; /* this is the URL, assuming it's valid */
s = (char *)find_whitespace(start);
if (!*s) return -1;
@@ -1947,13 +1990,8 @@ parse_http_url(const char *headers, char **url)
return -1;
}
- if (s-start < 5 || strcmpstart(start,"/tor/")) { /* need to rewrite it */
- *url = tor_malloc(s - start + 5);
- strlcpy(*url,"/tor", s-start+5);
- strlcat((*url)+4, start, s-start+1);
- } else {
- *url = tor_strndup(start, s-start);
- }
+ *url_out = tor_memdup_nulterm(start, s-start);
+ *command_out = tor_memdup_nulterm(command, end_of_command - command);
return 0;
}
@@ -1961,7 +1999,7 @@ parse_http_url(const char *headers, char **url)
* <b>which</b>. The key should be given with a terminating colon and space;
* this function copies everything after, up to but not including the
* following \\r\\n. */
-static char *
+char *
http_get_header(const char *headers, const char *which)
{
const char *cp = headers;
@@ -2159,16 +2197,6 @@ load_downloaded_routers(const char *body, smartlist_t *which,
return added;
}
-/** A structure to hold arguments passed into each directory response
- * handler */
-typedef struct response_handler_args_t {
- int status_code;
- const char *reason;
- const char *body;
- size_t body_len;
- const char *headers;
-} response_handler_args_t;
-
static int handle_response_fetch_consensus(dir_connection_t *,
const response_handler_args_t *);
static int handle_response_fetch_certificate(dir_connection_t *,
@@ -2189,6 +2217,8 @@ static int handle_response_fetch_renddesc_v2(dir_connection_t *,
const response_handler_args_t *);
static int handle_response_upload_renddesc_v2(dir_connection_t *,
const response_handler_args_t *);
+static int handle_response_upload_hsdesc(dir_connection_t *,
+ const response_handler_args_t *);
static int
dir_client_decompress_response_body(char **bodyp, size_t *bodylenp,
@@ -2489,6 +2519,12 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
rv = handle_response_upload_renddesc_v2(conn, &args);
break;
+ case DIR_PURPOSE_UPLOAD_HSDESC:
+ rv = handle_response_upload_hsdesc(conn, &args);
+ break;
+ case DIR_PURPOSE_FETCH_HSDESC:
+ rv = handle_response_fetch_hsdesc_v3(conn, &args);
+ break;
default:
tor_assert_nonfatal_unreached();
rv = -1;
@@ -2811,7 +2847,7 @@ handle_response_fetch_desc(dir_connection_t *conn,
conn->router_purpose,
conn->base_.address)) {
time_t now = approx_time();
- directory_info_has_arrived(now, 0, 0);
+ directory_info_has_arrived(now, 0, 1);
}
}
}
@@ -3035,6 +3071,60 @@ handle_response_upload_signatures(dir_connection_t *conn,
}
/**
+ * Handler function: processes a response to a request for a v3 hidden service
+ * descriptor.
+ **/
+STATIC int
+handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
+ const response_handler_args_t *args)
+{
+ const int status_code = args->status_code;
+ const char *reason = args->reason;
+ const char *body = args->body;
+ const size_t body_len = args->body_len;
+
+ tor_assert(conn->hs_ident);
+
+ log_info(LD_REND,"Received v3 hsdesc (body size %d, status %d (%s))",
+ (int)body_len, status_code, escaped(reason));
+
+ switch (status_code) {
+ case 200:
+ /* We got something: Try storing it in the cache. */
+ if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) {
+ log_warn(LD_REND, "Failed to store hidden service descriptor");
+ } else {
+ log_info(LD_REND, "Stored hidden service descriptor successfully.");
+ TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC;
+ hs_client_desc_has_arrived(conn->hs_ident);
+ }
+ break;
+ case 404:
+ /* Not there. We'll retry when connection_about_to_close_connection()
+ * tries to clean this conn up. */
+ log_info(LD_REND, "Fetching hidden service v3 descriptor not found: "
+ "Retrying at another directory.");
+ /* TODO: Inform the control port */
+ break;
+ case 400:
+ log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
+ "http status 400 (%s). Dirserver didn't like our "
+ "query? Retrying at another directory.",
+ escaped(reason));
+ break;
+ default:
+ log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
+ "http status %d (%s) response unexpected from HSDir server "
+ "'%s:%d'. Retrying at another directory.",
+ status_code, escaped(reason), TO_CONN(conn)->address,
+ TO_CONN(conn)->port);
+ break;
+ }
+
+ return 0;
+}
+
+/**
* Handler function: processes a response to a request for a v2 hidden service
* descriptor.
**/
@@ -3181,6 +3271,53 @@ handle_response_upload_renddesc_v2(dir_connection_t *conn,
return 0;
}
+/**
+ * Handler function: processes a response to a POST request to upload an
+ * hidden service descriptor.
+ **/
+static int
+handle_response_upload_hsdesc(dir_connection_t *conn,
+ const response_handler_args_t *args)
+{
+ const int status_code = args->status_code;
+ const char *reason = args->reason;
+
+ tor_assert(conn);
+ tor_assert(conn->base_.purpose == DIR_PURPOSE_UPLOAD_HSDESC);
+
+ log_info(LD_REND, "Uploaded hidden service descriptor (status %d "
+ "(%s))",
+ status_code, escaped(reason));
+ /* For this directory response, it MUST have an hidden service identifier on
+ * this connection. */
+ tor_assert(conn->hs_ident);
+ switch (status_code) {
+ case 200:
+ log_info(LD_REND, "Uploading hidden service descriptor: "
+ "finished with status 200 (%s)", escaped(reason));
+ /* XXX: Trigger control event. */
+ break;
+ case 400:
+ log_fn(LOG_PROTOCOL_WARN, LD_REND,
+ "Uploading hidden service descriptor: http "
+ "status 400 (%s) response from dirserver "
+ "'%s:%d'. Malformed hidden service descriptor?",
+ escaped(reason), conn->base_.address, conn->base_.port);
+ /* XXX: Trigger control event. */
+ break;
+ default:
+ log_warn(LD_REND, "Uploading hidden service descriptor: http "
+ "status %d (%s) response unexpected (server "
+ "'%s:%d').",
+ status_code, escaped(reason), conn->base_.address,
+ conn->base_.port);
+ /* XXX: Trigger control event. */
+ break;
+ }
+
+ return 0;
+}
+
/** Called when a directory connection reaches EOF. */
int
connection_dir_reached_eof(dir_connection_t *conn)
@@ -3252,6 +3389,33 @@ connection_dir_process_inbuf(dir_connection_t *conn)
return 0;
}
+/** We are closing a dir connection: If <b>dir_conn</b> is a dir connection
+ * that tried to fetch an HS descriptor, check if it successfuly fetched it,
+ * or if we need to try again. */
+static void
+refetch_hsdesc_if_needed(dir_connection_t *dir_conn)
+{
+ connection_t *conn = TO_CONN(dir_conn);
+
+ /* If we were trying to fetch a v2 rend desc and did not succeed, retry as
+ * needed. (If a fetch is successful, the connection state is changed to
+ * DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2 or DIR_PURPOSE_HAS_FETCHED_HSDESC to
+ * mark that refetching is unnecessary.) */
+ if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 &&
+ dir_conn->rend_data &&
+ rend_valid_v2_service_id(
+ rend_data_get_address(dir_conn->rend_data))) {
+ rend_client_refetch_v2_renddesc(dir_conn->rend_data);
+ }
+
+ /* Check for v3 rend desc fetch */
+ if (conn->purpose == DIR_PURPOSE_FETCH_HSDESC &&
+ dir_conn->hs_ident &&
+ !ed25519_public_key_is_zero(&dir_conn->hs_ident->identity_pk)) {
+ hs_client_refetch_hsdesc(&dir_conn->hs_ident->identity_pk);
+ }
+}
+
/** Called when we're about to finally unlink and free a directory connection:
* perform necessary accounting and cleanup */
void
@@ -3264,29 +3428,38 @@ connection_dir_about_to_close(dir_connection_t *dir_conn)
* failed: forget about this router, and maybe try again. */
connection_dir_request_failed(dir_conn);
}
- /* If we were trying to fetch a v2 rend desc and did not succeed,
- * retry as needed. (If a fetch is successful, the connection state
- * is changed to DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2 to mark that
- * refetching is unnecessary.) */
- if (conn->purpose == DIR_PURPOSE_FETCH_RENDDESC_V2 &&
- dir_conn->rend_data &&
- strlen(rend_data_get_address(dir_conn->rend_data)) ==
- REND_SERVICE_ID_LEN_BASE32)
- rend_client_refetch_v2_renddesc(dir_conn->rend_data);
+
+ refetch_hsdesc_if_needed(dir_conn);
}
/** Create an http response for the client <b>conn</b> out of
* <b>status</b> and <b>reason_phrase</b>. Write it to <b>conn</b>.
*/
static void
-write_http_status_line(dir_connection_t *conn, int status,
+write_short_http_response(dir_connection_t *conn, int status,
const char *reason_phrase)
{
char *buf = NULL;
- tor_asprintf(&buf, "HTTP/1.0 %d %s\r\n\r\n",
- status, reason_phrase ? reason_phrase : "OK");
+ char *datestring = NULL;
+
+ IF_BUG_ONCE(!reason_phrase) { /* bullet-proofing */
+ reason_phrase = "unspecified";
+ }
+
+ if (server_mode(get_options())) {
+ /* include the Date: header, but only if we're a relay or bridge */
+ char datebuf[RFC1123_TIME_LEN+1];
+ format_rfc1123_time(datebuf, time(NULL));
+ tor_asprintf(&datestring, "Date: %s\r\n", datebuf);
+ }
+
+ tor_asprintf(&buf, "HTTP/1.0 %d %s\r\n%s\r\n",
+ status, reason_phrase, datestring?datestring:"");
+
log_debug(LD_DIRSERV,"Wrote status 'HTTP/1.0 %d %s'", status, reason_phrase);
- connection_write_to_buf(buf, strlen(buf), TO_CONN(conn));
+ connection_buf_add(buf, strlen(buf), TO_CONN(conn));
+
+ tor_free(datestring);
tor_free(buf);
}
@@ -3360,7 +3533,7 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length,
memcpy(cp, "\r\n", 3);
else
tor_assert(0);
- connection_write_to_buf(tmp, strlen(tmp), TO_CONN(conn));
+ connection_buf_add(tmp, strlen(tmp), TO_CONN(conn));
}
/** As write_http_response_header_impl, but sets encoding and content-typed
@@ -3624,7 +3797,6 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers,
char *url, *url_mem, *header;
time_t if_modified_since = 0;
int zlib_compressed_in_url;
- size_t url_len;
unsigned compression_methods_supported;
/* We ignore the body of a GET request. */
@@ -3636,7 +3808,7 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers,
conn->base_.state = DIR_CONN_STATE_SERVER_WRITING;
if (parse_http_url(headers, &url) < 0) {
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
return 0;
}
if ((header = http_get_header(headers, "If-Modified-Since: "))) {
@@ -3655,12 +3827,13 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers,
log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
url_mem = url;
- url_len = strlen(url);
+ {
+ size_t url_len = strlen(url);
- zlib_compressed_in_url = url_len > 2 && !strcmp(url+url_len-2, ".z");
- if (zlib_compressed_in_url) {
- url[url_len-2] = '\0';
- url_len -= 2;
+ zlib_compressed_in_url = url_len > 2 && !strcmp(url+url_len-2, ".z");
+ if (zlib_compressed_in_url) {
+ url[url_len-2] = '\0';
+ }
}
if ((header = http_get_header(headers, "Accept-Encoding: "))) {
@@ -3697,7 +3870,7 @@ directory_handle_command_get,(dir_connection_t *conn, const char *headers,
}
/* we didn't recognize the url */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
result = 0;
done:
@@ -3723,9 +3896,9 @@ handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
* this page no matter what.] */
write_http_response_header_impl(conn, dlen, "text/html", "identity",
NULL, DIRPORTFRONTPAGE_CACHE_LIFETIME);
- connection_write_to_buf(frontpage, dlen, TO_CONN(conn));
+ connection_buf_add(frontpage, dlen, TO_CONN(conn));
} else {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
}
return 0;
}
@@ -4107,13 +4280,13 @@ handle_get_current_consensus(dir_connection_t *conn,
parsed_consensus_request_t req;
if (parse_consensus_request(&req, args) < 0) {
- write_http_status_line(conn, 404, "Couldn't parse request");
+ write_short_http_response(conn, 404, "Couldn't parse request");
goto done;
}
if (digest_list_contains_best_consensus(req.flav,
req.diff_from_digests)) {
- write_http_status_line(conn, 304, "Not modified");
+ write_short_http_response(conn, 304, "Not modified");
geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
goto done;
}
@@ -4128,7 +4301,7 @@ handle_get_current_consensus(dir_connection_t *conn,
}
if (req.diff_only && !cached_consensus) {
- write_http_status_line(conn, 404, "No such diff available");
+ write_short_http_response(conn, 404, "No such diff available");
// XXXX warn_consensus_is_too_old(v, req.flavor, now);
geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
goto done;
@@ -4151,7 +4324,7 @@ handle_get_current_consensus(dir_connection_t *conn,
if (cached_consensus && have_valid_until &&
!networkstatus_valid_until_is_reasonably_live(valid_until, now)) {
- write_http_status_line(conn, 404, "Consensus is too old");
+ write_short_http_response(conn, 404, "Consensus is too old");
warn_consensus_is_too_old(cached_consensus, req.flavor, now);
geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
goto done;
@@ -4159,7 +4332,7 @@ handle_get_current_consensus(dir_connection_t *conn,
if (cached_consensus && req.want_fps &&
!client_likes_consensus(cached_consensus, req.want_fps)) {
- write_http_status_line(conn, 404, "Consensus not signed by sufficient "
+ write_short_http_response(conn, 404, "Consensus not signed by sufficient "
"number of requested authorities");
geoip_note_ns_response(GEOIP_REJECT_NOT_ENOUGH_SIGS);
goto done;
@@ -4185,11 +4358,11 @@ handle_get_current_consensus(dir_connection_t *conn,
&n_expired);
if (!smartlist_len(conn->spool) && !n_expired) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
goto done;
} else if (!smartlist_len(conn->spool)) {
- write_http_status_line(conn, 304, "Not modified");
+ write_short_http_response(conn, 304, "Not modified");
geoip_note_ns_response(GEOIP_REJECT_NOT_MODIFIED);
goto done;
}
@@ -4198,7 +4371,7 @@ handle_get_current_consensus(dir_connection_t *conn,
log_debug(LD_DIRSERV,
"Client asked for network status lists, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
geoip_note_ns_response(GEOIP_REJECT_BUSY);
goto done;
}
@@ -4311,7 +4484,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
smartlist_free(fps);
}
if (!smartlist_len(dir_items) && !smartlist_len(items)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto vote_done;
}
@@ -4348,7 +4521,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
});
if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
goto vote_done;
}
write_http_response_header(conn, body_len ? body_len : -1,
@@ -4360,15 +4533,15 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
conn->compress_state = tor_compress_new(1, compress_method,
choose_compression_level(estimated_len));
SMARTLIST_FOREACH(items, const char *, c,
- connection_write_to_buf_compress(c, strlen(c), conn, 0));
- connection_write_to_buf_compress("", 0, conn, 1);
+ connection_buf_add_compress(c, strlen(c), conn, 0));
+ connection_buf_add_compress("", 0, conn, 1);
} else {
SMARTLIST_FOREACH(items, const char *, c,
- connection_write_to_buf(c, strlen(c), TO_CONN(conn)));
+ connection_buf_add(c, strlen(c), TO_CONN(conn)));
}
} else {
SMARTLIST_FOREACH(dir_items, cached_dir_t *, d,
- connection_write_to_buf(compress_method != NO_METHOD ?
+ connection_buf_add(compress_method != NO_METHOD ?
d->dir_compressed : d->dir,
compress_method != NO_METHOD ?
d->dir_compressed_len : d->dir_len,
@@ -4405,14 +4578,14 @@ handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
compress_method != NO_METHOD,
&size_guess, NULL);
if (smartlist_len(conn->spool) == 0) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto done;
}
if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
goto done;
}
@@ -4504,13 +4677,14 @@ handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
if (res < 0 || size_guess == 0 || smartlist_len(conn->spool) == 0) {
if (msg == NULL)
msg = "Not found";
- write_http_status_line(conn, 404, msg);
+ write_short_http_response(conn, 404, msg);
} else {
if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503,
+ "Directory busy, try again later");
dir_conn_clear_spool(conn);
goto done;
}
@@ -4583,18 +4757,18 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
});
smartlist_free(fp_sks);
} else {
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
goto keys_done;
}
if (!smartlist_len(certs)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto keys_done;
}
SMARTLIST_FOREACH(certs, authority_cert_t *, c,
if (c->cache_info.published_on < if_modified_since)
SMARTLIST_DEL_CURRENT(certs, c));
if (!smartlist_len(certs)) {
- write_http_status_line(conn, 304, "Not modified");
+ write_short_http_response(conn, 304, "Not modified");
goto keys_done;
}
len = 0;
@@ -4604,7 +4778,7 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
if (global_write_bucket_low(TO_CONN(conn),
compress_method != NO_METHOD ? len/2 : len,
2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later");
+ write_short_http_response(conn, 503, "Directory busy, try again later");
goto keys_done;
}
@@ -4616,14 +4790,14 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
conn->compress_state = tor_compress_new(1, compress_method,
choose_compression_level(len));
SMARTLIST_FOREACH(certs, authority_cert_t *, c,
- connection_write_to_buf_compress(
+ connection_buf_add_compress(
c->cache_info.signed_descriptor_body,
c->cache_info.signed_descriptor_len,
conn, 0));
- connection_write_to_buf_compress("", 0, conn, 1);
+ connection_buf_add_compress("", 0, conn, 1);
} else {
SMARTLIST_FOREACH(certs, authority_cert_t *, c,
- connection_write_to_buf(c->cache_info.signed_descriptor_body,
+ connection_buf_add(c->cache_info.signed_descriptor_body,
c->cache_info.signed_descriptor_len,
TO_CONN(conn)));
}
@@ -4652,22 +4826,22 @@ handle_get_hs_descriptor_v2(dir_connection_t *conn,
switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) {
case 1: /* valid */
write_http_response_header(conn, strlen(descp), NO_METHOD, 0);
- connection_write_to_buf(descp, strlen(descp), TO_CONN(conn));
+ connection_buf_add(descp, strlen(descp), TO_CONN(conn));
break;
case 0: /* well-formed but not present */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
break;
case -1: /* not well-formed */
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
break;
}
} else { /* not well-formed */
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
}
goto done;
} else {
/* Not encrypted! */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
}
done:
return 0;
@@ -4686,7 +4860,7 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn,
/* Reject unencrypted dir connections */
if (!connection_dir_is_encrypted(conn)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto done;
}
@@ -4698,13 +4872,13 @@ handle_get_hs_descriptor_v3(dir_connection_t *conn,
retval = hs_cache_lookup_as_dir(HS_VERSION_THREE,
pubkey_str, &desc_str);
if (retval <= 0 || desc_str == NULL) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
goto done;
}
/* Found requested descriptor! Pass it to this nice client. */
write_http_response_header(conn, strlen(desc_str), NO_METHOD, 0);
- connection_write_to_buf(desc_str, strlen(desc_str), TO_CONN(conn));
+ connection_buf_add(desc_str, strlen(desc_str), TO_CONN(conn));
done:
return 0;
@@ -4733,7 +4907,7 @@ handle_get_networkstatus_bridges(dir_connection_t *conn,
if (!header ||
tor_memneq(digest,
options->BridgePassword_AuthDigest_, DIGEST256_LEN)) {
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
tor_free(header);
goto done;
}
@@ -4743,7 +4917,7 @@ handle_get_networkstatus_bridges(dir_connection_t *conn,
status = networkstatus_getinfo_by_purpose("bridge", time(NULL));
size_t dlen = strlen(status);
write_http_response_header(conn, dlen, NO_METHOD, 0);
- connection_write_to_buf(status, dlen, TO_CONN(conn));
+ connection_buf_add(status, dlen, TO_CONN(conn));
tor_free(status);
goto done;
}
@@ -4760,7 +4934,7 @@ handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args)
const char robots[] = "User-agent: *\r\nDisallow: /\r\n";
size_t len = strlen(robots);
write_http_response_header(conn, len, NO_METHOD, ROBOTS_CACHE_LIFETIME);
- connection_write_to_buf(robots, len, TO_CONN(conn));
+ connection_buf_add(robots, len, TO_CONN(conn));
}
return 0;
}
@@ -4868,12 +5042,12 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
if (!public_server_mode(options)) {
log_info(LD_DIR, "Rejected dir post request from %s "
"since we're not a public relay.", conn->base_.address);
- write_http_status_line(conn, 503, "Not acting as a public relay");
+ write_short_http_response(conn, 503, "Not acting as a public relay");
goto done;
}
if (parse_http_url(headers, &url) < 0) {
- write_http_status_line(conn, 400, "Bad request");
+ write_short_http_response(conn, 400, "Bad request");
return 0;
}
log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url));
@@ -4884,10 +5058,10 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
if (rend_cache_store_v2_desc_as_dir(body) < 0) {
log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.",
(int)body_len, conn->base_.address);
- write_http_status_line(conn, 400,
+ write_short_http_response(conn, 400,
"Invalid v2 service descriptor rejected");
} else {
- write_http_status_line(conn, 200, "Service descriptor (v2) stored");
+ write_short_http_response(conn, 200, "Service descriptor (v2) stored");
log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
}
goto done;
@@ -4904,19 +5078,19 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
if (code != 200) {
msg = "Invalid HS descriptor. Rejected.";
}
- write_http_status_line(conn, code, msg);
+ write_short_http_response(conn, code, msg);
goto done;
}
if (!authdir_mode(options)) {
/* we just provide cached directories; we don't want to
* receive anything. */
- write_http_status_line(conn, 400, "Nonauthoritative directory does not "
+ write_short_http_response(conn, 400, "Nonauthoritative directory does not "
"accept posted server descriptors");
goto done;
}
- if (authdir_mode_handles_descs(options, -1) &&
+ if (authdir_mode(options) &&
!strcmp(url,"/tor/")) { /* server descriptor post */
const char *msg = "[None]";
uint8_t purpose = authdir_mode_bridge(options) ?
@@ -4926,7 +5100,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
tor_assert(msg);
if (r == ROUTER_ADDED_SUCCESSFULLY) {
- write_http_status_line(conn, 200, msg);
+ write_short_http_response(conn, 200, msg);
} else if (WRA_WAS_OUTDATED(r)) {
write_http_response_header_impl(conn, -1, NULL, NULL,
"X-Descriptor-Not-New: Yes\r\n", -1);
@@ -4935,7 +5109,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
"Rejected router descriptor or extra-info from %s "
"(\"%s\").",
conn->base_.address, msg);
- write_http_status_line(conn, 400, msg);
+ write_short_http_response(conn, 400, msg);
}
goto done;
}
@@ -4945,12 +5119,12 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
const char *msg = "OK";
int status;
if (dirvote_add_vote(body, &msg, &status)) {
- write_http_status_line(conn, status, "Vote stored");
+ write_short_http_response(conn, status, "Vote stored");
} else {
tor_assert(msg);
log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").",
conn->base_.address, msg);
- write_http_status_line(conn, status, msg);
+ write_short_http_response(conn, status, msg);
}
goto done;
}
@@ -4959,17 +5133,18 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
!strcmp(url,"/tor/post/consensus-signature")) { /* sigs on consensus. */
const char *msg = NULL;
if (dirvote_add_signatures(body, conn->base_.address, &msg)>=0) {
- write_http_status_line(conn, 200, msg?msg:"Signatures stored");
+ write_short_http_response(conn, 200, msg?msg:"Signatures stored");
} else {
log_warn(LD_DIR, "Unable to store signatures posted by %s: %s",
conn->base_.address, msg?msg:"???");
- write_http_status_line(conn, 400, msg?msg:"Unable to store signatures");
+ write_short_http_response(conn, 400,
+ msg?msg:"Unable to store signatures");
}
goto done;
}
/* we didn't recognize the url */
- write_http_status_line(conn, 404, "Not found");
+ write_short_http_response(conn, 404, "Not found");
done:
tor_free(url);
@@ -5122,7 +5297,7 @@ connection_dir_finished_connecting(dir_connection_t *conn)
* Helper function for download_status_increment_failure(),
* download_status_reset(), and download_status_increment_attempt(). */
STATIC const smartlist_t *
-find_dl_schedule(download_status_t *dls, const or_options_t *options)
+find_dl_schedule(const download_status_t *dls, const or_options_t *options)
{
switch (dls->schedule) {
case DL_SCHED_GENERIC:
@@ -5163,7 +5338,15 @@ find_dl_schedule(download_status_t *dls, const or_options_t *options)
}
}
case DL_SCHED_BRIDGE:
- return options->TestingBridgeDownloadSchedule;
+ if (options->UseBridges && num_bridges_usable(0) > 0) {
+ /* A bridge client that is sure that one or more of its bridges are
+ * running can afford to wait longer to update bridge descriptors. */
+ return options->TestingBridgeDownloadSchedule;
+ } else {
+ /* A bridge client which might have no running bridges, must try to
+ * get bridge descriptors straight away. */
+ return options->TestingBridgeBootstrapDownloadSchedule;
+ }
default:
tor_assert(0);
}
@@ -5191,59 +5374,76 @@ find_dl_min_and_max_delay(download_status_t *dls, const or_options_t *options,
const smartlist_t *schedule = find_dl_schedule(dls, options);
tor_assert(schedule != NULL && smartlist_len(schedule) >= 2);
*min = *((int *)(smartlist_get(schedule, 0)));
+ /* Increment on failure schedules always use exponential backoff, but they
+ * have a smaller limit when they're deterministic */
if (dls->backoff == DL_SCHED_DETERMINISTIC)
*max = *((int *)((smartlist_get(schedule, smartlist_len(schedule) - 1))));
else
*max = INT_MAX;
}
-/** Advance one delay step. The algorithm is to use the previous delay to
- * compute an increment, we construct a value uniformly at random between
- * delay and MAX(delay*2,delay+1). We then clamp that value to be no larger
- * than max_delay, and return it.
+/** As next_random_exponential_delay() below, but does not compute a random
+ * value. Instead, compute the range of values that
+ * next_random_exponential_delay() should use when computing its random value.
+ * Store the low bound into *<b>low_bound_out</b>, and the high bound into
+ * *<b>high_bound_out</b>. Guarantees that the low bound is strictly less
+ * than the high bound. */
+STATIC void
+next_random_exponential_delay_range(int *low_bound_out,
+ int *high_bound_out,
+ int delay,
+ int base_delay)
+{
+ // This is the "decorrelated jitter" approach, from
+ // https://www.awsarchitectureblog.com/2015/03/backoff.html
+ // The formula is
+ // sleep = min(cap, random_between(base, sleep * 3))
+
+ const int delay_times_3 = delay < INT_MAX/3 ? delay * 3 : INT_MAX;
+ *low_bound_out = base_delay;
+ if (delay_times_3 > base_delay) {
+ *high_bound_out = delay_times_3;
+ } else {
+ *high_bound_out = base_delay+1;
+ }
+}
+
+/** Advance one delay step. The algorithm will generate a random delay,
+ * such that each failure is possibly (random) longer than the ones before.
+ *
+ * We then clamp that value to be no larger than max_delay, and return it.
+ *
+ * The <b>base_delay</b> parameter is lowest possible delay time (can't be
+ * zero); the <b>backoff_position</b> parameter is the number of times we've
+ * generated a delay; and the <b>delay</b> argument is the most recently used
+ * delay.
*
* Requires that delay is less than INT_MAX, and delay is in [0,max_delay].
*/
STATIC int
-next_random_exponential_delay(int delay, int max_delay)
+next_random_exponential_delay(int delay,
+ int base_delay,
+ int max_delay)
{
/* Check preconditions */
if (BUG(max_delay < 0))
max_delay = 0;
if (BUG(delay > max_delay))
delay = max_delay;
- if (delay == INT_MAX)
- return INT_MAX; /* prevent overflow */
if (BUG(delay < 0))
delay = 0;
- /* How much are we willing to add to the delay? */
- int max_increment;
- int multiplier = 3; /* no more than quadruple the previous delay */
- if (get_options()->TestingTorNetwork) {
- /* Decrease the multiplier in testing networks. This reduces the variance,
- * so that bootstrap is more reliable. */
- multiplier = 2; /* no more than triple the previous delay */
- }
+ if (base_delay < 1)
+ base_delay = 1;
- if (delay && delay < (INT_MAX-1) / multiplier) {
- max_increment = delay * multiplier;
- } else if (delay) {
- max_increment = INT_MAX-1;
- } else {
- max_increment = 1;
- }
+ int low_bound=0, high_bound=max_delay;
- if (BUG(max_increment < 1))
- max_increment = 1;
+ next_random_exponential_delay_range(&low_bound, &high_bound,
+ delay, base_delay);
- /* the + 1 here is so that we always wait longer than last time. */
- int increment = crypto_rand_int(max_increment)+1;
+ int rand_delay = crypto_rand_int_range(low_bound, high_bound);
- if (increment < max_delay - delay)
- return delay + increment;
- else
- return max_delay;
+ return MIN(rand_delay, max_delay);
}
/** Find the current delay for dls based on schedule or min_delay/
@@ -5292,7 +5492,7 @@ download_status_schedule_get_delay(download_status_t *dls,
while (dls->last_backoff_position < dls_schedule_position) {
/* Do one increment step */
- delay = next_random_exponential_delay(delay, max_delay);
+ delay = next_random_exponential_delay(delay, min_delay, max_delay);
/* Update our position */
++(dls->last_backoff_position);
}
@@ -5375,6 +5575,11 @@ download_status_increment_failure(download_status_t *dls, int status_code,
tor_assert(dls);
+ /* dls wasn't reset before it was used */
+ if (dls->next_attempt_at == 0) {
+ download_status_reset(dls);
+ }
+
/* count the failure */
if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1) {
++dls->n_download_failures;
@@ -5399,14 +5604,16 @@ download_status_increment_failure(download_status_t *dls, int status_code,
download_status_log_helper(item, !dls->increment_on, "failed",
"concurrently", dls->n_download_failures,
- increment, dls->next_attempt_at, now);
+ increment,
+ download_status_get_next_attempt_at(dls),
+ now);
if (dls->increment_on == DL_SCHED_INCREMENT_ATTEMPT) {
/* stop this schedule retrying on failure, it will launch concurrent
* connections instead */
return TIME_MAX;
} else {
- return dls->next_attempt_at;
+ return download_status_get_next_attempt_at(dls);
}
}
@@ -5427,6 +5634,11 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
tor_assert(dls);
+ /* dls wasn't reset before it was used */
+ if (dls->next_attempt_at == 0) {
+ download_status_reset(dls);
+ }
+
if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
/* this schedule should retry on failure, and not launch any concurrent
attempts */
@@ -5445,9 +5657,19 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
download_status_log_helper(item, dls->increment_on, "attempted",
"on failure", dls->n_download_attempts,
- delay, dls->next_attempt_at, now);
+ delay, download_status_get_next_attempt_at(dls),
+ now);
- return dls->next_attempt_at;
+ return download_status_get_next_attempt_at(dls);
+}
+
+static time_t
+download_status_get_initial_delay_from_now(const download_status_t *dls)
+{
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
+ /* We use constant initial delays, even in exponential backoff
+ * schedules. */
+ return time(NULL) + *(int *)smartlist_get(schedule, 0);
}
/** Reset <b>dls</b> so that it will be considered downloadable
@@ -5459,8 +5681,8 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
* (We find the zeroth element of the download schedule, and set
* next_attempt_at to be the appropriate offset from 'now'. In most
* cases this means setting it to 'now', so the item will be immediately
- * downloadable; in the case of bridge descriptors, the zeroth element
- * is an hour from now.) */
+ * downloadable; when using authorities with fallbacks, there is a few seconds'
+ * delay.) */
void
download_status_reset(download_status_t *dls)
{
@@ -5468,11 +5690,9 @@ download_status_reset(download_status_t *dls)
|| dls->n_download_attempts == IMPOSSIBLE_TO_DOWNLOAD)
return; /* Don't reset this. */
- const smartlist_t *schedule = find_dl_schedule(dls, get_options());
-
dls->n_download_failures = 0;
dls->n_download_attempts = 0;
- dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0);
+ dls->next_attempt_at = download_status_get_initial_delay_from_now(dls);
dls->last_backoff_position = 0;
dls->last_delay_used = 0;
/* Don't reset dls->want_authority or dls->increment_on */
@@ -5499,6 +5719,12 @@ download_status_get_n_attempts(const download_status_t *dls)
time_t
download_status_get_next_attempt_at(const download_status_t *dls)
{
+ /* dls wasn't reset before it was used */
+ if (dls->next_attempt_at == 0) {
+ /* so give the answer we would have given if it had been */
+ return download_status_get_initial_delay_from_now(dls);
+ }
+
return dls->next_attempt_at;
}
diff --git a/src/or/directory.h b/src/or/directory.h
index 571c30a0fc..5e6a91d3e7 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -12,6 +12,8 @@
#ifndef TOR_DIRECTORY_H
#define TOR_DIRECTORY_H
+#include "hs_ident.h"
+
int directories_have_accepted_server_descriptor(void);
void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose,
dirinfo_type_t type, const char *payload,
@@ -71,6 +73,10 @@ void directory_request_set_if_modified_since(directory_request_t *req,
time_t if_modified_since);
void directory_request_set_rend_query(directory_request_t *req,
const rend_data_t *query);
+void directory_request_upload_set_hs_ident(directory_request_t *req,
+ const hs_ident_dir_conn_t *ident);
+void directory_request_fetch_set_hs_ident(directory_request_t *req,
+ const hs_ident_dir_conn_t *ident);
void directory_request_set_routerstatus(directory_request_t *req,
const routerstatus_t *rs);
@@ -81,6 +87,9 @@ MOCK_DECL(void, directory_initiate_request, (directory_request_t *request));
int parse_http_response(const char *headers, int *code, time_t *date,
compress_method_t *compression, char **response);
+int parse_http_command(const char *headers,
+ char **command_out, char **url_out);
+char *http_get_header(const char *headers, const char *which);
int connection_dir_is_encrypted(const dir_connection_t *conn);
int connection_dir_reached_eof(dir_connection_t *conn);
@@ -123,12 +132,19 @@ time_t download_status_increment_attempt(download_status_t *dls,
void download_status_reset(download_status_t *dls);
static int download_status_is_ready(download_status_t *dls, time_t now,
int max_failures);
+time_t download_status_get_next_attempt_at(const download_status_t *dls);
+
/** Return true iff, as of <b>now</b>, the resource tracked by <b>dls</b> is
* ready to get its download reattempted. */
static inline int
download_status_is_ready(download_status_t *dls, time_t now,
int max_failures)
{
+ /* dls wasn't reset before it was used */
+ if (dls->next_attempt_at == 0) {
+ download_status_reset(dls);
+ }
+
if (dls->backoff == DL_SCHED_DETERMINISTIC) {
/* Deterministic schedules can hit an endpoint; exponential backoff
* schedules just wait longer and longer. */
@@ -137,7 +153,7 @@ download_status_is_ready(download_status_t *dls, time_t now,
if (!under_failure_limit)
return 0;
}
- return dls->next_attempt_at <= now;
+ return download_status_get_next_attempt_at(dls) <= now;
}
static void download_status_mark_impossible(download_status_t *dl);
@@ -151,13 +167,64 @@ download_status_mark_impossible(download_status_t *dl)
int download_status_get_n_failures(const download_status_t *dls);
int download_status_get_n_attempts(const download_status_t *dls);
-time_t download_status_get_next_attempt_at(const download_status_t *dls);
int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose,
const char *resource);
#ifdef DIRECTORY_PRIVATE
+/** A structure to hold arguments passed into each directory response
+ * handler */
+typedef struct response_handler_args_t {
+ int status_code;
+ const char *reason;
+ const char *body;
+ size_t body_len;
+ const char *headers;
+} response_handler_args_t;
+
+struct directory_request_t {
+ /**
+ * These fields specify which directory we're contacting. Routerstatus,
+ * if present, overrides the other fields.
+ *
+ * @{ */
+ tor_addr_port_t or_addr_port;
+ tor_addr_port_t dir_addr_port;
+ char digest[DIGEST_LEN];
+
+ const routerstatus_t *routerstatus;
+ /** @} */
+ /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what
+ * kind of operation we'll be doing (upload/download), and of what kind
+ * of document. */
+ uint8_t dir_purpose;
+ /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo
+ * and extrainfo docs. */
+ uint8_t router_purpose;
+ /** Enum: determines whether to anonymize, and whether to use dirport or
+ * orport. */
+ dir_indirection_t indirection;
+ /** Alias to the variable part of the URL for this request */
+ const char *resource;
+ /** Alias to the payload to upload (if any) */
+ const char *payload;
+ /** Number of bytes to upload from payload</b> */
+ size_t payload_len;
+ /** Value to send in an if-modified-since header, or 0 for none. */
+ time_t if_modified_since;
+ /** Hidden-service-specific information v2. */
+ const rend_data_t *rend_query;
+ /** Extra headers to append to the request */
+ config_line_t *additional_headers;
+ /** Hidden-service-specific information for v3+. */
+ const hs_ident_dir_conn_t *hs_ident;
+ /** Used internally to directory.c: gets informed when the attempt to
+ * connect to the directory succeeds or fails, if that attempt bears on the
+ * directory's usability as a directory guard. */
+ struct circuit_guard_state_t *guard_state;
+};
+
struct get_handler_args_t;
STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn,
const struct get_handler_args_t *args);
@@ -166,15 +233,15 @@ STATIC char *accept_encoding_header(void);
STATIC int allowed_anonymous_connection_compression_method(compress_method_t);
STATIC void warn_disallowed_anonymous_compression_method(compress_method_t);
-struct response_handler_args_t;
-
+STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
+ const response_handler_args_t *args);
STATIC int handle_response_fetch_microdesc(dir_connection_t *conn,
- const struct response_handler_args_t *args);
+ const response_handler_args_t *args);
#endif /* defined(DIRECTORY_PRIVATE) */
#ifdef TOR_UNIT_TESTS
-/* Used only by test_dir.c */
+/* Used only by test_dir.c and test_hs_cache.c */
STATIC int parse_http_url(const char *headers, char **url);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
@@ -198,18 +265,36 @@ STATIC char* authdir_type_to_string(dirinfo_type_t auth);
STATIC const char * dir_conn_purpose_to_string(int purpose);
STATIC int should_use_directory_guards(const or_options_t *options);
STATIC compression_level_t choose_compression_level(ssize_t n_bytes);
-STATIC const smartlist_t *find_dl_schedule(download_status_t *dls,
+STATIC const smartlist_t *find_dl_schedule(const download_status_t *dls,
const or_options_t *options);
STATIC void find_dl_min_and_max_delay(download_status_t *dls,
const or_options_t *options,
int *min, int *max);
-STATIC int next_random_exponential_delay(int delay, int max_delay);
+
+STATIC int next_random_exponential_delay(int delay,
+ int base_delay,
+ int max_delay);
+
+STATIC void next_random_exponential_delay_range(int *low_bound_out,
+ int *high_bound_out,
+ int delay,
+ int base_delay);
STATIC int parse_hs_version_from_post(const char *url, const char *prefix,
const char **end_pos);
STATIC unsigned parse_accept_encoding_header(const char *h);
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#if defined(TOR_UNIT_TESTS) || defined(DIRECTORY_PRIVATE)
+/* Used only by directory.c and test_dir.c */
+
+/* no more than quadruple the previous delay (multiplier + 1) */
+#define DIR_DEFAULT_RANDOM_MULTIPLIER (3)
+/* no more than triple the previous delay */
+#define DIR_TEST_NET_RANDOM_MULTIPLIER (2)
+
+#endif /* defined(TOR_UNIT_TESTS) || defined(DIRECTORY_PRIVATE) */
-#endif
+#endif /* !defined(TOR_DIRECTORY_H) */
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 8cbf7d7bc8..95bef9889d 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -338,7 +338,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
}
return FP_REJECT;
}
-#endif
+#endif /* defined(DISABLE_DISABLING_ED25519) */
}
}
@@ -679,9 +679,6 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
ri->nickname, source, (int)ri->cache_info.signed_descriptor_len,
MAX_DESCRIPTOR_UPLOAD_SIZE);
*msg = "Router descriptor was too large.";
- control_event_or_authdir_new_descriptor("REJECTED",
- ri->cache_info.signed_descriptor_body,
- desclen, *msg);
r = ROUTER_AUTHDIR_REJECTS;
goto fail;
}
@@ -700,9 +697,6 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
router_describe(ri), source);
*msg = "Not replacing router descriptor; no information has changed since "
"the last one with this identity.";
- control_event_or_authdir_new_descriptor("DROPPED",
- ri->cache_info.signed_descriptor_body,
- desclen, *msg);
r = ROUTER_IS_ALREADY_KNOWN;
goto fail;
}
@@ -710,10 +704,19 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
/* Do keypinning again ... this time, to add the pin if appropriate */
int keypin_status;
if (ri->cache_info.signing_key_cert) {
+ ed25519_public_key_t *pkey = &ri->cache_info.signing_key_cert->signing_key;
+ /* First let's validate this pubkey before pinning it */
+ if (ed25519_validate_pubkey(pkey) < 0) {
+ log_warn(LD_DIRSERV, "Received bad key from %s (source %s)",
+ router_describe(ri), source);
+ routerinfo_free(ri);
+ return ROUTER_AUTHDIR_REJECTS;
+ }
+
+ /* Now pin it! */
keypin_status = keypin_check_and_add(
(const uint8_t*)ri->cache_info.identity_digest,
- ri->cache_info.signing_key_cert->signing_key.pubkey,
- ! key_pinning);
+ pkey->pubkey, ! key_pinning);
} else {
keypin_status = keypin_check_lone_rsa(
(const uint8_t*)ri->cache_info.identity_digest);
@@ -748,14 +751,11 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
r = router_add_to_routerlist(ri, msg, 0, 0);
if (!WRA_WAS_ADDED(r)) {
/* unless the routerinfo was fine, just out-of-date */
- if (WRA_WAS_REJECTED(r))
- control_event_or_authdir_new_descriptor("REJECTED", desc, desclen, *msg);
log_info(LD_DIRSERV,
"Did not add descriptor from '%s' (source: %s): %s.",
nickname, source, *msg ? *msg : "(no message)");
} else {
smartlist_t *changed;
- control_event_or_authdir_new_descriptor("ACCEPTED", desc, desclen, *msg);
changed = smartlist_new();
smartlist_add(changed, ri);
@@ -2699,7 +2699,7 @@ measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
}
cp+=strlen("bw=");
- out->bw_kb = tor_parse_long(cp, 0, 0, LONG_MAX, &parse_ok, &endptr);
+ out->bw_kb = tor_parse_long(cp, 10, 0, LONG_MAX, &parse_ok, &endptr);
if (!parse_ok || (*endptr && !TOR_ISSPACE(*endptr))) {
log_warn(LD_DIRSERV, "Invalid bandwidth in bandwidth file line: %s",
escaped(orig_line));
@@ -3620,9 +3620,9 @@ spooled_resource_flush_some(spooled_resource_t *spooled,
return SRFS_DONE;
}
if (conn->compress_state) {
- connection_write_to_buf_compress((const char*)body, bodylen, conn, 0);
+ connection_buf_add_compress((const char*)body, bodylen, conn, 0);
} else {
- connection_write_to_buf((const char*)body, bodylen, TO_CONN(conn));
+ connection_buf_add((const char*)body, bodylen, TO_CONN(conn));
}
return SRFS_DONE;
} else {
@@ -3659,11 +3659,11 @@ spooled_resource_flush_some(spooled_resource_t *spooled,
return SRFS_ERR;
ssize_t bytes = (ssize_t) MIN(DIRSERV_CACHED_DIR_CHUNK_SIZE, remaining);
if (conn->compress_state) {
- connection_write_to_buf_compress(
+ connection_buf_add_compress(
ptr + spooled->cached_dir_offset,
bytes, conn, 0);
} else {
- connection_write_to_buf(ptr + spooled->cached_dir_offset,
+ connection_buf_add(ptr + spooled->cached_dir_offset,
bytes, TO_CONN(conn));
}
spooled->cached_dir_offset += bytes;
@@ -3928,7 +3928,7 @@ connection_dirserv_flushed_some(dir_connection_t *conn)
if (conn->compress_state) {
/* Flush the compression state: there could be more bytes pending in there,
* and we don't want to omit bytes. */
- connection_write_to_buf_compress("", 0, conn, 1);
+ connection_buf_add_compress("", 0, conn, 1);
tor_compress_free(conn->compress_state);
conn->compress_state = NULL;
}
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index 480174d5bb..46967a6cb2 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -182,7 +182,7 @@ STATIC int dirserv_has_measured_bw(const char *node_id);
STATIC int
dirserv_read_guardfraction_file_from_str(const char *guardfraction_file_str,
smartlist_t *vote_routerstatuses);
-#endif
+#endif /* defined(DIRSERV_PRIVATE) */
int dirserv_read_measured_bandwidths(const char *from_file,
smartlist_t *routerstatuses);
@@ -204,5 +204,5 @@ void dirserv_spool_remove_missing_and_guess_size(dir_connection_t *conn,
void dirserv_spool_sort(dir_connection_t *conn);
void dir_conn_clear_spool(dir_connection_t *conn);
-#endif
+#endif /* !defined(TOR_DIRSERV_H) */
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index f5e29eb786..ce82a5ef4a 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -306,7 +306,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
signing_key_fingerprint);
}
- note_crypto_pk_op(SIGN_DIR);
{
char *sig = router_get_dirobj_signature(digest, DIGEST_LEN,
private_signing_key);
@@ -542,8 +541,8 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
if (cur_n > most_n ||
(cur && cur_n == most_n && cur->status.published_on > most_published)) {
most = cur;
- most_n = cur_n;
- most_published = cur->status.published_on;
+ // most_n = cur_n; // unused after this point.
+ // most_published = cur->status.published_on; // unused after this point.
}
tor_assert(most);
@@ -737,12 +736,12 @@ dirvote_get_intermediate_param_value(const smartlist_t *param_list,
}
} SMARTLIST_FOREACH_END(k_v_pair);
- if (n_found == 1)
+ if (n_found == 1) {
return value;
- else if (BUG(n_found > 1))
- return default_val;
- else
+ } else {
+ tor_assert_nonfatal(n_found == 0);
return default_val;
+ }
}
/** Minimum number of directory authorities voting for a parameter to
@@ -2788,48 +2787,10 @@ dirvote_get_start_of_next_interval(time_t now, int interval, int offset)
return next;
}
-/* Using the time <b>now</b>, return the next voting valid-after time. */
-time_t
-get_next_valid_after_time(time_t now)
-{
- time_t next_valid_after_time;
- const or_options_t *options = get_options();
- voting_schedule_t *new_voting_schedule =
- get_voting_schedule(options, now, LOG_INFO);
- tor_assert(new_voting_schedule);
-
- next_valid_after_time = new_voting_schedule->interval_starts;
- voting_schedule_free(new_voting_schedule);
-
- return next_valid_after_time;
-}
-
-static voting_schedule_t voting_schedule;
-
-/** Set voting_schedule to hold the timing for the next vote we should be
- * doing. */
-void
-dirvote_recalculate_timing(const or_options_t *options, time_t now)
-{
- voting_schedule_t *new_voting_schedule;
-
- if (!authdir_mode_v3(options)) {
- return;
- }
-
- /* get the new voting schedule */
- new_voting_schedule = get_voting_schedule(options, now, LOG_NOTICE);
- tor_assert(new_voting_schedule);
-
- /* Fill in the global static struct now */
- memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule));
- voting_schedule_free(new_voting_schedule);
-}
-
/* Populate and return a new voting_schedule_t that can be used to schedule
* voting. The object is allocated on the heap and it's the responsibility of
* the caller to free it. Can't fail. */
-voting_schedule_t *
+static voting_schedule_t *
get_voting_schedule(const or_options_t *options, time_t now, int severity)
{
int interval, vote_delay, dist_delay;
@@ -2884,7 +2845,7 @@ get_voting_schedule(const or_options_t *options, time_t now, int severity)
/** Frees a voting_schedule_t. This should be used instead of the generic
* tor_free. */
-void
+static void
voting_schedule_free(voting_schedule_t *voting_schedule_to_free)
{
if (!voting_schedule_to_free)
@@ -2892,13 +2853,53 @@ voting_schedule_free(voting_schedule_t *voting_schedule_to_free)
tor_free(voting_schedule_to_free);
}
+static voting_schedule_t voting_schedule;
+
+/* Using the time <b>now</b>, return the next voting valid-after time. */
+time_t
+dirvote_get_next_valid_after_time(void)
+{
+ /* This is a safe guard in order to make sure that the voting schedule
+ * static object is at least initialized. Using this function with a zeroed
+ * voting schedule can lead to bugs. */
+ if (tor_mem_is_zero((const char *) &voting_schedule,
+ sizeof(voting_schedule))) {
+ dirvote_recalculate_timing(get_options(), time(NULL));
+ voting_schedule.created_on_demand = 1;
+ }
+ return voting_schedule.interval_starts;
+}
+
+/** Set voting_schedule to hold the timing for the next vote we should be
+ * doing. All type of tor do that because HS subsystem needs the timing as
+ * well to function properly. */
+void
+dirvote_recalculate_timing(const or_options_t *options, time_t now)
+{
+ voting_schedule_t *new_voting_schedule;
+
+ /* get the new voting schedule */
+ new_voting_schedule = get_voting_schedule(options, now, LOG_INFO);
+ tor_assert(new_voting_schedule);
+
+ /* Fill in the global static struct now */
+ memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule));
+ voting_schedule_free(new_voting_schedule);
+}
+
/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */
void
dirvote_act(const or_options_t *options, time_t now)
{
if (!authdir_mode_v3(options))
return;
- if (!voting_schedule.voting_starts) {
+ tor_assert_nonfatal(voting_schedule.voting_starts);
+ /* If we haven't initialized this object through this codeflow, we need to
+ * recalculate the timings to match our vote. The reason to do that is if we
+ * have a voting schedule initialized 1 minute ago, the voting timings might
+ * not be aligned to what we should expect with "now". This is especially
+ * true for TestingTorNetwork using smaller timings. */
+ if (voting_schedule.created_on_demand) {
char *keys = list_v3_auth_ids();
authority_cert_t *c = get_my_v3_authority_cert();
log_notice(LD_DIR, "Scheduling voting. Known authority IDs are %s. "
@@ -3994,14 +3995,15 @@ dirvote_format_all_microdesc_vote_lines(const routerinfo_t *ri, time_t now,
while ((ep = entries)) {
char buf[128];
vote_microdesc_hash_t *h;
- dirvote_format_microdesc_vote_line(buf, sizeof(buf), ep->md,
- ep->low, ep->high);
- h = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
- h->microdesc_hash_line = tor_strdup(buf);
- h->next = result;
- result = h;
- ep->md->last_listed = now;
- smartlist_add(microdescriptors_out, ep->md);
+ if (dirvote_format_microdesc_vote_line(buf, sizeof(buf), ep->md,
+ ep->low, ep->high) >= 0) {
+ h = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
+ h->microdesc_hash_line = tor_strdup(buf);
+ h->next = result;
+ result = h;
+ ep->md->last_listed = now;
+ smartlist_add(microdescriptors_out, ep->md);
+ }
entries = ep->next;
tor_free(ep);
}
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index e342dc78ea..72a35fea6d 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -168,12 +168,14 @@ typedef struct {
int have_fetched_missing_signatures;
/* True iff we have published our consensus. */
int have_published_consensus;
-} voting_schedule_t;
-
-voting_schedule_t *get_voting_schedule(const or_options_t *options,
- time_t now, int severity);
-void voting_schedule_free(voting_schedule_t *voting_schedule_to_free);
+ /* True iff this voting schedule was set on demand meaning not through the
+ * normal vote operation of a dirauth or when a consensus is set. This only
+ * applies to a directory authority that needs to recalculate the voting
+ * timings only for the first vote even though this object was initilized
+ * prior to voting. */
+ int created_on_demand;
+} voting_schedule_t;
void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
time_t dirvote_get_start_of_next_interval(time_t now,
@@ -181,7 +183,7 @@ time_t dirvote_get_start_of_next_interval(time_t now,
int offset);
void dirvote_recalculate_timing(const or_options_t *options, time_t now);
void dirvote_act(const or_options_t *options, time_t now);
-time_t get_next_valid_after_time(time_t now);
+time_t dirvote_get_next_valid_after_time(void);
/* invoked on timers and by outside triggers. */
struct pending_vote_t * dirvote_add_vote(const char *vote_body,
@@ -242,7 +244,7 @@ STATIC int
networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G,
int64_t M, int64_t E, int64_t D,
int64_t T, int64_t weight_scale);
-#endif
+#endif /* defined(DIRVOTE_PRIVATE) */
-#endif
+#endif /* !defined(TOR_DIRVOTE_H) */
diff --git a/src/or/dns.c b/src/or/dns.c
index b5344469b5..7dc3575f53 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -102,7 +102,7 @@ static void assert_cache_ok_(void);
#define assert_cache_ok() assert_cache_ok_()
#else
#define assert_cache_ok() STMT_NIL
-#endif
+#endif /* defined(DEBUG_DNS_CACHE) */
static void assert_resolve_ok(cached_resolve_t *resolve);
/** Hash table of cached_resolve objects. */
@@ -182,6 +182,18 @@ evdns_log_cb(int warn, const char *msg)
} else if (!strcmp(msg, "All nameservers have failed")) {
control_event_server_status(LOG_WARN, "NAMESERVER_ALL_DOWN");
all_down = 1;
+ } else if (!strcmpstart(msg, "Address mismatch on received DNS")) {
+ static ratelim_t mismatch_limit = RATELIM_INIT(3600);
+ const char *src = strstr(msg, " Apparent source");
+ if (!src || get_options()->SafeLogging) {
+ src = "";
+ }
+ log_fn_ratelim(&mismatch_limit, severity, LD_EXIT,
+ "eventdns: Received a DNS packet from "
+ "an IP address to which we did not send a request. This "
+ "could be a DNS spoofing attempt, or some kind of "
+ "misconfiguration.%s", src);
+ return;
}
tor_log(severity, LD_EXIT, "eventdns: %s", msg);
}
@@ -366,7 +378,7 @@ set_expiry(cached_resolve_t *resolve, time_t expires)
resolve->expire = expires;
smartlist_pqueue_add(cached_resolve_pqueue,
compare_cached_resolves_by_expiry_,
- STRUCT_OFFSET(cached_resolve_t, minheap_idx),
+ offsetof(cached_resolve_t, minheap_idx),
resolve);
}
@@ -413,7 +425,7 @@ purge_expired_resolves(time_t now)
break;
smartlist_pqueue_pop(cached_resolve_pqueue,
compare_cached_resolves_by_expiry_,
- STRUCT_OFFSET(cached_resolve_t, minheap_idx));
+ offsetof(cached_resolve_t, minheap_idx));
if (resolve->state == CACHE_STATE_PENDING) {
log_debug(LD_EXIT,
@@ -949,14 +961,14 @@ assert_connection_edge_not_dns_pending(edge_connection_t *conn)
for (pend = resolve->pending_connections; pend; pend = pend->next) {
tor_assert(pend->conn != conn);
}
-#else
+#else /* !(1) */
cached_resolve_t **resolve;
HT_FOREACH(resolve, cache_map, &cache_root) {
for (pend = (*resolve)->pending_connections; pend; pend = pend->next) {
tor_assert(pend->conn != conn);
}
}
-#endif
+#endif /* 1 */
}
/** Log an error and abort if any connection waiting for a DNS resolve is
@@ -1384,7 +1396,7 @@ configure_nameservers(int force)
evdns_base_load_hosts(the_evdns_base,
sandbox_intern_string("/etc/hosts"));
}
-#endif
+#endif /* defined(DNS_OPTION_HOSTSFILE) && defined(USE_LIBSECCOMP) */
log_info(LD_EXIT, "Parsing resolver configuration in '%s'", conf_fname);
if ((r = evdns_base_resolv_conf_parse(the_evdns_base, flags,
sandbox_intern_string(conf_fname)))) {
@@ -1422,7 +1434,7 @@ configure_nameservers(int force)
tor_free(resolv_conf_fname);
resolv_conf_mtime = 0;
}
-#endif
+#endif /* defined(_WIN32) */
#define SET(k,v) evdns_base_set_option(the_evdns_base, (k), (v))
@@ -1566,10 +1578,11 @@ evdns_callback(int result, char type, int count, int ttl, void *addresses,
escaped_safe_str(hostname));
tor_free(escaped_address);
} else if (count) {
- log_warn(LD_EXIT, "eventdns returned only non-IPv4 answers for %s.",
+ log_info(LD_EXIT, "eventdns returned only unrecognized answer types "
+ " for %s.",
escaped_safe_str(string_address));
} else {
- log_warn(LD_BUG, "eventdns returned no addresses or error for %s!",
+ log_info(LD_EXIT, "eventdns returned no addresses or error for %s.",
escaped_safe_str(string_address));
}
}
@@ -1945,7 +1958,7 @@ dns_launch_wildcard_checks(void)
launch_wildcard_check(8, 16, ipv6, ".com");
launch_wildcard_check(8, 16, ipv6, ".org");
launch_wildcard_check(8, 16, ipv6, ".net");
- }
+ }
}
}
@@ -2038,7 +2051,7 @@ assert_resolve_ok(cached_resolve_t *resolve)
tor_assert(!resolve->hostname);
else
tor_assert(!resolve->result_ipv4.addr_ipv4);
-#endif
+#endif /* 0 */
/*XXXXX ADD MORE */
}
}
@@ -2088,7 +2101,7 @@ assert_cache_ok_(void)
smartlist_pqueue_assert_ok(cached_resolve_pqueue,
compare_cached_resolves_by_expiry_,
- STRUCT_OFFSET(cached_resolve_t, minheap_idx));
+ offsetof(cached_resolve_t, minheap_idx));
SMARTLIST_FOREACH(cached_resolve_pqueue, cached_resolve_t *, res,
{
@@ -2102,7 +2115,7 @@ assert_cache_ok_(void)
});
}
-#endif
+#endif /* defined(DEBUG_DNS_CACHE) */
cached_resolve_t *
dns_get_cache_entry(cached_resolve_t *query)
diff --git a/src/or/dns.h b/src/or/dns.h
index a81cbd20da..28d9f947b4 100644
--- a/src/or/dns.h
+++ b/src/or/dns.h
@@ -64,7 +64,7 @@ set_exitconn_info_from_resolve,(edge_connection_t *exitconn,
MOCK_DECL(STATIC int,
launch_resolve,(cached_resolve_t *resolve));
-#endif
+#endif /* defined(DNS_PRIVATE) */
-#endif
+#endif /* !defined(TOR_DNS_H) */
diff --git a/src/or/dns_structs.h b/src/or/dns_structs.h
index dc00e9f7b9..e22f23ac15 100644
--- a/src/or/dns_structs.h
+++ b/src/or/dns_structs.h
@@ -98,5 +98,5 @@ typedef struct cached_resolve_t {
int minheap_idx;
} cached_resolve_t;
-#endif
+#endif /* !defined(TOR_DNS_STRUCTS_H) */
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index 54a22a5150..d254717a54 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -226,10 +226,10 @@ dnsserv_launch_request(const char *name, int reverse,
TO_CONN(conn)->port = control_conn->base_.port;
TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
}
-#else
+#else /* !(defined(AF_UNIX)) */
TO_CONN(conn)->port = control_conn->base_.port;
TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
-#endif
+#endif /* defined(AF_UNIX) */
if (reverse)
entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
diff --git a/src/or/dnsserv.h b/src/or/dnsserv.h
index 6c0643b8dc..2af366eee5 100644
--- a/src/or/dnsserv.h
+++ b/src/or/dnsserv.h
@@ -23,5 +23,5 @@ void dnsserv_reject_request(entry_connection_t *conn);
int dnsserv_launch_request(const char *name, int is_reverse,
control_connection_t *control_conn);
-#endif
+#endif /* !defined(TOR_DNSSERV_H) */
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 2cbc8019d4..67b0259243 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -814,7 +814,7 @@ STATIC entry_guard_t *
entry_guard_add_to_sample(guard_selection_t *gs,
const node_t *node)
{
- log_info(LD_GUARD, "Adding %s as to the entry guard sample set.",
+ log_info(LD_GUARD, "Adding %s to the entry guard sample set.",
node_describe(node));
/* make sure that the guard is not already sampled. */
@@ -1841,7 +1841,7 @@ entry_guards_update_primary(guard_selection_t *gs)
bool_eq(guard->is_primary,
smartlist_contains(new_primary_guards, guard)));
});
-#endif
+#endif /* 1 */
int any_change = 0;
if (smartlist_len(gs->primary_entry_guards) !=
@@ -3136,20 +3136,34 @@ entry_list_is_constrained(const or_options_t *options)
}
/** Return the number of bridges that have descriptors that are marked with
- * purpose 'bridge' and are running.
- */
-int
-num_bridges_usable(void)
+ * purpose 'bridge' and are running. If use_maybe_reachable is
+ * true, include bridges that might be reachable in the count.
+ * Otherwise, if it is false, only include bridges that have recently been
+ * found running in the count.
+ *
+ * We use this function to decide if we're ready to start building
+ * circuits through our bridges, or if we need to wait until the
+ * directory "server/authority" requests finish. */
+MOCK_IMPL(int,
+num_bridges_usable,(int use_maybe_reachable))
{
int n_options = 0;
- tor_assert(get_options()->UseBridges);
+ if (BUG(!get_options()->UseBridges)) {
+ return 0;
+ }
guard_selection_t *gs = get_guard_selection_info();
- tor_assert(gs->type == GS_TYPE_BRIDGE);
+ if (BUG(gs->type != GS_TYPE_BRIDGE)) {
+ return 0;
+ }
SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) {
+ /* Definitely not usable */
if (guard->is_reachable == GUARD_REACHABLE_NO)
continue;
+ /* If we want to be really sure the bridges will work, skip maybes */
+ if (!use_maybe_reachable && guard->is_reachable == GUARD_REACHABLE_MAYBE)
+ continue;
if (tor_digest_is_zero(guard->identity))
continue;
const node_t *node = node_get_by_id(guard->identity);
@@ -3538,15 +3552,20 @@ guards_retry_optimistic(const or_options_t *options)
}
/**
- * Return true iff we know enough directory information to construct
- * circuits through all of the primary guards we'd currently use.
- */
-int
-guard_selection_have_enough_dir_info_to_build_circuits(guard_selection_t *gs)
+ * Check if we are missing any crucial dirinfo for the guard subsystem to
+ * work. Return NULL if everything went well, otherwise return a newly
+ * allocated string with an informative error message. In the latter case, use
+ * the genreal descriptor information <b>using_mds</b>, <b>num_present</b> and
+ * <b>num_usable</b> to improve the error message. */
+char *
+guard_selection_get_err_str_if_dir_info_missing(guard_selection_t *gs,
+ int using_mds,
+ int num_present, int num_usable)
{
if (!gs->primary_guards_up_to_date)
entry_guards_update_primary(gs);
+ char *ret_str = NULL;
int n_missing_descriptors = 0;
int n_considered = 0;
int num_primary_to_check;
@@ -3568,16 +3587,30 @@ guard_selection_have_enough_dir_info_to_build_circuits(guard_selection_t *gs)
break;
} SMARTLIST_FOREACH_END(guard);
- return n_missing_descriptors == 0;
+ /* If we are not missing any descriptors, return NULL. */
+ if (!n_missing_descriptors) {
+ return NULL;
+ }
+
+ /* otherwise return a helpful error string */
+ tor_asprintf(&ret_str, "We're missing descriptors for %d/%d of our "
+ "primary entry guards (total %sdescriptors: %d/%d).",
+ n_missing_descriptors, num_primary_to_check,
+ using_mds?"micro":"", num_present, num_usable);
+
+ return ret_str;
}
/** As guard_selection_have_enough_dir_info_to_build_circuits, but uses
* the default guard selection. */
-int
-entry_guards_have_enough_dir_info_to_build_circuits(void)
-{
- return guard_selection_have_enough_dir_info_to_build_circuits(
- get_guard_selection_info());
+char *
+entry_guards_get_err_str_if_dir_info_missing(int using_mds,
+ int num_present, int num_usable)
+{
+ return guard_selection_get_err_str_if_dir_info_missing(
+ get_guard_selection_info(),
+ using_mds,
+ num_present, num_usable);
}
/** Free one guard selection context */
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index 39bff14381..d909115b1f 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -316,7 +316,7 @@ struct circuit_guard_state_t {
*/
entry_guard_restriction_t *restrictions;
};
-#endif
+#endif /* defined(ENTRYNODES_PRIVATE) */
/* Common entry points for old and new guard code */
int guards_update_all(void);
@@ -342,7 +342,7 @@ int num_live_entry_guards_for_guard_selection(
guard_selection_t *gs,
int for_directory);
int num_live_entry_guards(int for_directory);
-#endif
+#endif /* 1 */
const node_t *entry_guard_find_node(const entry_guard_t *guard);
const char *entry_guard_get_rsa_id_digest(const entry_guard_t *guard);
@@ -383,8 +383,7 @@ void entry_guards_note_internet_connectivity(guard_selection_t *gs);
int update_guard_selection_choice(const or_options_t *options);
-/* Used by bridges.c only. */
-int num_bridges_usable(void);
+MOCK_DECL(int,num_bridges_usable,(int use_maybe_reachable));
#ifdef ENTRYNODES_PRIVATE
/**
@@ -558,14 +557,12 @@ STATIC unsigned entry_guards_note_guard_success(guard_selection_t *gs,
STATIC int entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b);
STATIC char *getinfo_helper_format_single_entry_guard(const entry_guard_t *e);
-STATIC entry_guard_restriction_t *
-guard_create_exit_restriction(const uint8_t *exit_id);
+STATIC entry_guard_restriction_t *guard_create_exit_restriction(
+ const uint8_t *exit_id);
-STATIC entry_guard_restriction_t *
-guard_create_dirserver_md_restriction(void);
+STATIC entry_guard_restriction_t *guard_create_dirserver_md_restriction(void);
-STATIC void
-entry_guard_restriction_free(entry_guard_restriction_t *rst);
+STATIC void entry_guard_restriction_free(entry_guard_restriction_t *rst);
#endif /* defined(ENTRYNODES_PRIVATE) */
@@ -589,9 +586,11 @@ int getinfo_helper_entry_guards(control_connection_t *conn,
int entries_known_but_down(const or_options_t *options);
void entries_retry_all(const or_options_t *options);
-int guard_selection_have_enough_dir_info_to_build_circuits(
- guard_selection_t *gs);
-int entry_guards_have_enough_dir_info_to_build_circuits(void);
+char *entry_guards_get_err_str_if_dir_info_missing(int using_mds,
+ int num_present, int num_usable);
+char *guard_selection_get_err_str_if_dir_info_missing(guard_selection_t *gs,
+ int using_mds,
+ int num_present, int num_usable);
void entry_guards_free_all(void);
@@ -614,5 +613,5 @@ guard_get_guardfraction_bandwidth(guardfraction_bandwidth_t *guardfraction_bw,
int orig_bandwidth,
uint32_t guardfraction_percentage);
-#endif
+#endif /* !defined(TOR_ENTRYNODES_H) */
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
index b60d2e55c8..28377a3f36 100644
--- a/src/or/ext_orport.c
+++ b/src/or/ext_orport.c
@@ -23,15 +23,16 @@
#include "ext_orport.h"
#include "control.h"
#include "config.h"
-#include "util.h"
#include "main.h"
+#include "proto_ext_or.h"
+#include "util.h"
/** Allocate and return a structure capable of holding an Extended
* ORPort message of body length <b>len</b>. */
ext_or_cmd_t *
ext_or_cmd_new(uint16_t len)
{
- size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len;
+ size_t size = offsetof(ext_or_cmd_t, body) + len;
ext_or_cmd_t *cmd = tor_malloc(size);
cmd->len = len;
return cmd;
@@ -69,10 +70,10 @@ connection_write_ext_or_command(connection_t *conn,
return -1;
set_uint16(header, htons(command));
set_uint16(header+2, htons(bodylen));
- connection_write_to_buf(header, 4, conn);
+ connection_buf_add(header, 4, conn);
if (bodylen) {
tor_assert(body);
- connection_write_to_buf(body, bodylen, conn);
+ connection_buf_add(body, bodylen, conn);
}
return 0;
}
@@ -170,7 +171,7 @@ connection_ext_or_auth_neg_auth_type(connection_t *conn)
if (connection_get_inbuf_len(conn) < 1)
return 0;
- if (connection_fetch_from_buf(authtype, 1, conn) < 0)
+ if (connection_buf_get_bytes(authtype, 1, conn) < 0)
return -1;
log_debug(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]);
@@ -310,7 +311,7 @@ connection_ext_or_auth_handle_client_nonce(connection_t *conn)
if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN)
return 0;
- if (connection_fetch_from_buf(client_nonce,
+ if (connection_buf_get_bytes(client_nonce,
EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0)
return -1;
@@ -325,7 +326,7 @@ connection_ext_or_auth_handle_client_nonce(connection_t *conn)
&reply, &reply_len) < 0)
return -1;
- connection_write_to_buf(reply, reply_len, conn);
+ connection_buf_add(reply, reply_len, conn);
memwipe(reply, 0, reply_len);
tor_free(reply);
@@ -347,9 +348,9 @@ static void
connection_ext_or_auth_send_result(connection_t *conn, int success)
{
if (success)
- connection_write_to_buf("\x01", 1, conn);
+ connection_buf_add("\x01", 1, conn);
else
- connection_write_to_buf("\x00", 1, conn);
+ connection_buf_add("\x00", 1, conn);
}
/** Receive the client's hash from <b>conn</b>, validate that it's
@@ -367,7 +368,7 @@ connection_ext_or_auth_handle_client_hash(connection_t *conn)
if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN)
return 0;
- if (connection_fetch_from_buf(provided_client_hash,
+ if (connection_buf_get_bytes(provided_client_hash,
EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0)
return -1;
@@ -459,6 +460,11 @@ connection_ext_or_handle_cmd_useraddr(connection_t *conn,
tor_free(addr_str);
if (res<0)
return -1;
+ if (port == 0) {
+ log_warn(LD_GENERAL, "Server transport proxy gave us an empty port "
+ "in ExtORPort UserAddr command.");
+ // return -1; // enable this if nothing breaks after a while.
+ }
res = tor_addr_parse(&addr, address_part);
tor_free(address_part);
@@ -637,7 +643,7 @@ connection_ext_or_start_auth(or_connection_t *or_conn)
log_debug(LD_GENERAL,
"ExtORPort authentication: Sending supported authentication types");
- connection_write_to_buf((const char *)authtypes, sizeof(authtypes), conn);
+ connection_buf_add((const char *)authtypes, sizeof(authtypes), conn);
conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE;
return 0;
diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h
index b2cd05db8f..af2b97e77c 100644
--- a/src/or/ext_orport.h
+++ b/src/or/ext_orport.h
@@ -36,7 +36,7 @@ STATIC int handle_client_auth_nonce(const char *client_nonce,
extern uint8_t *ext_or_auth_cookie;
extern int ext_or_auth_cookie_is_set;
#endif
-#endif
+#endif /* defined(EXT_ORPORT_PRIVATE) */
-#endif
+#endif /* !defined(EXT_ORPORT_H) */
diff --git a/src/or/fp_pair.h b/src/or/fp_pair.h
index 4cea3eda6d..f7c060b459 100644
--- a/src/or/fp_pair.h
+++ b/src/or/fp_pair.h
@@ -41,5 +41,5 @@ void fp_pair_map_assert_ok(const fp_pair_map_t *map);
#undef DECLARE_MAP_FNS
-#endif
+#endif /* !defined(_TOR_FP_PAIR_H) */
diff --git a/src/or/geoip.c b/src/or/geoip.c
index ff46990de6..14e0b1b6fa 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -1802,6 +1802,15 @@ getinfo_helper_geoip(control_connection_t *control_conn,
sa_family_t family;
tor_addr_t addr;
question += strlen("ip-to-country/");
+
+ if (!strcmp(question, "ipv4-available") ||
+ !strcmp(question, "ipv6-available")) {
+ family = !strcmp(question, "ipv4-available") ? AF_INET : AF_INET6;
+ const int available = geoip_is_loaded(family);
+ tor_asprintf(answer, "%d", !! available);
+ return 0;
+ }
+
family = tor_addr_parse(&addr, question);
if (family != AF_INET && family != AF_INET6) {
*errmsg = "Invalid address family";
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 773525ccfe..753bdbf82a 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -20,7 +20,7 @@ STATIC int geoip_parse_entry(const char *line, sa_family_t family);
STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr);
STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr);
STATIC void clear_geoip_db(void);
-#endif
+#endif /* defined(GEOIP_PRIVATE) */
/** Entry in a map from IP address to the last time we've seen an incoming
* connection from that IP address. Used by bridges only to track which
@@ -96,5 +96,5 @@ const char *geoip_get_bridge_stats_extrainfo(time_t);
char *geoip_get_bridge_stats_controller(time_t);
char *format_client_stats_heartbeat(time_t now);
-#endif
+#endif /* !defined(TOR_GEOIP_H) */
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 8c48a6f47d..74ab766468 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -1124,5 +1124,5 @@ hibernate_set_state_for_testing_(hibernate_state_t newstate)
{
hibernate_state = newstate;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/hibernate.h b/src/or/hibernate.h
index 8bdb65a927..85fb42864b 100644
--- a/src/or/hibernate.h
+++ b/src/or/hibernate.h
@@ -53,7 +53,7 @@ typedef enum {
#ifdef TOR_UNIT_TESTS
void hibernate_set_state_for_testing_(hibernate_state_t newstate);
#endif
-#endif
+#endif /* defined(HIBERNATE_PRIVATE) */
-#endif
+#endif /* !defined(TOR_HIBERNATE_H) */
diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c
index 29681b42b5..6a5a3895b0 100644
--- a/src/or/hs_cache.c
+++ b/src/or/hs_cache.c
@@ -9,15 +9,22 @@
/* For unit tests.*/
#define HS_CACHE_PRIVATE
-#include "hs_cache.h"
-
#include "or.h"
#include "config.h"
+#include "hs_ident.h"
#include "hs_common.h"
+#include "hs_client.h"
#include "hs_descriptor.h"
#include "networkstatus.h"
#include "rendcache.h"
+#include "hs_cache.h"
+
+static int cached_client_descriptor_has_expired(time_t now,
+ const hs_cache_client_descriptor_t *cached_desc);
+
+/********************** Directory HS cache ******************/
+
/* Directory descriptor cache. Map indexed by blinded key. */
static digest256map_t *hs_cache_v3_dir;
@@ -98,7 +105,7 @@ cache_dir_desc_new(const char *desc)
/* Return the size of a cache entry in bytes. */
static size_t
-cache_get_entry_size(const hs_cache_dir_descriptor_t *entry)
+cache_get_dir_entry_size(const hs_cache_dir_descriptor_t *entry)
{
return (sizeof(*entry) + hs_desc_plaintext_obj_size(entry->plaintext_data)
+ strlen(entry->encoded_desc));
@@ -124,15 +131,17 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
if (cache_entry->plaintext_data->revision_counter >=
desc->plaintext_data->revision_counter) {
log_info(LD_REND, "Descriptor revision counter in our cache is "
- "greater or equal than the one we received. "
- "Rejecting!");
+ "greater or equal than the one we received (%d/%d). "
+ "Rejecting!",
+ (int)cache_entry->plaintext_data->revision_counter,
+ (int)desc->plaintext_data->revision_counter);
goto err;
}
/* We now know that the descriptor we just received is a new one so
* remove the entry we currently have from our cache so we can then
* store the new one. */
remove_v3_desc_as_dir(cache_entry);
- rend_cache_decrement_allocation(cache_get_entry_size(cache_entry));
+ rend_cache_decrement_allocation(cache_get_dir_entry_size(cache_entry));
cache_dir_desc_free(cache_entry);
}
/* Store the descriptor we just got. We are sure here that either we
@@ -142,7 +151,7 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
/* Update our total cache size with this entry for the OOM. This uses the
* old HS protocol cache subsystem for which we are tied with. */
- rend_cache_increment_allocation(cache_get_entry_size(desc));
+ rend_cache_increment_allocation(cache_get_dir_entry_size(desc));
/* XXX: Update HS statistics. We should have specific stats for v3. */
@@ -219,7 +228,7 @@ cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
}
/* Here, our entry has expired, remove and free. */
MAP_DEL_CURRENT(key);
- entry_size = cache_get_entry_size(entry);
+ entry_size = cache_get_dir_entry_size(entry);
bytes_removed += entry_size;
/* Entry is not in the cache anymore, destroy it. */
cache_dir_desc_free(entry);
@@ -228,8 +237,7 @@ cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
/* Logging. */
{
char key_b64[BASE64_DIGEST256_LEN + 1];
- base64_encode(key_b64, sizeof(key_b64), (const char *) key,
- DIGEST256_LEN, 0);
+ digest256_to_base64(key_b64, (const char *) key);
log_info(LD_REND, "Removing v3 descriptor '%s' from HSDir cache",
safe_str_client(key_b64));
}
@@ -313,6 +321,536 @@ hs_cache_clean_as_dir(time_t now)
cache_clean_v3_as_dir(now, 0);
}
+/********************** Client-side HS cache ******************/
+
+/* Client-side HS descriptor cache. Map indexed by service identity key. */
+static digest256map_t *hs_cache_v3_client;
+
+/* Client-side introduction point state cache. Map indexed by service public
+ * identity key (onion address). It contains hs_cache_client_intro_state_t
+ * objects all related to a specific service. */
+static digest256map_t *hs_cache_client_intro_state;
+
+/* Return the size of a client cache entry in bytes. */
+static size_t
+cache_get_client_entry_size(const hs_cache_client_descriptor_t *entry)
+{
+ return sizeof(*entry) +
+ strlen(entry->encoded_desc) + hs_desc_obj_size(entry->desc);
+}
+
+/* Remove a given descriptor from our cache. */
+static void
+remove_v3_desc_as_client(const hs_cache_client_descriptor_t *desc)
+{
+ tor_assert(desc);
+ digest256map_remove(hs_cache_v3_client, desc->key.pubkey);
+ /* Update cache size with this entry for the OOM handler. */
+ rend_cache_decrement_allocation(cache_get_client_entry_size(desc));
+}
+
+/* Store a given descriptor in our cache. */
+static void
+store_v3_desc_as_client(hs_cache_client_descriptor_t *desc)
+{
+ tor_assert(desc);
+ digest256map_set(hs_cache_v3_client, desc->key.pubkey, desc);
+ /* Update cache size with this entry for the OOM handler. */
+ rend_cache_increment_allocation(cache_get_client_entry_size(desc));
+}
+
+/* Query our cache and return the entry or NULL if not found or if expired. */
+STATIC hs_cache_client_descriptor_t *
+lookup_v3_desc_as_client(const uint8_t *key)
+{
+ time_t now = approx_time();
+ hs_cache_client_descriptor_t *cached_desc;
+
+ tor_assert(key);
+
+ /* Do the lookup */
+ cached_desc = digest256map_get(hs_cache_v3_client, key);
+ if (!cached_desc) {
+ return NULL;
+ }
+
+ /* Don't return expired entries */
+ if (cached_client_descriptor_has_expired(now, cached_desc)) {
+ return NULL;
+ }
+
+ return cached_desc;
+}
+
+/* Parse the encoded descriptor in <b>desc_str</b> using
+ * <b>service_identity_pk<b> to decrypt it first.
+ *
+ * If everything goes well, allocate and return a new
+ * hs_cache_client_descriptor_t object. In case of error, return NULL. */
+static hs_cache_client_descriptor_t *
+cache_client_desc_new(const char *desc_str,
+ const ed25519_public_key_t *service_identity_pk)
+{
+ hs_descriptor_t *desc = NULL;
+ hs_cache_client_descriptor_t *client_desc = NULL;
+
+ tor_assert(desc_str);
+ tor_assert(service_identity_pk);
+
+ /* Decode the descriptor we just fetched. */
+ if (hs_client_decode_descriptor(desc_str, service_identity_pk, &desc) < 0) {
+ goto end;
+ }
+ tor_assert(desc);
+
+ /* All is good: make a cache object for this descriptor */
+ client_desc = tor_malloc_zero(sizeof(hs_cache_client_descriptor_t));
+ ed25519_pubkey_copy(&client_desc->key, service_identity_pk);
+ /* Set expiration time for this cached descriptor to be the start of the next
+ * time period since that's when clients need to start using the next blinded
+ * pk of the service (and hence will need its next descriptor). */
+ client_desc->expiration_ts = hs_get_start_time_of_next_time_period(0);
+ client_desc->desc = desc;
+ client_desc->encoded_desc = tor_strdup(desc_str);
+
+ end:
+ return client_desc;
+}
+
+/** Free memory allocated by <b>desc</b>. */
+static void
+cache_client_desc_free(hs_cache_client_descriptor_t *desc)
+{
+ if (desc == NULL) {
+ return;
+ }
+ hs_descriptor_free(desc->desc);
+ memwipe(&desc->key, 0, sizeof(desc->key));
+ memwipe(desc->encoded_desc, 0, strlen(desc->encoded_desc));
+ tor_free(desc->encoded_desc);
+ tor_free(desc);
+}
+
+/** Helper function: Use by the free all function to clear the client cache */
+static void
+cache_client_desc_free_(void *ptr)
+{
+ hs_cache_client_descriptor_t *desc = ptr;
+ cache_client_desc_free(desc);
+}
+
+/* Return a newly allocated and initialized hs_cache_intro_state_t object. */
+static hs_cache_intro_state_t *
+cache_intro_state_new(void)
+{
+ hs_cache_intro_state_t *state = tor_malloc_zero(sizeof(*state));
+ state->created_ts = approx_time();
+ return state;
+}
+
+/* Free an hs_cache_intro_state_t object. */
+static void
+cache_intro_state_free(hs_cache_intro_state_t *state)
+{
+ tor_free(state);
+}
+
+/* Helper function: use by the free all function. */
+static void
+cache_intro_state_free_(void *state)
+{
+ cache_intro_state_free(state);
+}
+
+/* Return a newly allocated and initialized hs_cache_client_intro_state_t
+ * object. */
+static hs_cache_client_intro_state_t *
+cache_client_intro_state_new(void)
+{
+ hs_cache_client_intro_state_t *cache = tor_malloc_zero(sizeof(*cache));
+ cache->intro_points = digest256map_new();
+ return cache;
+}
+
+/* Free a cache client intro state object. */
+static void
+cache_client_intro_state_free(hs_cache_client_intro_state_t *cache)
+{
+ if (cache == NULL) {
+ return;
+ }
+ digest256map_free(cache->intro_points, cache_intro_state_free_);
+ tor_free(cache);
+}
+
+/* Helper function: use by the free all function. */
+static void
+cache_client_intro_state_free_(void *entry)
+{
+ cache_client_intro_state_free(entry);
+}
+
+/* For the given service identity key service_pk and an introduction
+ * authentication key auth_key, lookup the intro state object. Return 1 if
+ * found and put it in entry if not NULL. Return 0 if not found and entry is
+ * untouched. */
+static int
+cache_client_intro_state_lookup(const ed25519_public_key_t *service_pk,
+ const ed25519_public_key_t *auth_key,
+ hs_cache_intro_state_t **entry)
+{
+ hs_cache_intro_state_t *state;
+ hs_cache_client_intro_state_t *cache;
+
+ tor_assert(service_pk);
+ tor_assert(auth_key);
+
+ /* Lookup the intro state cache for this service key. */
+ cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
+ if (cache == NULL) {
+ goto not_found;
+ }
+
+ /* From the cache we just found for the service, lookup in the introduction
+ * points map for the given authentication key. */
+ state = digest256map_get(cache->intro_points, auth_key->pubkey);
+ if (state == NULL) {
+ goto not_found;
+ }
+ if (entry) {
+ *entry = state;
+ }
+ return 1;
+ not_found:
+ return 0;
+}
+
+/* Note the given failure in state. */
+static void
+cache_client_intro_state_note(hs_cache_intro_state_t *state,
+ rend_intro_point_failure_t failure)
+{
+ tor_assert(state);
+ switch (failure) {
+ case INTRO_POINT_FAILURE_GENERIC:
+ state->error = 1;
+ break;
+ case INTRO_POINT_FAILURE_TIMEOUT:
+ state->timed_out = 1;
+ break;
+ case INTRO_POINT_FAILURE_UNREACHABLE:
+ state->unreachable_count++;
+ break;
+ default:
+ tor_assert_nonfatal_unreached();
+ return;
+ }
+}
+
+/* For the given service identity key service_pk and an introduction
+ * authentication key auth_key, add an entry in the client intro state cache
+ * If no entry exists for the service, it will create one. If state is non
+ * NULL, it will point to the new intro state entry. */
+static void
+cache_client_intro_state_add(const ed25519_public_key_t *service_pk,
+ const ed25519_public_key_t *auth_key,
+ hs_cache_intro_state_t **state)
+{
+ hs_cache_intro_state_t *entry, *old_entry;
+ hs_cache_client_intro_state_t *cache;
+
+ tor_assert(service_pk);
+ tor_assert(auth_key);
+
+ /* Lookup the state cache for this service key. */
+ cache = digest256map_get(hs_cache_client_intro_state, service_pk->pubkey);
+ if (cache == NULL) {
+ cache = cache_client_intro_state_new();
+ digest256map_set(hs_cache_client_intro_state, service_pk->pubkey, cache);
+ }
+
+ entry = cache_intro_state_new();
+ old_entry = digest256map_set(cache->intro_points, auth_key->pubkey, entry);
+ /* This should never happened because the code flow is to lookup the entry
+ * before adding it. But, just in case, non fatal assert and free it. */
+ tor_assert_nonfatal(old_entry == NULL);
+ tor_free(old_entry);
+
+ if (state) {
+ *state = entry;
+ }
+}
+
+/* Remove every intro point state entry from cache that has been created
+ * before or at the cutoff. */
+static void
+cache_client_intro_state_clean(time_t cutoff,
+ hs_cache_client_intro_state_t *cache)
+{
+ tor_assert(cache);
+
+ DIGEST256MAP_FOREACH_MODIFY(cache->intro_points, key,
+ hs_cache_intro_state_t *, entry) {
+ if (entry->created_ts <= cutoff) {
+ cache_intro_state_free(entry);
+ MAP_DEL_CURRENT(key);
+ }
+ } DIGEST256MAP_FOREACH_END;
+}
+
+/* Return true iff no intro points are in this cache. */
+static int
+cache_client_intro_state_is_empty(const hs_cache_client_intro_state_t *cache)
+{
+ return digest256map_isempty(cache->intro_points);
+}
+
+/** Check whether <b>client_desc</b> is useful for us, and store it in the
+ * client-side HS cache if so. The client_desc is freed if we already have a
+ * fresher (higher revision counter count) in the cache. */
+static int
+cache_store_as_client(hs_cache_client_descriptor_t *client_desc)
+{
+ hs_cache_client_descriptor_t *cache_entry;
+
+ /* TODO: Heavy code duplication with cache_store_as_dir(). Consider
+ * refactoring and uniting! */
+
+ tor_assert(client_desc);
+
+ /* Check if we already have a descriptor from this HS in cache. If we do,
+ * check if this descriptor is newer than the cached one */
+ cache_entry = lookup_v3_desc_as_client(client_desc->key.pubkey);
+ if (cache_entry != NULL) {
+ /* If we have an entry in our cache that has a revision counter greater
+ * than the one we just fetched, discard the one we fetched. */
+ if (cache_entry->desc->plaintext_data.revision_counter >
+ client_desc->desc->plaintext_data.revision_counter) {
+ cache_client_desc_free(client_desc);
+ goto done;
+ }
+ /* Remove old entry. Make space for the new one! */
+ remove_v3_desc_as_client(cache_entry);
+ cache_client_desc_free(cache_entry);
+ }
+
+ /* Store descriptor in cache */
+ store_v3_desc_as_client(client_desc);
+
+ done:
+ return 0;
+}
+
+/* Return true iff the cached client descriptor at <b>cached_desc</b has
+ * expired. */
+static int
+cached_client_descriptor_has_expired(time_t now,
+ const hs_cache_client_descriptor_t *cached_desc)
+{
+ /* We use the current consensus time to see if we should expire this
+ * descriptor since we use consensus time for all other parts of the protocol
+ * as well (e.g. to build the blinded key and compute time periods). */
+ const networkstatus_t *ns = networkstatus_get_live_consensus(now);
+ /* If we don't have a recent consensus, consider this entry expired since we
+ * will want to fetch a new HS desc when we get a live consensus. */
+ if (!ns) {
+ return 1;
+ }
+
+ if (cached_desc->expiration_ts <= ns->valid_after) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* clean the client cache using now as the current time. Return the total size
+ * of removed bytes from the cache. */
+static size_t
+cache_clean_v3_as_client(time_t now)
+{
+ size_t bytes_removed = 0;
+
+ if (!hs_cache_v3_client) { /* No cache to clean. Just return. */
+ return 0;
+ }
+
+ DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
+ hs_cache_client_descriptor_t *, entry) {
+ size_t entry_size;
+
+ /* If the entry has not expired, continue to the next cached entry */
+ if (!cached_client_descriptor_has_expired(now, entry)) {
+ continue;
+ }
+ /* Here, our entry has expired, remove and free. */
+ MAP_DEL_CURRENT(key);
+ entry_size = cache_get_client_entry_size(entry);
+ bytes_removed += entry_size;
+ /* Entry is not in the cache anymore, destroy it. */
+ cache_client_desc_free(entry);
+ /* Update our OOM. We didn't use the remove() function because we are in
+ * a loop so we have to explicitely decrement. */
+ rend_cache_decrement_allocation(entry_size);
+ /* Logging. */
+ {
+ char key_b64[BASE64_DIGEST256_LEN + 1];
+ digest256_to_base64(key_b64, (const char *) key);
+ log_info(LD_REND, "Removing hidden service v3 descriptor '%s' "
+ "from client cache",
+ safe_str_client(key_b64));
+ }
+ } DIGEST256MAP_FOREACH_END;
+
+ return bytes_removed;
+}
+
+/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
+ * its HS descriptor if it's stored in our cache, or NULL if not. */
+const hs_descriptor_t *
+hs_cache_lookup_as_client(const ed25519_public_key_t *key)
+{
+ hs_cache_client_descriptor_t *cached_desc = NULL;
+
+ tor_assert(key);
+
+ cached_desc = lookup_v3_desc_as_client(key->pubkey);
+ if (cached_desc) {
+ tor_assert(cached_desc->desc);
+ return cached_desc->desc;
+ }
+
+ return NULL;
+}
+
+/** Public API: Given an encoded descriptor, store it in the client HS
+ * cache. Return -1 on error, 0 on success .*/
+int
+hs_cache_store_as_client(const char *desc_str,
+ const ed25519_public_key_t *identity_pk)
+{
+ hs_cache_client_descriptor_t *client_desc = NULL;
+
+ tor_assert(desc_str);
+ tor_assert(identity_pk);
+
+ /* Create client cache descriptor object */
+ client_desc = cache_client_desc_new(desc_str, identity_pk);
+ if (!client_desc) {
+ log_warn(LD_GENERAL, "Failed to parse received descriptor %s.",
+ escaped(desc_str));
+ goto err;
+ }
+
+ /* Push it to the cache */
+ if (cache_store_as_client(client_desc) < 0) {
+ goto err;
+ }
+
+ return 0;
+
+ err:
+ cache_client_desc_free(client_desc);
+ return -1;
+}
+
+/* Clean all client caches using the current time now. */
+void
+hs_cache_clean_as_client(time_t now)
+{
+ /* Start with v2 cache cleaning. */
+ rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
+ /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
+ * to compute the cutoff by itself using the lifetime value. */
+ cache_clean_v3_as_client(now);
+}
+
+/* Purge the client descriptor cache. */
+void
+hs_cache_purge_as_client(void)
+{
+ DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_client, key,
+ hs_cache_client_descriptor_t *, entry) {
+ size_t entry_size = cache_get_client_entry_size(entry);
+ MAP_DEL_CURRENT(key);
+ cache_client_desc_free(entry);
+ /* Update our OOM. We didn't use the remove() function because we are in
+ * a loop so we have to explicitely decrement. */
+ rend_cache_decrement_allocation(entry_size);
+ } DIGEST256MAP_FOREACH_END;
+
+ log_info(LD_REND, "Hidden service client descriptor cache purged.");
+}
+
+/* For a given service identity public key and an introduction authentication
+ * key, note the given failure in the client intro state cache. */
+void
+hs_cache_client_intro_state_note(const ed25519_public_key_t *service_pk,
+ const ed25519_public_key_t *auth_key,
+ rend_intro_point_failure_t failure)
+{
+ int found;
+ hs_cache_intro_state_t *entry;
+
+ tor_assert(service_pk);
+ tor_assert(auth_key);
+
+ found = cache_client_intro_state_lookup(service_pk, auth_key, &entry);
+ if (!found) {
+ /* Create a new entry and add it to the cache. */
+ cache_client_intro_state_add(service_pk, auth_key, &entry);
+ }
+ /* Note down the entry. */
+ cache_client_intro_state_note(entry, failure);
+}
+
+/* For a given service identity public key and an introduction authentication
+ * key, return true iff it is present in the failure cache. */
+const hs_cache_intro_state_t *
+hs_cache_client_intro_state_find(const ed25519_public_key_t *service_pk,
+ const ed25519_public_key_t *auth_key)
+{
+ hs_cache_intro_state_t *state = NULL;
+ cache_client_intro_state_lookup(service_pk, auth_key, &state);
+ return state;
+}
+
+/* Cleanup the client introduction state cache. */
+void
+hs_cache_client_intro_state_clean(time_t now)
+{
+ time_t cutoff = now - HS_CACHE_CLIENT_INTRO_STATE_MAX_AGE;
+
+ DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
+ hs_cache_client_intro_state_t *, cache) {
+ /* Cleanup intro points failure. */
+ cache_client_intro_state_clean(cutoff, cache);
+
+ /* Is this cache empty for this service key? If yes, remove it from the
+ * cache. Else keep it. */
+ if (cache_client_intro_state_is_empty(cache)) {
+ cache_client_intro_state_free(cache);
+ MAP_DEL_CURRENT(key);
+ }
+ } DIGEST256MAP_FOREACH_END;
+}
+
+/* Purge the client introduction state cache. */
+void
+hs_cache_client_intro_state_purge(void)
+{
+ DIGEST256MAP_FOREACH_MODIFY(hs_cache_client_intro_state, key,
+ hs_cache_client_intro_state_t *, cache) {
+ MAP_DEL_CURRENT(key);
+ cache_client_intro_state_free(cache);
+ } DIGEST256MAP_FOREACH_END;
+
+ log_info(LD_REND, "Hidden service client introduction point state "
+ "cache purged.");
+}
+
+/**************** Generics *********************************/
+
/* Do a round of OOM cleanup on all directory caches. Return the amount of
* removed bytes. It is possible that the returned value is lower than
* min_remove_bytes if the caches get emptied out so the caller should be
@@ -367,10 +905,7 @@ hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
return bytes_removed;
}
-/**
- * Return the maximum size of an HS descriptor we are willing to accept as an
- * HSDir.
- */
+/* Return the maximum size of a v3 HS descriptor. */
unsigned int
hs_cache_get_max_descriptor_size(void)
{
@@ -386,6 +921,12 @@ hs_cache_init(void)
/* Calling this twice is very wrong code flow. */
tor_assert(!hs_cache_v3_dir);
hs_cache_v3_dir = digest256map_new();
+
+ tor_assert(!hs_cache_v3_client);
+ hs_cache_v3_client = digest256map_new();
+
+ tor_assert(!hs_cache_client_intro_state);
+ hs_cache_client_intro_state = digest256map_new();
}
/* Cleanup the hidden service cache subsystem. */
@@ -394,5 +935,12 @@ hs_cache_free_all(void)
{
digest256map_free(hs_cache_v3_dir, cache_dir_desc_free_);
hs_cache_v3_dir = NULL;
+
+ digest256map_free(hs_cache_v3_client, cache_client_desc_free_);
+ hs_cache_v3_client = NULL;
+
+ digest256map_free(hs_cache_client_intro_state,
+ cache_client_intro_state_free_);
+ hs_cache_client_intro_state = NULL;
}
diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h
index ed00424234..2dcc518a71 100644
--- a/src/or/hs_cache.h
+++ b/src/or/hs_cache.h
@@ -15,8 +15,34 @@
#include "crypto_ed25519.h"
#include "hs_common.h"
#include "hs_descriptor.h"
+#include "rendcommon.h"
#include "torcert.h"
+/* This is the maximum time an introduction point state object can stay in the
+ * client cache in seconds (2 mins or 120 seconds). */
+#define HS_CACHE_CLIENT_INTRO_STATE_MAX_AGE (2 * 60)
+
+/* Introduction point state. */
+typedef struct hs_cache_intro_state_t {
+ /* When this entry was created and put in the cache. */
+ time_t created_ts;
+
+ /* Did it suffered a generic error? */
+ unsigned int error : 1;
+
+ /* Did it timed out? */
+ unsigned int timed_out : 1;
+
+ /* How many times we tried to reached it and it was unreachable. */
+ uint32_t unreachable_count;
+} hs_cache_intro_state_t;
+
+typedef struct hs_cache_client_intro_state_t {
+ /* Contains hs_cache_intro_state_t object indexed by introduction point
+ * authentication key. */
+ digest256map_t *intro_points;
+} hs_cache_client_intro_state_t;
+
/* Descriptor representation on the directory side which is a subset of
* information that the HSDir can decode and serve it. */
typedef struct hs_cache_dir_descriptor_t {
@@ -53,11 +79,49 @@ int hs_cache_store_as_dir(const char *desc);
int hs_cache_lookup_as_dir(uint32_t version, const char *query,
const char **desc_out);
+const hs_descriptor_t *
+hs_cache_lookup_as_client(const ed25519_public_key_t *key);
+int hs_cache_store_as_client(const char *desc_str,
+ const ed25519_public_key_t *identity_pk);
+void hs_cache_clean_as_client(time_t now);
+void hs_cache_purge_as_client(void);
+
+/* Client failure cache. */
+void hs_cache_client_intro_state_note(const ed25519_public_key_t *service_pk,
+ const ed25519_public_key_t *auth_key,
+ rend_intro_point_failure_t failure);
+const hs_cache_intro_state_t *hs_cache_client_intro_state_find(
+ const ed25519_public_key_t *service_pk,
+ const ed25519_public_key_t *auth_key);
+void hs_cache_client_intro_state_clean(time_t now);
+void hs_cache_client_intro_state_purge(void);
+
#ifdef HS_CACHE_PRIVATE
+/** Represents a locally cached HS descriptor on a hidden service client. */
+typedef struct hs_cache_client_descriptor_t {
+ /* This object is indexed using the service identity public key */
+ ed25519_public_key_t key;
+
+ /* When will this entry expire? We expire cached client descriptors in the
+ * start of the next time period, since that's when clients need to start
+ * using the next blinded key of the service. */
+ time_t expiration_ts;
+
+ /* The cached descriptor, this object is the owner. It can't be NULL. A
+ * cache object without a valid descriptor is not possible. */
+ hs_descriptor_t *desc;
+
+ /* Encoded descriptor in string form. Can't be NULL. */
+ char *encoded_desc;
+} hs_cache_client_descriptor_t;
+
STATIC size_t cache_clean_v3_as_dir(time_t now, time_t global_cutoff);
-#endif /* HS_CACHE_PRIVATE */
+STATIC hs_cache_client_descriptor_t *
+lookup_v3_desc_as_client(const uint8_t *key);
+
+#endif /* defined(HS_CACHE_PRIVATE) */
-#endif /* TOR_HS_CACHE_H */
+#endif /* !defined(TOR_HS_CACHE_H) */
diff --git a/src/or/hs_cell.c b/src/or/hs_cell.c
new file mode 100644
index 0000000000..5244cfa3dd
--- /dev/null
+++ b/src/or/hs_cell.c
@@ -0,0 +1,948 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_cell.c
+ * \brief Hidden service API for cell creation and handling.
+ **/
+
+#include "or.h"
+#include "config.h"
+#include "rendservice.h"
+#include "replaycache.h"
+#include "util.h"
+
+#include "hs_cell.h"
+#include "hs_ntor.h"
+
+/* Trunnel. */
+#include "ed25519_cert.h"
+#include "hs/cell_common.h"
+#include "hs/cell_establish_intro.h"
+#include "hs/cell_introduce1.h"
+#include "hs/cell_rendezvous.h"
+
+/* Compute the MAC of an INTRODUCE cell in mac_out. The encoded_cell param is
+ * the cell content up to the ENCRYPTED section of length encoded_cell_len.
+ * The encrypted param is the start of the ENCRYPTED section of length
+ * encrypted_len. The mac_key is the key needed for the computation of the MAC
+ * derived from the ntor handshake of length mac_key_len.
+ *
+ * The length mac_out_len must be at least DIGEST256_LEN. */
+static void
+compute_introduce_mac(const uint8_t *encoded_cell, size_t encoded_cell_len,
+ const uint8_t *encrypted, size_t encrypted_len,
+ const uint8_t *mac_key, size_t mac_key_len,
+ uint8_t *mac_out, size_t mac_out_len)
+{
+ size_t offset = 0;
+ size_t mac_msg_len;
+ uint8_t mac_msg[RELAY_PAYLOAD_SIZE] = {0};
+
+ tor_assert(encoded_cell);
+ tor_assert(encrypted);
+ tor_assert(mac_key);
+ tor_assert(mac_out);
+ tor_assert(mac_out_len >= DIGEST256_LEN);
+
+ /* Compute the size of the message which is basically the entire cell until
+ * the MAC field of course. */
+ mac_msg_len = encoded_cell_len + (encrypted_len - DIGEST256_LEN);
+ tor_assert(mac_msg_len <= sizeof(mac_msg));
+
+ /* First, put the encoded cell in the msg. */
+ memcpy(mac_msg, encoded_cell, encoded_cell_len);
+ offset += encoded_cell_len;
+ /* Second, put the CLIENT_PK + ENCRYPTED_DATA but ommit the MAC field (which
+ * is junk at this point). */
+ memcpy(mac_msg + offset, encrypted, (encrypted_len - DIGEST256_LEN));
+ offset += (encrypted_len - DIGEST256_LEN);
+ tor_assert(offset == mac_msg_len);
+
+ crypto_mac_sha3_256(mac_out, mac_out_len,
+ mac_key, mac_key_len,
+ mac_msg, mac_msg_len);
+ memwipe(mac_msg, 0, sizeof(mac_msg));
+}
+
+/* From a set of keys, subcredential and the ENCRYPTED section of an
+ * INTRODUCE2 cell, return a newly allocated intro cell keys structure.
+ * Finally, the client public key is copied in client_pk. On error, return
+ * NULL. */
+static hs_ntor_intro_cell_keys_t *
+get_introduce2_key_material(const ed25519_public_key_t *auth_key,
+ const curve25519_keypair_t *enc_key,
+ const uint8_t *subcredential,
+ const uint8_t *encrypted_section,
+ curve25519_public_key_t *client_pk)
+{
+ hs_ntor_intro_cell_keys_t *keys;
+
+ tor_assert(auth_key);
+ tor_assert(enc_key);
+ tor_assert(subcredential);
+ tor_assert(encrypted_section);
+ tor_assert(client_pk);
+
+ keys = tor_malloc_zero(sizeof(*keys));
+
+ /* First bytes of the ENCRYPTED section are the client public key. */
+ memcpy(client_pk->public_key, encrypted_section, CURVE25519_PUBKEY_LEN);
+
+ if (hs_ntor_service_get_introduce1_keys(auth_key, enc_key, client_pk,
+ subcredential, keys) < 0) {
+ /* Don't rely on the caller to wipe this on error. */
+ memwipe(client_pk, 0, sizeof(curve25519_public_key_t));
+ tor_free(keys);
+ keys = NULL;
+ }
+ return keys;
+}
+
+/* Using the given encryption key, decrypt the encrypted_section of length
+ * encrypted_section_len of an INTRODUCE2 cell and return a newly allocated
+ * buffer containing the decrypted data. On decryption failure, NULL is
+ * returned. */
+static uint8_t *
+decrypt_introduce2(const uint8_t *enc_key, const uint8_t *encrypted_section,
+ size_t encrypted_section_len)
+{
+ uint8_t *decrypted = NULL;
+ crypto_cipher_t *cipher = NULL;
+
+ tor_assert(enc_key);
+ tor_assert(encrypted_section);
+
+ /* Decrypt ENCRYPTED section. */
+ cipher = crypto_cipher_new_with_bits((char *) enc_key,
+ CURVE25519_PUBKEY_LEN * 8);
+ tor_assert(cipher);
+
+ /* This is symmetric encryption so can't be bigger than the encrypted
+ * section length. */
+ decrypted = tor_malloc_zero(encrypted_section_len);
+ if (crypto_cipher_decrypt(cipher, (char *) decrypted,
+ (const char *) encrypted_section,
+ encrypted_section_len) < 0) {
+ tor_free(decrypted);
+ decrypted = NULL;
+ goto done;
+ }
+
+ done:
+ crypto_cipher_free(cipher);
+ return decrypted;
+}
+
+/* Given a pointer to the decrypted data of the ENCRYPTED section of an
+ * INTRODUCE2 cell of length decrypted_len, parse and validate the cell
+ * content. Return a newly allocated cell structure or NULL on error. The
+ * circuit and service object are only used for logging purposes. */
+static trn_cell_introduce_encrypted_t *
+parse_introduce2_encrypted(const uint8_t *decrypted_data,
+ size_t decrypted_len, const origin_circuit_t *circ,
+ const hs_service_t *service)
+{
+ trn_cell_introduce_encrypted_t *enc_cell = NULL;
+
+ tor_assert(decrypted_data);
+ tor_assert(circ);
+ tor_assert(service);
+
+ if (trn_cell_introduce_encrypted_parse(&enc_cell, decrypted_data,
+ decrypted_len) < 0) {
+ log_info(LD_REND, "Unable to parse the decrypted ENCRYPTED section of "
+ "the INTRODUCE2 cell on circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto err;
+ }
+
+ if (trn_cell_introduce_encrypted_get_onion_key_type(enc_cell) !=
+ HS_CELL_ONION_KEY_TYPE_NTOR) {
+ log_info(LD_REND, "INTRODUCE2 onion key type is invalid. Got %u but "
+ "expected %u on circuit %u for service %s",
+ trn_cell_introduce_encrypted_get_onion_key_type(enc_cell),
+ HS_CELL_ONION_KEY_TYPE_NTOR, TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto err;
+ }
+
+ if (trn_cell_introduce_encrypted_getlen_onion_key(enc_cell) !=
+ CURVE25519_PUBKEY_LEN) {
+ log_info(LD_REND, "INTRODUCE2 onion key length is invalid. Got %u but "
+ "expected %d on circuit %u for service %s",
+ (unsigned)trn_cell_introduce_encrypted_getlen_onion_key(enc_cell),
+ CURVE25519_PUBKEY_LEN, TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto err;
+ }
+ /* XXX: Validate NSPEC field as well. */
+
+ return enc_cell;
+ err:
+ trn_cell_introduce_encrypted_free(enc_cell);
+ return NULL;
+}
+
+/* Build a legacy ESTABLISH_INTRO cell with the given circuit nonce and RSA
+ * encryption key. The encoded cell is put in cell_out that MUST at least be
+ * of the size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on
+ * success else a negative value and cell_out is untouched. */
+static ssize_t
+build_legacy_establish_intro(const char *circ_nonce, crypto_pk_t *enc_key,
+ uint8_t *cell_out)
+{
+ ssize_t cell_len;
+
+ tor_assert(circ_nonce);
+ tor_assert(enc_key);
+ tor_assert(cell_out);
+
+ memwipe(cell_out, 0, RELAY_PAYLOAD_SIZE);
+
+ cell_len = rend_service_encode_establish_intro_cell((char*)cell_out,
+ RELAY_PAYLOAD_SIZE,
+ enc_key, circ_nonce);
+ return cell_len;
+}
+
+/* Parse an INTRODUCE2 cell from payload of size payload_len for the given
+ * service and circuit which are used only for logging purposes. The resulting
+ * parsed cell is put in cell_ptr_out.
+ *
+ * This function only parses prop224 INTRODUCE2 cells even when the intro point
+ * is a legacy intro point. That's because intro points don't actually care
+ * about the contents of the introduce cell. Legacy INTRODUCE cells are only
+ * used by the legacy system now.
+ *
+ * Return 0 on success else a negative value and cell_ptr_out is untouched. */
+static int
+parse_introduce2_cell(const hs_service_t *service,
+ const origin_circuit_t *circ, const uint8_t *payload,
+ size_t payload_len,
+ trn_cell_introduce1_t **cell_ptr_out)
+{
+ trn_cell_introduce1_t *cell = NULL;
+
+ tor_assert(service);
+ tor_assert(circ);
+ tor_assert(payload);
+ tor_assert(cell_ptr_out);
+
+ /* Parse the cell so we can start cell validation. */
+ if (trn_cell_introduce1_parse(&cell, payload, payload_len) < 0) {
+ log_info(LD_PROTOCOL, "Unable to parse INTRODUCE2 cell on circuit %u "
+ "for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto err;
+ }
+
+ /* Success. */
+ *cell_ptr_out = cell;
+ return 0;
+ err:
+ return -1;
+}
+
+/* Set the onion public key onion_pk in cell, the encrypted section of an
+ * INTRODUCE1 cell. */
+static void
+introduce1_set_encrypted_onion_key(trn_cell_introduce_encrypted_t *cell,
+ const uint8_t *onion_pk)
+{
+ tor_assert(cell);
+ tor_assert(onion_pk);
+ /* There is only one possible key type for a non legacy cell. */
+ trn_cell_introduce_encrypted_set_onion_key_type(cell,
+ HS_CELL_ONION_KEY_TYPE_NTOR);
+ trn_cell_introduce_encrypted_set_onion_key_len(cell, CURVE25519_PUBKEY_LEN);
+ trn_cell_introduce_encrypted_setlen_onion_key(cell, CURVE25519_PUBKEY_LEN);
+ memcpy(trn_cell_introduce_encrypted_getarray_onion_key(cell), onion_pk,
+ trn_cell_introduce_encrypted_getlen_onion_key(cell));
+}
+
+/* Set the link specifiers in lspecs in cell, the encrypted section of an
+ * INTRODUCE1 cell. */
+static void
+introduce1_set_encrypted_link_spec(trn_cell_introduce_encrypted_t *cell,
+ const smartlist_t *lspecs)
+{
+ tor_assert(cell);
+ tor_assert(lspecs);
+ tor_assert(smartlist_len(lspecs) > 0);
+ tor_assert(smartlist_len(lspecs) <= UINT8_MAX);
+
+ uint8_t lspecs_num = (uint8_t) smartlist_len(lspecs);
+ trn_cell_introduce_encrypted_set_nspec(cell, lspecs_num);
+ /* We aren't duplicating the link specifiers object here which means that
+ * the ownership goes to the trn_cell_introduce_encrypted_t cell and those
+ * object will be freed when the cell is. */
+ SMARTLIST_FOREACH(lspecs, link_specifier_t *, ls,
+ trn_cell_introduce_encrypted_add_nspecs(cell, ls));
+}
+
+/* Set padding in the enc_cell only if needed that is the total length of both
+ * sections are below the mininum required for an INTRODUCE1 cell. */
+static void
+introduce1_set_encrypted_padding(const trn_cell_introduce1_t *cell,
+ trn_cell_introduce_encrypted_t *enc_cell)
+{
+ tor_assert(cell);
+ tor_assert(enc_cell);
+ /* This is the length we expect to have once encoded of the whole cell. */
+ ssize_t full_len = trn_cell_introduce1_encoded_len(cell) +
+ trn_cell_introduce_encrypted_encoded_len(enc_cell);
+ tor_assert(full_len > 0);
+ if (full_len < HS_CELL_INTRODUCE1_MIN_SIZE) {
+ size_t padding = HS_CELL_INTRODUCE1_MIN_SIZE - full_len;
+ trn_cell_introduce_encrypted_setlen_pad(enc_cell, padding);
+ memset(trn_cell_introduce_encrypted_getarray_pad(enc_cell), 0,
+ trn_cell_introduce_encrypted_getlen_pad(enc_cell));
+ }
+}
+
+/* Encrypt the ENCRYPTED payload and encode it in the cell using the enc_cell
+ * and the INTRODUCE1 data.
+ *
+ * This can't fail but it is very important that the caller sets every field
+ * in data so the computation of the INTRODUCE1 keys doesn't fail. */
+static void
+introduce1_encrypt_and_encode(trn_cell_introduce1_t *cell,
+ const trn_cell_introduce_encrypted_t *enc_cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ size_t offset = 0;
+ ssize_t encrypted_len;
+ ssize_t encoded_cell_len, encoded_enc_cell_len;
+ uint8_t encoded_cell[RELAY_PAYLOAD_SIZE] = {0};
+ uint8_t encoded_enc_cell[RELAY_PAYLOAD_SIZE] = {0};
+ uint8_t *encrypted = NULL;
+ uint8_t mac[DIGEST256_LEN];
+ crypto_cipher_t *cipher = NULL;
+ hs_ntor_intro_cell_keys_t keys;
+
+ tor_assert(cell);
+ tor_assert(enc_cell);
+ tor_assert(data);
+
+ /* Encode the cells up to now of what we have to we can perform the MAC
+ * computation on it. */
+ encoded_cell_len = trn_cell_introduce1_encode(encoded_cell,
+ sizeof(encoded_cell), cell);
+ /* We have a much more serious issue if this isn't true. */
+ tor_assert(encoded_cell_len > 0);
+
+ encoded_enc_cell_len =
+ trn_cell_introduce_encrypted_encode(encoded_enc_cell,
+ sizeof(encoded_enc_cell), enc_cell);
+ /* We have a much more serious issue if this isn't true. */
+ tor_assert(encoded_enc_cell_len > 0);
+
+ /* Get the key material for the encryption. */
+ if (hs_ntor_client_get_introduce1_keys(data->auth_pk, data->enc_pk,
+ data->client_kp,
+ data->subcredential, &keys) < 0) {
+ tor_assert_unreached();
+ }
+
+ /* Prepare cipher with the encryption key just computed. */
+ cipher = crypto_cipher_new_with_bits((const char *) keys.enc_key,
+ sizeof(keys.enc_key) * 8);
+ tor_assert(cipher);
+
+ /* Compute the length of the ENCRYPTED section which is the CLIENT_PK,
+ * ENCRYPTED_DATA and MAC length. */
+ encrypted_len = sizeof(data->client_kp->pubkey) + encoded_enc_cell_len +
+ sizeof(mac);
+ tor_assert(encrypted_len < RELAY_PAYLOAD_SIZE);
+ encrypted = tor_malloc_zero(encrypted_len);
+
+ /* Put the CLIENT_PK first. */
+ memcpy(encrypted, data->client_kp->pubkey.public_key,
+ sizeof(data->client_kp->pubkey.public_key));
+ offset += sizeof(data->client_kp->pubkey.public_key);
+ /* Then encrypt and set the ENCRYPTED_DATA. This can't fail. */
+ crypto_cipher_encrypt(cipher, (char *) encrypted + offset,
+ (const char *) encoded_enc_cell, encoded_enc_cell_len);
+ crypto_cipher_free(cipher);
+ offset += encoded_enc_cell_len;
+ /* Compute MAC from the above and put it in the buffer. This function will
+ * make the adjustment to the encryptled_len to ommit the MAC length. */
+ compute_introduce_mac(encoded_cell, encoded_cell_len,
+ encrypted, encrypted_len,
+ keys.mac_key, sizeof(keys.mac_key),
+ mac, sizeof(mac));
+ memcpy(encrypted + offset, mac, sizeof(mac));
+ offset += sizeof(mac);
+ tor_assert(offset == (size_t) encrypted_len);
+
+ /* Set the ENCRYPTED section in the cell. */
+ trn_cell_introduce1_setlen_encrypted(cell, encrypted_len);
+ memcpy(trn_cell_introduce1_getarray_encrypted(cell),
+ encrypted, encrypted_len);
+
+ /* Cleanup. */
+ memwipe(&keys, 0, sizeof(keys));
+ memwipe(mac, 0, sizeof(mac));
+ memwipe(encrypted, 0, sizeof(encrypted_len));
+ memwipe(encoded_enc_cell, 0, sizeof(encoded_enc_cell));
+ tor_free(encrypted);
+}
+
+/* Using the INTRODUCE1 data, setup the ENCRYPTED section in cell. This means
+ * set it, encrypt it and encode it. */
+static void
+introduce1_set_encrypted(trn_cell_introduce1_t *cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ trn_cell_introduce_encrypted_t *enc_cell;
+ trn_cell_extension_t *ext;
+
+ tor_assert(cell);
+ tor_assert(data);
+
+ enc_cell = trn_cell_introduce_encrypted_new();
+ tor_assert(enc_cell);
+
+ /* Set extension data. None are used. */
+ ext = trn_cell_extension_new();
+ tor_assert(ext);
+ trn_cell_extension_set_num(ext, 0);
+ trn_cell_introduce_encrypted_set_extensions(enc_cell, ext);
+
+ /* Set the rendezvous cookie. */
+ memcpy(trn_cell_introduce_encrypted_getarray_rend_cookie(enc_cell),
+ data->rendezvous_cookie, REND_COOKIE_LEN);
+
+ /* Set the onion public key. */
+ introduce1_set_encrypted_onion_key(enc_cell, data->onion_pk->public_key);
+
+ /* Set the link specifiers. */
+ introduce1_set_encrypted_link_spec(enc_cell, data->link_specifiers);
+
+ /* Set padding. */
+ introduce1_set_encrypted_padding(cell, enc_cell);
+
+ /* Encrypt and encode it in the cell. */
+ introduce1_encrypt_and_encode(cell, enc_cell, data);
+
+ /* Cleanup. */
+ trn_cell_introduce_encrypted_free(enc_cell);
+}
+
+/* Set the authentication key in the INTRODUCE1 cell from the given data. */
+static void
+introduce1_set_auth_key(trn_cell_introduce1_t *cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ tor_assert(cell);
+ tor_assert(data);
+ /* There is only one possible type for a non legacy cell. */
+ trn_cell_introduce1_set_auth_key_type(cell, HS_INTRO_AUTH_KEY_TYPE_ED25519);
+ trn_cell_introduce1_set_auth_key_len(cell, ED25519_PUBKEY_LEN);
+ trn_cell_introduce1_setlen_auth_key(cell, ED25519_PUBKEY_LEN);
+ memcpy(trn_cell_introduce1_getarray_auth_key(cell),
+ data->auth_pk->pubkey, trn_cell_introduce1_getlen_auth_key(cell));
+}
+
+/* Set the legacy ID field in the INTRODUCE1 cell from the given data. */
+static void
+introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ tor_assert(cell);
+ tor_assert(data);
+
+ if (data->is_legacy) {
+ uint8_t digest[DIGEST_LEN];
+ if (BUG(crypto_pk_get_digest(data->legacy_key, (char *) digest) < 0)) {
+ return;
+ }
+ memcpy(trn_cell_introduce1_getarray_legacy_key_id(cell),
+ digest, trn_cell_introduce1_getlen_legacy_key_id(cell));
+ } else {
+ /* We have to zeroed the LEGACY_KEY_ID field. */
+ memset(trn_cell_introduce1_getarray_legacy_key_id(cell), 0,
+ trn_cell_introduce1_getlen_legacy_key_id(cell));
+ }
+}
+
+/* ========== */
+/* Public API */
+/* ========== */
+
+/* Build an ESTABLISH_INTRO cell with the given circuit nonce and intro point
+ * object. The encoded cell is put in cell_out that MUST at least be of the
+ * size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on success else
+ * a negative value and cell_out is untouched. This function also supports
+ * legacy cell creation. */
+ssize_t
+hs_cell_build_establish_intro(const char *circ_nonce,
+ const hs_service_intro_point_t *ip,
+ uint8_t *cell_out)
+{
+ ssize_t cell_len = -1;
+ uint16_t sig_len = ED25519_SIG_LEN;
+ trn_cell_extension_t *ext;
+ trn_cell_establish_intro_t *cell = NULL;
+
+ tor_assert(circ_nonce);
+ tor_assert(ip);
+
+ /* Quickly handle the legacy IP. */
+ if (ip->base.is_only_legacy) {
+ tor_assert(ip->legacy_key);
+ cell_len = build_legacy_establish_intro(circ_nonce, ip->legacy_key,
+ cell_out);
+ tor_assert(cell_len <= RELAY_PAYLOAD_SIZE);
+ /* Success or not we are done here. */
+ goto done;
+ }
+
+ /* Set extension data. None used here. */
+ ext = trn_cell_extension_new();
+ trn_cell_extension_set_num(ext, 0);
+ cell = trn_cell_establish_intro_new();
+ trn_cell_establish_intro_set_extensions(cell, ext);
+ /* Set signature size. Array is then allocated in the cell. We need to do
+ * this early so we can use trunnel API to get the signature length. */
+ trn_cell_establish_intro_set_sig_len(cell, sig_len);
+ trn_cell_establish_intro_setlen_sig(cell, sig_len);
+
+ /* Set AUTH_KEY_TYPE: 2 means ed25519 */
+ trn_cell_establish_intro_set_auth_key_type(cell,
+ HS_INTRO_AUTH_KEY_TYPE_ED25519);
+
+ /* Set AUTH_KEY and AUTH_KEY_LEN field. Must also set byte-length of
+ * AUTH_KEY to match */
+ {
+ uint16_t auth_key_len = ED25519_PUBKEY_LEN;
+ trn_cell_establish_intro_set_auth_key_len(cell, auth_key_len);
+ trn_cell_establish_intro_setlen_auth_key(cell, auth_key_len);
+ /* We do this call _after_ setting the length because it's reallocated at
+ * that point only. */
+ uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell);
+ memcpy(auth_key_ptr, ip->auth_key_kp.pubkey.pubkey, auth_key_len);
+ }
+
+ /* Calculate HANDSHAKE_AUTH field (MAC). */
+ {
+ ssize_t tmp_cell_enc_len = 0;
+ ssize_t tmp_cell_mac_offset =
+ sig_len + sizeof(cell->sig_len) +
+ trn_cell_establish_intro_getlen_handshake_mac(cell);
+ uint8_t tmp_cell_enc[RELAY_PAYLOAD_SIZE] = {0};
+ uint8_t mac[TRUNNEL_SHA3_256_LEN], *handshake_ptr;
+
+ /* We first encode the current fields we have in the cell so we can
+ * compute the MAC using the raw bytes. */
+ tmp_cell_enc_len = trn_cell_establish_intro_encode(tmp_cell_enc,
+ sizeof(tmp_cell_enc),
+ cell);
+ if (BUG(tmp_cell_enc_len < 0)) {
+ goto done;
+ }
+ /* Sanity check. */
+ tor_assert(tmp_cell_enc_len > tmp_cell_mac_offset);
+
+ /* Circuit nonce is always DIGEST_LEN according to tor-spec.txt. */
+ crypto_mac_sha3_256(mac, sizeof(mac),
+ (uint8_t *) circ_nonce, DIGEST_LEN,
+ tmp_cell_enc, tmp_cell_enc_len - tmp_cell_mac_offset);
+ handshake_ptr = trn_cell_establish_intro_getarray_handshake_mac(cell);
+ memcpy(handshake_ptr, mac, sizeof(mac));
+
+ memwipe(mac, 0, sizeof(mac));
+ memwipe(tmp_cell_enc, 0, sizeof(tmp_cell_enc));
+ }
+
+ /* Calculate the cell signature SIG. */
+ {
+ ssize_t tmp_cell_enc_len = 0;
+ ssize_t tmp_cell_sig_offset = (sig_len + sizeof(cell->sig_len));
+ uint8_t tmp_cell_enc[RELAY_PAYLOAD_SIZE] = {0}, *sig_ptr;
+ ed25519_signature_t sig;
+
+ /* We first encode the current fields we have in the cell so we can
+ * compute the signature from the raw bytes of the cell. */
+ tmp_cell_enc_len = trn_cell_establish_intro_encode(tmp_cell_enc,
+ sizeof(tmp_cell_enc),
+ cell);
+ if (BUG(tmp_cell_enc_len < 0)) {
+ goto done;
+ }
+
+ if (ed25519_sign_prefixed(&sig, tmp_cell_enc,
+ tmp_cell_enc_len - tmp_cell_sig_offset,
+ ESTABLISH_INTRO_SIG_PREFIX, &ip->auth_key_kp)) {
+ log_warn(LD_BUG, "Unable to make signature for ESTABLISH_INTRO cell.");
+ goto done;
+ }
+ /* Copy the signature into the cell. */
+ sig_ptr = trn_cell_establish_intro_getarray_sig(cell);
+ memcpy(sig_ptr, sig.sig, sig_len);
+
+ memwipe(tmp_cell_enc, 0, sizeof(tmp_cell_enc));
+ }
+
+ /* Encode the cell. Can't be bigger than a standard cell. */
+ cell_len = trn_cell_establish_intro_encode(cell_out, RELAY_PAYLOAD_SIZE,
+ cell);
+
+ done:
+ trn_cell_establish_intro_free(cell);
+ return cell_len;
+}
+
+/* Parse the INTRO_ESTABLISHED cell in the payload of size payload_len. If we
+ * are successful at parsing it, return the length of the parsed cell else a
+ * negative value on error. */
+ssize_t
+hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
+{
+ ssize_t ret;
+ trn_cell_intro_established_t *cell = NULL;
+
+ tor_assert(payload);
+
+ /* Try to parse the payload into a cell making sure we do actually have a
+ * valid cell. */
+ ret = trn_cell_intro_established_parse(&cell, payload, payload_len);
+ if (ret >= 0) {
+ /* On success, we do not keep the cell, we just notify the caller that it
+ * was successfully parsed. */
+ trn_cell_intro_established_free(cell);
+ }
+ return ret;
+}
+
+/* Parsse the INTRODUCE2 cell using data which contains everything we need to
+ * do so and contains the destination buffers of information we extract and
+ * compute from the cell. Return 0 on success else a negative value. The
+ * service and circ are only used for logging purposes. */
+ssize_t
+hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
+ const origin_circuit_t *circ,
+ const hs_service_t *service)
+{
+ int ret = -1;
+ time_t elapsed;
+ uint8_t *decrypted = NULL;
+ size_t encrypted_section_len;
+ const uint8_t *encrypted_section;
+ trn_cell_introduce1_t *cell = NULL;
+ trn_cell_introduce_encrypted_t *enc_cell = NULL;
+ hs_ntor_intro_cell_keys_t *intro_keys = NULL;
+
+ tor_assert(data);
+ tor_assert(circ);
+ tor_assert(service);
+
+ /* Parse the cell into a decoded data structure pointed by cell_ptr. */
+ if (parse_introduce2_cell(service, circ, data->payload, data->payload_len,
+ &cell) < 0) {
+ goto done;
+ }
+
+ log_info(LD_REND, "Received a decodable INTRODUCE2 cell on circuit %u "
+ "for service %s. Decoding encrypted section...",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+
+ encrypted_section = trn_cell_introduce1_getconstarray_encrypted(cell);
+ encrypted_section_len = trn_cell_introduce1_getlen_encrypted(cell);
+
+ /* Encrypted section must at least contain the CLIENT_PK and MAC which is
+ * defined in section 3.3.2 of the specification. */
+ if (encrypted_section_len < (CURVE25519_PUBKEY_LEN + DIGEST256_LEN)) {
+ log_info(LD_REND, "Invalid INTRODUCE2 encrypted section length "
+ "for service %s. Dropping cell.",
+ safe_str_client(service->onion_address));
+ goto done;
+ }
+
+ /* Check our replay cache for this introduction point. */
+ if (replaycache_add_test_and_elapsed(data->replay_cache, encrypted_section,
+ encrypted_section_len, &elapsed)) {
+ log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the"
+ "same ENCRYPTED section was seen %ld seconds ago. "
+ "Dropping cell.", (long int) elapsed);
+ goto done;
+ }
+
+ /* Build the key material out of the key material found in the cell. */
+ intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp,
+ data->subcredential,
+ encrypted_section,
+ &data->client_pk);
+ if (intro_keys == NULL) {
+ log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to "
+ "compute key material on circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto done;
+ }
+
+ /* Validate MAC from the cell and our computed key material. The MAC field
+ * in the cell is at the end of the encrypted section. */
+ {
+ uint8_t mac[DIGEST256_LEN];
+ /* The MAC field is at the very end of the ENCRYPTED section. */
+ size_t mac_offset = encrypted_section_len - sizeof(mac);
+ /* Compute the MAC. Use the entire encoded payload with a length up to the
+ * ENCRYPTED section. */
+ compute_introduce_mac(data->payload,
+ data->payload_len - encrypted_section_len,
+ encrypted_section, encrypted_section_len,
+ intro_keys->mac_key, sizeof(intro_keys->mac_key),
+ mac, sizeof(mac));
+ if (tor_memcmp(mac, encrypted_section + mac_offset, sizeof(mac))) {
+ log_info(LD_REND, "Invalid MAC validation for INTRODUCE2 cell on "
+ "circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto done;
+ }
+ }
+
+ {
+ /* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */
+ const uint8_t *encrypted_data =
+ encrypted_section + sizeof(data->client_pk);
+ /* It's symmetric encryption so it's correct to use the ENCRYPTED length
+ * for decryption. Computes the length of ENCRYPTED_DATA meaning removing
+ * the CLIENT_PK and MAC length. */
+ size_t encrypted_data_len =
+ encrypted_section_len - (sizeof(data->client_pk) + DIGEST256_LEN);
+
+ /* This decrypts the ENCRYPTED_DATA section of the cell. */
+ decrypted = decrypt_introduce2(intro_keys->enc_key,
+ encrypted_data, encrypted_data_len);
+ if (decrypted == NULL) {
+ log_info(LD_REND, "Unable to decrypt the ENCRYPTED section of an "
+ "INTRODUCE2 cell on circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto done;
+ }
+
+ /* Parse this blob into an encrypted cell structure so we can then extract
+ * the data we need out of it. */
+ enc_cell = parse_introduce2_encrypted(decrypted, encrypted_data_len,
+ circ, service);
+ memwipe(decrypted, 0, encrypted_data_len);
+ if (enc_cell == NULL) {
+ goto done;
+ }
+ }
+
+ /* XXX: Implement client authorization checks. */
+
+ /* Extract onion key and rendezvous cookie from the cell used for the
+ * rendezvous point circuit e2e encryption. */
+ memcpy(data->onion_pk.public_key,
+ trn_cell_introduce_encrypted_getconstarray_onion_key(enc_cell),
+ CURVE25519_PUBKEY_LEN);
+ memcpy(data->rendezvous_cookie,
+ trn_cell_introduce_encrypted_getconstarray_rend_cookie(enc_cell),
+ sizeof(data->rendezvous_cookie));
+
+ /* Extract rendezvous link specifiers. */
+ for (size_t idx = 0;
+ idx < trn_cell_introduce_encrypted_get_nspec(enc_cell); idx++) {
+ link_specifier_t *lspec =
+ trn_cell_introduce_encrypted_get_nspecs(enc_cell, idx);
+ smartlist_add(data->link_specifiers, hs_link_specifier_dup(lspec));
+ }
+
+ /* Success. */
+ ret = 0;
+ log_info(LD_REND, "Valid INTRODUCE2 cell. Launching rendezvous circuit.");
+
+ done:
+ if (intro_keys) {
+ memwipe(intro_keys, 0, sizeof(hs_ntor_intro_cell_keys_t));
+ tor_free(intro_keys);
+ }
+ tor_free(decrypted);
+ trn_cell_introduce_encrypted_free(enc_cell);
+ trn_cell_introduce1_free(cell);
+ return ret;
+}
+
+/* Build a RENDEZVOUS1 cell with the given rendezvous cookie and handshake
+ * info. The encoded cell is put in cell_out and the length of the data is
+ * returned. This can't fail. */
+ssize_t
+hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
+ size_t rendezvous_cookie_len,
+ const uint8_t *rendezvous_handshake_info,
+ size_t rendezvous_handshake_info_len,
+ uint8_t *cell_out)
+{
+ ssize_t cell_len;
+ trn_cell_rendezvous1_t *cell;
+
+ tor_assert(rendezvous_cookie);
+ tor_assert(rendezvous_handshake_info);
+ tor_assert(cell_out);
+
+ cell = trn_cell_rendezvous1_new();
+ /* Set the RENDEZVOUS_COOKIE. */
+ memcpy(trn_cell_rendezvous1_getarray_rendezvous_cookie(cell),
+ rendezvous_cookie, rendezvous_cookie_len);
+ /* Set the HANDSHAKE_INFO. */
+ trn_cell_rendezvous1_setlen_handshake_info(cell,
+ rendezvous_handshake_info_len);
+ memcpy(trn_cell_rendezvous1_getarray_handshake_info(cell),
+ rendezvous_handshake_info, rendezvous_handshake_info_len);
+ /* Encoding. */
+ cell_len = trn_cell_rendezvous1_encode(cell_out, RELAY_PAYLOAD_SIZE, cell);
+ tor_assert(cell_len > 0);
+
+ trn_cell_rendezvous1_free(cell);
+ return cell_len;
+}
+
+/* Build an INTRODUCE1 cell from the given data. The encoded cell is put in
+ * cell_out which must be of at least size RELAY_PAYLOAD_SIZE. On success, the
+ * encoded length is returned else a negative value and the content of
+ * cell_out should be ignored. */
+ssize_t
+hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
+ uint8_t *cell_out)
+{
+ ssize_t cell_len;
+ trn_cell_introduce1_t *cell;
+ trn_cell_extension_t *ext;
+
+ tor_assert(data);
+ tor_assert(cell_out);
+
+ cell = trn_cell_introduce1_new();
+ tor_assert(cell);
+
+ /* Set extension data. None are used. */
+ ext = trn_cell_extension_new();
+ tor_assert(ext);
+ trn_cell_extension_set_num(ext, 0);
+ trn_cell_introduce1_set_extensions(cell, ext);
+
+ /* Set the legacy ID field. */
+ introduce1_set_legacy_id(cell, data);
+
+ /* Set the authentication key. */
+ introduce1_set_auth_key(cell, data);
+
+ /* Set the encrypted section. This will set, encrypt and encode the
+ * ENCRYPTED section in the cell. After this, we'll be ready to encode. */
+ introduce1_set_encrypted(cell, data);
+
+ /* Final encoding. */
+ cell_len = trn_cell_introduce1_encode(cell_out, RELAY_PAYLOAD_SIZE, cell);
+
+ trn_cell_introduce1_free(cell);
+ return cell_len;
+}
+
+/* Build an ESTABLISH_RENDEZVOUS cell from the given rendezvous_cookie. The
+ * encoded cell is put in cell_out which must be of at least
+ * RELAY_PAYLOAD_SIZE. On success, the encoded length is returned and the
+ * caller should clear up the content of the cell.
+ *
+ * This function can't fail. */
+ssize_t
+hs_cell_build_establish_rendezvous(const uint8_t *rendezvous_cookie,
+ uint8_t *cell_out)
+{
+ tor_assert(rendezvous_cookie);
+ tor_assert(cell_out);
+
+ memcpy(cell_out, rendezvous_cookie, HS_REND_COOKIE_LEN);
+ return HS_REND_COOKIE_LEN;
+}
+
+/* Handle an INTRODUCE_ACK cell encoded in payload of length payload_len.
+ * Return the status code on success else a negative value if the cell as not
+ * decodable. */
+int
+hs_cell_parse_introduce_ack(const uint8_t *payload, size_t payload_len)
+{
+ int ret = -1;
+ trn_cell_introduce_ack_t *cell = NULL;
+
+ tor_assert(payload);
+
+ /* If it is a legacy IP, rend-spec.txt specifies that a ACK is 0 byte and a
+ * NACK is 1 byte. We can't use the legacy function for this so we have to
+ * do a special case. */
+ if (payload_len <= 1) {
+ if (payload_len == 0) {
+ ret = HS_CELL_INTRO_ACK_SUCCESS;
+ } else {
+ ret = HS_CELL_INTRO_ACK_FAILURE;
+ }
+ goto end;
+ }
+
+ if (trn_cell_introduce_ack_parse(&cell, payload, payload_len) < 0) {
+ log_info(LD_REND, "Invalid INTRODUCE_ACK cell. Unable to parse it.");
+ goto end;
+ }
+
+ ret = trn_cell_introduce_ack_get_status(cell);
+
+ end:
+ trn_cell_introduce_ack_free(cell);
+ return ret;
+}
+
+/* Handle a RENDEZVOUS2 cell encoded in payload of length payload_len. On
+ * success, handshake_info contains the data in the HANDSHAKE_INFO field, and
+ * 0 is returned. On error, a negative value is returned. */
+int
+hs_cell_parse_rendezvous2(const uint8_t *payload, size_t payload_len,
+ uint8_t *handshake_info, size_t handshake_info_len)
+{
+ int ret = -1;
+ trn_cell_rendezvous2_t *cell = NULL;
+
+ tor_assert(payload);
+ tor_assert(handshake_info);
+
+ if (trn_cell_rendezvous2_parse(&cell, payload, payload_len) < 0) {
+ log_info(LD_REND, "Invalid RENDEZVOUS2 cell. Unable to parse it.");
+ goto end;
+ }
+
+ /* Static size, we should never have an issue with this else we messed up
+ * our code flow. */
+ tor_assert(trn_cell_rendezvous2_getlen_handshake_info(cell) ==
+ handshake_info_len);
+ memcpy(handshake_info,
+ trn_cell_rendezvous2_getconstarray_handshake_info(cell),
+ handshake_info_len);
+ ret = 0;
+
+ end:
+ trn_cell_rendezvous2_free(cell);
+ return ret;
+}
+
+/* Clear the given INTRODUCE1 data structure data. */
+void
+hs_cell_introduce1_data_clear(hs_cell_introduce1_data_t *data)
+{
+ if (data == NULL) {
+ return;
+ }
+ /* Object in this list have been moved to the cell object when building it
+ * so they've been freed earlier. We do that in order to avoid duplicating
+ * them leading to more memory and CPU time being used for nothing. */
+ smartlist_free(data->link_specifiers);
+ /* The data object has no ownership of any members. */
+ memwipe(data, 0, sizeof(hs_cell_introduce1_data_t));
+}
+
diff --git a/src/or/hs_cell.h b/src/or/hs_cell.h
new file mode 100644
index 0000000000..958dde4ffc
--- /dev/null
+++ b/src/or/hs_cell.h
@@ -0,0 +1,122 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_cell.h
+ * \brief Header file containing cell data for the whole HS subsytem.
+ **/
+
+#ifndef TOR_HS_CELL_H
+#define TOR_HS_CELL_H
+
+#include "or.h"
+#include "hs_service.h"
+
+/* An INTRODUCE1 cell requires at least this amount of bytes (see section
+ * 3.2.2 of the specification). Below this value, the cell must be padded. */
+#define HS_CELL_INTRODUCE1_MIN_SIZE 246
+
+/* Status code of an INTRODUCE_ACK cell. */
+typedef enum {
+ HS_CELL_INTRO_ACK_SUCCESS = 0x0000, /* Cell relayed to service. */
+ HS_CELL_INTRO_ACK_FAILURE = 0x0001, /* Service ID not recognized */
+ HS_CELL_INTRO_ACK_BADFMT = 0x0002, /* Bad message format */
+ HS_CELL_INTRO_ACK_NORELAY = 0x0003, /* Can't relay cell to service */
+} hs_cell_introd_ack_status_t;
+
+/* Onion key type found in the INTRODUCE1 cell. */
+typedef enum {
+ HS_CELL_ONION_KEY_TYPE_NTOR = 1,
+} hs_cell_onion_key_type_t;
+
+/* This data structure contains data that we need to build an INTRODUCE1 cell
+ * used by the INTRODUCE1 build function. */
+typedef struct hs_cell_introduce1_data_t {
+ /* Is this a legacy introduction point? */
+ unsigned int is_legacy : 1;
+ /* (Legacy only) The encryption key for a legacy intro point. Only set if
+ * is_legacy is true. */
+ const crypto_pk_t *legacy_key;
+ /* Introduction point authentication public key. */
+ const ed25519_public_key_t *auth_pk;
+ /* Introduction point encryption public key. */
+ const curve25519_public_key_t *enc_pk;
+ /* Subcredentials of the service. */
+ const uint8_t *subcredential;
+ /* Onion public key for the ntor handshake. */
+ const curve25519_public_key_t *onion_pk;
+ /* Rendezvous cookie. */
+ const uint8_t *rendezvous_cookie;
+ /* Public key put before the encrypted data (CLIENT_PK). */
+ const curve25519_keypair_t *client_kp;
+ /* Rendezvous point link specifiers. */
+ smartlist_t *link_specifiers;
+} hs_cell_introduce1_data_t;
+
+/* This data structure contains data that we need to parse an INTRODUCE2 cell
+ * which is used by the INTRODUCE2 cell parsing function. On a successful
+ * parsing, the onion_pk and rendezvous_cookie will be populated with the
+ * computed key material from the cell data. This structure is only used during
+ * INTRO2 parsing and discarded after that. */
+typedef struct hs_cell_introduce2_data_t {
+ /*** Immutable Section: Set on structure init. ***/
+
+ /* Introduction point authentication public key. Pointer owned by the
+ introduction point object through which we received the INTRO2 cell. */
+ const ed25519_public_key_t *auth_pk;
+ /* Introduction point encryption keypair for the ntor handshake. Pointer
+ owned by the introduction point object through which we received the
+ INTRO2 cell*/
+ const curve25519_keypair_t *enc_kp;
+ /* Subcredentials of the service. Pointer owned by the descriptor that owns
+ the introduction point through which we received the INTRO2 cell. */
+ const uint8_t *subcredential;
+ /* Payload of the received encoded cell. */
+ const uint8_t *payload;
+ /* Size of the payload of the received encoded cell. */
+ size_t payload_len;
+
+ /*** Mutable Section: Set upon parsing INTRODUCE2 cell. ***/
+
+ /* Onion public key computed using the INTRODUCE2 encrypted section. */
+ curve25519_public_key_t onion_pk;
+ /* Rendezvous cookie taken from the INTRODUCE2 encrypted section. */
+ uint8_t rendezvous_cookie[REND_COOKIE_LEN];
+ /* Client public key from the INTRODUCE2 encrypted section. */
+ curve25519_public_key_t client_pk;
+ /* Link specifiers of the rendezvous point. Contains link_specifier_t. */
+ smartlist_t *link_specifiers;
+ /* Replay cache of the introduction point. */
+ replaycache_t *replay_cache;
+} hs_cell_introduce2_data_t;
+
+/* Build cell API. */
+ssize_t hs_cell_build_establish_intro(const char *circ_nonce,
+ const hs_service_intro_point_t *ip,
+ uint8_t *cell_out);
+ssize_t hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
+ size_t rendezvous_cookie_len,
+ const uint8_t *rendezvous_handshake_info,
+ size_t rendezvous_handshake_info_len,
+ uint8_t *cell_out);
+ssize_t hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
+ uint8_t *cell_out);
+ssize_t hs_cell_build_establish_rendezvous(const uint8_t *rendezvous_cookie,
+ uint8_t *cell_out);
+
+/* Parse cell API. */
+ssize_t hs_cell_parse_intro_established(const uint8_t *payload,
+ size_t payload_len);
+ssize_t hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
+ const origin_circuit_t *circ,
+ const hs_service_t *service);
+int hs_cell_parse_introduce_ack(const uint8_t *payload, size_t payload_len);
+int hs_cell_parse_rendezvous2(const uint8_t *payload, size_t payload_len,
+ uint8_t *handshake_info,
+ size_t handshake_info_len);
+
+/* Util API. */
+void hs_cell_introduce1_data_clear(hs_cell_introduce1_data_t *data);
+
+#endif /* !defined(TOR_HS_CELL_H) */
+
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
new file mode 100644
index 0000000000..66c59e0dc7
--- /dev/null
+++ b/src/or/hs_circuit.c
@@ -0,0 +1,1213 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_circuit.c
+ **/
+
+#define HS_CIRCUIT_PRIVATE
+
+#include "or.h"
+#include "circpathbias.h"
+#include "circuitbuild.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "config.h"
+#include "policies.h"
+#include "relay.h"
+#include "rendservice.h"
+#include "rephist.h"
+#include "router.h"
+
+#include "hs_cell.h"
+#include "hs_ident.h"
+#include "hs_ntor.h"
+#include "hs_service.h"
+#include "hs_circuit.h"
+
+/* Trunnel. */
+#include "ed25519_cert.h"
+#include "hs/cell_common.h"
+#include "hs/cell_establish_intro.h"
+
+/* A circuit is about to become an e2e rendezvous circuit. Check
+ * <b>circ_purpose</b> and ensure that it's properly set. Return true iff
+ * circuit purpose is properly set, otherwise return false. */
+static int
+circuit_purpose_is_correct_for_rend(unsigned int circ_purpose,
+ int is_service_side)
+{
+ if (is_service_side) {
+ if (circ_purpose != CIRCUIT_PURPOSE_S_CONNECT_REND) {
+ log_warn(LD_BUG,
+ "HS e2e circuit setup with wrong purpose (%d)", circ_purpose);
+ return 0;
+ }
+ }
+
+ if (!is_service_side) {
+ if (circ_purpose != CIRCUIT_PURPOSE_C_REND_READY &&
+ circ_purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) {
+ log_warn(LD_BUG,
+ "Client e2e circuit setup with wrong purpose (%d)", circ_purpose);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Create and return a crypt path for the final hop of a v3 prop224 rendezvous
+ * circuit. Initialize the crypt path crypto using the output material from the
+ * ntor key exchange at <b>ntor_key_seed</b>.
+ *
+ * If <b>is_service_side</b> is set, we are the hidden service and the final
+ * hop of the rendezvous circuit is the client on the other side. */
+static crypt_path_t *
+create_rend_cpath(const uint8_t *ntor_key_seed, size_t seed_len,
+ int is_service_side)
+{
+ uint8_t keys[HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN];
+ crypt_path_t *cpath = NULL;
+
+ /* Do the key expansion */
+ if (hs_ntor_circuit_key_expansion(ntor_key_seed, seed_len,
+ keys, sizeof(keys)) < 0) {
+ goto err;
+ }
+
+ /* Setup the cpath */
+ cpath = tor_malloc_zero(sizeof(crypt_path_t));
+ cpath->magic = CRYPT_PATH_MAGIC;
+
+ if (circuit_init_cpath_crypto(cpath, (char*)keys, sizeof(keys),
+ is_service_side, 1) < 0) {
+ tor_free(cpath);
+ goto err;
+ }
+
+ err:
+ memwipe(keys, 0, sizeof(keys));
+ return cpath;
+}
+
+/* We are a v2 legacy HS client: Create and return a crypt path for the hidden
+ * service on the other side of the rendezvous circuit <b>circ</b>. Initialize
+ * the crypt path crypto using the body of the RENDEZVOUS1 cell at
+ * <b>rend_cell_body</b> (which must be at least DH_KEY_LEN+DIGEST_LEN bytes).
+ */
+static crypt_path_t *
+create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body)
+{
+ crypt_path_t *hop = NULL;
+ char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
+
+ /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh
+ * handshake...*/
+ tor_assert(circ->build_state);
+ tor_assert(circ->build_state->pending_final_cpath);
+ hop = circ->build_state->pending_final_cpath;
+
+ tor_assert(hop->rend_dh_handshake_state);
+ if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, hop->rend_dh_handshake_state,
+ (char*)rend_cell_body, DH_KEY_LEN,
+ keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
+ log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
+ goto err;
+ }
+ /* ... and set up cpath. */
+ if (circuit_init_cpath_crypto(hop,
+ keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
+ 0, 0) < 0)
+ goto err;
+
+ /* Check whether the digest is right... */
+ if (tor_memneq(keys, rend_cell_body+DH_KEY_LEN, DIGEST_LEN)) {
+ log_warn(LD_PROTOCOL, "Incorrect digest of key material.");
+ goto err;
+ }
+
+ /* clean up the crypto stuff we just made */
+ crypto_dh_free(hop->rend_dh_handshake_state);
+ hop->rend_dh_handshake_state = NULL;
+
+ goto done;
+
+ err:
+ hop = NULL;
+
+ done:
+ memwipe(keys, 0, sizeof(keys));
+ return hop;
+}
+
+/* Append the final <b>hop</b> to the cpath of the rend <b>circ</b>, and mark
+ * <b>circ</b> ready for use to transfer HS relay cells. */
+static void
+finalize_rend_circuit(origin_circuit_t *circ, crypt_path_t *hop,
+ int is_service_side)
+{
+ tor_assert(circ);
+ tor_assert(hop);
+
+ /* Notify the circuit state machine that we are splicing this circuit */
+ int new_circ_purpose = is_service_side ?
+ CIRCUIT_PURPOSE_S_REND_JOINED : CIRCUIT_PURPOSE_C_REND_JOINED;
+ circuit_change_purpose(TO_CIRCUIT(circ), new_circ_purpose);
+
+ /* All is well. Extend the circuit. */
+ hop->state = CPATH_STATE_OPEN;
+ /* Set the windows to default. */
+ hop->package_window = circuit_initial_package_window();
+ hop->deliver_window = CIRCWINDOW_START;
+
+ /* Now that this circuit has finished connecting to its destination,
+ * make sure circuit_get_open_circ_or_launch is willing to return it
+ * so we can actually use it. */
+ circ->hs_circ_has_timed_out = 0;
+
+ /* Append the hop to the cpath of this circuit */
+ onion_append_to_cpath(&circ->cpath, hop);
+
+ /* In legacy code, 'pending_final_cpath' points to the final hop we just
+ * appended to the cpath. We set the original pointer to NULL so that we
+ * don't double free it. */
+ if (circ->build_state) {
+ circ->build_state->pending_final_cpath = NULL;
+ }
+
+ /* Finally, mark circuit as ready to be used for client streams */
+ if (!is_service_side) {
+ circuit_try_attaching_streams(circ);
+ }
+}
+
+/* For a given circuit and a service introduction point object, register the
+ * intro circuit to the circuitmap. This supports legacy intro point. */
+static void
+register_intro_circ(const hs_service_intro_point_t *ip,
+ origin_circuit_t *circ)
+{
+ tor_assert(ip);
+ tor_assert(circ);
+
+ if (ip->base.is_only_legacy) {
+ uint8_t digest[DIGEST_LEN];
+ if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) {
+ return;
+ }
+ hs_circuitmap_register_intro_circ_v2_service_side(circ, digest);
+ } else {
+ hs_circuitmap_register_intro_circ_v3_service_side(circ,
+ &ip->auth_key_kp.pubkey);
+ }
+}
+
+/* Return the number of opened introduction circuit for the given circuit that
+ * is matching its identity key. */
+static unsigned int
+count_opened_desc_intro_point_circuits(const hs_service_t *service,
+ const hs_service_descriptor_t *desc)
+{
+ unsigned int count = 0;
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ DIGEST256MAP_FOREACH(desc->intro_points.map, key,
+ const hs_service_intro_point_t *, ip) {
+ const circuit_t *circ;
+ const origin_circuit_t *ocirc = hs_circ_service_get_intro_circ(ip);
+ if (ocirc == NULL) {
+ continue;
+ }
+ circ = TO_CIRCUIT(ocirc);
+ tor_assert(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+ circ->purpose == CIRCUIT_PURPOSE_S_INTRO);
+ /* Having a circuit not for the requested service is really bad. */
+ tor_assert(ed25519_pubkey_eq(&service->keys.identity_pk,
+ &ocirc->hs_ident->identity_pk));
+ /* Only count opened circuit and skip circuit that will be closed. */
+ if (!circ->marked_for_close && circ->state == CIRCUIT_STATE_OPEN) {
+ count++;
+ }
+ } DIGEST256MAP_FOREACH_END;
+ return count;
+}
+
+/* From a given service, rendezvous cookie and handshake info, create a
+ * rendezvous point circuit identifier. This can't fail. */
+STATIC hs_ident_circuit_t *
+create_rp_circuit_identifier(const hs_service_t *service,
+ const uint8_t *rendezvous_cookie,
+ const curve25519_public_key_t *server_pk,
+ const hs_ntor_rend_cell_keys_t *keys)
+{
+ hs_ident_circuit_t *ident;
+ uint8_t handshake_info[CURVE25519_PUBKEY_LEN + DIGEST256_LEN];
+
+ tor_assert(service);
+ tor_assert(rendezvous_cookie);
+ tor_assert(server_pk);
+ tor_assert(keys);
+
+ ident = hs_ident_circuit_new(&service->keys.identity_pk,
+ HS_IDENT_CIRCUIT_RENDEZVOUS);
+ /* Copy the RENDEZVOUS_COOKIE which is the unique identifier. */
+ memcpy(ident->rendezvous_cookie, rendezvous_cookie,
+ sizeof(ident->rendezvous_cookie));
+ /* Build the HANDSHAKE_INFO which looks like this:
+ * SERVER_PK [32 bytes]
+ * AUTH_INPUT_MAC [32 bytes]
+ */
+ memcpy(handshake_info, server_pk->public_key, CURVE25519_PUBKEY_LEN);
+ memcpy(handshake_info + CURVE25519_PUBKEY_LEN, keys->rend_cell_auth_mac,
+ DIGEST256_LEN);
+ tor_assert(sizeof(ident->rendezvous_handshake_info) ==
+ sizeof(handshake_info));
+ memcpy(ident->rendezvous_handshake_info, handshake_info,
+ sizeof(ident->rendezvous_handshake_info));
+ /* Finally copy the NTOR_KEY_SEED for e2e encryption on the circuit. */
+ tor_assert(sizeof(ident->rendezvous_ntor_key_seed) ==
+ sizeof(keys->ntor_key_seed));
+ memcpy(ident->rendezvous_ntor_key_seed, keys->ntor_key_seed,
+ sizeof(ident->rendezvous_ntor_key_seed));
+ return ident;
+}
+
+/* From a given service and service intro point, create an introduction point
+ * circuit identifier. This can't fail. */
+static hs_ident_circuit_t *
+create_intro_circuit_identifier(const hs_service_t *service,
+ const hs_service_intro_point_t *ip)
+{
+ hs_ident_circuit_t *ident;
+
+ tor_assert(service);
+ tor_assert(ip);
+
+ ident = hs_ident_circuit_new(&service->keys.identity_pk,
+ HS_IDENT_CIRCUIT_INTRO);
+ ed25519_pubkey_copy(&ident->intro_auth_pk, &ip->auth_key_kp.pubkey);
+
+ return ident;
+}
+
+/* For a given introduction point and an introduction circuit, send the
+ * ESTABLISH_INTRO cell. The service object is used for logging. This can fail
+ * and if so, the circuit is closed and the intro point object is flagged
+ * that the circuit is not established anymore which is important for the
+ * retry mechanism. */
+static void
+send_establish_intro(const hs_service_t *service,
+ hs_service_intro_point_t *ip, origin_circuit_t *circ)
+{
+ ssize_t cell_len;
+ uint8_t payload[RELAY_PAYLOAD_SIZE];
+
+ tor_assert(service);
+ tor_assert(ip);
+ tor_assert(circ);
+
+ /* Encode establish intro cell. */
+ cell_len = hs_cell_build_establish_intro(circ->cpath->prev->rend_circ_nonce,
+ ip, payload);
+ if (cell_len < 0) {
+ log_warn(LD_REND, "Unable to encode ESTABLISH_INTRO cell for service %s "
+ "on circuit %u. Closing circuit.",
+ safe_str_client(service->onion_address),
+ TO_CIRCUIT(circ)->n_circ_id);
+ goto err;
+ }
+
+ /* Send the cell on the circuit. */
+ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ),
+ RELAY_COMMAND_ESTABLISH_INTRO,
+ (char *) payload, cell_len,
+ circ->cpath->prev) < 0) {
+ log_info(LD_REND, "Unable to send ESTABLISH_INTRO cell for service %s "
+ "on circuit %u.",
+ safe_str_client(service->onion_address),
+ TO_CIRCUIT(circ)->n_circ_id);
+ /* On error, the circuit has been closed. */
+ goto done;
+ }
+
+ /* Record the attempt to use this circuit. */
+ pathbias_count_use_attempt(circ);
+ goto done;
+
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
+ done:
+ memwipe(payload, 0, sizeof(payload));
+}
+
+/* Return a string constant describing the anonymity of service. */
+static const char *
+get_service_anonymity_string(const hs_service_t *service)
+{
+ if (service->config.is_single_onion) {
+ return "single onion";
+ } else {
+ return "hidden";
+ }
+}
+
+/* For a given service, the ntor onion key and a rendezvous cookie, launch a
+ * circuit to the rendezvous point specified by the link specifiers. On
+ * success, a circuit identifier is attached to the circuit with the needed
+ * data. This function will try to open a circuit for a maximum value of
+ * MAX_REND_FAILURES then it will give up. */
+static void
+launch_rendezvous_point_circuit(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ const hs_cell_introduce2_data_t *data)
+{
+ int circ_needs_uptime;
+ time_t now = time(NULL);
+ extend_info_t *info = NULL;
+ origin_circuit_t *circ;
+
+ tor_assert(service);
+ tor_assert(ip);
+ tor_assert(data);
+
+ circ_needs_uptime = hs_service_requires_uptime_circ(service->config.ports);
+
+ /* Get the extend info data structure for the chosen rendezvous point
+ * specified by the given link specifiers. */
+ info = hs_get_extend_info_from_lspecs(data->link_specifiers,
+ &data->onion_pk,
+ service->config.is_single_onion);
+ if (info == NULL) {
+ /* We are done here, we can't extend to the rendezvous point.
+ * If you're running an IPv6-only v3 single onion service on 0.3.2 or with
+ * 0.3.2 clients, and somehow disable the option check, it will fail here.
+ */
+ log_fn(LOG_PROTOCOL_WARN, LD_REND,
+ "Not enough info to open a circuit to a rendezvous point for "
+ "%s service %s.",
+ get_service_anonymity_string(service),
+ safe_str_client(service->onion_address));
+ goto end;
+ }
+
+ for (int i = 0; i < MAX_REND_FAILURES; i++) {
+ int circ_flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
+ if (circ_needs_uptime) {
+ circ_flags |= CIRCLAUNCH_NEED_UPTIME;
+ }
+ /* Firewall and policies are checked when getting the extend info. */
+ if (service->config.is_single_onion) {
+ circ_flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
+ }
+
+ circ = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND, info,
+ circ_flags);
+ if (circ != NULL) {
+ /* Stop retrying, we have a circuit! */
+ break;
+ }
+ }
+ if (circ == NULL) {
+ log_warn(LD_REND, "Giving up on launching a rendezvous circuit to %s "
+ "for %s service %s",
+ safe_str_client(extend_info_describe(info)),
+ get_service_anonymity_string(service),
+ safe_str_client(service->onion_address));
+ goto end;
+ }
+ log_info(LD_REND, "Rendezvous circuit launched to %s with cookie %s "
+ "for %s service %s",
+ safe_str_client(extend_info_describe(info)),
+ safe_str_client(hex_str((const char *) data->rendezvous_cookie,
+ REND_COOKIE_LEN)),
+ get_service_anonymity_string(service),
+ safe_str_client(service->onion_address));
+ tor_assert(circ->build_state);
+ /* Rendezvous circuit have a specific timeout for the time spent on trying
+ * to connect to the rendezvous point. */
+ circ->build_state->expiry_time = now + MAX_REND_TIMEOUT;
+
+ /* Create circuit identifier and key material. */
+ {
+ hs_ntor_rend_cell_keys_t keys;
+ curve25519_keypair_t ephemeral_kp;
+ /* No need for extra strong, this is only for this circuit life time. This
+ * key will be used for the RENDEZVOUS1 cell that will be sent on the
+ * circuit once opened. */
+ curve25519_keypair_generate(&ephemeral_kp, 0);
+ if (hs_ntor_service_get_rendezvous1_keys(&ip->auth_key_kp.pubkey,
+ &ip->enc_key_kp,
+ &ephemeral_kp, &data->client_pk,
+ &keys) < 0) {
+ /* This should not really happened but just in case, don't make tor
+ * freak out, close the circuit and move on. */
+ log_info(LD_REND, "Unable to get RENDEZVOUS1 key material for "
+ "service %s",
+ safe_str_client(service->onion_address));
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
+ goto end;
+ }
+ circ->hs_ident = create_rp_circuit_identifier(service,
+ data->rendezvous_cookie,
+ &ephemeral_kp.pubkey, &keys);
+ memwipe(&ephemeral_kp, 0, sizeof(ephemeral_kp));
+ memwipe(&keys, 0, sizeof(keys));
+ tor_assert(circ->hs_ident);
+ }
+
+ end:
+ extend_info_free(info);
+}
+
+/* Return true iff the given service rendezvous circuit circ is allowed for a
+ * relaunch to the rendezvous point. */
+static int
+can_relaunch_service_rendezvous_point(const origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ /* This is initialized when allocating an origin circuit. */
+ tor_assert(circ->build_state);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
+
+ /* XXX: Retrying under certain condition. This is related to #22455. */
+
+ /* Avoid to relaunch twice a circuit to the same rendezvous point at the
+ * same time. */
+ if (circ->hs_service_side_rend_circ_has_been_relaunched) {
+ log_info(LD_REND, "Rendezvous circuit to %s has already been retried. "
+ "Skipping retry.",
+ safe_str_client(
+ extend_info_describe(circ->build_state->chosen_exit)));
+ goto disallow;
+ }
+
+ /* We check failure_count >= hs_get_service_max_rend_failures()-1 below, and
+ * the -1 is because we increment the failure count for our current failure
+ * *after* this clause. */
+ int max_rend_failures = hs_get_service_max_rend_failures() - 1;
+
+ /* A failure count that has reached maximum allowed or circuit that expired,
+ * we skip relaunching. */
+ if (circ->build_state->failure_count > max_rend_failures ||
+ circ->build_state->expiry_time <= time(NULL)) {
+ log_info(LD_REND, "Attempt to build a rendezvous circuit to %s has "
+ "failed with %d attempts and expiry time %ld. "
+ "Giving up building.",
+ safe_str_client(
+ extend_info_describe(circ->build_state->chosen_exit)),
+ circ->build_state->failure_count,
+ (long int) circ->build_state->expiry_time);
+ goto disallow;
+ }
+
+ /* Allowed to relaunch. */
+ return 1;
+ disallow:
+ return 0;
+}
+
+/* Retry the rendezvous point of circ by launching a new circuit to it. */
+static void
+retry_service_rendezvous_point(const origin_circuit_t *circ)
+{
+ int flags = 0;
+ origin_circuit_t *new_circ;
+ cpath_build_state_t *bstate;
+
+ tor_assert(circ);
+ /* This is initialized when allocating an origin circuit. */
+ tor_assert(circ->build_state);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
+
+ /* Ease our life. */
+ bstate = circ->build_state;
+
+ log_info(LD_REND, "Retrying rendezvous point circuit to %s",
+ safe_str_client(extend_info_describe(bstate->chosen_exit)));
+
+ /* Get the current build state flags for the next circuit. */
+ flags |= (bstate->need_uptime) ? CIRCLAUNCH_NEED_UPTIME : 0;
+ flags |= (bstate->need_capacity) ? CIRCLAUNCH_NEED_CAPACITY : 0;
+ flags |= (bstate->is_internal) ? CIRCLAUNCH_IS_INTERNAL : 0;
+
+ /* We do NOT add the onehop tunnel flag even though it might be a single
+ * onion service. The reason is that if we failed once to connect to the RP
+ * with a direct connection, we consider that chances are that we will fail
+ * again so try a 3-hop circuit and hope for the best. Because the service
+ * has no anonymity (single onion), this change of behavior won't affect
+ * security directly. */
+
+ new_circ = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND,
+ bstate->chosen_exit, flags);
+ if (new_circ == NULL) {
+ log_warn(LD_REND, "Failed to launch rendezvous circuit to %s",
+ safe_str_client(extend_info_describe(bstate->chosen_exit)));
+ goto done;
+ }
+
+ /* Transfer build state information to the new circuit state in part to
+ * catch any other failures. */
+ new_circ->build_state->failure_count = bstate->failure_count+1;
+ new_circ->build_state->expiry_time = bstate->expiry_time;
+ new_circ->hs_ident = hs_ident_circuit_dup(circ->hs_ident);
+
+ done:
+ return;
+}
+
+/* Using an extend info object ei, set all possible link specifiers in lspecs.
+ * legacy ID is mandatory thus MUST be present in ei. If IPv4 is not present,
+ * logs a BUG() warning, and returns an empty smartlist. Clients never make
+ * direct connections to rendezvous points, so they should always have an
+ * IPv4 address in ei. */
+static void
+get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs)
+{
+ link_specifier_t *ls;
+
+ tor_assert(ei);
+ tor_assert(lspecs);
+
+ /* We require IPv4, we will add IPv6 support in a later tor version */
+ if (BUG(!tor_addr_is_v4(&ei->addr))) {
+ return;
+ }
+
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr));
+ link_specifier_set_un_ipv4_port(ls, ei->port);
+ /* Four bytes IPv4 and two bytes port. */
+ link_specifier_set_ls_len(ls, sizeof(ei->addr.addr.in_addr) +
+ sizeof(ei->port));
+ smartlist_add(lspecs, ls);
+
+ /* Legacy ID is mandatory. */
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_LEGACY_ID);
+ memcpy(link_specifier_getarray_un_legacy_id(ls), ei->identity_digest,
+ link_specifier_getlen_un_legacy_id(ls));
+ link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls));
+ smartlist_add(lspecs, ls);
+
+ /* ed25519 ID is only included if the extend_info has it. */
+ if (!ed25519_public_key_is_zero(&ei->ed_identity)) {
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_ED25519_ID);
+ memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity,
+ link_specifier_getlen_un_ed25519_id(ls));
+ link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls));
+ smartlist_add(lspecs, ls);
+ }
+}
+
+/* Using the given descriptor intro point ip, the extend information of the
+ * rendezvous point rp_ei and the service's subcredential, populate the
+ * already allocated intro1_data object with the needed key material and link
+ * specifiers.
+ *
+ * This can't fail but the ip MUST be a valid object containing the needed
+ * keys and authentication method. */
+static void
+setup_introduce1_data(const hs_desc_intro_point_t *ip,
+ const extend_info_t *rp_ei,
+ const uint8_t *subcredential,
+ hs_cell_introduce1_data_t *intro1_data)
+{
+ smartlist_t *rp_lspecs;
+
+ tor_assert(ip);
+ tor_assert(rp_ei);
+ tor_assert(subcredential);
+ tor_assert(intro1_data);
+
+ /* Build the link specifiers from the extend information of the rendezvous
+ * circuit that we've picked previously. */
+ rp_lspecs = smartlist_new();
+ get_lspecs_from_extend_info(rp_ei, rp_lspecs);
+
+ /* Populate the introduce1 data object. */
+ memset(intro1_data, 0, sizeof(hs_cell_introduce1_data_t));
+ if (ip->legacy.key != NULL) {
+ intro1_data->is_legacy = 1;
+ intro1_data->legacy_key = ip->legacy.key;
+ }
+ intro1_data->auth_pk = &ip->auth_key_cert->signed_key;
+ intro1_data->enc_pk = &ip->enc_key;
+ intro1_data->subcredential = subcredential;
+ intro1_data->onion_pk = &rp_ei->curve25519_onion_key;
+ intro1_data->link_specifiers = rp_lspecs;
+}
+
+/* ========== */
+/* Public API */
+/* ========== */
+
+/* Return an introduction point circuit matching the given intro point object.
+ * NULL is returned is no such circuit can be found. */
+origin_circuit_t *
+hs_circ_service_get_intro_circ(const hs_service_intro_point_t *ip)
+{
+ origin_circuit_t *circ = NULL;
+
+ tor_assert(ip);
+
+ if (ip->base.is_only_legacy) {
+ uint8_t digest[DIGEST_LEN];
+ if (BUG(crypto_pk_get_digest(ip->legacy_key, (char *) digest) < 0)) {
+ goto end;
+ }
+ circ = hs_circuitmap_get_intro_circ_v2_service_side(digest);
+ } else {
+ circ = hs_circuitmap_get_intro_circ_v3_service_side(
+ &ip->auth_key_kp.pubkey);
+ }
+ end:
+ return circ;
+}
+
+/* Called when we fail building a rendezvous circuit at some point other than
+ * the last hop: launches a new circuit to the same rendezvous point. This
+ * supports legacy service.
+ *
+ * We currently relaunch connections to rendezvous points if:
+ * - A rendezvous circuit timed out before connecting to RP.
+ * - The redenzvous circuit failed to connect to the RP.
+ *
+ * We avoid relaunching a connection to this rendezvous point if:
+ * - We have already tried MAX_REND_FAILURES times to connect to this RP.
+ * - We've been trying to connect to this RP for more than MAX_REND_TIMEOUT
+ * seconds
+ * - We've already retried this specific rendezvous circuit.
+ */
+void
+hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
+
+ /* Check if we are allowed to relaunch to the rendezvous point of circ. */
+ if (!can_relaunch_service_rendezvous_point(circ)) {
+ goto done;
+ }
+
+ /* Flag the circuit that we are relaunching so to avoid to relaunch twice a
+ * circuit to the same rendezvous point at the same time. */
+ circ->hs_service_side_rend_circ_has_been_relaunched = 1;
+
+ /* Legacy service don't have an hidden service ident. */
+ if (circ->hs_ident) {
+ retry_service_rendezvous_point(circ);
+ } else {
+ rend_service_relaunch_rendezvous(circ);
+ }
+
+ done:
+ return;
+}
+
+/* For a given service and a service intro point, launch a circuit to the
+ * extend info ei. If the service is a single onion, a one-hop circuit will be
+ * requested. Return 0 if the circuit was successfully launched and tagged
+ * with the correct identifier. On error, a negative value is returned. */
+int
+hs_circ_launch_intro_point(hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ extend_info_t *ei)
+{
+ /* Standard flags for introduction circuit. */
+ int ret = -1, circ_flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ origin_circuit_t *circ;
+
+ tor_assert(service);
+ tor_assert(ip);
+ tor_assert(ei);
+
+ /* Update circuit flags in case of a single onion service that requires a
+ * direct connection. */
+ if (service->config.is_single_onion) {
+ circ_flags |= CIRCLAUNCH_ONEHOP_TUNNEL;
+ }
+
+ log_info(LD_REND, "Launching a circuit to intro point %s for service %s.",
+ safe_str_client(extend_info_describe(ei)),
+ safe_str_client(service->onion_address));
+
+ /* Note down the launch for the retry period. Even if the circuit fails to
+ * be launched, we still want to respect the retry period to avoid stress on
+ * the circuit subsystem. */
+ service->state.num_intro_circ_launched++;
+ circ = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
+ ei, circ_flags);
+ if (circ == NULL) {
+ goto end;
+ }
+
+ /* Setup the circuit identifier and attach it to it. */
+ circ->hs_ident = create_intro_circuit_identifier(service, ip);
+ tor_assert(circ->hs_ident);
+ /* Register circuit in the global circuitmap. */
+ register_intro_circ(ip, circ);
+
+ /* Success. */
+ ret = 0;
+ end:
+ return ret;
+}
+
+/* Called when a service introduction point circuit is done building. Given
+ * the service and intro point object, this function will send the
+ * ESTABLISH_INTRO cell on the circuit. Return 0 on success. Return 1 if the
+ * circuit has been repurposed to General because we already have too many
+ * opened. */
+int
+hs_circ_service_intro_has_opened(hs_service_t *service,
+ hs_service_intro_point_t *ip,
+ const hs_service_descriptor_t *desc,
+ origin_circuit_t *circ)
+{
+ int ret = 0;
+ unsigned int num_intro_circ, num_needed_circ;
+
+ tor_assert(service);
+ tor_assert(ip);
+ tor_assert(desc);
+ tor_assert(circ);
+
+ /* Cound opened circuits that have sent ESTABLISH_INTRO cells or are already
+ * established introduction circuits */
+ num_intro_circ = count_opened_desc_intro_point_circuits(service, desc);
+ num_needed_circ = service->config.num_intro_points;
+ if (num_intro_circ > num_needed_circ) {
+ /* There are too many opened valid intro circuit for what the service
+ * needs so repurpose this one. */
+
+ /* XXX: Legacy code checks options->ExcludeNodes and if not NULL it just
+ * closes the circuit. I have NO idea why it does that so it hasn't been
+ * added here. I can only assume in case our ExcludeNodes list changes but
+ * in that case, all circuit are flagged unusable (config.c). --dgoulet */
+
+ log_info(LD_CIRC | LD_REND, "Introduction circuit just opened but we "
+ "have enough for service %s. Repurposing "
+ "it to general and leaving internal.",
+ safe_str_client(service->onion_address));
+ tor_assert(circ->build_state->is_internal);
+ /* Remove it from the circuitmap. */
+ hs_circuitmap_remove_circuit(TO_CIRCUIT(circ));
+ /* Cleaning up the hidden service identifier and repurpose. */
+ hs_ident_circuit_free(circ->hs_ident);
+ circ->hs_ident = NULL;
+ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_GENERAL);
+ /* Inform that this circuit just opened for this new purpose. */
+ circuit_has_opened(circ);
+ /* This return value indicate to the caller that the IP object should be
+ * removed from the service because it's corresponding circuit has just
+ * been repurposed. */
+ ret = 1;
+ goto done;
+ }
+
+ log_info(LD_REND, "Introduction circuit %u established for service %s.",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ circuit_log_path(LOG_INFO, LD_REND, circ);
+
+ /* Time to send an ESTABLISH_INTRO cell on this circuit. On error, this call
+ * makes sure the circuit gets closed. */
+ send_establish_intro(service, ip, circ);
+
+ done:
+ return ret;
+}
+
+/* Called when a service rendezvous point circuit is done building. Given the
+ * service and the circuit, this function will send a RENDEZVOUS1 cell on the
+ * circuit using the information in the circuit identifier. If the cell can't
+ * be sent, the circuit is closed. */
+void
+hs_circ_service_rp_has_opened(const hs_service_t *service,
+ origin_circuit_t *circ)
+{
+ size_t payload_len;
+ uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
+
+ tor_assert(service);
+ tor_assert(circ);
+ tor_assert(circ->hs_ident);
+
+ /* Some useful logging. */
+ log_info(LD_REND, "Rendezvous circuit %u has opened with cookie %s "
+ "for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ hex_str((const char *) circ->hs_ident->rendezvous_cookie,
+ REND_COOKIE_LEN),
+ safe_str_client(service->onion_address));
+ circuit_log_path(LOG_INFO, LD_REND, circ);
+
+ /* This can't fail. */
+ payload_len = hs_cell_build_rendezvous1(
+ circ->hs_ident->rendezvous_cookie,
+ sizeof(circ->hs_ident->rendezvous_cookie),
+ circ->hs_ident->rendezvous_handshake_info,
+ sizeof(circ->hs_ident->rendezvous_handshake_info),
+ payload);
+
+ /* Pad the payload with random bytes so it matches the size of a legacy cell
+ * which is normally always bigger. Also, the size of a legacy cell is
+ * always smaller than the RELAY_PAYLOAD_SIZE so this is safe. */
+ if (payload_len < HS_LEGACY_RENDEZVOUS_CELL_SIZE) {
+ crypto_rand((char *) payload + payload_len,
+ HS_LEGACY_RENDEZVOUS_CELL_SIZE - payload_len);
+ payload_len = HS_LEGACY_RENDEZVOUS_CELL_SIZE;
+ }
+
+ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ),
+ RELAY_COMMAND_RENDEZVOUS1,
+ (const char *) payload, payload_len,
+ circ->cpath->prev) < 0) {
+ /* On error, circuit is closed. */
+ log_warn(LD_REND, "Unable to send RENDEZVOUS1 cell on circuit %u "
+ "for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto done;
+ }
+
+ /* Setup end-to-end rendezvous circuit between the client and us. */
+ if (hs_circuit_setup_e2e_rend_circ(circ,
+ circ->hs_ident->rendezvous_ntor_key_seed,
+ sizeof(circ->hs_ident->rendezvous_ntor_key_seed),
+ 1) < 0) {
+ log_warn(LD_GENERAL, "Failed to setup circ");
+ goto done;
+ }
+
+ done:
+ memwipe(payload, 0, sizeof(payload));
+}
+
+/* Circ has been expecting an INTRO_ESTABLISHED cell that just arrived. Handle
+ * the INTRO_ESTABLISHED cell payload of length payload_len arriving on the
+ * given introduction circuit circ. The service is only used for logging
+ * purposes. Return 0 on success else a negative value. */
+int
+hs_circ_handle_intro_established(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ origin_circuit_t *circ,
+ const uint8_t *payload, size_t payload_len)
+{
+ int ret = -1;
+
+ tor_assert(service);
+ tor_assert(ip);
+ tor_assert(circ);
+ tor_assert(payload);
+
+ if (BUG(TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)) {
+ goto done;
+ }
+
+ /* Try to parse the payload into a cell making sure we do actually have a
+ * valid cell. For a legacy node, it's an empty payload so as long as we
+ * have the cell, we are good. */
+ if (!ip->base.is_only_legacy &&
+ hs_cell_parse_intro_established(payload, payload_len) < 0) {
+ log_warn(LD_REND, "Unable to parse the INTRO_ESTABLISHED cell on "
+ "circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto done;
+ }
+
+ /* Switch the purpose to a fully working intro point. */
+ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_S_INTRO);
+ /* Getting a valid INTRODUCE_ESTABLISHED means we've successfully used the
+ * circuit so update our pathbias subsystem. */
+ pathbias_mark_use_success(circ);
+ /* Success. */
+ ret = 0;
+
+ done:
+ return ret;
+}
+
+/* We just received an INTRODUCE2 cell on the established introduction circuit
+ * circ. Handle the INTRODUCE2 payload of size payload_len for the given
+ * circuit and service. This cell is associated with the intro point object ip
+ * and the subcredential. Return 0 on success else a negative value. */
+int
+hs_circ_handle_introduce2(const hs_service_t *service,
+ const origin_circuit_t *circ,
+ hs_service_intro_point_t *ip,
+ const uint8_t *subcredential,
+ const uint8_t *payload, size_t payload_len)
+{
+ int ret = -1;
+ time_t elapsed;
+ hs_cell_introduce2_data_t data;
+
+ tor_assert(service);
+ tor_assert(circ);
+ tor_assert(ip);
+ tor_assert(subcredential);
+ tor_assert(payload);
+
+ /* Populate the data structure with everything we need for the cell to be
+ * parsed, decrypted and key material computed correctly. */
+ data.auth_pk = &ip->auth_key_kp.pubkey;
+ data.enc_kp = &ip->enc_key_kp;
+ data.subcredential = subcredential;
+ data.payload = payload;
+ data.payload_len = payload_len;
+ data.link_specifiers = smartlist_new();
+ data.replay_cache = ip->replay_cache;
+
+ if (hs_cell_parse_introduce2(&data, circ, service) < 0) {
+ goto done;
+ }
+
+ /* Check whether we've seen this REND_COOKIE before to detect repeats. */
+ if (replaycache_add_test_and_elapsed(
+ service->state.replay_cache_rend_cookie,
+ data.rendezvous_cookie, sizeof(data.rendezvous_cookie),
+ &elapsed)) {
+ /* A Tor client will send a new INTRODUCE1 cell with the same REND_COOKIE
+ * as its previous one if its intro circ times out while in state
+ * CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT. If we received the first
+ * INTRODUCE1 cell (the intro-point relay converts it into an INTRODUCE2
+ * cell), we are already trying to connect to that rend point (and may
+ * have already succeeded); drop this cell. */
+ log_info(LD_REND, "We received an INTRODUCE2 cell with same REND_COOKIE "
+ "field %ld seconds ago. Dropping cell.",
+ (long int) elapsed);
+ goto done;
+ }
+
+ /* At this point, we just confirmed that the full INTRODUCE2 cell is valid
+ * so increment our counter that we've seen one on this intro point. */
+ ip->introduce2_count++;
+
+ /* Launch rendezvous circuit with the onion key and rend cookie. */
+ launch_rendezvous_point_circuit(service, ip, &data);
+ /* Success. */
+ ret = 0;
+
+ done:
+ SMARTLIST_FOREACH(data.link_specifiers, link_specifier_t *, lspec,
+ link_specifier_free(lspec));
+ smartlist_free(data.link_specifiers);
+ memwipe(&data, 0, sizeof(data));
+ return ret;
+}
+
+/* Circuit <b>circ</b> just finished the rend ntor key exchange. Use the key
+ * exchange output material at <b>ntor_key_seed</b> and setup <b>circ</b> to
+ * serve as a rendezvous end-to-end circuit between the client and the
+ * service. If <b>is_service_side</b> is set, then we are the hidden service
+ * and the other side is the client.
+ *
+ * Return 0 if the operation went well; in case of error return -1. */
+int
+hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
+ const uint8_t *ntor_key_seed, size_t seed_len,
+ int is_service_side)
+{
+ if (BUG(!circuit_purpose_is_correct_for_rend(TO_CIRCUIT(circ)->purpose,
+ is_service_side))) {
+ return -1;
+ }
+
+ crypt_path_t *hop = create_rend_cpath(ntor_key_seed, seed_len,
+ is_service_side);
+ if (!hop) {
+ log_warn(LD_REND, "Couldn't get v3 %s cpath!",
+ is_service_side ? "service-side" : "client-side");
+ return -1;
+ }
+
+ finalize_rend_circuit(circ, hop, is_service_side);
+
+ return 0;
+}
+
+/* We are a v2 legacy HS client and we just received a RENDEZVOUS1 cell
+ * <b>rend_cell_body</b> on <b>circ</b>. Finish up the DH key exchange and then
+ * extend the crypt path of <b>circ</b> so that the hidden service is on the
+ * other side. */
+int
+hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ,
+ const uint8_t *rend_cell_body)
+{
+
+ if (BUG(!circuit_purpose_is_correct_for_rend(
+ TO_CIRCUIT(circ)->purpose, 0))) {
+ return -1;
+ }
+
+ crypt_path_t *hop = create_rend_cpath_legacy(circ, rend_cell_body);
+ if (!hop) {
+ log_warn(LD_GENERAL, "Couldn't get v2 cpath.");
+ return -1;
+ }
+
+ finalize_rend_circuit(circ, hop, 0);
+
+ return 0;
+}
+
+/* Given the introduction circuit intro_circ, the rendezvous circuit
+ * rend_circ, a descriptor intro point object ip and the service's
+ * subcredential, send an INTRODUCE1 cell on intro_circ.
+ *
+ * This will also setup the circuit identifier on rend_circ containing the key
+ * material for the handshake and e2e encryption. Return 0 on success else
+ * negative value. Because relay_send_command_from_edge() closes the circuit
+ * on error, it is possible that intro_circ is closed on error. */
+int
+hs_circ_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ,
+ const hs_desc_intro_point_t *ip,
+ const uint8_t *subcredential)
+{
+ int ret = -1;
+ ssize_t payload_len;
+ uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
+ hs_cell_introduce1_data_t intro1_data;
+
+ tor_assert(intro_circ);
+ tor_assert(rend_circ);
+ tor_assert(ip);
+ tor_assert(subcredential);
+
+ /* This takes various objects in order to populate the introduce1 data
+ * object which is used to build the content of the cell. */
+ setup_introduce1_data(ip, rend_circ->build_state->chosen_exit,
+ subcredential, &intro1_data);
+ /* If we didn't get any link specifiers, it's because our extend info was
+ * bad. */
+ if (BUG(!intro1_data.link_specifiers) ||
+ !smartlist_len(intro1_data.link_specifiers)) {
+ log_warn(LD_REND, "Unable to get link specifiers for INTRODUCE1 cell on "
+ "circuit %u.", TO_CIRCUIT(intro_circ)->n_circ_id);
+ goto done;
+ }
+
+ /* Final step before we encode a cell, we setup the circuit identifier which
+ * will generate both the rendezvous cookie and client keypair for this
+ * connection. Those are put in the ident. */
+ intro1_data.rendezvous_cookie = rend_circ->hs_ident->rendezvous_cookie;
+ intro1_data.client_kp = &rend_circ->hs_ident->rendezvous_client_kp;
+
+ memcpy(intro_circ->hs_ident->rendezvous_cookie,
+ rend_circ->hs_ident->rendezvous_cookie,
+ sizeof(intro_circ->hs_ident->rendezvous_cookie));
+
+ /* From the introduce1 data object, this will encode the INTRODUCE1 cell
+ * into payload which is then ready to be sent as is. */
+ payload_len = hs_cell_build_introduce1(&intro1_data, payload);
+ if (BUG(payload_len < 0)) {
+ goto done;
+ }
+
+ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(intro_circ),
+ RELAY_COMMAND_INTRODUCE1,
+ (const char *) payload, payload_len,
+ intro_circ->cpath->prev) < 0) {
+ /* On error, circuit is closed. */
+ log_warn(LD_REND, "Unable to send INTRODUCE1 cell on circuit %u.",
+ TO_CIRCUIT(intro_circ)->n_circ_id);
+ goto done;
+ }
+
+ /* Success. */
+ ret = 0;
+ goto done;
+
+ done:
+ hs_cell_introduce1_data_clear(&intro1_data);
+ memwipe(payload, 0, sizeof(payload));
+ return ret;
+}
+
+/* Send an ESTABLISH_RENDEZVOUS cell along the rendezvous circuit circ. On
+ * success, 0 is returned else -1 and the circuit is marked for close. */
+int
+hs_circ_send_establish_rendezvous(origin_circuit_t *circ)
+{
+ ssize_t cell_len = 0;
+ uint8_t cell[RELAY_PAYLOAD_SIZE] = {0};
+
+ tor_assert(circ);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+
+ log_info(LD_REND, "Send an ESTABLISH_RENDEZVOUS cell on circuit %u",
+ TO_CIRCUIT(circ)->n_circ_id);
+
+ /* Set timestamp_dirty, because circuit_expire_building expects it,
+ * and the rend cookie also means we've used the circ. */
+ TO_CIRCUIT(circ)->timestamp_dirty = time(NULL);
+
+ /* We've attempted to use this circuit. Probe it if we fail */
+ pathbias_count_use_attempt(circ);
+
+ /* Generate the RENDEZVOUS_COOKIE and place it in the identifier so we can
+ * complete the handshake when receiving the acknowledgement. */
+ crypto_rand((char *) circ->hs_ident->rendezvous_cookie, HS_REND_COOKIE_LEN);
+ /* Generate the client keypair. No need to be extra strong, not long term */
+ curve25519_keypair_generate(&circ->hs_ident->rendezvous_client_kp, 0);
+
+ cell_len =
+ hs_cell_build_establish_rendezvous(circ->hs_ident->rendezvous_cookie,
+ cell);
+ if (BUG(cell_len < 0)) {
+ goto err;
+ }
+
+ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ),
+ RELAY_COMMAND_ESTABLISH_RENDEZVOUS,
+ (const char *) cell, cell_len,
+ circ->cpath->prev) < 0) {
+ /* Circuit has been marked for close */
+ log_warn(LD_REND, "Unable to send ESTABLISH_RENDEZVOUS cell on "
+ "circuit %u", TO_CIRCUIT(circ)->n_circ_id);
+ memwipe(cell, 0, cell_len);
+ goto err;
+ }
+
+ memwipe(cell, 0, cell_len);
+ return 0;
+ err:
+ return -1;
+}
+
+/* We are about to close or free this <b>circ</b>. Clean it up from any
+ * related HS data structures. This function can be called multiple times
+ * safely for the same circuit. */
+void
+hs_circ_cleanup(circuit_t *circ)
+{
+ tor_assert(circ);
+
+ /* If it's a service-side intro circ, notify the HS subsystem for the intro
+ * point circuit closing so it can be dealt with cleanly. */
+ if (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+ circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
+ hs_service_intro_circ_has_closed(TO_ORIGIN_CIRCUIT(circ));
+ }
+
+ /* Clear HS circuitmap token for this circ (if any). Very important to be
+ * done after the HS subsystem has been notified of the close else the
+ * circuit will not be found.
+ *
+ * We do this at the close if possible because from that point on, the
+ * circuit is good as dead. We can't rely on removing it in the circuit
+ * free() function because we open a race window between the close and free
+ * where we can't register a new circuit for the same intro point. */
+ if (circ->hs_token) {
+ hs_circuitmap_remove_circuit(circ);
+ }
+}
+
diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h
new file mode 100644
index 0000000000..63ff5e463c
--- /dev/null
+++ b/src/or/hs_circuit.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_circuit.h
+ * \brief Header file containing circuit data for the whole HS subsytem.
+ **/
+
+#ifndef TOR_HS_CIRCUIT_H
+#define TOR_HS_CIRCUIT_H
+
+#include "or.h"
+#include "crypto.h"
+#include "crypto_ed25519.h"
+
+#include "hs_service.h"
+
+/* Cleanup function when the circuit is closed or/and freed. */
+void hs_circ_cleanup(circuit_t *circ);
+
+/* Circuit API. */
+int hs_circ_service_intro_has_opened(hs_service_t *service,
+ hs_service_intro_point_t *ip,
+ const hs_service_descriptor_t *desc,
+ origin_circuit_t *circ);
+void hs_circ_service_rp_has_opened(const hs_service_t *service,
+ origin_circuit_t *circ);
+int hs_circ_launch_intro_point(hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ extend_info_t *ei);
+int hs_circ_launch_rendezvous_point(const hs_service_t *service,
+ const curve25519_public_key_t *onion_key,
+ const uint8_t *rendezvous_cookie);
+void hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ);
+
+origin_circuit_t *hs_circ_service_get_intro_circ(
+ const hs_service_intro_point_t *ip);
+
+/* Cell API. */
+int hs_circ_handle_intro_established(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len);
+int hs_circ_handle_introduce2(const hs_service_t *service,
+ const origin_circuit_t *circ,
+ hs_service_intro_point_t *ip,
+ const uint8_t *subcredential,
+ const uint8_t *payload, size_t payload_len);
+int hs_circ_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ,
+ const hs_desc_intro_point_t *ip,
+ const uint8_t *subcredential);
+int hs_circ_send_establish_rendezvous(origin_circuit_t *circ);
+
+/* e2e circuit API. */
+
+int hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
+ const uint8_t *ntor_key_seed,
+ size_t seed_len,
+ int is_service_side);
+int hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ,
+ const uint8_t *rend_cell_body);
+
+#ifdef HS_CIRCUIT_PRIVATE
+
+STATIC hs_ident_circuit_t *
+create_rp_circuit_identifier(const hs_service_t *service,
+ const uint8_t *rendezvous_cookie,
+ const curve25519_public_key_t *server_pk,
+ const hs_ntor_rend_cell_keys_t *keys);
+
+#endif
+
+#endif /* !defined(TOR_HS_CIRCUIT_H) */
+
diff --git a/src/or/hs_circuitmap.c b/src/or/hs_circuitmap.c
index ea66fb5194..97d6053e9b 100644
--- a/src/or/hs_circuitmap.c
+++ b/src/or/hs_circuitmap.c
@@ -5,8 +5,10 @@
* \file hs_circuitmap.c
*
* \brief Hidden service circuitmap: A hash table that maps binary tokens to
- * introduction and rendezvous circuits; it's used both by relays acting as
- * intro points and rendezvous points, and also by hidden services themselves.
+ * introduction and rendezvous circuits; it's used:
+ * (a) by relays acting as intro points and rendezvous points
+ * (b) by hidden services to find intro and rend circuits and
+ * (c) by HS clients to find rendezvous circuits.
**/
#define HS_CIRCUITMAP_PRIVATE
@@ -85,7 +87,7 @@ get_hs_circuitmap(void)
return the_hs_circuitmap;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
/****************** HS circuitmap utility functions **************************/
@@ -404,6 +406,55 @@ hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie)
return circ;
}
+/* Public function: Return client-side rendezvous circuit with rendezvous
+ * <b>cookie</b>. It will look for circuits with the following purposes:
+
+ * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received
+ * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for
+ * INTRODUCE_ACK from intro point.
+ *
+ * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and
+ * introduce circuit acked. Waiting for RENDEZVOUS2 from service.
+ *
+ * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received
+ * RENDEZVOUS2 from service.
+ *
+ * d) CIRCUIT_PURPOSE_C_ESTABLISH_REND: Rend circuit open but not yet
+ * established.
+ *
+ * Return NULL if no such circuit is found in the circuitmap. */
+origin_circuit_t *
+hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie)
+{
+ origin_circuit_t *circ = NULL;
+
+ circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
+ REND_TOKEN_LEN, cookie,
+ CIRCUIT_PURPOSE_C_REND_READY);
+ if (circ) {
+ return circ;
+ }
+
+ circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
+ REND_TOKEN_LEN, cookie,
+ CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
+ if (circ) {
+ return circ;
+ }
+
+ circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
+ REND_TOKEN_LEN, cookie,
+ CIRCUIT_PURPOSE_C_REND_JOINED);
+ if (circ) {
+ return circ;
+ }
+
+ circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
+ REND_TOKEN_LEN, cookie,
+ CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+ return circ;
+}
+
/**** Public servide-side setters: */
/* Public function: Register v2 intro circuit with key <b>digest</b> to the
@@ -439,6 +490,21 @@ hs_circuitmap_register_rend_circ_service_side(origin_circuit_t *circ,
REND_TOKEN_LEN, cookie);
}
+/* Public function: Register rendezvous circuit with key <b>cookie</b> to the
+ * client-side circuitmap. */
+void
+hs_circuitmap_register_rend_circ_client_side(origin_circuit_t *or_circ,
+ const uint8_t *cookie)
+{
+ circuit_t *circ = TO_CIRCUIT(or_circ);
+ { /* Basic circ purpose sanity checking */
+ tor_assert_nonfatal(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+ }
+
+ hs_circuitmap_register_circuit(circ, HS_TOKEN_REND_CLIENT_SIDE,
+ REND_TOKEN_LEN, cookie);
+}
+
/**** Misc public functions: */
/** Public function: Remove this circuit from the HS circuitmap. Clear its HS
diff --git a/src/or/hs_circuitmap.h b/src/or/hs_circuitmap.h
index 33d5b64117..43b2947c17 100644
--- a/src/or/hs_circuitmap.h
+++ b/src/or/hs_circuitmap.h
@@ -43,6 +43,8 @@ struct origin_circuit_t *
hs_circuitmap_get_intro_circ_v2_service_side(const uint8_t *digest);
struct origin_circuit_t *
hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie);
+struct origin_circuit_t *
+hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie);
void hs_circuitmap_register_intro_circ_v2_service_side(
struct origin_circuit_t *circ,
@@ -53,6 +55,9 @@ void hs_circuitmap_register_intro_circ_v3_service_side(
void hs_circuitmap_register_rend_circ_service_side(
struct origin_circuit_t *circ,
const uint8_t *cookie);
+void hs_circuitmap_register_rend_circ_client_side(
+ struct origin_circuit_t *circ,
+ const uint8_t *cookie);
void hs_circuitmap_remove_circuit(struct circuit_t *circ);
@@ -76,6 +81,9 @@ typedef enum {
HS_TOKEN_INTRO_V2_SERVICE_SIDE,
/** A v3 introduction point pubkey on a hidden service (256bit) */
HS_TOKEN_INTRO_V3_SERVICE_SIDE,
+
+ /** A rendezvous cookie on the client side (128bit) */
+ HS_TOKEN_REND_CLIENT_SIDE,
} hs_token_type_t;
/** Represents a token used in the HS protocol. Each such token maps to a
@@ -91,7 +99,7 @@ struct hs_token_s {
uint8_t *token;
};
-#endif /* HS_CIRCUITMAP_PRIVATE */
+#endif /* defined(HS_CIRCUITMAP_PRIVATE) */
#ifdef TOR_UNIT_TESTS
@@ -99,5 +107,5 @@ hs_circuitmap_ht *get_hs_circuitmap(void);
#endif /* TOR_UNIT_TESTS */
-#endif /* TOR_HS_CIRCUITMAP_H */
+#endif /* !defined(TOR_HS_CIRCUITMAP_H) */
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
new file mode 100644
index 0000000000..551cf50554
--- /dev/null
+++ b/src/or/hs_client.c
@@ -0,0 +1,1611 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_client.c
+ * \brief Implement next generation hidden service client functionality
+ **/
+
+#define HS_CLIENT_PRIVATE
+
+#include "or.h"
+#include "hs_circuit.h"
+#include "hs_ident.h"
+#include "connection_edge.h"
+#include "container.h"
+#include "rendclient.h"
+#include "hs_descriptor.h"
+#include "hs_cache.h"
+#include "hs_cell.h"
+#include "hs_ident.h"
+#include "config.h"
+#include "directory.h"
+#include "hs_client.h"
+#include "router.h"
+#include "routerset.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "connection.h"
+#include "nodelist.h"
+#include "circpathbias.h"
+#include "connection.h"
+#include "hs_ntor.h"
+#include "circuitbuild.h"
+#include "networkstatus.h"
+#include "reasons.h"
+
+/* Return a human-readable string for the client fetch status code. */
+static const char *
+fetch_status_to_string(hs_client_fetch_status_t status)
+{
+ switch (status) {
+ case HS_CLIENT_FETCH_ERROR:
+ return "Internal error";
+ case HS_CLIENT_FETCH_LAUNCHED:
+ return "Descriptor fetch launched";
+ case HS_CLIENT_FETCH_HAVE_DESC:
+ return "Already have descriptor";
+ case HS_CLIENT_FETCH_NO_HSDIRS:
+ return "No more HSDir available to query";
+ case HS_CLIENT_FETCH_NOT_ALLOWED:
+ return "Fetching descriptors is not allowed";
+ case HS_CLIENT_FETCH_MISSING_INFO:
+ return "Missing directory information";
+ case HS_CLIENT_FETCH_PENDING:
+ return "Pending descriptor fetch";
+ default:
+ return "(Unknown client fetch status code)";
+ }
+}
+
+/* Return true iff tor should close the SOCKS request(s) for the descriptor
+ * fetch that ended up with this given status code. */
+static int
+fetch_status_should_close_socks(hs_client_fetch_status_t status)
+{
+ switch (status) {
+ case HS_CLIENT_FETCH_NO_HSDIRS:
+ /* No more HSDir to query, we can't complete the SOCKS request(s). */
+ case HS_CLIENT_FETCH_ERROR:
+ /* The fetch triggered an internal error. */
+ case HS_CLIENT_FETCH_NOT_ALLOWED:
+ /* Client is not allowed to fetch (FetchHidServDescriptors 0). */
+ goto close;
+ case HS_CLIENT_FETCH_MISSING_INFO:
+ case HS_CLIENT_FETCH_HAVE_DESC:
+ case HS_CLIENT_FETCH_PENDING:
+ case HS_CLIENT_FETCH_LAUNCHED:
+ /* The rest doesn't require tor to close the SOCKS request(s). */
+ goto no_close;
+ }
+
+ no_close:
+ return 0;
+ close:
+ return 1;
+}
+
+/* Cancel all descriptor fetches currently in progress. */
+static void
+cancel_descriptor_fetches(void)
+{
+ smartlist_t *conns =
+ connection_list_by_type_state(CONN_TYPE_DIR, DIR_PURPOSE_FETCH_HSDESC);
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
+ const hs_ident_dir_conn_t *ident = TO_DIR_CONN(conn)->hs_ident;
+ if (BUG(ident == NULL)) {
+ /* A directory connection fetching a service descriptor can't have an
+ * empty hidden service identifier. */
+ continue;
+ }
+ log_debug(LD_REND, "Marking for close a directory connection fetching "
+ "a hidden service descriptor for service %s.",
+ safe_str_client(ed25519_fmt(&ident->identity_pk)));
+ connection_mark_for_close(conn);
+ } SMARTLIST_FOREACH_END(conn);
+
+ /* No ownership of the objects in this list. */
+ smartlist_free(conns);
+ log_info(LD_REND, "Hidden service client descriptor fetches cancelled.");
+}
+
+/* Get all connections that are waiting on a circuit and flag them back to
+ * waiting for a hidden service descriptor for the given service key
+ * service_identity_pk. */
+static void
+flag_all_conn_wait_desc(const ed25519_public_key_t *service_identity_pk)
+{
+ tor_assert(service_identity_pk);
+
+ smartlist_t *conns =
+ connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_CIRCUIT_WAIT);
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
+ edge_connection_t *edge_conn;
+ if (BUG(!CONN_IS_EDGE(conn))) {
+ continue;
+ }
+ edge_conn = TO_EDGE_CONN(conn);
+ if (edge_conn->hs_ident &&
+ ed25519_pubkey_eq(&edge_conn->hs_ident->identity_pk,
+ service_identity_pk)) {
+ connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
+ conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ smartlist_free(conns);
+}
+
+/* Remove tracked HSDir requests from our history for this hidden service
+ * identity public key. */
+static void
+purge_hid_serv_request(const ed25519_public_key_t *identity_pk)
+{
+ char base64_blinded_pk[ED25519_BASE64_LEN + 1];
+ ed25519_public_key_t blinded_pk;
+
+ tor_assert(identity_pk);
+
+ /* Get blinded pubkey of hidden service. It is possible that we just moved
+ * to a new time period meaning that we won't be able to purge the request
+ * from the previous time period. That is fine because they will expire at
+ * some point and we don't care about those anymore. */
+ hs_build_blinded_pubkey(identity_pk, NULL, 0,
+ hs_get_time_period_num(0), &blinded_pk);
+ if (BUG(ed25519_public_to_base64(base64_blinded_pk, &blinded_pk) < 0)) {
+ return;
+ }
+ /* Purge last hidden service request from cache for this blinded key. */
+ hs_purge_hid_serv_from_last_hid_serv_requests(base64_blinded_pk);
+}
+
+/* Return true iff there is at least one pending directory descriptor request
+ * for the service identity_pk. */
+static int
+directory_request_is_pending(const ed25519_public_key_t *identity_pk)
+{
+ int ret = 0;
+ smartlist_t *conns =
+ connection_list_by_type_purpose(CONN_TYPE_DIR, DIR_PURPOSE_FETCH_HSDESC);
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
+ const hs_ident_dir_conn_t *ident = TO_DIR_CONN(conn)->hs_ident;
+ if (BUG(ident == NULL)) {
+ /* A directory connection fetching a service descriptor can't have an
+ * empty hidden service identifier. */
+ continue;
+ }
+ if (!ed25519_pubkey_eq(identity_pk, &ident->identity_pk)) {
+ continue;
+ }
+ ret = 1;
+ break;
+ } SMARTLIST_FOREACH_END(conn);
+
+ /* No ownership of the objects in this list. */
+ smartlist_free(conns);
+ return ret;
+}
+
+/* We failed to fetch a descriptor for the service with <b>identity_pk</b>
+ * because of <b>status</b>. Find all pending SOCKS connections for this
+ * service that are waiting on the descriptor and close them with
+ * <b>reason</b>. */
+static void
+close_all_socks_conns_waiting_for_desc(const ed25519_public_key_t *identity_pk,
+ hs_client_fetch_status_t status,
+ int reason)
+{
+ unsigned int count = 0;
+ time_t now = approx_time();
+ smartlist_t *conns =
+ connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_RENDDESC_WAIT);
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn);
+ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
+
+ /* Only consider the entry connections that matches the service for which
+ * we tried to get the descriptor */
+ if (!edge_conn->hs_ident ||
+ !ed25519_pubkey_eq(identity_pk,
+ &edge_conn->hs_ident->identity_pk)) {
+ continue;
+ }
+ assert_connection_ok(base_conn, now);
+ /* Unattach the entry connection which will close for the reason. */
+ connection_mark_unattached_ap(entry_conn, reason);
+ count++;
+ } SMARTLIST_FOREACH_END(base_conn);
+
+ if (count > 0) {
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ hs_build_address(identity_pk, HS_VERSION_THREE, onion_address);
+ log_notice(LD_REND, "Closed %u streams for service %s.onion "
+ "for reason %s. Fetch status: %s.",
+ count, safe_str_client(onion_address),
+ stream_end_reason_to_string(reason),
+ fetch_status_to_string(status));
+ }
+
+ /* No ownership of the object(s) in this list. */
+ smartlist_free(conns);
+}
+
+/* Find all pending SOCKS connection waiting for a descriptor and retry them
+ * all. This is called when the directory information changed. */
+static void
+retry_all_socks_conn_waiting_for_desc(void)
+{
+ smartlist_t *conns =
+ connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_RENDDESC_WAIT);
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ hs_client_fetch_status_t status;
+ const edge_connection_t *edge_conn =
+ ENTRY_TO_EDGE_CONN(TO_ENTRY_CONN(base_conn));
+
+ /* Ignore non HS or non v3 connection. */
+ if (edge_conn->hs_ident == NULL) {
+ continue;
+ }
+ /* In this loop, we will possibly try to fetch a descriptor for the
+ * pending connections because we just got more directory information.
+ * However, the refetch process can cleanup all SOCKS request so the same
+ * service if an internal error happens. Thus, we can end up with closed
+ * connections in our list. */
+ if (base_conn->marked_for_close) {
+ continue;
+ }
+
+ /* XXX: There is an optimization we could do which is that for a service
+ * key, we could check if we can fetch and remember that decision. */
+
+ /* Order a refetch in case it works this time. */
+ status = hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk);
+ if (BUG(status == HS_CLIENT_FETCH_HAVE_DESC)) {
+ /* This case is unique because it can NOT happen in theory. Once we get
+ * a new descriptor, the HS client subsystem is notified immediately and
+ * the connections waiting for it are handled which means the state will
+ * change from renddesc wait state. Log this and continue to next
+ * connection. */
+ continue;
+ }
+ /* In the case of an error, either all SOCKS connections have been
+ * closed or we are still missing directory information. Leave the
+ * connection in renddesc wait state so when we get more info, we'll be
+ * able to try it again. */
+ } SMARTLIST_FOREACH_END(base_conn);
+
+ /* We don't have ownership of those objects. */
+ smartlist_free(conns);
+}
+
+/* A v3 HS circuit successfully connected to the hidden service. Update the
+ * stream state at <b>hs_conn_ident</b> appropriately. */
+static void
+note_connection_attempt_succeeded(const hs_ident_edge_conn_t *hs_conn_ident)
+{
+ tor_assert(hs_conn_ident);
+
+ /* Remove from the hid serv cache all requests for that service so we can
+ * query the HSDir again later on for various reasons. */
+ purge_hid_serv_request(&hs_conn_ident->identity_pk);
+
+ /* The v2 subsystem cleans up the intro point time out flag at this stage.
+ * We don't try to do it here because we still need to keep intact the intro
+ * point state for future connections. Even though we are able to connect to
+ * the service, doesn't mean we should reset the timed out intro points.
+ *
+ * It is not possible to have successfully connected to an intro point
+ * present in our cache that was on error or timed out. Every entry in that
+ * cache have a 2 minutes lifetime so ultimately the intro point(s) state
+ * will be reset and thus possible to be retried. */
+}
+
+/* Given the pubkey of a hidden service in <b>onion_identity_pk</b>, fetch its
+ * descriptor by launching a dir connection to <b>hsdir</b>. Return a
+ * hs_client_fetch_status_t status code depending on how it went. */
+static hs_client_fetch_status_t
+directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
+ const routerstatus_t *hsdir)
+{
+ uint64_t current_time_period = hs_get_time_period_num(0);
+ ed25519_public_key_t blinded_pubkey;
+ char base64_blinded_pubkey[ED25519_BASE64_LEN + 1];
+ hs_ident_dir_conn_t hs_conn_dir_ident;
+ int retval;
+
+ tor_assert(hsdir);
+ tor_assert(onion_identity_pk);
+
+ /* Get blinded pubkey */
+ hs_build_blinded_pubkey(onion_identity_pk, NULL, 0,
+ current_time_period, &blinded_pubkey);
+ /* ...and base64 it. */
+ retval = ed25519_public_to_base64(base64_blinded_pubkey, &blinded_pubkey);
+ if (BUG(retval < 0)) {
+ return HS_CLIENT_FETCH_ERROR;
+ }
+
+ /* Copy onion pk to a dir_ident so that we attach it to the dir conn */
+ hs_ident_dir_conn_init(onion_identity_pk, &blinded_pubkey,
+ &hs_conn_dir_ident);
+
+ /* Setup directory request */
+ directory_request_t *req =
+ directory_request_new(DIR_PURPOSE_FETCH_HSDESC);
+ directory_request_set_routerstatus(req, hsdir);
+ directory_request_set_indirection(req, DIRIND_ANONYMOUS);
+ directory_request_set_resource(req, base64_blinded_pubkey);
+ directory_request_fetch_set_hs_ident(req, &hs_conn_dir_ident);
+ directory_initiate_request(req);
+ directory_request_free(req);
+
+ log_info(LD_REND, "Descriptor fetch request for service %s with blinded "
+ "key %s to directory %s",
+ safe_str_client(ed25519_fmt(onion_identity_pk)),
+ safe_str_client(base64_blinded_pubkey),
+ safe_str_client(routerstatus_describe(hsdir)));
+
+ /* Cleanup memory. */
+ memwipe(&blinded_pubkey, 0, sizeof(blinded_pubkey));
+ memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey));
+ memwipe(&hs_conn_dir_ident, 0, sizeof(hs_conn_dir_ident));
+
+ return HS_CLIENT_FETCH_LAUNCHED;
+}
+
+/** Return the HSDir we should use to fetch the descriptor of the hidden
+ * service with identity key <b>onion_identity_pk</b>. */
+STATIC routerstatus_t *
+pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk)
+{
+ int retval;
+ char base64_blinded_pubkey[ED25519_BASE64_LEN + 1];
+ uint64_t current_time_period = hs_get_time_period_num(0);
+ smartlist_t *responsible_hsdirs;
+ ed25519_public_key_t blinded_pubkey;
+ routerstatus_t *hsdir_rs = NULL;
+
+ tor_assert(onion_identity_pk);
+
+ responsible_hsdirs = smartlist_new();
+
+ /* Get blinded pubkey of hidden service */
+ hs_build_blinded_pubkey(onion_identity_pk, NULL, 0,
+ current_time_period, &blinded_pubkey);
+ /* ...and base64 it. */
+ retval = ed25519_public_to_base64(base64_blinded_pubkey, &blinded_pubkey);
+ if (BUG(retval < 0)) {
+ return NULL;
+ }
+
+ /* Get responsible hsdirs of service for this time period */
+ hs_get_responsible_hsdirs(&blinded_pubkey, current_time_period,
+ 0, 1, responsible_hsdirs);
+
+ log_debug(LD_REND, "Found %d responsible HSDirs and about to pick one.",
+ smartlist_len(responsible_hsdirs));
+
+ /* Pick an HSDir from the responsible ones. The ownership of
+ * responsible_hsdirs is given to this function so no need to free it. */
+ hsdir_rs = hs_pick_hsdir(responsible_hsdirs, base64_blinded_pubkey);
+
+ return hsdir_rs;
+}
+
+/** Fetch a v3 descriptor using the given <b>onion_identity_pk</b>.
+ *
+ * On success, HS_CLIENT_FETCH_LAUNCHED is returned. Otherwise, an error from
+ * hs_client_fetch_status_t is returned. */
+MOCK_IMPL(STATIC hs_client_fetch_status_t,
+fetch_v3_desc, (const ed25519_public_key_t *onion_identity_pk))
+{
+ routerstatus_t *hsdir_rs =NULL;
+
+ tor_assert(onion_identity_pk);
+
+ hsdir_rs = pick_hsdir_v3(onion_identity_pk);
+ if (!hsdir_rs) {
+ log_info(LD_REND, "Couldn't pick a v3 hsdir.");
+ return HS_CLIENT_FETCH_NO_HSDIRS;
+ }
+
+ return directory_launch_v3_desc_fetch(onion_identity_pk, hsdir_rs);
+}
+
+/* Make sure that the given v3 origin circuit circ is a valid correct
+ * introduction circuit. This will BUG() on any problems and hard assert if
+ * the anonymity of the circuit is not ok. Return 0 on success else -1 where
+ * the circuit should be mark for closed immediately. */
+static int
+intro_circ_is_ok(const origin_circuit_t *circ)
+{
+ int ret = 0;
+
+ tor_assert(circ);
+
+ if (BUG(TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_INTRODUCING &&
+ TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT &&
+ TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)) {
+ ret = -1;
+ }
+ if (BUG(circ->hs_ident == NULL)) {
+ ret = -1;
+ }
+ if (BUG(!hs_ident_intro_circ_is_valid(circ->hs_ident))) {
+ ret = -1;
+ }
+
+ /* This can stop the tor daemon but we want that since if we don't have
+ * anonymity on this circuit, something went really wrong. */
+ assert_circ_anonymity_ok(circ, get_options());
+ return ret;
+}
+
+/* Find a descriptor intro point object that matches the given ident in the
+ * given descriptor desc. Return NULL if not found. */
+static const hs_desc_intro_point_t *
+find_desc_intro_point_by_ident(const hs_ident_circuit_t *ident,
+ const hs_descriptor_t *desc)
+{
+ const hs_desc_intro_point_t *intro_point = NULL;
+
+ tor_assert(ident);
+ tor_assert(desc);
+
+ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
+ const hs_desc_intro_point_t *, ip) {
+ if (ed25519_pubkey_eq(&ident->intro_auth_pk,
+ &ip->auth_key_cert->signed_key)) {
+ intro_point = ip;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ip);
+
+ return intro_point;
+}
+
+/* Find a descriptor intro point object from the descriptor object desc that
+ * matches the given legacy identity digest in legacy_id. Return NULL if not
+ * found. */
+static hs_desc_intro_point_t *
+find_desc_intro_point_by_legacy_id(const char *legacy_id,
+ const hs_descriptor_t *desc)
+{
+ hs_desc_intro_point_t *ret_ip = NULL;
+
+ tor_assert(legacy_id);
+ tor_assert(desc);
+
+ /* We will go over every intro point and try to find which one is linked to
+ * that circuit. Those lists are small so it's not that expensive. */
+ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
+ hs_desc_intro_point_t *, ip) {
+ SMARTLIST_FOREACH_BEGIN(ip->link_specifiers,
+ const hs_desc_link_specifier_t *, lspec) {
+ /* Not all tor node have an ed25519 identity key so we still rely on the
+ * legacy identity digest. */
+ if (lspec->type != LS_LEGACY_ID) {
+ continue;
+ }
+ if (fast_memneq(legacy_id, lspec->u.legacy_id, DIGEST_LEN)) {
+ break;
+ }
+ /* Found it. */
+ ret_ip = ip;
+ goto end;
+ } SMARTLIST_FOREACH_END(lspec);
+ } SMARTLIST_FOREACH_END(ip);
+
+ end:
+ return ret_ip;
+}
+
+/* Send an INTRODUCE1 cell along the intro circuit and populate the rend
+ * circuit identifier with the needed key material for the e2e encryption.
+ * Return 0 on success, -1 if there is a transient error such that an action
+ * has been taken to recover and -2 if there is a permanent error indicating
+ * that both circuits were closed. */
+static int
+send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ)
+{
+ int status;
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ const ed25519_public_key_t *service_identity_pk = NULL;
+ const hs_desc_intro_point_t *ip;
+
+ tor_assert(rend_circ);
+ if (intro_circ_is_ok(intro_circ) < 0) {
+ goto perm_err;
+ }
+
+ service_identity_pk = &intro_circ->hs_ident->identity_pk;
+ /* For logging purposes. There will be a time where the hs_ident will have a
+ * version number but for now there is none because it's all v3. */
+ hs_build_address(service_identity_pk, HS_VERSION_THREE, onion_address);
+
+ log_info(LD_REND, "Sending INTRODUCE1 cell to service %s on circuit %u",
+ safe_str_client(onion_address), TO_CIRCUIT(intro_circ)->n_circ_id);
+
+ /* 1) Get descriptor from our cache. */
+ const hs_descriptor_t *desc =
+ hs_cache_lookup_as_client(service_identity_pk);
+ if (desc == NULL || !hs_client_any_intro_points_usable(service_identity_pk,
+ desc)) {
+ log_info(LD_REND, "Request to %s %s. Trying to fetch a new descriptor.",
+ safe_str_client(onion_address),
+ (desc) ? "didn't have usable intro points" :
+ "didn't have a descriptor");
+ hs_client_refetch_hsdesc(service_identity_pk);
+ /* We just triggered a refetch, make sure every connections are back
+ * waiting for that descriptor. */
+ flag_all_conn_wait_desc(service_identity_pk);
+ /* We just asked for a refetch so this is a transient error. */
+ goto tran_err;
+ }
+
+ /* We need to find which intro point in the descriptor we are connected to
+ * on intro_circ. */
+ ip = find_desc_intro_point_by_ident(intro_circ->hs_ident, desc);
+ if (BUG(ip == NULL)) {
+ /* If we can find a descriptor from this introduction circuit ident, we
+ * must have a valid intro point object. Permanent error. */
+ goto perm_err;
+ }
+
+ /* Send the INTRODUCE1 cell. */
+ if (hs_circ_send_introduce1(intro_circ, rend_circ, ip,
+ desc->subcredential) < 0) {
+ /* Unable to send the cell, the intro circuit has been marked for close so
+ * this is a permanent error. */
+ tor_assert_nonfatal(TO_CIRCUIT(intro_circ)->marked_for_close);
+ goto perm_err;
+ }
+
+ /* Cell has been sent successfully. Copy the introduction point
+ * authentication and encryption key in the rendezvous circuit identifier so
+ * we can compute the ntor keys when we receive the RENDEZVOUS2 cell. */
+ memcpy(&rend_circ->hs_ident->intro_enc_pk, &ip->enc_key,
+ sizeof(rend_circ->hs_ident->intro_enc_pk));
+ ed25519_pubkey_copy(&rend_circ->hs_ident->intro_auth_pk,
+ &intro_circ->hs_ident->intro_auth_pk);
+
+ /* Now, we wait for an ACK or NAK on this circuit. */
+ circuit_change_purpose(TO_CIRCUIT(intro_circ),
+ CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT);
+ /* Set timestamp_dirty, because circuit_expire_building expects it to
+ * specify when a circuit entered the _C_INTRODUCE_ACK_WAIT state. */
+ TO_CIRCUIT(intro_circ)->timestamp_dirty = time(NULL);
+ pathbias_count_use_attempt(intro_circ);
+
+ /* Success. */
+ status = 0;
+ goto end;
+
+ perm_err:
+ /* Permanent error: it is possible that the intro circuit was closed prior
+ * because we weren't able to send the cell. Make sure we don't double close
+ * it which would result in a warning. */
+ if (!TO_CIRCUIT(intro_circ)->marked_for_close) {
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ), END_CIRC_REASON_INTERNAL);
+ }
+ circuit_mark_for_close(TO_CIRCUIT(rend_circ), END_CIRC_REASON_INTERNAL);
+ status = -2;
+ goto end;
+
+ tran_err:
+ status = -1;
+
+ end:
+ memwipe(onion_address, 0, sizeof(onion_address));
+ return status;
+}
+
+/* Using the introduction circuit circ, setup the authentication key of the
+ * intro point this circuit has extended to. */
+static void
+setup_intro_circ_auth_key(origin_circuit_t *circ)
+{
+ const hs_descriptor_t *desc;
+ const hs_desc_intro_point_t *ip;
+
+ tor_assert(circ);
+
+ desc = hs_cache_lookup_as_client(&circ->hs_ident->identity_pk);
+ if (BUG(desc == NULL)) {
+ /* Opening intro circuit without the descriptor is no good... */
+ goto end;
+ }
+
+ /* We will go over every intro point and try to find which one is linked to
+ * that circuit. Those lists are small so it's not that expensive. */
+ ip = find_desc_intro_point_by_legacy_id(
+ circ->build_state->chosen_exit->identity_digest, desc);
+ if (ip) {
+ /* We got it, copy its authentication key to the identifier. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_cert->signed_key);
+ goto end;
+ }
+
+ /* Reaching this point means we didn't find any intro point for this circuit
+ * which is not suppose to happen. */
+ tor_assert_nonfatal_unreached();
+
+ end:
+ return;
+}
+
+/* Called when an introduction circuit has opened. */
+static void
+client_intro_circ_has_opened(origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+ log_info(LD_REND, "Introduction circuit %u has opened. Attaching streams.",
+ (unsigned int) TO_CIRCUIT(circ)->n_circ_id);
+
+ /* This is an introduction circuit so we'll attach the correct
+ * authentication key to the circuit identifier so it can be identified
+ * properly later on. */
+ setup_intro_circ_auth_key(circ);
+
+ connection_ap_attach_pending(1);
+}
+
+/* Called when a rendezvous circuit has opened. */
+static void
+client_rendezvous_circ_has_opened(origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
+
+ const extend_info_t *rp_ei = circ->build_state->chosen_exit;
+
+ /* Check that we didn't accidentally choose a node that does not understand
+ * the v3 rendezvous protocol */
+ if (rp_ei) {
+ const node_t *rp_node = node_get_by_id(rp_ei->identity_digest);
+ if (rp_node) {
+ if (BUG(!node_supports_v3_rendezvous_point(rp_node))) {
+ return;
+ }
+ }
+ }
+
+ log_info(LD_REND, "Rendezvous circuit has opened to %s.",
+ safe_str_client(extend_info_describe(rp_ei)));
+
+ /* Ignore returned value, nothing we can really do. On failure, the circuit
+ * will be marked for close. */
+ hs_circ_send_establish_rendezvous(circ);
+
+ /* Register rend circuit in circuitmap if it's still alive. */
+ if (!TO_CIRCUIT(circ)->marked_for_close) {
+ hs_circuitmap_register_rend_circ_client_side(circ,
+ circ->hs_ident->rendezvous_cookie);
+ }
+}
+
+/* This is an helper function that convert a descriptor intro point object ip
+ * to a newly allocated extend_info_t object fully initialized. Return NULL if
+ * we can't convert it for which chances are that we are missing or malformed
+ * link specifiers. */
+STATIC extend_info_t *
+desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip)
+{
+ extend_info_t *ei;
+ smartlist_t *lspecs = smartlist_new();
+
+ tor_assert(ip);
+
+ /* We first encode the descriptor link specifiers into the binary
+ * representation which is a trunnel object. */
+ SMARTLIST_FOREACH_BEGIN(ip->link_specifiers,
+ const hs_desc_link_specifier_t *, desc_lspec) {
+ link_specifier_t *lspec = hs_desc_lspec_to_trunnel(desc_lspec);
+ smartlist_add(lspecs, lspec);
+ } SMARTLIST_FOREACH_END(desc_lspec);
+
+ /* Explicitely put the direct connection option to 0 because this is client
+ * side and there is no such thing as a non anonymous client. */
+ ei = hs_get_extend_info_from_lspecs(lspecs, &ip->onion_key, 0);
+
+ SMARTLIST_FOREACH(lspecs, link_specifier_t *, ls, link_specifier_free(ls));
+ smartlist_free(lspecs);
+ return ei;
+}
+
+/* Return true iff the intro point ip for the service service_pk is usable.
+ * This function checks if the intro point is in the client intro state cache
+ * and checks at the failures. It is considered usable if:
+ * - No error happened (INTRO_POINT_FAILURE_GENERIC)
+ * - It is not flagged as timed out (INTRO_POINT_FAILURE_TIMEOUT)
+ * - The unreachable count is lower than
+ * MAX_INTRO_POINT_REACHABILITY_FAILURES (INTRO_POINT_FAILURE_UNREACHABLE)
+ */
+static int
+intro_point_is_usable(const ed25519_public_key_t *service_pk,
+ const hs_desc_intro_point_t *ip)
+{
+ const hs_cache_intro_state_t *state;
+
+ tor_assert(service_pk);
+ tor_assert(ip);
+
+ state = hs_cache_client_intro_state_find(service_pk,
+ &ip->auth_key_cert->signed_key);
+ if (state == NULL) {
+ /* This means we've never encountered any problem thus usable. */
+ goto usable;
+ }
+ if (state->error) {
+ log_info(LD_REND, "Intro point with auth key %s had an error. Not usable",
+ safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key)));
+ goto not_usable;
+ }
+ if (state->timed_out) {
+ log_info(LD_REND, "Intro point with auth key %s timed out. Not usable",
+ safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key)));
+ goto not_usable;
+ }
+ if (state->unreachable_count >= MAX_INTRO_POINT_REACHABILITY_FAILURES) {
+ log_info(LD_REND, "Intro point with auth key %s unreachable. Not usable",
+ safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key)));
+ goto not_usable;
+ }
+
+ usable:
+ return 1;
+ not_usable:
+ return 0;
+}
+
+/* Using a descriptor desc, return a newly allocated extend_info_t object of a
+ * randomly picked introduction point from its list. Return NULL if none are
+ * usable. */
+STATIC extend_info_t *
+client_get_random_intro(const ed25519_public_key_t *service_pk)
+{
+ extend_info_t *ei = NULL, *ei_excluded = NULL;
+ smartlist_t *usable_ips = NULL;
+ const hs_descriptor_t *desc;
+ const hs_desc_encrypted_data_t *enc_data;
+ const or_options_t *options = get_options();
+ /* Calculate the onion address for logging purposes */
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+ tor_assert(service_pk);
+
+ desc = hs_cache_lookup_as_client(service_pk);
+ /* Assume the service is v3 if the descriptor is missing. This is ok,
+ * because we only use the address in log messages */
+ hs_build_address(service_pk,
+ desc ? desc->plaintext_data.version : HS_VERSION_THREE,
+ onion_address);
+ if (desc == NULL || !hs_client_any_intro_points_usable(service_pk,
+ desc)) {
+ log_info(LD_REND, "Unable to randomly select an introduction point "
+ "for service %s because descriptor %s. We can't connect.",
+ safe_str_client(onion_address),
+ (desc) ? "doesn't have any usable intro points"
+ : "is missing (assuming v3 onion address)");
+ goto end;
+ }
+
+ enc_data = &desc->encrypted_data;
+ usable_ips = smartlist_new();
+ smartlist_add_all(usable_ips, enc_data->intro_points);
+ while (smartlist_len(usable_ips) != 0) {
+ int idx;
+ const hs_desc_intro_point_t *ip;
+
+ /* Pick a random intro point and immediately remove it from the usable
+ * list so we don't pick it again if we have to iterate more. */
+ idx = crypto_rand_int(smartlist_len(usable_ips));
+ ip = smartlist_get(usable_ips, idx);
+ smartlist_del(usable_ips, idx);
+
+ /* We need to make sure we have a usable intro points which is in a good
+ * state in our cache. */
+ if (!intro_point_is_usable(service_pk, ip)) {
+ continue;
+ }
+
+ /* Generate an extend info object from the intro point object. */
+ ei = desc_intro_point_to_extend_info(ip);
+ if (ei == NULL) {
+ /* We can get here for instance if the intro point is a private address
+ * and we aren't allowed to extend to those. */
+ log_info(LD_REND, "Unable to select introduction point with auth key %s "
+ "for service %s, because we could not extend to it.",
+ safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key)),
+ safe_str_client(onion_address));
+ continue;
+ }
+
+ /* Test the pick against ExcludeNodes. */
+ if (routerset_contains_extendinfo(options->ExcludeNodes, ei)) {
+ /* If this pick is in the ExcludeNodes list, we keep its reference so if
+ * we ever end up not being able to pick anything else and StrictNodes is
+ * unset, we'll use it. */
+ if (ei_excluded) {
+ /* If something was already here free it. After the loop is gone we
+ * will examine the last excluded intro point, and that's fine since
+ * that's random anyway */
+ extend_info_free(ei_excluded);
+ }
+ ei_excluded = ei;
+ continue;
+ }
+
+ /* Good pick! Let's go with this. */
+ goto end;
+ }
+
+ /* Reaching this point means a couple of things. Either we can't use any of
+ * the intro point listed because the IP address can't be extended to or it
+ * is listed in the ExcludeNodes list. In the later case, if StrictNodes is
+ * set, we are forced to not use anything. */
+ ei = ei_excluded;
+ if (options->StrictNodes) {
+ log_warn(LD_REND, "Every introduction point for service %s is in the "
+ "ExcludeNodes set and StrictNodes is set. We can't connect.",
+ safe_str_client(onion_address));
+ extend_info_free(ei);
+ ei = NULL;
+ } else {
+ log_fn(LOG_PROTOCOL_WARN, LD_REND, "Every introduction point for service "
+ "%s is unusable or we can't extend to it. We can't connect.",
+ safe_str_client(onion_address));
+ }
+
+ end:
+ smartlist_free(usable_ips);
+ memwipe(onion_address, 0, sizeof(onion_address));
+ return ei;
+}
+
+/* For this introduction circuit, we'll look at if we have any usable
+ * introduction point left for this service. If so, we'll use the circuit to
+ * re-extend to a new intro point. Else, we'll close the circuit and its
+ * corresponding rendezvous circuit. Return 0 if we are re-extending else -1
+ * if we are closing the circuits.
+ *
+ * This is called when getting an INTRODUCE_ACK cell with a NACK. */
+static int
+close_or_reextend_intro_circ(origin_circuit_t *intro_circ)
+{
+ int ret = -1;
+ const hs_descriptor_t *desc;
+ origin_circuit_t *rend_circ;
+
+ tor_assert(intro_circ);
+
+ desc = hs_cache_lookup_as_client(&intro_circ->hs_ident->identity_pk);
+ if (BUG(desc == NULL)) {
+ /* We can't continue without a descriptor. */
+ goto close;
+ }
+ /* We still have the descriptor, great! Let's try to see if we can
+ * re-extend by looking up if there are any usable intro points. */
+ if (!hs_client_any_intro_points_usable(&intro_circ->hs_ident->identity_pk,
+ desc)) {
+ goto close;
+ }
+ /* Try to re-extend now. */
+ if (hs_client_reextend_intro_circuit(intro_circ) < 0) {
+ goto close;
+ }
+ /* Success on re-extending. Don't return an error. */
+ ret = 0;
+ goto end;
+
+ close:
+ /* Change the intro circuit purpose before so we don't report an intro point
+ * failure again triggering an extra descriptor fetch. The circuit can
+ * already be closed on failure to re-extend. */
+ if (!TO_CIRCUIT(intro_circ)->marked_for_close) {
+ circuit_change_purpose(TO_CIRCUIT(intro_circ),
+ CIRCUIT_PURPOSE_C_INTRODUCE_ACKED);
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ), END_CIRC_REASON_FINISHED);
+ }
+ /* Close the related rendezvous circuit. */
+ rend_circ = hs_circuitmap_get_rend_circ_client_side(
+ intro_circ->hs_ident->rendezvous_cookie);
+ /* The rendezvous circuit might have collapsed while the INTRODUCE_ACK was
+ * inflight so we can't expect one every time. */
+ if (rend_circ) {
+ circuit_mark_for_close(TO_CIRCUIT(rend_circ), END_CIRC_REASON_FINISHED);
+ }
+
+ end:
+ return ret;
+}
+
+/* Called when we get an INTRODUCE_ACK success status code. Do the appropriate
+ * actions for the rendezvous point and finally close intro_circ. */
+static void
+handle_introduce_ack_success(origin_circuit_t *intro_circ)
+{
+ origin_circuit_t *rend_circ = NULL;
+
+ tor_assert(intro_circ);
+
+ log_info(LD_REND, "Received INTRODUCE_ACK ack! Informing rendezvous");
+
+ /* Get the rendezvous circuit for this rendezvous cookie. */
+ uint8_t *rendezvous_cookie = intro_circ->hs_ident->rendezvous_cookie;
+ rend_circ = hs_circuitmap_get_rend_circ_client_side(rendezvous_cookie);
+ if (rend_circ == NULL) {
+ log_warn(LD_REND, "Can't find any rendezvous circuit. Stopping");
+ goto end;
+ }
+
+ assert_circ_anonymity_ok(rend_circ, get_options());
+
+ /* It is possible to get a RENDEZVOUS2 cell before the INTRODUCE_ACK which
+ * means that the circuit will be joined and already transmitting data. In
+ * that case, simply skip the purpose change and close the intro circuit
+ * like it should be. */
+ if (TO_CIRCUIT(rend_circ)->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
+ goto end;
+ }
+ circuit_change_purpose(TO_CIRCUIT(rend_circ),
+ CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
+ /* Set timestamp_dirty, because circuit_expire_building expects it to
+ * specify when a circuit entered the
+ * CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED state. */
+ TO_CIRCUIT(rend_circ)->timestamp_dirty = time(NULL);
+
+ end:
+ /* We don't need the intro circuit anymore. It did what it had to do! */
+ circuit_change_purpose(TO_CIRCUIT(intro_circ),
+ CIRCUIT_PURPOSE_C_INTRODUCE_ACKED);
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ), END_CIRC_REASON_FINISHED);
+
+ /* XXX: Close pending intro circuits we might have in parallel. */
+ return;
+}
+
+/* Called when we get an INTRODUCE_ACK failure status code. Depending on our
+ * failure cache status, either close the circuit or re-extend to a new
+ * introduction point. */
+static void
+handle_introduce_ack_bad(origin_circuit_t *circ, int status)
+{
+ tor_assert(circ);
+
+ log_info(LD_REND, "Received INTRODUCE_ACK nack by %s. Reason: %u",
+ safe_str_client(extend_info_describe(circ->build_state->chosen_exit)),
+ status);
+
+ /* It's a NAK. The introduction point didn't relay our request. */
+ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_INTRODUCING);
+
+ /* Note down this failure in the intro point failure cache. Depending on how
+ * many times we've tried this intro point, close it or reextend. */
+ hs_cache_client_intro_state_note(&circ->hs_ident->identity_pk,
+ &circ->hs_ident->intro_auth_pk,
+ INTRO_POINT_FAILURE_GENERIC);
+}
+
+/* Called when we get an INTRODUCE_ACK on the intro circuit circ. The encoded
+ * cell is in payload of length payload_len. Return 0 on success else a
+ * negative value. The circuit is either close or reuse to re-extend to a new
+ * introduction point. */
+static int
+handle_introduce_ack(origin_circuit_t *circ, const uint8_t *payload,
+ size_t payload_len)
+{
+ int status, ret = -1;
+
+ tor_assert(circ);
+ tor_assert(circ->build_state);
+ tor_assert(circ->build_state->chosen_exit);
+ assert_circ_anonymity_ok(circ, get_options());
+ tor_assert(payload);
+
+ status = hs_cell_parse_introduce_ack(payload, payload_len);
+ switch (status) {
+ case HS_CELL_INTRO_ACK_SUCCESS:
+ ret = 0;
+ handle_introduce_ack_success(circ);
+ goto end;
+ case HS_CELL_INTRO_ACK_FAILURE:
+ case HS_CELL_INTRO_ACK_BADFMT:
+ case HS_CELL_INTRO_ACK_NORELAY:
+ handle_introduce_ack_bad(circ, status);
+ /* We are going to see if we have to close the circuits (IP and RP) or we
+ * can re-extend to a new intro point. */
+ ret = close_or_reextend_intro_circ(circ);
+ break;
+ default:
+ log_info(LD_PROTOCOL, "Unknown INTRODUCE_ACK status code %u from %s",
+ status,
+ safe_str_client(extend_info_describe(circ->build_state->chosen_exit)));
+ break;
+ }
+
+ end:
+ return ret;
+}
+
+/* Called when we get a RENDEZVOUS2 cell on the rendezvous circuit circ. The
+ * encoded cell is in payload of length payload_len. Return 0 on success or a
+ * negative value on error. On error, the circuit is marked for close. */
+STATIC int
+handle_rendezvous2(origin_circuit_t *circ, const uint8_t *payload,
+ size_t payload_len)
+{
+ int ret = -1;
+ curve25519_public_key_t server_pk;
+ uint8_t auth_mac[DIGEST256_LEN] = {0};
+ uint8_t handshake_info[CURVE25519_PUBKEY_LEN + sizeof(auth_mac)] = {0};
+ hs_ntor_rend_cell_keys_t keys;
+ const hs_ident_circuit_t *ident;
+
+ tor_assert(circ);
+ tor_assert(payload);
+
+ /* Make things easier. */
+ ident = circ->hs_ident;
+ tor_assert(ident);
+
+ if (hs_cell_parse_rendezvous2(payload, payload_len, handshake_info,
+ sizeof(handshake_info)) < 0) {
+ goto err;
+ }
+ /* Get from the handshake info the SERVER_PK and AUTH_MAC. */
+ memcpy(&server_pk, handshake_info, CURVE25519_PUBKEY_LEN);
+ memcpy(auth_mac, handshake_info + CURVE25519_PUBKEY_LEN, sizeof(auth_mac));
+
+ /* Generate the handshake info. */
+ if (hs_ntor_client_get_rendezvous1_keys(&ident->intro_auth_pk,
+ &ident->rendezvous_client_kp,
+ &ident->intro_enc_pk, &server_pk,
+ &keys) < 0) {
+ log_info(LD_REND, "Unable to compute the rendezvous keys.");
+ goto err;
+ }
+
+ /* Critical check, make sure that the MAC matches what we got with what we
+ * computed just above. */
+ if (!hs_ntor_client_rendezvous2_mac_is_good(&keys, auth_mac)) {
+ log_info(LD_REND, "Invalid MAC in RENDEZVOUS2. Rejecting cell.");
+ goto err;
+ }
+
+ /* Setup the e2e encryption on the circuit and finalize its state. */
+ if (hs_circuit_setup_e2e_rend_circ(circ, keys.ntor_key_seed,
+ sizeof(keys.ntor_key_seed), 0) < 0) {
+ log_info(LD_REND, "Unable to setup the e2e encryption.");
+ goto err;
+ }
+ /* Success. Hidden service connection finalized! */
+ ret = 0;
+ goto end;
+
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ end:
+ memwipe(&keys, 0, sizeof(keys));
+ return ret;
+}
+
+/* Return true iff the client can fetch a descriptor for this service public
+ * identity key and status_out if not NULL is untouched. If the client can
+ * _not_ fetch the descriptor and if status_out is not NULL, it is set with
+ * the fetch status code. */
+static unsigned int
+can_client_refetch_desc(const ed25519_public_key_t *identity_pk,
+ hs_client_fetch_status_t *status_out)
+{
+ hs_client_fetch_status_t status;
+
+ tor_assert(identity_pk);
+
+ /* Are we configured to fetch descriptors? */
+ if (!get_options()->FetchHidServDescriptors) {
+ log_warn(LD_REND, "We received an onion address for a hidden service "
+ "descriptor but we are configured to not fetch.");
+ status = HS_CLIENT_FETCH_NOT_ALLOWED;
+ goto cannot;
+ }
+
+ /* Without a live consensus we can't do any client actions. It is needed to
+ * compute the hashring for a service. */
+ if (!networkstatus_get_live_consensus(approx_time())) {
+ log_info(LD_REND, "Can't fetch descriptor for service %s because we "
+ "are missing a live consensus. Stalling connection.",
+ safe_str_client(ed25519_fmt(identity_pk)));
+ status = HS_CLIENT_FETCH_MISSING_INFO;
+ goto cannot;
+ }
+
+ if (!router_have_minimum_dir_info()) {
+ log_info(LD_REND, "Can't fetch descriptor for service %s because we "
+ "dont have enough descriptors. Stalling connection.",
+ safe_str_client(ed25519_fmt(identity_pk)));
+ status = HS_CLIENT_FETCH_MISSING_INFO;
+ goto cannot;
+ }
+
+ /* Check if fetching a desc for this HS is useful to us right now */
+ {
+ const hs_descriptor_t *cached_desc = NULL;
+ cached_desc = hs_cache_lookup_as_client(identity_pk);
+ if (cached_desc && hs_client_any_intro_points_usable(identity_pk,
+ cached_desc)) {
+ log_info(LD_GENERAL, "We would fetch a v3 hidden service descriptor "
+ "but we already have a usable descriptor.");
+ status = HS_CLIENT_FETCH_HAVE_DESC;
+ goto cannot;
+ }
+ }
+
+ /* Don't try to refetch while we have a pending request for it. */
+ if (directory_request_is_pending(identity_pk)) {
+ log_info(LD_REND, "Already a pending directory request. Waiting on it.");
+ status = HS_CLIENT_FETCH_PENDING;
+ goto cannot;
+ }
+
+ /* Yes, client can fetch! */
+ return 1;
+ cannot:
+ if (status_out) {
+ *status_out = status;
+ }
+ return 0;
+}
+
+/* ========== */
+/* Public API */
+/* ========== */
+
+/** A circuit just finished connecting to a hidden service that the stream
+ * <b>conn</b> has been waiting for. Let the HS subsystem know about this. */
+void
+hs_client_note_connection_attempt_succeeded(const edge_connection_t *conn)
+{
+ tor_assert(connection_edge_is_rendezvous_stream(conn));
+
+ if (BUG(conn->rend_data && conn->hs_ident)) {
+ log_warn(LD_BUG, "Stream had both rend_data and hs_ident..."
+ "Prioritizing hs_ident");
+ }
+
+ if (conn->hs_ident) { /* It's v3: pass it to the prop224 handler */
+ note_connection_attempt_succeeded(conn->hs_ident);
+ return;
+ } else if (conn->rend_data) { /* It's v2: pass it to the legacy handler */
+ rend_client_note_connection_attempt_ended(conn->rend_data);
+ return;
+ }
+}
+
+/* With the given encoded descriptor in desc_str and the service key in
+ * service_identity_pk, decode the descriptor and set the desc pointer with a
+ * newly allocated descriptor object.
+ *
+ * Return 0 on success else a negative value and desc is set to NULL. */
+int
+hs_client_decode_descriptor(const char *desc_str,
+ const ed25519_public_key_t *service_identity_pk,
+ hs_descriptor_t **desc)
+{
+ int ret;
+ uint8_t subcredential[DIGEST256_LEN];
+ ed25519_public_key_t blinded_pubkey;
+
+ tor_assert(desc_str);
+ tor_assert(service_identity_pk);
+ tor_assert(desc);
+
+ /* Create subcredential for this HS so that we can decrypt */
+ {
+ uint64_t current_time_period = hs_get_time_period_num(0);
+ hs_build_blinded_pubkey(service_identity_pk, NULL, 0, current_time_period,
+ &blinded_pubkey);
+ hs_get_subcredential(service_identity_pk, &blinded_pubkey, subcredential);
+ }
+
+ /* Parse descriptor */
+ ret = hs_desc_decode_descriptor(desc_str, subcredential, desc);
+ memwipe(subcredential, 0, sizeof(subcredential));
+ if (ret < 0) {
+ log_warn(LD_GENERAL, "Could not parse received descriptor as client.");
+ if (get_options()->SafeLogging_ == SAFELOG_SCRUB_NONE) {
+ log_warn(LD_GENERAL, "%s", escaped(desc_str));
+ }
+ goto err;
+ }
+
+ /* Make sure the descriptor signing key cross certifies with the computed
+ * blinded key. Without this validation, anyone knowing the subcredential
+ * and onion address can forge a descriptor. */
+ tor_cert_t *cert = (*desc)->plaintext_data.signing_key_cert;
+ if (tor_cert_checksig(cert,
+ &blinded_pubkey, approx_time()) < 0) {
+ log_warn(LD_GENERAL, "Descriptor signing key certificate signature "
+ "doesn't validate with computed blinded key: %s",
+ tor_cert_describe_signature_status(cert));
+ goto err;
+ }
+
+ return 0;
+ err:
+ return -1;
+}
+
+/* Return true iff there are at least one usable intro point in the service
+ * descriptor desc. */
+int
+hs_client_any_intro_points_usable(const ed25519_public_key_t *service_pk,
+ const hs_descriptor_t *desc)
+{
+ tor_assert(service_pk);
+ tor_assert(desc);
+
+ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
+ const hs_desc_intro_point_t *, ip) {
+ if (intro_point_is_usable(service_pk, ip)) {
+ goto usable;
+ }
+ } SMARTLIST_FOREACH_END(ip);
+
+ return 0;
+ usable:
+ return 1;
+}
+
+/** Launch a connection to a hidden service directory to fetch a hidden
+ * service descriptor using <b>identity_pk</b> to get the necessary keys.
+ *
+ * A hs_client_fetch_status_t code is returned. */
+int
+hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk)
+{
+ hs_client_fetch_status_t status;
+
+ tor_assert(identity_pk);
+
+ if (!can_client_refetch_desc(identity_pk, &status)) {
+ return status;
+ }
+
+ /* Try to fetch the desc and if we encounter an unrecoverable error, mark
+ * the desc as unavailable for now. */
+ status = fetch_v3_desc(identity_pk);
+ if (fetch_status_should_close_socks(status)) {
+ close_all_socks_conns_waiting_for_desc(identity_pk, status,
+ END_STREAM_REASON_RESOLVEFAILED);
+ /* Remove HSDir fetch attempts so that we can retry later if the user
+ * wants us to regardless of if we closed any connections. */
+ purge_hid_serv_request(identity_pk);
+ }
+ return status;
+}
+
+/* This is called when we are trying to attach an AP connection to these
+ * hidden service circuits from connection_ap_handshake_attach_circuit().
+ * Return 0 on success, -1 for a transient error that is actions were
+ * triggered to recover or -2 for a permenent error where both circuits will
+ * marked for close.
+ *
+ * The following supports every hidden service version. */
+int
+hs_client_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ)
+{
+ return (intro_circ->hs_ident) ? send_introduce1(intro_circ, rend_circ) :
+ rend_client_send_introduction(intro_circ,
+ rend_circ);
+}
+
+/* Called when the client circuit circ has been established. It can be either
+ * an introduction or rendezvous circuit. This function handles all hidden
+ * service versions. */
+void
+hs_client_circuit_has_opened(origin_circuit_t *circ)
+{
+ tor_assert(circ);
+
+ /* Handle both version. v2 uses rend_data and v3 uses the hs circuit
+ * identifier hs_ident. Can't be both. */
+ switch (TO_CIRCUIT(circ)->purpose) {
+ case CIRCUIT_PURPOSE_C_INTRODUCING:
+ if (circ->hs_ident) {
+ client_intro_circ_has_opened(circ);
+ } else {
+ rend_client_introcirc_has_opened(circ);
+ }
+ break;
+ case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
+ if (circ->hs_ident) {
+ client_rendezvous_circ_has_opened(circ);
+ } else {
+ rend_client_rendcirc_has_opened(circ);
+ }
+ break;
+ default:
+ tor_assert_nonfatal_unreached();
+ }
+}
+
+/* Called when we receive a RENDEZVOUS_ESTABLISHED cell. Change the state of
+ * the circuit to CIRCUIT_PURPOSE_C_REND_READY. Return 0 on success else a
+ * negative value and the circuit marked for close. */
+int
+hs_client_receive_rendezvous_acked(origin_circuit_t *circ,
+ const uint8_t *payload, size_t payload_len)
+{
+ tor_assert(circ);
+ tor_assert(payload);
+
+ (void) payload_len;
+
+ if (TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
+ log_warn(LD_PROTOCOL, "Got a RENDEZVOUS_ESTABLISHED but we were not "
+ "expecting one. Closing circuit.");
+ goto err;
+ }
+
+ log_info(LD_REND, "Received an RENDEZVOUS_ESTABLISHED. This circuit is "
+ "now ready for rendezvous.");
+ circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_READY);
+
+ /* Set timestamp_dirty, because circuit_expire_building expects it to
+ * specify when a circuit entered the _C_REND_READY state. */
+ TO_CIRCUIT(circ)->timestamp_dirty = time(NULL);
+
+ /* From a path bias point of view, this circuit is now successfully used.
+ * Waiting any longer opens us up to attacks from malicious hidden services.
+ * They could induce the client to attempt to connect to their hidden
+ * service and never reply to the client's rend requests */
+ pathbias_mark_use_success(circ);
+
+ /* If we already have the introduction circuit built, make sure we send
+ * the INTRODUCE cell _now_ */
+ connection_ap_attach_pending(1);
+
+ return 0;
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ return -1;
+}
+
+/* This is called when a descriptor has arrived following a fetch request and
+ * has been stored in the client cache. Every entry connection that matches
+ * the service identity key in the ident will get attached to the hidden
+ * service circuit. */
+void
+hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident)
+{
+ time_t now = time(NULL);
+ smartlist_t *conns = NULL;
+
+ tor_assert(ident);
+
+ conns = connection_list_by_type_state(CONN_TYPE_AP,
+ AP_CONN_STATE_RENDDESC_WAIT);
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, base_conn) {
+ const hs_descriptor_t *desc;
+ entry_connection_t *entry_conn = TO_ENTRY_CONN(base_conn);
+ const edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn);
+
+ /* Only consider the entry connections that matches the service for which
+ * we just fetched its descriptor. */
+ if (!edge_conn->hs_ident ||
+ !ed25519_pubkey_eq(&ident->identity_pk,
+ &edge_conn->hs_ident->identity_pk)) {
+ continue;
+ }
+ assert_connection_ok(base_conn, now);
+
+ /* We were just called because we stored the descriptor for this service
+ * so not finding a descriptor means we have a bigger problem. */
+ desc = hs_cache_lookup_as_client(&ident->identity_pk);
+ if (BUG(desc == NULL)) {
+ goto end;
+ }
+
+ if (!hs_client_any_intro_points_usable(&ident->identity_pk, desc)) {
+ log_info(LD_REND, "Hidden service descriptor is unusable. "
+ "Closing streams.");
+ connection_mark_unattached_ap(entry_conn,
+ END_STREAM_REASON_RESOLVEFAILED);
+ /* We are unable to use the descriptor so remove the directory request
+ * from the cache so the next connection can try again. */
+ note_connection_attempt_succeeded(edge_conn->hs_ident);
+ goto end;
+ }
+
+ log_info(LD_REND, "Descriptor has arrived. Launching circuits.");
+
+ /* Because the connection can now proceed to opening circuit and
+ * ultimately connect to the service, reset those timestamp so the
+ * connection is considered "fresh" and can continue without being closed
+ * too early. */
+ base_conn->timestamp_created = now;
+ base_conn->timestamp_lastread = now;
+ base_conn->timestamp_lastwritten = now;
+ /* Change connection's state into waiting for a circuit. */
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+
+ connection_ap_mark_as_pending_circuit(entry_conn);
+ } SMARTLIST_FOREACH_END(base_conn);
+
+ end:
+ /* We don't have ownership of the objects in this list. */
+ smartlist_free(conns);
+}
+
+/* Return a newly allocated extend_info_t for a randomly chosen introduction
+ * point for the given edge connection identifier ident. Return NULL if we
+ * can't pick any usable introduction points. */
+extend_info_t *
+hs_client_get_random_intro_from_edge(const edge_connection_t *edge_conn)
+{
+ tor_assert(edge_conn);
+
+ return (edge_conn->hs_ident) ?
+ client_get_random_intro(&edge_conn->hs_ident->identity_pk) :
+ rend_client_get_random_intro(edge_conn->rend_data);
+}
+/* Called when get an INTRODUCE_ACK cell on the introduction circuit circ.
+ * Return 0 on success else a negative value is returned. The circuit will be
+ * closed or reuse to extend again to another intro point. */
+int
+hs_client_receive_introduce_ack(origin_circuit_t *circ,
+ const uint8_t *payload, size_t payload_len)
+{
+ int ret = -1;
+
+ tor_assert(circ);
+ tor_assert(payload);
+
+ if (TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
+ log_warn(LD_PROTOCOL, "Unexpected INTRODUCE_ACK on circuit %u.",
+ (unsigned int) TO_CIRCUIT(circ)->n_circ_id);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ goto end;
+ }
+
+ ret = (circ->hs_ident) ? handle_introduce_ack(circ, payload, payload_len) :
+ rend_client_introduction_acked(circ, payload,
+ payload_len);
+ /* For path bias: This circuit was used successfully. NACK or ACK counts. */
+ pathbias_mark_use_success(circ);
+
+ end:
+ return ret;
+}
+
+/* Called when get a RENDEZVOUS2 cell on the rendezvous circuit circ. Return
+ * 0 on success else a negative value is returned. The circuit will be closed
+ * on error. */
+int
+hs_client_receive_rendezvous2(origin_circuit_t *circ,
+ const uint8_t *payload, size_t payload_len)
+{
+ int ret = -1;
+
+ tor_assert(circ);
+ tor_assert(payload);
+
+ /* Circuit can possibly be in both state because we could receive a
+ * RENDEZVOUS2 cell before the INTRODUCE_ACK has been received. */
+ if (TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_REND_READY &&
+ TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) {
+ log_warn(LD_PROTOCOL, "Unexpected RENDEZVOUS2 cell on circuit %u. "
+ "Closing circuit.",
+ (unsigned int) TO_CIRCUIT(circ)->n_circ_id);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ goto end;
+ }
+
+ log_info(LD_REND, "Got RENDEZVOUS2 cell from hidden service on circuit %u.",
+ TO_CIRCUIT(circ)->n_circ_id);
+
+ ret = (circ->hs_ident) ? handle_rendezvous2(circ, payload, payload_len) :
+ rend_client_receive_rendezvous(circ, payload,
+ payload_len);
+ end:
+ return ret;
+}
+
+/* Extend the introduction circuit circ to another valid introduction point
+ * for the hidden service it is trying to connect to, or mark it and launch a
+ * new circuit if we can't extend it. Return 0 on success or possible
+ * success. Return -1 and mark the introduction circuit for close on permanent
+ * failure.
+ *
+ * On failure, the caller is responsible for marking the associated rendezvous
+ * circuit for close. */
+int
+hs_client_reextend_intro_circuit(origin_circuit_t *circ)
+{
+ int ret = -1;
+ extend_info_t *ei;
+
+ tor_assert(circ);
+
+ ei = (circ->hs_ident) ?
+ client_get_random_intro(&circ->hs_ident->identity_pk) :
+ rend_client_get_random_intro(circ->rend_data);
+ if (ei == NULL) {
+ log_warn(LD_REND, "No usable introduction points left. Closing.");
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
+ goto end;
+ }
+
+ if (circ->remaining_relay_early_cells) {
+ log_info(LD_REND, "Re-extending circ %u, this time to %s.",
+ (unsigned int) TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(extend_info_describe(ei)));
+ ret = circuit_extend_to_new_exit(circ, ei);
+ if (ret == 0) {
+ /* We were able to extend so update the timestamp so we avoid expiring
+ * this circuit too early. The intro circuit is short live so the
+ * linkability issue is minimized, we just need the circuit to hold a
+ * bit longer so we can introduce. */
+ TO_CIRCUIT(circ)->timestamp_dirty = time(NULL);
+ }
+ } else {
+ log_info(LD_REND, "Closing intro circ %u (out of RELAY_EARLY cells).",
+ (unsigned int) TO_CIRCUIT(circ)->n_circ_id);
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
+ /* connection_ap_handshake_attach_circuit will launch a new intro circ. */
+ ret = 0;
+ }
+
+ end:
+ extend_info_free(ei);
+ return ret;
+}
+
+/* Release all the storage held by the client subsystem. */
+void
+hs_client_free_all(void)
+{
+ /* Purge the hidden service request cache. */
+ hs_purge_last_hid_serv_requests();
+}
+
+/* Purge all potentially remotely-detectable state held in the hidden
+ * service client code. Called on SIGNAL NEWNYM. */
+void
+hs_client_purge_state(void)
+{
+ /* v2 subsystem. */
+ rend_client_purge_state();
+
+ /* Cancel all descriptor fetches. Do this first so once done we are sure
+ * that our descriptor cache won't modified. */
+ cancel_descriptor_fetches();
+ /* Purge the introduction point state cache. */
+ hs_cache_client_intro_state_purge();
+ /* Purge the descriptor cache. */
+ hs_cache_purge_as_client();
+ /* Purge the last hidden service request cache. */
+ hs_purge_last_hid_serv_requests();
+
+ log_info(LD_REND, "Hidden service client state has been purged.");
+}
+
+/* Called when our directory information has changed. */
+void
+hs_client_dir_info_changed(void)
+{
+ /* We have possibly reached the minimum directory information or new
+ * consensus so retry all pending SOCKS connection in
+ * AP_CONN_STATE_RENDDESC_WAIT state in order to fetch the descriptor. */
+ retry_all_socks_conn_waiting_for_desc();
+}
+
diff --git a/src/or/hs_client.h b/src/or/hs_client.h
new file mode 100644
index 0000000000..2523568ad1
--- /dev/null
+++ b/src/or/hs_client.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_client.h
+ * \brief Header file containing client data for the HS subsytem.
+ **/
+
+#ifndef TOR_HS_CLIENT_H
+#define TOR_HS_CLIENT_H
+
+#include "crypto_ed25519.h"
+#include "hs_descriptor.h"
+#include "hs_ident.h"
+
+/* Status code of a descriptor fetch request. */
+typedef enum {
+ /* Something internally went wrong. */
+ HS_CLIENT_FETCH_ERROR = -1,
+ /* The fetch request has been launched successfully. */
+ HS_CLIENT_FETCH_LAUNCHED = 0,
+ /* We already have a usable descriptor. No fetch. */
+ HS_CLIENT_FETCH_HAVE_DESC = 1,
+ /* No more HSDir available to query. */
+ HS_CLIENT_FETCH_NO_HSDIRS = 2,
+ /* The fetch request is not allowed. */
+ HS_CLIENT_FETCH_NOT_ALLOWED = 3,
+ /* We are missing information to be able to launch a request. */
+ HS_CLIENT_FETCH_MISSING_INFO = 4,
+ /* There is a pending fetch for the requested service. */
+ HS_CLIENT_FETCH_PENDING = 5,
+} hs_client_fetch_status_t;
+
+void hs_client_note_connection_attempt_succeeded(
+ const edge_connection_t *conn);
+
+int hs_client_decode_descriptor(
+ const char *desc_str,
+ const ed25519_public_key_t *service_identity_pk,
+ hs_descriptor_t **desc);
+int hs_client_any_intro_points_usable(const ed25519_public_key_t *service_pk,
+ const hs_descriptor_t *desc);
+int hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk);
+void hs_client_dir_info_changed(void);
+
+int hs_client_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ);
+
+void hs_client_circuit_has_opened(origin_circuit_t *circ);
+
+int hs_client_receive_rendezvous_acked(origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len);
+int hs_client_receive_introduce_ack(origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len);
+int hs_client_receive_rendezvous2(origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len);
+
+void hs_client_desc_has_arrived(const hs_ident_dir_conn_t *ident);
+
+extend_info_t *hs_client_get_random_intro_from_edge(
+ const edge_connection_t *edge_conn);
+
+int hs_client_reextend_intro_circuit(origin_circuit_t *circ);
+
+void hs_client_purge_state(void);
+
+void hs_client_free_all(void);
+
+#ifdef HS_CLIENT_PRIVATE
+
+STATIC routerstatus_t *
+pick_hsdir_v3(const ed25519_public_key_t *onion_identity_pk);
+
+STATIC extend_info_t *
+client_get_random_intro(const ed25519_public_key_t *service_pk);
+
+STATIC extend_info_t *
+desc_intro_point_to_extend_info(const hs_desc_intro_point_t *ip);
+
+STATIC int handle_rendezvous2(origin_circuit_t *circ, const uint8_t *payload,
+ size_t payload_len);
+
+MOCK_DECL(STATIC hs_client_fetch_status_t,
+ fetch_v3_desc, (const ed25519_public_key_t *onion_identity_pk));
+
+#endif /* defined(HS_CLIENT_PRIVATE) */
+
+#endif /* !defined(TOR_HS_CLIENT_H) */
+
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index c9af3f6887..e9d7323316 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -14,9 +14,167 @@
#include "or.h"
#include "config.h"
+#include "circuitbuild.h"
#include "networkstatus.h"
+#include "nodelist.h"
+#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_client.h"
+#include "hs_ident.h"
+#include "hs_service.h"
+#include "hs_circuitmap.h"
+#include "policies.h"
#include "rendcommon.h"
+#include "rendservice.h"
+#include "routerset.h"
+#include "router.h"
+#include "routerset.h"
+#include "shared_random.h"
+#include "shared_random_state.h"
+
+/* Trunnel */
+#include "ed25519_cert.h"
+
+/* Ed25519 Basepoint value. Taken from section 5 of
+ * https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03 */
+static const char *str_ed25519_basepoint =
+ "(15112221349535400772501151409588531511"
+ "454012693041857206046113283949847762202, "
+ "463168356949264781694283940034751631413"
+ "07993866256225615783033603165251855960)";
+
+#ifdef HAVE_SYS_UN_H
+
+/** Given <b>ports</b>, a smarlist containing rend_service_port_config_t,
+ * add the given <b>p</b>, a AF_UNIX port to the list. Return 0 on success
+ * else return -ENOSYS if AF_UNIX is not supported (see function in the
+ * #else statement below). */
+static int
+add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
+{
+ tor_assert(ports);
+ tor_assert(p);
+ tor_assert(p->is_unix_addr);
+
+ smartlist_add(ports, p);
+ return 0;
+}
+
+/** Given <b>conn</b> set it to use the given port <b>p</b> values. Return 0
+ * on success else return -ENOSYS if AF_UNIX is not supported (see function
+ * in the #else statement below). */
+static int
+set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
+{
+ tor_assert(conn);
+ tor_assert(p);
+ tor_assert(p->is_unix_addr);
+
+ conn->base_.socket_family = AF_UNIX;
+ tor_addr_make_unspec(&conn->base_.addr);
+ conn->base_.port = 1;
+ conn->base_.address = tor_strdup(p->unix_addr);
+ return 0;
+}
+
+#else /* !(defined(HAVE_SYS_UN_H)) */
+
+static int
+set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
+{
+ (void) conn;
+ (void) p;
+ return -ENOSYS;
+}
+
+static int
+add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
+{
+ (void) ports;
+ (void) p;
+ return -ENOSYS;
+}
+
+#endif /* defined(HAVE_SYS_UN_H) */
+
+/* Helper function: The key is a digest that we compare to a node_t object
+ * current hsdir_index. */
+static int
+compare_digest_to_fetch_hsdir_index(const void *_key, const void **_member)
+{
+ const char *key = _key;
+ const node_t *node = *_member;
+ return tor_memcmp(key, node->hsdir_index->fetch, DIGEST256_LEN);
+}
+
+/* Helper function: The key is a digest that we compare to a node_t object
+ * next hsdir_index. */
+static int
+compare_digest_to_store_first_hsdir_index(const void *_key,
+ const void **_member)
+{
+ const char *key = _key;
+ const node_t *node = *_member;
+ return tor_memcmp(key, node->hsdir_index->store_first, DIGEST256_LEN);
+}
+
+/* Helper function: The key is a digest that we compare to a node_t object
+ * next hsdir_index. */
+static int
+compare_digest_to_store_second_hsdir_index(const void *_key,
+ const void **_member)
+{
+ const char *key = _key;
+ const node_t *node = *_member;
+ return tor_memcmp(key, node->hsdir_index->store_second, DIGEST256_LEN);
+}
+
+/* Helper function: Compare two node_t objects current hsdir_index. */
+static int
+compare_node_fetch_hsdir_index(const void **a, const void **b)
+{
+ const node_t *node1= *a;
+ const node_t *node2 = *b;
+ return tor_memcmp(node1->hsdir_index->fetch,
+ node2->hsdir_index->fetch,
+ DIGEST256_LEN);
+}
+
+/* Helper function: Compare two node_t objects next hsdir_index. */
+static int
+compare_node_store_first_hsdir_index(const void **a, const void **b)
+{
+ const node_t *node1= *a;
+ const node_t *node2 = *b;
+ return tor_memcmp(node1->hsdir_index->store_first,
+ node2->hsdir_index->store_first,
+ DIGEST256_LEN);
+}
+
+/* Helper function: Compare two node_t objects next hsdir_index. */
+static int
+compare_node_store_second_hsdir_index(const void **a, const void **b)
+{
+ const node_t *node1= *a;
+ const node_t *node2 = *b;
+ return tor_memcmp(node1->hsdir_index->store_second,
+ node2->hsdir_index->store_second,
+ DIGEST256_LEN);
+}
+
+/* Allocate and return a string containing the path to filename in directory.
+ * This function will never return NULL. The caller must free this path. */
+char *
+hs_path_from_filename(const char *directory, const char *filename)
+{
+ char *file_path = NULL;
+
+ tor_assert(directory);
+ tor_assert(filename);
+
+ tor_asprintf(&file_path, "%s%s%s", directory, PATH_SEPARATOR, filename);
+ return file_path;
+}
/* Make sure that the directory for <b>service</b> is private, using the config
* <b>username</b>.
@@ -52,10 +210,38 @@ hs_check_service_private_dir(const char *username, const char *path,
return 0;
}
+/* Default, minimum and maximum values for the maximum rendezvous failures
+ * consensus parameter. */
+#define MAX_REND_FAILURES_DEFAULT 2
+#define MAX_REND_FAILURES_MIN 1
+#define MAX_REND_FAILURES_MAX 10
+
+/** How many times will a hidden service operator attempt to connect to
+ * a requested rendezvous point before giving up? */
+int
+hs_get_service_max_rend_failures(void)
+{
+ return networkstatus_get_param(NULL, "hs_service_max_rdv_failures",
+ MAX_REND_FAILURES_DEFAULT,
+ MAX_REND_FAILURES_MIN,
+ MAX_REND_FAILURES_MAX);
+}
+
/** Get the default HS time period length in minutes from the consensus. */
STATIC uint64_t
get_time_period_length(void)
{
+ /* If we are on a test network, make the time period smaller than normal so
+ that we actually see it rotate. Specifically, make it the same length as
+ an SRV protocol run. */
+ if (get_options()->TestingTorNetwork) {
+ unsigned run_duration = sr_state_get_protocol_run_duration();
+ /* An SRV run should take more than a minute (it's 24 rounds) */
+ tor_assert_nonfatal(run_duration > 60);
+ /* Turn it from seconds to minutes before returning: */
+ return sr_state_get_protocol_run_duration() / 60;
+ }
+
int32_t time_period_length = networkstatus_get_param(NULL, "hsdir_interval",
HS_TIME_PERIOD_LENGTH_DEFAULT,
HS_TIME_PERIOD_LENGTH_MIN,
@@ -66,18 +252,34 @@ get_time_period_length(void)
return (uint64_t) time_period_length;
}
-/** Get the HS time period number at time <b>now</b> */
-STATIC uint64_t
-get_time_period_num(time_t now)
+/** Get the HS time period number at time <b>now</b>. If <b>now</b> is not set,
+ * we try to get the time ourselves from a live consensus. */
+uint64_t
+hs_get_time_period_num(time_t now)
{
uint64_t time_period_num;
+ time_t current_time;
+
+ /* If no time is specified, set current time based on consensus time, and
+ * only fall back to system time if that fails. */
+ if (now != 0) {
+ current_time = now;
+ } else {
+ networkstatus_t *ns = networkstatus_get_live_consensus(approx_time());
+ current_time = ns ? ns->valid_after : approx_time();
+ }
+
+ /* Start by calculating minutes since the epoch */
uint64_t time_period_length = get_time_period_length();
- uint64_t minutes_since_epoch = now / 60;
+ uint64_t minutes_since_epoch = current_time / 60;
- /* Now subtract half a day to fit the prop224 time period schedule (see
- * section [TIME-PERIODS]). */
- tor_assert(minutes_since_epoch > HS_TIME_PERIOD_ROTATION_OFFSET);
- minutes_since_epoch -= HS_TIME_PERIOD_ROTATION_OFFSET;
+ /* Apply the rotation offset as specified by prop224 (section
+ * [TIME-PERIODS]), so that new time periods synchronize nicely with SRV
+ * publication */
+ unsigned int time_period_rotation_offset = sr_state_get_phase_duration();
+ time_period_rotation_offset /= 60; /* go from seconds to minutes */
+ tor_assert(minutes_since_epoch > time_period_rotation_offset);
+ minutes_since_epoch -= time_period_rotation_offset;
/* Calculate the time period */
time_period_num = minutes_since_epoch / time_period_length;
@@ -85,11 +287,38 @@ get_time_period_num(time_t now)
}
/** Get the number of the _upcoming_ HS time period, given that the current
- * time is <b>now</b>. */
+ * time is <b>now</b>. If <b>now</b> is not set, we try to get the time from a
+ * live consensus. */
uint64_t
hs_get_next_time_period_num(time_t now)
{
- return get_time_period_num(now) + 1;
+ return hs_get_time_period_num(now) + 1;
+}
+
+/* Get the number of the _previous_ HS time period, given that the current time
+ * is <b>now</b>. If <b>now</b> is not set, we try to get the time from a live
+ * consensus. */
+uint64_t
+hs_get_previous_time_period_num(time_t now)
+{
+ return hs_get_time_period_num(now) - 1;
+}
+
+/* Return the start time of the upcoming time period based on <b>now</b>. If
+ <b>now</b> is not set, we try to get the time ourselves from a live
+ consensus. */
+time_t
+hs_get_start_time_of_next_time_period(time_t now)
+{
+ uint64_t time_period_length = get_time_period_length();
+
+ /* Get start time of next time period */
+ uint64_t next_time_period_num = hs_get_next_time_period_num(now);
+ uint64_t start_of_next_tp_in_mins = next_time_period_num *time_period_length;
+
+ /* Apply rotation offset as specified by prop224 section [TIME-PERIODS] */
+ unsigned int time_period_rotation_offset = sr_state_get_phase_duration();
+ return (time_t)(start_of_next_tp_in_mins * 60 + time_period_rotation_offset);
}
/* Create a new rend_data_t for a specific given <b>version</b>.
@@ -344,20 +573,1238 @@ rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out)
}
}
-/* Default, minimum and maximum values for the maximum rendezvous failures
- * consensus parameter. */
-#define MAX_REND_FAILURES_DEFAULT 2
-#define MAX_REND_FAILURES_MIN 1
-#define MAX_REND_FAILURES_MAX 10
+/* Using the given time period number, compute the disaster shared random
+ * value and put it in srv_out. It MUST be at least DIGEST256_LEN bytes. */
+static void
+compute_disaster_srv(uint64_t time_period_num, uint8_t *srv_out)
+{
+ crypto_digest_t *digest;
-/** How many times will a hidden service operator attempt to connect to
- * a requested rendezvous point before giving up? */
+ tor_assert(srv_out);
+
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+
+ /* Start setting up payload:
+ * H("shared-random-disaster" | INT_8(period_length) | INT_8(period_num)) */
+ crypto_digest_add_bytes(digest, HS_SRV_DISASTER_PREFIX,
+ HS_SRV_DISASTER_PREFIX_LEN);
+
+ /* Setup INT_8(period_length) | INT_8(period_num) */
+ {
+ uint64_t time_period_length = get_time_period_length();
+ char period_stuff[sizeof(uint64_t)*2];
+ size_t offset = 0;
+ set_uint64(period_stuff, tor_htonll(time_period_length));
+ offset += sizeof(uint64_t);
+ set_uint64(period_stuff+offset, tor_htonll(time_period_num));
+ offset += sizeof(uint64_t);
+ tor_assert(offset == sizeof(period_stuff));
+
+ crypto_digest_add_bytes(digest, period_stuff, sizeof(period_stuff));
+ }
+
+ crypto_digest_get_digest(digest, (char *) srv_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+}
+
+/** Due to the high cost of computing the disaster SRV and that potentially we
+ * would have to do it thousands of times in a row, we always cache the
+ * computer disaster SRV (and its corresponding time period num) in case we
+ * want to reuse it soon after. We need to cache two SRVs, one for each active
+ * time period.
+ */
+static uint8_t cached_disaster_srv[2][DIGEST256_LEN];
+static uint64_t cached_time_period_nums[2] = {0};
+
+/** Compute the disaster SRV value for this <b>time_period_num</b> and put it
+ * in <b>srv_out</b> (of size at least DIGEST256_LEN). First check our caches
+ * to see if we have already computed it. */
+STATIC void
+get_disaster_srv(uint64_t time_period_num, uint8_t *srv_out)
+{
+ if (time_period_num == cached_time_period_nums[0]) {
+ memcpy(srv_out, cached_disaster_srv[0], DIGEST256_LEN);
+ return;
+ } else if (time_period_num == cached_time_period_nums[1]) {
+ memcpy(srv_out, cached_disaster_srv[1], DIGEST256_LEN);
+ return;
+ } else {
+ int replace_idx;
+ // Replace the lower period number.
+ if (cached_time_period_nums[0] <= cached_time_period_nums[1]) {
+ replace_idx = 0;
+ } else {
+ replace_idx = 1;
+ }
+ cached_time_period_nums[replace_idx] = time_period_num;
+ compute_disaster_srv(time_period_num, cached_disaster_srv[replace_idx]);
+ memcpy(srv_out, cached_disaster_srv[replace_idx], DIGEST256_LEN);
+ return;
+ }
+}
+
+#ifdef TOR_UNIT_TESTS
+
+/** Get the first cached disaster SRV. Only used by unittests. */
+STATIC uint8_t *
+get_first_cached_disaster_srv(void)
+{
+ return cached_disaster_srv[0];
+}
+
+/** Get the second cached disaster SRV. Only used by unittests. */
+STATIC uint8_t *
+get_second_cached_disaster_srv(void)
+{
+ return cached_disaster_srv[1];
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/* When creating a blinded key, we need a parameter which construction is as
+ * follow: H(pubkey | [secret] | ed25519-basepoint | nonce).
+ *
+ * The nonce has a pre-defined format which uses the time period number
+ * period_num and the start of the period in second start_time_period.
+ *
+ * The secret of size secret_len is optional meaning that it can be NULL and
+ * thus will be ignored for the param construction.
+ *
+ * The result is put in param_out. */
+static void
+build_blinded_key_param(const ed25519_public_key_t *pubkey,
+ const uint8_t *secret, size_t secret_len,
+ uint64_t period_num, uint64_t period_length,
+ uint8_t *param_out)
+{
+ size_t offset = 0;
+ const char blind_str[] = "Derive temporary signing key";
+ uint8_t nonce[HS_KEYBLIND_NONCE_LEN];
+ crypto_digest_t *digest;
+
+ tor_assert(pubkey);
+ tor_assert(param_out);
+
+ /* Create the nonce N. The construction is as follow:
+ * N = "key-blind" || INT_8(period_num) || INT_8(period_length) */
+ memcpy(nonce, HS_KEYBLIND_NONCE_PREFIX, HS_KEYBLIND_NONCE_PREFIX_LEN);
+ offset += HS_KEYBLIND_NONCE_PREFIX_LEN;
+ set_uint64(nonce + offset, tor_htonll(period_num));
+ offset += sizeof(uint64_t);
+ set_uint64(nonce + offset, tor_htonll(period_length));
+ offset += sizeof(uint64_t);
+ tor_assert(offset == HS_KEYBLIND_NONCE_LEN);
+
+ /* Generate the parameter h and the construction is as follow:
+ * h = H(BLIND_STRING | pubkey | [secret] | ed25519-basepoint | N) */
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto_digest_add_bytes(digest, blind_str, sizeof(blind_str));
+ crypto_digest_add_bytes(digest, (char *) pubkey, ED25519_PUBKEY_LEN);
+ /* Optional secret. */
+ if (secret) {
+ crypto_digest_add_bytes(digest, (char *) secret, secret_len);
+ }
+ crypto_digest_add_bytes(digest, str_ed25519_basepoint,
+ strlen(str_ed25519_basepoint));
+ crypto_digest_add_bytes(digest, (char *) nonce, sizeof(nonce));
+
+ /* Extract digest and put it in the param. */
+ crypto_digest_get_digest(digest, (char *) param_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+
+ memwipe(nonce, 0, sizeof(nonce));
+}
+
+/* Using an ed25519 public key and version to build the checksum of an
+ * address. Put in checksum_out. Format is:
+ * SHA3-256(".onion checksum" || PUBKEY || VERSION)
+ *
+ * checksum_out must be large enough to receive 32 bytes (DIGEST256_LEN). */
+static void
+build_hs_checksum(const ed25519_public_key_t *key, uint8_t version,
+ uint8_t *checksum_out)
+{
+ size_t offset = 0;
+ char data[HS_SERVICE_ADDR_CHECKSUM_INPUT_LEN];
+
+ /* Build checksum data. */
+ memcpy(data, HS_SERVICE_ADDR_CHECKSUM_PREFIX,
+ HS_SERVICE_ADDR_CHECKSUM_PREFIX_LEN);
+ offset += HS_SERVICE_ADDR_CHECKSUM_PREFIX_LEN;
+ memcpy(data + offset, key->pubkey, ED25519_PUBKEY_LEN);
+ offset += ED25519_PUBKEY_LEN;
+ set_uint8(data + offset, version);
+ offset += sizeof(version);
+ tor_assert(offset == HS_SERVICE_ADDR_CHECKSUM_INPUT_LEN);
+
+ /* Hash the data payload to create the checksum. */
+ crypto_digest256((char *) checksum_out, data, sizeof(data),
+ DIGEST_SHA3_256);
+}
+
+/* Using an ed25519 public key, checksum and version to build the binary
+ * representation of a service address. Put in addr_out. Format is:
+ * addr_out = PUBKEY || CHECKSUM || VERSION
+ *
+ * addr_out must be large enough to receive HS_SERVICE_ADDR_LEN bytes. */
+static void
+build_hs_address(const ed25519_public_key_t *key, const uint8_t *checksum,
+ uint8_t version, char *addr_out)
+{
+ size_t offset = 0;
+
+ tor_assert(key);
+ tor_assert(checksum);
+
+ memcpy(addr_out, key->pubkey, ED25519_PUBKEY_LEN);
+ offset += ED25519_PUBKEY_LEN;
+ memcpy(addr_out + offset, checksum, HS_SERVICE_ADDR_CHECKSUM_LEN_USED);
+ offset += HS_SERVICE_ADDR_CHECKSUM_LEN_USED;
+ set_uint8(addr_out + offset, version);
+ offset += sizeof(uint8_t);
+ tor_assert(offset == HS_SERVICE_ADDR_LEN);
+}
+
+/* Helper for hs_parse_address(): Using a binary representation of a service
+ * address, parse its content into the key_out, checksum_out and version_out.
+ * Any out variable can be NULL in case the caller would want only one field.
+ * checksum_out MUST at least be 2 bytes long. address must be at least
+ * HS_SERVICE_ADDR_LEN bytes but doesn't need to be NUL terminated. */
+static void
+hs_parse_address_impl(const char *address, ed25519_public_key_t *key_out,
+ uint8_t *checksum_out, uint8_t *version_out)
+{
+ size_t offset = 0;
+
+ tor_assert(address);
+
+ if (key_out) {
+ /* First is the key. */
+ memcpy(key_out->pubkey, address, ED25519_PUBKEY_LEN);
+ }
+ offset += ED25519_PUBKEY_LEN;
+ if (checksum_out) {
+ /* Followed by a 2 bytes checksum. */
+ memcpy(checksum_out, address + offset, HS_SERVICE_ADDR_CHECKSUM_LEN_USED);
+ }
+ offset += HS_SERVICE_ADDR_CHECKSUM_LEN_USED;
+ if (version_out) {
+ /* Finally, version value is 1 byte. */
+ *version_out = get_uint8(address + offset);
+ }
+ offset += sizeof(uint8_t);
+ /* Extra safety. */
+ tor_assert(offset == HS_SERVICE_ADDR_LEN);
+}
+
+/* Using the given identity public key and a blinded public key, compute the
+ * subcredential and put it in subcred_out (must be of size DIGEST256_LEN).
+ * This can't fail. */
+void
+hs_get_subcredential(const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ uint8_t *subcred_out)
+{
+ uint8_t credential[DIGEST256_LEN];
+ crypto_digest_t *digest;
+
+ tor_assert(identity_pk);
+ tor_assert(blinded_pk);
+ tor_assert(subcred_out);
+
+ /* First, build the credential. Construction is as follow:
+ * credential = H("credential" | public-identity-key) */
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto_digest_add_bytes(digest, HS_CREDENTIAL_PREFIX,
+ HS_CREDENTIAL_PREFIX_LEN);
+ crypto_digest_add_bytes(digest, (const char *) identity_pk->pubkey,
+ ED25519_PUBKEY_LEN);
+ crypto_digest_get_digest(digest, (char *) credential, DIGEST256_LEN);
+ crypto_digest_free(digest);
+
+ /* Now, compute the subcredential. Construction is as follow:
+ * subcredential = H("subcredential" | credential | blinded-public-key). */
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto_digest_add_bytes(digest, HS_SUBCREDENTIAL_PREFIX,
+ HS_SUBCREDENTIAL_PREFIX_LEN);
+ crypto_digest_add_bytes(digest, (const char *) credential,
+ sizeof(credential));
+ crypto_digest_add_bytes(digest, (const char *) blinded_pk->pubkey,
+ ED25519_PUBKEY_LEN);
+ crypto_digest_get_digest(digest, (char *) subcred_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+
+ memwipe(credential, 0, sizeof(credential));
+}
+
+/* From the given list of hidden service ports, find the ones that much the
+ * given edge connection conn, pick one at random and use it to set the
+ * connection address. Return 0 on success or -1 if none. */
int
-hs_get_service_max_rend_failures(void)
+hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn)
{
- return networkstatus_get_param(NULL, "hs_service_max_rdv_failures",
- MAX_REND_FAILURES_DEFAULT,
- MAX_REND_FAILURES_MIN,
- MAX_REND_FAILURES_MAX);
+ rend_service_port_config_t *chosen_port;
+ unsigned int warn_once = 0;
+ smartlist_t *matching_ports;
+
+ tor_assert(ports);
+ tor_assert(conn);
+
+ matching_ports = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(ports, rend_service_port_config_t *, p) {
+ if (TO_CONN(conn)->port != p->virtual_port) {
+ continue;
+ }
+ if (!(p->is_unix_addr)) {
+ smartlist_add(matching_ports, p);
+ } else {
+ if (add_unix_port(matching_ports, p)) {
+ if (!warn_once) {
+ /* Unix port not supported so warn only once. */
+ log_warn(LD_REND, "Saw AF_UNIX virtual port mapping for port %d "
+ "which is unsupported on this platform. "
+ "Ignoring it.",
+ TO_CONN(conn)->port);
+ }
+ warn_once++;
+ }
+ }
+ } SMARTLIST_FOREACH_END(p);
+
+ chosen_port = smartlist_choose(matching_ports);
+ smartlist_free(matching_ports);
+ if (chosen_port) {
+ if (!(chosen_port->is_unix_addr)) {
+ /* Get a non-AF_UNIX connection ready for connection_exit_connect() */
+ tor_addr_copy(&TO_CONN(conn)->addr, &chosen_port->real_addr);
+ TO_CONN(conn)->port = chosen_port->real_port;
+ } else {
+ if (set_unix_port(conn, chosen_port)) {
+ /* Simply impossible to end up here else we were able to add a Unix
+ * port without AF_UNIX support... ? */
+ tor_assert(0);
+ }
+ }
+ }
+ return (chosen_port) ? 0 : -1;
+}
+
+/* Using a base32 representation of a service address, parse its content into
+ * the key_out, checksum_out and version_out. Any out variable can be NULL in
+ * case the caller would want only one field. checksum_out MUST at least be 2
+ * bytes long.
+ *
+ * Return 0 if parsing went well; return -1 in case of error. */
+int
+hs_parse_address(const char *address, ed25519_public_key_t *key_out,
+ uint8_t *checksum_out, uint8_t *version_out)
+{
+ char decoded[HS_SERVICE_ADDR_LEN];
+
+ tor_assert(address);
+
+ /* Obvious length check. */
+ if (strlen(address) != HS_SERVICE_ADDR_LEN_BASE32) {
+ log_warn(LD_REND, "Service address %s has an invalid length. "
+ "Expected %lu but got %lu.",
+ escaped_safe_str(address),
+ (unsigned long) HS_SERVICE_ADDR_LEN_BASE32,
+ (unsigned long) strlen(address));
+ goto invalid;
+ }
+
+ /* Decode address so we can extract needed fields. */
+ if (base32_decode(decoded, sizeof(decoded), address, strlen(address)) < 0) {
+ log_warn(LD_REND, "Service address %s can't be decoded.",
+ escaped_safe_str(address));
+ goto invalid;
+ }
+
+ /* Parse the decoded address into the fields we need. */
+ hs_parse_address_impl(decoded, key_out, checksum_out, version_out);
+
+ return 0;
+ invalid:
+ return -1;
+}
+
+/* Validate a given onion address. The length, the base32 decoding and
+ * checksum are validated. Return 1 if valid else 0. */
+int
+hs_address_is_valid(const char *address)
+{
+ uint8_t version;
+ uint8_t checksum[HS_SERVICE_ADDR_CHECKSUM_LEN_USED];
+ uint8_t target_checksum[DIGEST256_LEN];
+ ed25519_public_key_t service_pubkey;
+
+ /* Parse the decoded address into the fields we need. */
+ if (hs_parse_address(address, &service_pubkey, checksum, &version) < 0) {
+ goto invalid;
+ }
+
+ /* Get the checksum it's suppose to be and compare it with what we have
+ * encoded in the address. */
+ build_hs_checksum(&service_pubkey, version, target_checksum);
+ if (tor_memcmp(checksum, target_checksum, sizeof(checksum))) {
+ log_warn(LD_REND, "Service address %s invalid checksum.",
+ escaped_safe_str(address));
+ goto invalid;
+ }
+
+ /* Validate that this pubkey does not have a torsion component. We need to do
+ * this on the prop224 client-side so that attackers can't give equivalent
+ * forms of an onion address to users. */
+ if (ed25519_validate_pubkey(&service_pubkey) < 0) {
+ log_warn(LD_REND, "Service address %s has bad pubkey .",
+ escaped_safe_str(address));
+ goto invalid;
+ }
+
+ /* Valid address. */
+ return 1;
+ invalid:
+ return 0;
+}
+
+/* Build a service address using an ed25519 public key and a given version.
+ * The returned address is base32 encoded and put in addr_out. The caller MUST
+ * make sure the addr_out is at least HS_SERVICE_ADDR_LEN_BASE32 + 1 long.
+ *
+ * Format is as follow:
+ * base32(PUBKEY || CHECKSUM || VERSION)
+ * CHECKSUM = H(".onion checksum" || PUBKEY || VERSION)
+ * */
+void
+hs_build_address(const ed25519_public_key_t *key, uint8_t version,
+ char *addr_out)
+{
+ uint8_t checksum[DIGEST256_LEN];
+ char address[HS_SERVICE_ADDR_LEN];
+
+ tor_assert(key);
+ tor_assert(addr_out);
+
+ /* Get the checksum of the address. */
+ build_hs_checksum(key, version, checksum);
+ /* Get the binary address representation. */
+ build_hs_address(key, checksum, version, address);
+
+ /* Encode the address. addr_out will be NUL terminated after this. */
+ base32_encode(addr_out, HS_SERVICE_ADDR_LEN_BASE32 + 1, address,
+ sizeof(address));
+ /* Validate what we just built. */
+ tor_assert(hs_address_is_valid(addr_out));
+}
+
+/* Return a newly allocated copy of lspec. */
+link_specifier_t *
+hs_link_specifier_dup(const link_specifier_t *lspec)
+{
+ link_specifier_t *result = link_specifier_new();
+ memcpy(result, lspec, sizeof(*result));
+ /* The unrecognized field is a dynamic array so make sure to copy its
+ * content and not the pointer. */
+ link_specifier_setlen_un_unrecognized(
+ result, link_specifier_getlen_un_unrecognized(lspec));
+ if (link_specifier_getlen_un_unrecognized(result)) {
+ memcpy(link_specifier_getarray_un_unrecognized(result),
+ link_specifier_getconstarray_un_unrecognized(lspec),
+ link_specifier_getlen_un_unrecognized(result));
+ }
+ return result;
+}
+
+/* From a given ed25519 public key pk and an optional secret, compute a
+ * blinded public key and put it in blinded_pk_out. This is only useful to
+ * the client side because the client only has access to the identity public
+ * key of the service. */
+void
+hs_build_blinded_pubkey(const ed25519_public_key_t *pk,
+ const uint8_t *secret, size_t secret_len,
+ uint64_t time_period_num,
+ ed25519_public_key_t *blinded_pk_out)
+{
+ /* Our blinding key API requires a 32 bytes parameter. */
+ uint8_t param[DIGEST256_LEN];
+
+ tor_assert(pk);
+ tor_assert(blinded_pk_out);
+ tor_assert(!tor_mem_is_zero((char *) pk, ED25519_PUBKEY_LEN));
+
+ build_blinded_key_param(pk, secret, secret_len,
+ time_period_num, get_time_period_length(), param);
+ ed25519_public_blind(blinded_pk_out, pk, param);
+
+ memwipe(param, 0, sizeof(param));
+}
+
+/* From a given ed25519 keypair kp and an optional secret, compute a blinded
+ * keypair for the current time period and put it in blinded_kp_out. This is
+ * only useful by the service side because the client doesn't have access to
+ * the identity secret key. */
+void
+hs_build_blinded_keypair(const ed25519_keypair_t *kp,
+ const uint8_t *secret, size_t secret_len,
+ uint64_t time_period_num,
+ ed25519_keypair_t *blinded_kp_out)
+{
+ /* Our blinding key API requires a 32 bytes parameter. */
+ uint8_t param[DIGEST256_LEN];
+
+ tor_assert(kp);
+ tor_assert(blinded_kp_out);
+ /* Extra safety. A zeroed key is bad. */
+ tor_assert(!tor_mem_is_zero((char *) &kp->pubkey, ED25519_PUBKEY_LEN));
+ tor_assert(!tor_mem_is_zero((char *) &kp->seckey, ED25519_SECKEY_LEN));
+
+ build_blinded_key_param(&kp->pubkey, secret, secret_len,
+ time_period_num, get_time_period_length(), param);
+ ed25519_keypair_blind(blinded_kp_out, kp, param);
+
+ memwipe(param, 0, sizeof(param));
+}
+
+/* Return true if we are currently in the time segment between a new time
+ * period and a new SRV (in the real network that happens between 12:00 and
+ * 00:00 UTC). Here is a diagram showing exactly when this returns true:
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^^^^^^^^^^^^ ^^^^^^^^^^^^ |
+ * | |
+ * +------------------------------------------------------------------+
+ */
+MOCK_IMPL(int,
+hs_in_period_between_tp_and_srv,(const networkstatus_t *consensus, time_t now))
+{
+ time_t valid_after;
+ time_t srv_start_time, tp_start_time;
+
+ if (!consensus) {
+ consensus = networkstatus_get_live_consensus(now);
+ if (!consensus) {
+ return 0;
+ }
+ }
+
+ /* Get start time of next TP and of current SRV protocol run, and check if we
+ * are between them. */
+ valid_after = consensus->valid_after;
+ srv_start_time =
+ sr_state_get_start_time_of_current_protocol_run(valid_after);
+ tp_start_time = hs_get_start_time_of_next_time_period(srv_start_time);
+
+ if (valid_after >= srv_start_time && valid_after < tp_start_time) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return 1 if any virtual port in ports needs a circuit with good uptime.
+ * Else return 0. */
+int
+hs_service_requires_uptime_circ(const smartlist_t *ports)
+{
+ tor_assert(ports);
+
+ SMARTLIST_FOREACH_BEGIN(ports, rend_service_port_config_t *, p) {
+ if (smartlist_contains_int_as_string(get_options()->LongLivedPorts,
+ p->virtual_port)) {
+ return 1;
+ }
+ } SMARTLIST_FOREACH_END(p);
+ return 0;
+}
+
+/* Build hs_index which is used to find the responsible hsdirs. This index
+ * value is used to select the responsible HSDir where their hsdir_index is
+ * closest to this value.
+ * SHA3-256("store-at-idx" | blinded_public_key |
+ * INT_8(replicanum) | INT_8(period_length) | INT_8(period_num) )
+ *
+ * hs_index_out must be large enough to receive DIGEST256_LEN bytes. */
+void
+hs_build_hs_index(uint64_t replica, const ed25519_public_key_t *blinded_pk,
+ uint64_t period_num, uint8_t *hs_index_out)
+{
+ crypto_digest_t *digest;
+
+ tor_assert(blinded_pk);
+ tor_assert(hs_index_out);
+
+ /* Build hs_index. See construction at top of function comment. */
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto_digest_add_bytes(digest, HS_INDEX_PREFIX, HS_INDEX_PREFIX_LEN);
+ crypto_digest_add_bytes(digest, (const char *) blinded_pk->pubkey,
+ ED25519_PUBKEY_LEN);
+
+ /* Now setup INT_8(replicanum) | INT_8(period_length) | INT_8(period_num) */
+ {
+ uint64_t period_length = get_time_period_length();
+ char buf[sizeof(uint64_t)*3];
+ size_t offset = 0;
+ set_uint64(buf, tor_htonll(replica));
+ offset += sizeof(uint64_t);
+ set_uint64(buf+offset, tor_htonll(period_length));
+ offset += sizeof(uint64_t);
+ set_uint64(buf+offset, tor_htonll(period_num));
+ offset += sizeof(uint64_t);
+ tor_assert(offset == sizeof(buf));
+
+ crypto_digest_add_bytes(digest, buf, sizeof(buf));
+ }
+
+ crypto_digest_get_digest(digest, (char *) hs_index_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+}
+
+/* Build hsdir_index which is used to find the responsible hsdirs. This is the
+ * index value that is compare to the hs_index when selecting an HSDir.
+ * SHA3-256("node-idx" | node_identity |
+ * shared_random_value | INT_8(period_length) | INT_8(period_num) )
+ *
+ * hsdir_index_out must be large enough to receive DIGEST256_LEN bytes. */
+void
+hs_build_hsdir_index(const ed25519_public_key_t *identity_pk,
+ const uint8_t *srv_value, uint64_t period_num,
+ uint8_t *hsdir_index_out)
+{
+ crypto_digest_t *digest;
+
+ tor_assert(identity_pk);
+ tor_assert(srv_value);
+ tor_assert(hsdir_index_out);
+
+ /* Build hsdir_index. See construction at top of function comment. */
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto_digest_add_bytes(digest, HSDIR_INDEX_PREFIX, HSDIR_INDEX_PREFIX_LEN);
+ crypto_digest_add_bytes(digest, (const char *) identity_pk->pubkey,
+ ED25519_PUBKEY_LEN);
+ crypto_digest_add_bytes(digest, (const char *) srv_value, DIGEST256_LEN);
+
+ {
+ uint64_t time_period_length = get_time_period_length();
+ char period_stuff[sizeof(uint64_t)*2];
+ size_t offset = 0;
+ set_uint64(period_stuff, tor_htonll(period_num));
+ offset += sizeof(uint64_t);
+ set_uint64(period_stuff+offset, tor_htonll(time_period_length));
+ offset += sizeof(uint64_t);
+ tor_assert(offset == sizeof(period_stuff));
+
+ crypto_digest_add_bytes(digest, period_stuff, sizeof(period_stuff));
+ }
+
+ crypto_digest_get_digest(digest, (char *) hsdir_index_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+}
+
+/* Return a newly allocated buffer containing the current shared random value
+ * or if not present, a disaster value is computed using the given time period
+ * number. If a consensus is provided in <b>ns</b>, use it to get the SRV
+ * value. This function can't fail. */
+uint8_t *
+hs_get_current_srv(uint64_t time_period_num, const networkstatus_t *ns)
+{
+ uint8_t *sr_value = tor_malloc_zero(DIGEST256_LEN);
+ const sr_srv_t *current_srv = sr_get_current(ns);
+
+ if (current_srv) {
+ memcpy(sr_value, current_srv->value, sizeof(current_srv->value));
+ } else {
+ /* Disaster mode. */
+ get_disaster_srv(time_period_num, sr_value);
+ }
+ return sr_value;
+}
+
+/* Return a newly allocated buffer containing the previous shared random
+ * value or if not present, a disaster value is computed using the given time
+ * period number. This function can't fail. */
+uint8_t *
+hs_get_previous_srv(uint64_t time_period_num, const networkstatus_t *ns)
+{
+ uint8_t *sr_value = tor_malloc_zero(DIGEST256_LEN);
+ const sr_srv_t *previous_srv = sr_get_previous(ns);
+
+ if (previous_srv) {
+ memcpy(sr_value, previous_srv->value, sizeof(previous_srv->value));
+ } else {
+ /* Disaster mode. */
+ get_disaster_srv(time_period_num, sr_value);
+ }
+ return sr_value;
+}
+
+/* Return the number of replicas defined by a consensus parameter or the
+ * default value. */
+int32_t
+hs_get_hsdir_n_replicas(void)
+{
+ /* The [1,16] range is a specification requirement. */
+ return networkstatus_get_param(NULL, "hsdir_n_replicas",
+ HS_DEFAULT_HSDIR_N_REPLICAS, 1, 16);
+}
+
+/* Return the spread fetch value defined by a consensus parameter or the
+ * default value. */
+int32_t
+hs_get_hsdir_spread_fetch(void)
+{
+ /* The [1,128] range is a specification requirement. */
+ return networkstatus_get_param(NULL, "hsdir_spread_fetch",
+ HS_DEFAULT_HSDIR_SPREAD_FETCH, 1, 128);
+}
+
+/* Return the spread store value defined by a consensus parameter or the
+ * default value. */
+int32_t
+hs_get_hsdir_spread_store(void)
+{
+ /* The [1,128] range is a specification requirement. */
+ return networkstatus_get_param(NULL, "hsdir_spread_store",
+ HS_DEFAULT_HSDIR_SPREAD_STORE, 1, 128);
+}
+
+/** <b>node</b> is an HSDir so make sure that we have assigned an hsdir index.
+ * Return 0 if everything is as expected, else return -1. */
+static int
+node_has_hsdir_index(const node_t *node)
+{
+ tor_assert(node_supports_v3_hsdir(node));
+
+ /* A node can't have an HSDir index without a descriptor since we need desc
+ * to get its ed25519 key */
+ if (!node_has_descriptor(node)) {
+ return 0;
+ }
+
+ /* At this point, since the node has a desc, this node must also have an
+ * hsdir index. If not, something went wrong, so BUG out. */
+ if (BUG(node->hsdir_index == NULL)) {
+ return 0;
+ }
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->fetch,
+ DIGEST256_LEN))) {
+ return 0;
+ }
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->store_first,
+ DIGEST256_LEN))) {
+ return 0;
+ }
+ if (BUG(tor_mem_is_zero((const char*)node->hsdir_index->store_second,
+ DIGEST256_LEN))) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* For a given blinded key and time period number, get the responsible HSDir
+ * and put their routerstatus_t object in the responsible_dirs list. If
+ * 'use_second_hsdir_index' is true, use the second hsdir_index of the node_t
+ * is used. If 'for_fetching' is true, the spread fetch consensus parameter is
+ * used else the spread store is used which is only for upload. This function
+ * can't fail but it is possible that the responsible_dirs list contains fewer
+ * nodes than expected.
+ *
+ * This function goes over the latest consensus routerstatus list and sorts it
+ * by their node_t hsdir_index then does a binary search to find the closest
+ * node. All of this makes it a bit CPU intensive so use it wisely. */
+void
+hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
+ uint64_t time_period_num, int use_second_hsdir_index,
+ int for_fetching, smartlist_t *responsible_dirs)
+{
+ smartlist_t *sorted_nodes;
+ /* The compare function used for the smartlist bsearch. We have two
+ * different depending on is_next_period. */
+ int (*cmp_fct)(const void *, const void **);
+
+ tor_assert(blinded_pk);
+ tor_assert(responsible_dirs);
+
+ sorted_nodes = smartlist_new();
+
+ /* Add every node_t that support HSDir v3 for which we do have a valid
+ * hsdir_index already computed for them for this consensus. */
+ {
+ networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (!c || smartlist_len(c->routerstatus_list) == 0) {
+ log_warn(LD_REND, "No valid consensus so we can't get the responsible "
+ "hidden service directories.");
+ goto done;
+ }
+ SMARTLIST_FOREACH_BEGIN(c->routerstatus_list, const routerstatus_t *, rs) {
+ /* Even though this node_t object won't be modified and should be const,
+ * we can't add const object in a smartlist_t. */
+ node_t *n = node_get_mutable_by_id(rs->identity_digest);
+ tor_assert(n);
+ if (node_supports_v3_hsdir(n) && rs->is_hs_dir) {
+ if (!node_has_hsdir_index(n)) {
+ log_info(LD_GENERAL, "Node %s was found without hsdir index.",
+ node_describe(n));
+ continue;
+ }
+ smartlist_add(sorted_nodes, n);
+ }
+ } SMARTLIST_FOREACH_END(rs);
+ }
+ if (smartlist_len(sorted_nodes) == 0) {
+ log_warn(LD_REND, "No nodes found to be HSDir or supporting v3.");
+ goto done;
+ }
+
+ /* First thing we have to do is sort all node_t by hsdir_index. The
+ * is_next_period tells us if we want the current or the next one. Set the
+ * bsearch compare function also while we are at it. */
+ if (for_fetching) {
+ smartlist_sort(sorted_nodes, compare_node_fetch_hsdir_index);
+ cmp_fct = compare_digest_to_fetch_hsdir_index;
+ } else if (use_second_hsdir_index) {
+ smartlist_sort(sorted_nodes, compare_node_store_second_hsdir_index);
+ cmp_fct = compare_digest_to_store_second_hsdir_index;
+ } else {
+ smartlist_sort(sorted_nodes, compare_node_store_first_hsdir_index);
+ cmp_fct = compare_digest_to_store_first_hsdir_index;
+ }
+
+ /* For all replicas, we'll select a set of HSDirs using the consensus
+ * parameters and the sorted list. The replica starting at value 1 is
+ * defined by the specification. */
+ for (int replica = 1; replica <= hs_get_hsdir_n_replicas(); replica++) {
+ int idx, start, found, n_added = 0;
+ uint8_t hs_index[DIGEST256_LEN] = {0};
+ /* Number of node to add to the responsible dirs list depends on if we are
+ * trying to fetch or store. A client always fetches. */
+ int n_to_add = (for_fetching) ? hs_get_hsdir_spread_fetch() :
+ hs_get_hsdir_spread_store();
+
+ /* Get the index that we should use to select the node. */
+ hs_build_hs_index(replica, blinded_pk, time_period_num, hs_index);
+ /* The compare function pointer has been set correctly earlier. */
+ start = idx = smartlist_bsearch_idx(sorted_nodes, hs_index, cmp_fct,
+ &found);
+ /* Getting the length of the list if no member is greater than the key we
+ * are looking for so start at the first element. */
+ if (idx == smartlist_len(sorted_nodes)) {
+ start = idx = 0;
+ }
+ while (n_added < n_to_add) {
+ const node_t *node = smartlist_get(sorted_nodes, idx);
+ /* If the node has already been selected which is possible between
+ * replicas, the specification says to skip over. */
+ if (!smartlist_contains(responsible_dirs, node->rs)) {
+ smartlist_add(responsible_dirs, node->rs);
+ ++n_added;
+ }
+ if (++idx == smartlist_len(sorted_nodes)) {
+ /* Wrap if we've reached the end of the list. */
+ idx = 0;
+ }
+ if (idx == start) {
+ /* We've gone over the whole list, stop and avoid infinite loop. */
+ break;
+ }
+ }
+ }
+
+ done:
+ smartlist_free(sorted_nodes);
+}
+
+/*********************** HSDir request tracking ***************************/
+
+/** Return the period for which a hidden service directory cannot be queried
+ * for the same descriptor ID again, taking TestingTorNetwork into account. */
+time_t
+hs_hsdir_requery_period(const or_options_t *options)
+{
+ tor_assert(options);
+
+ if (options->TestingTorNetwork) {
+ return REND_HID_SERV_DIR_REQUERY_PERIOD_TESTING;
+ } else {
+ return REND_HID_SERV_DIR_REQUERY_PERIOD;
+ }
+}
+
+/** Tracks requests for fetching hidden service descriptors. It's used by
+ * hidden service clients, to avoid querying HSDirs that have already failed
+ * giving back a descriptor. The same data structure is used to track both v2
+ * and v3 HS descriptor requests.
+ *
+ * The string map is a key/value store that contains the last request times to
+ * hidden service directories for certain queries. Specifically:
+ *
+ * key = base32(hsdir_identity) + base32(hs_identity)
+ * value = time_t of last request for that hs_identity to that HSDir
+ *
+ * where 'hsdir_identity' is the identity digest of the HSDir node, and
+ * 'hs_identity' is the descriptor ID of the HS in the v2 case, or the ed25519
+ * blinded public key of the HS in the v3 case. */
+static strmap_t *last_hid_serv_requests_ = NULL;
+
+/** Returns last_hid_serv_requests_, initializing it to a new strmap if
+ * necessary. */
+STATIC strmap_t *
+get_last_hid_serv_requests(void)
+{
+ if (!last_hid_serv_requests_)
+ last_hid_serv_requests_ = strmap_new();
+ return last_hid_serv_requests_;
+}
+
+/** Look up the last request time to hidden service directory <b>hs_dir</b>
+ * for descriptor request key <b>req_key_str</b> which is the descriptor ID
+ * for a v2 service or the blinded key for v3. If <b>set</b> is non-zero,
+ * assign the current time <b>now</b> and return that. Otherwise, return the
+ * most recent request time, or 0 if no such request has been sent before. */
+time_t
+hs_lookup_last_hid_serv_request(routerstatus_t *hs_dir,
+ const char *req_key_str,
+ time_t now, int set)
+{
+ char hsdir_id_base32[BASE32_DIGEST_LEN + 1];
+ char *hsdir_desc_comb_id = NULL;
+ time_t *last_request_ptr;
+ strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
+
+ /* Create the key */
+ base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
+ hs_dir->identity_digest, DIGEST_LEN);
+ tor_asprintf(&hsdir_desc_comb_id, "%s%s", hsdir_id_base32, req_key_str);
+
+ if (set) {
+ time_t *oldptr;
+ last_request_ptr = tor_malloc_zero(sizeof(time_t));
+ *last_request_ptr = now;
+ oldptr = strmap_set(last_hid_serv_requests, hsdir_desc_comb_id,
+ last_request_ptr);
+ tor_free(oldptr);
+ } else {
+ last_request_ptr = strmap_get(last_hid_serv_requests,
+ hsdir_desc_comb_id);
+ }
+
+ tor_free(hsdir_desc_comb_id);
+ return (last_request_ptr) ? *last_request_ptr : 0;
+}
+
+/** Clean the history of request times to hidden service directories, so that
+ * it does not contain requests older than REND_HID_SERV_DIR_REQUERY_PERIOD
+ * seconds any more. */
+void
+hs_clean_last_hid_serv_requests(time_t now)
+{
+ strmap_iter_t *iter;
+ time_t cutoff = now - hs_hsdir_requery_period(get_options());
+ strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
+ for (iter = strmap_iter_init(last_hid_serv_requests);
+ !strmap_iter_done(iter); ) {
+ const char *key;
+ void *val;
+ time_t *ent;
+ strmap_iter_get(iter, &key, &val);
+ ent = (time_t *) val;
+ if (*ent < cutoff) {
+ iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
+ tor_free(ent);
+ } else {
+ iter = strmap_iter_next(last_hid_serv_requests, iter);
+ }
+ }
+}
+
+/** Remove all requests related to the descriptor request key string
+ * <b>req_key_str</b> from the history of times of requests to hidden service
+ * directories.
+ *
+ * This is called from rend_client_note_connection_attempt_ended(), which
+ * must be idempotent, so any future changes to this function must leave it
+ * idempotent too. */
+void
+hs_purge_hid_serv_from_last_hid_serv_requests(const char *req_key_str)
+{
+ strmap_iter_t *iter;
+ strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
+
+ for (iter = strmap_iter_init(last_hid_serv_requests);
+ !strmap_iter_done(iter); ) {
+ const char *key;
+ void *val;
+ strmap_iter_get(iter, &key, &val);
+
+ /* XXX: The use of REND_DESC_ID_V2_LEN_BASE32 is very wrong in terms of
+ * semantic, see #23305. */
+
+ /* This strmap contains variable-sized elements so this is a basic length
+ * check on the strings we are about to compare. The key is variable sized
+ * since it's composed as follows:
+ * key = base32(hsdir_identity) + base32(req_key_str)
+ * where 'req_key_str' is the descriptor ID of the HS in the v2 case, or
+ * the ed25519 blinded public key of the HS in the v3 case. */
+ if (strlen(key) < REND_DESC_ID_V2_LEN_BASE32 + strlen(req_key_str)) {
+ iter = strmap_iter_next(last_hid_serv_requests, iter);
+ continue;
+ }
+
+ /* Check if the tracked request matches our request key */
+ if (tor_memeq(key + REND_DESC_ID_V2_LEN_BASE32, req_key_str,
+ strlen(req_key_str))) {
+ iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
+ tor_free(val);
+ } else {
+ iter = strmap_iter_next(last_hid_serv_requests, iter);
+ }
+ }
+}
+
+/** Purge the history of request times to hidden service directories,
+ * so that future lookups of an HS descriptor will not fail because we
+ * accessed all of the HSDir relays responsible for the descriptor
+ * recently. */
+void
+hs_purge_last_hid_serv_requests(void)
+{
+ /* Don't create the table if it doesn't exist yet (and it may very
+ * well not exist if the user hasn't accessed any HSes)... */
+ strmap_t *old_last_hid_serv_requests = last_hid_serv_requests_;
+ /* ... and let get_last_hid_serv_requests re-create it for us if
+ * necessary. */
+ last_hid_serv_requests_ = NULL;
+
+ if (old_last_hid_serv_requests != NULL) {
+ log_info(LD_REND, "Purging client last-HS-desc-request-time table");
+ strmap_free(old_last_hid_serv_requests, tor_free_);
+ }
+}
+
+/***********************************************************************/
+
+/** Given the list of responsible HSDirs in <b>responsible_dirs</b>, pick the
+ * one that we should use to fetch a descriptor right now. Take into account
+ * previous failed attempts at fetching this descriptor from HSDirs using the
+ * string identifier <b>req_key_str</b>.
+ *
+ * Steals ownership of <b>responsible_dirs</b>.
+ *
+ * Return the routerstatus of the chosen HSDir if successful, otherwise return
+ * NULL if no HSDirs are worth trying right now. */
+routerstatus_t *
+hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
+{
+ smartlist_t *usable_responsible_dirs = smartlist_new();
+ const or_options_t *options = get_options();
+ routerstatus_t *hs_dir;
+ time_t now = time(NULL);
+ int excluded_some;
+
+ tor_assert(req_key_str);
+
+ /* Clean outdated request history first. */
+ hs_clean_last_hid_serv_requests(now);
+
+ /* Only select those hidden service directories to which we did not send a
+ * request recently and for which we have a router descriptor here. */
+ SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
+ time_t last = hs_lookup_last_hid_serv_request(dir, req_key_str, 0, 0);
+ const node_t *node = node_get_by_id(dir->identity_digest);
+ if (last + hs_hsdir_requery_period(options) >= now ||
+ !node || !node_has_descriptor(node)) {
+ SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
+ continue;
+ }
+ if (!routerset_contains_node(options->ExcludeNodes, node)) {
+ smartlist_add(usable_responsible_dirs, dir);
+ }
+ } SMARTLIST_FOREACH_END(dir);
+
+ excluded_some =
+ smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs);
+
+ hs_dir = smartlist_choose(usable_responsible_dirs);
+ if (!hs_dir && !options->StrictNodes) {
+ hs_dir = smartlist_choose(responsible_dirs);
+ }
+
+ smartlist_free(responsible_dirs);
+ smartlist_free(usable_responsible_dirs);
+ if (!hs_dir) {
+ log_info(LD_REND, "Could not pick one of the responsible hidden "
+ "service directories, because we requested them all "
+ "recently without success.");
+ if (options->StrictNodes && excluded_some) {
+ log_warn(LD_REND, "Could not pick a hidden service directory for the "
+ "requested hidden service: they are all either down or "
+ "excluded, and StrictNodes is set.");
+ }
+ } else {
+ /* Remember that we are requesting a descriptor from this hidden service
+ * directory now. */
+ hs_lookup_last_hid_serv_request(hs_dir, req_key_str, now, 1);
+ }
+
+ return hs_dir;
+}
+
+/* From a list of link specifier, an onion key and if we are requesting a
+ * direct connection (ex: single onion service), return a newly allocated
+ * extend_info_t object. This function always returns an extend info with
+ * an IPv4 address, or NULL.
+ *
+ * It performs the following checks:
+ * if either IPv4 or legacy ID is missing, return NULL.
+ * if direct_conn, and we can't reach the IPv4 address, return NULL.
+ */
+extend_info_t *
+hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
+ const curve25519_public_key_t *onion_key,
+ int direct_conn)
+{
+ int have_v4 = 0, have_legacy_id = 0, have_ed25519_id = 0;
+ char legacy_id[DIGEST_LEN] = {0};
+ uint16_t port_v4 = 0;
+ tor_addr_t addr_v4;
+ ed25519_public_key_t ed25519_pk;
+ extend_info_t *info = NULL;
+
+ tor_assert(lspecs);
+
+ SMARTLIST_FOREACH_BEGIN(lspecs, const link_specifier_t *, ls) {
+ switch (link_specifier_get_ls_type(ls)) {
+ case LS_IPV4:
+ /* Skip if we already seen a v4. */
+ if (have_v4) continue;
+ tor_addr_from_ipv4h(&addr_v4,
+ link_specifier_get_un_ipv4_addr(ls));
+ port_v4 = link_specifier_get_un_ipv4_port(ls);
+ have_v4 = 1;
+ break;
+ case LS_LEGACY_ID:
+ /* Make sure we do have enough bytes for the legacy ID. */
+ if (link_specifier_getlen_un_legacy_id(ls) < sizeof(legacy_id)) {
+ break;
+ }
+ memcpy(legacy_id, link_specifier_getconstarray_un_legacy_id(ls),
+ sizeof(legacy_id));
+ have_legacy_id = 1;
+ break;
+ case LS_ED25519_ID:
+ memcpy(ed25519_pk.pubkey,
+ link_specifier_getconstarray_un_ed25519_id(ls),
+ ED25519_PUBKEY_LEN);
+ have_ed25519_id = 1;
+ break;
+ default:
+ /* Ignore unknown. */
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ls);
+
+ /* Legacy ID is mandatory, and we require IPv4. */
+ if (!have_v4 || !have_legacy_id) {
+ goto done;
+ }
+
+ /* We know we have IPv4, because we just checked. */
+ if (!direct_conn) {
+ /* All clients can extend to any IPv4 via a 3-hop path. */
+ goto validate;
+ } else if (direct_conn &&
+ fascist_firewall_allows_address_addr(&addr_v4, port_v4,
+ FIREWALL_OR_CONNECTION,
+ 0, 0)) {
+ /* Direct connection and we can reach it in IPv4 so go for it. */
+ goto validate;
+
+ /* We will add support for falling back to a 3-hop path in a later
+ * release. */
+ } else {
+ /* If we can't reach IPv4, return NULL. */
+ goto done;
+ }
+
+ /* We will add support for IPv6 in a later release. */
+
+ validate:
+ /* We'll validate now that the address we've picked isn't a private one. If
+ * it is, are we allowing to extend to private address? */
+ if (!extend_info_addr_is_allowed(&addr_v4)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_REND,
+ "Requested address is private and we are not allowed to extend to "
+ "it: %s:%u", fmt_addr(&addr_v4), port_v4);
+ goto done;
+ }
+
+ /* We do have everything for which we think we can connect successfully. */
+ info = extend_info_new(NULL, legacy_id,
+ (have_ed25519_id) ? &ed25519_pk : NULL, NULL,
+ onion_key, &addr_v4, port_v4);
+ done:
+ return info;
+}
+
+/***********************************************************************/
+
+/* Initialize the entire HS subsytem. This is called in tor_init() before any
+ * torrc options are loaded. Only for >= v3. */
+void
+hs_init(void)
+{
+ hs_circuitmap_init();
+ hs_service_init();
+ hs_cache_init();
+}
+
+/* Release and cleanup all memory of the HS subsystem (all version). This is
+ * called by tor_free_all(). */
+void
+hs_free_all(void)
+{
+ hs_circuitmap_free_all();
+ hs_service_free_all();
+ hs_cache_free_all();
+ hs_client_free_all();
+}
+
+/* For the given origin circuit circ, decrement the number of rendezvous
+ * stream counter. This handles every hidden service version. */
+void
+hs_dec_rdv_stream_counter(origin_circuit_t *circ)
+{
+ tor_assert(circ);
+
+ if (circ->rend_data) {
+ circ->rend_data->nr_streams--;
+ } else if (circ->hs_ident) {
+ circ->hs_ident->num_rdv_streams--;
+ } else {
+ /* Should not be called if this circuit is not for hidden service. */
+ tor_assert_nonfatal_unreached();
+ }
+}
+
+/* For the given origin circuit circ, increment the number of rendezvous
+ * stream counter. This handles every hidden service version. */
+void
+hs_inc_rdv_stream_counter(origin_circuit_t *circ)
+{
+ tor_assert(circ);
+
+ if (circ->rend_data) {
+ circ->rend_data->nr_streams++;
+ } else if (circ->hs_ident) {
+ circ->hs_ident->num_rdv_streams++;
+ } else {
+ /* Should not be called if this circuit is not for hidden service. */
+ tor_assert_nonfatal_unreached();
+ }
}
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 7eef5fc97e..7c5ea4792c 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -11,15 +11,21 @@
#include "or.h"
+/* Trunnel */
+#include "ed25519_cert.h"
+
/* Protocol version 2. Use this instead of hardcoding "2" in the code base,
* this adds a clearer semantic to the value when used. */
#define HS_VERSION_TWO 2
/* Version 3 of the protocol (prop224). */
#define HS_VERSION_THREE 3
+/* Earliest and latest version we support. */
+#define HS_VERSION_MIN HS_VERSION_TWO
+#define HS_VERSION_MAX HS_VERSION_THREE
/** Try to maintain this many intro points per service by default. */
#define NUM_INTRO_POINTS_DEFAULT 3
-/** Maximum number of intro points per service. */
+/** Maximum number of intro points per generic and version 2 service. */
#define NUM_INTRO_POINTS_MAX 10
/** Number of extra intro points we launch if our set of intro nodes is empty.
* See proposal 155, section 4. */
@@ -46,14 +52,139 @@
#define HS_TIME_PERIOD_LENGTH_MIN 30 /* minutes */
/* The minimum time period length as seen in prop224 section [TIME-PERIODS] */
#define HS_TIME_PERIOD_LENGTH_MAX (60 * 24 * 10) /* 10 days or 14400 minutes */
+
+/* Prefix of the onion address checksum. */
+#define HS_SERVICE_ADDR_CHECKSUM_PREFIX ".onion checksum"
+/* Length of the checksum prefix minus the NUL terminated byte. */
+#define HS_SERVICE_ADDR_CHECKSUM_PREFIX_LEN \
+ (sizeof(HS_SERVICE_ADDR_CHECKSUM_PREFIX) - 1)
+/* Length of the resulting checksum of the address. The construction of this
+ * checksum looks like:
+ * CHECKSUM = ".onion checksum" || PUBKEY || VERSION
+ * where VERSION is 1 byte. This is pre-hashing. */
+#define HS_SERVICE_ADDR_CHECKSUM_INPUT_LEN \
+ (HS_SERVICE_ADDR_CHECKSUM_PREFIX_LEN + ED25519_PUBKEY_LEN + sizeof(uint8_t))
+/* The amount of bytes we use from the address checksum. */
+#define HS_SERVICE_ADDR_CHECKSUM_LEN_USED 2
+/* Length of the binary encoded service address which is of course before the
+ * base32 encoding. Construction is:
+ * PUBKEY || CHECKSUM || VERSION
+ * with 1 byte VERSION and 2 bytes CHECKSUM. The following is 35 bytes. */
+#define HS_SERVICE_ADDR_LEN \
+ (ED25519_PUBKEY_LEN + HS_SERVICE_ADDR_CHECKSUM_LEN_USED + sizeof(uint8_t))
+/* Length of 'y' portion of 'y.onion' URL. This is base32 encoded and the
+ * length ends up to 56 bytes (not counting the terminated NUL byte.) */
+#define HS_SERVICE_ADDR_LEN_BASE32 \
+ (CEIL_DIV(HS_SERVICE_ADDR_LEN * 8, 5))
+
+/* The default HS time period length */
+#define HS_TIME_PERIOD_LENGTH_DEFAULT 1440 /* 1440 minutes == one day */
+/* The minimum time period length as seen in prop224 section [TIME-PERIODS] */
+#define HS_TIME_PERIOD_LENGTH_MIN 30 /* minutes */
+/* The minimum time period length as seen in prop224 section [TIME-PERIODS] */
+#define HS_TIME_PERIOD_LENGTH_MAX (60 * 24 * 10) /* 10 days or 14400 minutes */
/* The time period rotation offset as seen in prop224 section [TIME-PERIODS] */
#define HS_TIME_PERIOD_ROTATION_OFFSET (12 * 60) /* minutes */
+/* Keyblinding parameter construction is as follow:
+ * "key-blind" || INT_8(period_num) || INT_8(start_period_sec) */
+#define HS_KEYBLIND_NONCE_PREFIX "key-blind"
+#define HS_KEYBLIND_NONCE_PREFIX_LEN (sizeof(HS_KEYBLIND_NONCE_PREFIX) - 1)
+#define HS_KEYBLIND_NONCE_LEN \
+ (HS_KEYBLIND_NONCE_PREFIX_LEN + sizeof(uint64_t) + sizeof(uint64_t))
+
+/* Credential and subcredential prefix value. */
+#define HS_CREDENTIAL_PREFIX "credential"
+#define HS_CREDENTIAL_PREFIX_LEN (sizeof(HS_CREDENTIAL_PREFIX) - 1)
+#define HS_SUBCREDENTIAL_PREFIX "subcredential"
+#define HS_SUBCREDENTIAL_PREFIX_LEN (sizeof(HS_SUBCREDENTIAL_PREFIX) - 1)
+
+/* Node hidden service stored at index prefix value. */
+#define HS_INDEX_PREFIX "store-at-idx"
+#define HS_INDEX_PREFIX_LEN (sizeof(HS_INDEX_PREFIX) - 1)
+
+/* Node hidden service directory index prefix value. */
+#define HSDIR_INDEX_PREFIX "node-idx"
+#define HSDIR_INDEX_PREFIX_LEN (sizeof(HSDIR_INDEX_PREFIX) - 1)
+
+/* Prefix of the shared random value disaster mode. */
+#define HS_SRV_DISASTER_PREFIX "shared-random-disaster"
+#define HS_SRV_DISASTER_PREFIX_LEN (sizeof(HS_SRV_DISASTER_PREFIX) - 1)
+
+/* Default value of number of hsdir replicas (hsdir_n_replicas). */
+#define HS_DEFAULT_HSDIR_N_REPLICAS 2
+/* Default value of hsdir spread store (hsdir_spread_store). */
+#define HS_DEFAULT_HSDIR_SPREAD_STORE 4
+/* Default value of hsdir spread fetch (hsdir_spread_fetch). */
+#define HS_DEFAULT_HSDIR_SPREAD_FETCH 3
+
+/* The size of a legacy RENDEZVOUS1 cell which adds up to 168 bytes. It is
+ * bigger than the 84 bytes needed for version 3 so we need to pad up to that
+ * length so it is indistinguishable between versions. */
+#define HS_LEGACY_RENDEZVOUS_CELL_SIZE \
+ (REND_COOKIE_LEN + DH_KEY_LEN + DIGEST_LEN)
+
+/* Type of authentication key used by an introduction point. */
+typedef enum {
+ HS_AUTH_KEY_TYPE_LEGACY = 1,
+ HS_AUTH_KEY_TYPE_ED25519 = 2,
+} hs_auth_key_type_t;
+
+/* Represents the mapping from a virtual port of a rendezvous service to a
+ * real port on some IP. */
+typedef struct rend_service_port_config_t {
+ /* The incoming HS virtual port we're mapping */
+ uint16_t virtual_port;
+ /* Is this an AF_UNIX port? */
+ unsigned int is_unix_addr:1;
+ /* The outgoing TCP port to use, if !is_unix_addr */
+ uint16_t real_port;
+ /* The outgoing IPv4 or IPv6 address to use, if !is_unix_addr */
+ tor_addr_t real_addr;
+ /* The socket path to connect to, if is_unix_addr */
+ char unix_addr[FLEXIBLE_ARRAY_MEMBER];
+} rend_service_port_config_t;
+
+/* Hidden service directory index used in a node_t which is set once we set
+ * the consensus. */
+typedef struct hsdir_index_t {
+ /* HSDir index to use when fetching a descriptor. */
+ uint8_t fetch[DIGEST256_LEN];
+
+ /* HSDir index used by services to store their first and second
+ * descriptor. The first descriptor is chronologically older than the second
+ * one and uses older TP and SRV values. */
+ uint8_t store_first[DIGEST256_LEN];
+ uint8_t store_second[DIGEST256_LEN];
+} hsdir_index_t;
+
+void hs_init(void);
+void hs_free_all(void);
+
+void hs_cleanup_circ(circuit_t *circ);
+
int hs_check_service_private_dir(const char *username, const char *path,
unsigned int dir_group_readable,
unsigned int create);
int hs_get_service_max_rend_failures(void);
+char *hs_path_from_filename(const char *directory, const char *filename);
+void hs_build_address(const ed25519_public_key_t *key, uint8_t version,
+ char *addr_out);
+int hs_address_is_valid(const char *address);
+int hs_parse_address(const char *address, ed25519_public_key_t *key_out,
+ uint8_t *checksum_out, uint8_t *version_out);
+
+void hs_build_blinded_pubkey(const ed25519_public_key_t *pubkey,
+ const uint8_t *secret, size_t secret_len,
+ uint64_t time_period_num,
+ ed25519_public_key_t *pubkey_out);
+void hs_build_blinded_keypair(const ed25519_keypair_t *kp,
+ const uint8_t *secret, size_t secret_len,
+ uint64_t time_period_num,
+ ed25519_keypair_t *kp_out);
+int hs_service_requires_uptime_circ(const smartlist_t *ports);
+
void rend_data_free(rend_data_t *data);
rend_data_t *rend_data_dup(const rend_data_t *data);
rend_data_t *rend_data_client_create(const char *onion_address,
@@ -70,18 +201,84 @@ const char *rend_data_get_desc_id(const rend_data_t *rend_data,
const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data,
size_t *len_out);
+routerstatus_t *pick_hsdir(const char *desc_id, const char *desc_id_base32);
+
+void hs_get_subcredential(const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ uint8_t *subcred_out);
+
+uint64_t hs_get_previous_time_period_num(time_t now);
+uint64_t hs_get_time_period_num(time_t now);
uint64_t hs_get_next_time_period_num(time_t now);
+time_t hs_get_start_time_of_next_time_period(time_t now);
+
+link_specifier_t *hs_link_specifier_dup(const link_specifier_t *lspec);
+
+MOCK_DECL(int, hs_in_period_between_tp_and_srv,
+ (const networkstatus_t *consensus, time_t now));
+
+uint8_t *hs_get_current_srv(uint64_t time_period_num,
+ const networkstatus_t *ns);
+uint8_t *hs_get_previous_srv(uint64_t time_period_num,
+ const networkstatus_t *ns);
+
+void hs_build_hsdir_index(const ed25519_public_key_t *identity_pk,
+ const uint8_t *srv, uint64_t period_num,
+ uint8_t *hsdir_index_out);
+void hs_build_hs_index(uint64_t replica,
+ const ed25519_public_key_t *blinded_pk,
+ uint64_t period_num, uint8_t *hs_index_out);
+
+int32_t hs_get_hsdir_n_replicas(void);
+int32_t hs_get_hsdir_spread_fetch(void);
+int32_t hs_get_hsdir_spread_store(void);
+
+void hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
+ uint64_t time_period_num,
+ int use_second_hsdir_index,
+ int for_fetching, smartlist_t *responsible_dirs);
+routerstatus_t *hs_pick_hsdir(smartlist_t *responsible_dirs,
+ const char *req_key_str);
+
+time_t hs_hsdir_requery_period(const or_options_t *options);
+time_t hs_lookup_last_hid_serv_request(routerstatus_t *hs_dir,
+ const char *desc_id_base32,
+ time_t now, int set);
+void hs_clean_last_hid_serv_requests(time_t now);
+void hs_purge_hid_serv_from_last_hid_serv_requests(const char *desc_id);
+void hs_purge_last_hid_serv_requests(void);
+
+int hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn);
+
+void hs_inc_rdv_stream_counter(origin_circuit_t *circ);
+void hs_dec_rdv_stream_counter(origin_circuit_t *circ);
+
+extend_info_t *hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
+ const curve25519_public_key_t *onion_key,
+ int direct_conn);
#ifdef HS_COMMON_PRIVATE
+STATIC void get_disaster_srv(uint64_t time_period_num, uint8_t *srv_out);
+
+/** The period for which a hidden service directory cannot be queried for
+ * the same descriptor ID again. */
+#define REND_HID_SERV_DIR_REQUERY_PERIOD (15 * 60)
+/** Test networks generate a new consensus every 5 or 10 seconds.
+ * So allow them to requery HSDirs much faster. */
+#define REND_HID_SERV_DIR_REQUERY_PERIOD_TESTING (5)
+
#ifdef TOR_UNIT_TESTS
+STATIC strmap_t *get_last_hid_serv_requests(void);
STATIC uint64_t get_time_period_length(void);
-STATIC uint64_t get_time_period_num(time_t now);
-#endif /* TOR_UNIT_TESTS */
+STATIC uint8_t *get_first_cached_disaster_srv(void);
+STATIC uint8_t *get_second_cached_disaster_srv(void);
+
+#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* HS_COMMON_PRIVATE */
+#endif /* defined(HS_COMMON_PRIVATE) */
-#endif /* TOR_HS_COMMON_H */
+#endif /* !defined(TOR_HS_COMMON_H) */
diff --git a/src/or/hs_config.c b/src/or/hs_config.c
new file mode 100644
index 0000000000..fa5c1ab176
--- /dev/null
+++ b/src/or/hs_config.c
@@ -0,0 +1,590 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_config.c
+ * \brief Implement hidden service configuration subsystem.
+ *
+ * \details
+ *
+ * This file has basically one main entry point: hs_config_service_all(). It
+ * takes the torrc options and configure hidden service from it. In validate
+ * mode, nothing is added to the global service list or keys are not generated
+ * nor loaded.
+ *
+ * A service is configured in two steps. It is first created using the tor
+ * options and then put in a staging list. It will stay there until
+ * hs_service_load_all_keys() is called. That function is responsible to
+ * load/generate the keys for the service in the staging list and if
+ * successful, transfert the service to the main global service list where
+ * at that point it is ready to be used.
+ *
+ * Configuration functions are per-version and there is a main generic one for
+ * every option that is common to all version (config_generic_service).
+ **/
+
+#define HS_CONFIG_PRIVATE
+
+#include "hs_common.h"
+#include "hs_config.h"
+#include "hs_service.h"
+#include "rendservice.h"
+
+/* Using the given list of services, stage them into our global state. Every
+ * service version are handled. This function can remove entries in the given
+ * service_list.
+ *
+ * Staging a service means that we take all services in service_list and we
+ * put them in the staging list (global) which acts as a temporary list that
+ * is used by the service loading key process. In other words, staging a
+ * service puts it in a list to be considered when loading the keys and then
+ * moved to the main global list. */
+static void
+stage_services(smartlist_t *service_list)
+{
+ tor_assert(service_list);
+
+ /* This is v2 specific. Trigger service pruning which will make sure the
+ * just configured services end up in the main global list. It should only
+ * be done in non validation mode because v2 subsystem handles service
+ * object differently. */
+ rend_service_prune_list();
+
+ /* Cleanup v2 service from the list, we don't need those object anymore
+ * because we validated them all against the others and we want to stage
+ * only >= v3 service. And remember, v2 has a different object type which is
+ * shadow copied from an hs_service_t type. */
+ SMARTLIST_FOREACH_BEGIN(service_list, hs_service_t *, s) {
+ if (s->config.version == HS_VERSION_TWO) {
+ SMARTLIST_DEL_CURRENT(service_list, s);
+ hs_service_free(s);
+ }
+ } SMARTLIST_FOREACH_END(s);
+
+ /* This is >= v3 specific. Using the newly configured service list, stage
+ * them into our global state. Every object ownership is lost after. */
+ hs_service_stage_services(service_list);
+}
+
+/* Validate the given service against all service in the given list. If the
+ * service is ephemeral, this function ignores it. Services with the same
+ * directory path aren't allowed and will return an error. If a duplicate is
+ * found, 1 is returned else 0 if none found. */
+static int
+service_is_duplicate_in_list(const smartlist_t *service_list,
+ const hs_service_t *service)
+{
+ int ret = 0;
+
+ tor_assert(service_list);
+ tor_assert(service);
+
+ /* Ephemeral service don't have a directory configured so no need to check
+ * for a service in the list having the same path. */
+ if (service->config.is_ephemeral) {
+ goto end;
+ }
+
+ /* XXX: Validate if we have any service that has the given service dir path.
+ * This has two problems:
+ *
+ * a) It's O(n^2), but the same comment from the bottom of
+ * rend_config_services() should apply.
+ *
+ * b) We only compare directory paths as strings, so we can't
+ * detect two distinct paths that specify the same directory
+ * (which can arise from symlinks, case-insensitivity, bind
+ * mounts, etc.).
+ *
+ * It also can't detect that two separate Tor instances are trying
+ * to use the same HiddenServiceDir; for that, we would need a
+ * lock file. But this is enough to detect a simple mistake that
+ * at least one person has actually made. */
+ SMARTLIST_FOREACH_BEGIN(service_list, const hs_service_t *, s) {
+ if (!strcmp(s->config.directory_path, service->config.directory_path)) {
+ log_warn(LD_REND, "Another hidden service is already configured "
+ "for directory %s",
+ escaped(service->config.directory_path));
+ ret = 1;
+ goto end;
+ }
+ } SMARTLIST_FOREACH_END(s);
+
+ end:
+ return ret;
+}
+
+/* Helper function: Given an configuration option name, its value, a minimum
+ * min and a maxium max, parse the value as a uint64_t. On success, ok is set
+ * to 1 and ret is the parsed value. On error, ok is set to 0 and ret must be
+ * ignored. This function logs both on error and success. */
+static uint64_t
+helper_parse_uint64(const char *opt, const char *value, uint64_t min,
+ uint64_t max, int *ok)
+{
+ uint64_t ret = 0;
+
+ tor_assert(opt);
+ tor_assert(value);
+ tor_assert(ok);
+
+ *ok = 0;
+ ret = tor_parse_uint64(value, 10, min, max, ok, NULL);
+ if (!*ok) {
+ log_warn(LD_CONFIG, "%s must be between %" PRIu64 " and %"PRIu64
+ ", not %s.",
+ opt, min, max, value);
+ goto err;
+ }
+ log_info(LD_CONFIG, "%s was parsed to %" PRIu64, opt, ret);
+ err:
+ return ret;
+}
+
+/* Return true iff the given options starting at line_ for a hidden service
+ * contains at least one invalid option. Each hidden service option don't
+ * apply to all versions so this function can find out. The line_ MUST start
+ * right after the HiddenServiceDir line of this service.
+ *
+ * This is mainly for usability so we can inform the user of any invalid
+ * option for the hidden service version instead of silently ignoring. */
+static int
+config_has_invalid_options(const config_line_t *line_,
+ const hs_service_t *service)
+{
+ int ret = 0;
+ const char **optlist;
+ const config_line_t *line;
+
+ tor_assert(service);
+ tor_assert(service->config.version <= HS_VERSION_MAX);
+
+ /* List of options that a v3 service doesn't support thus must exclude from
+ * its configuration. */
+ const char *opts_exclude_v3[] = {
+ "HiddenServiceAuthorizeClient",
+ NULL /* End marker. */
+ };
+
+ /* Defining the size explicitly allows us to take advantage of the compiler
+ * which warns us if we ever bump the max version but forget to grow this
+ * array. The plus one is because we have a version 0 :). */
+ struct {
+ const char **list;
+ } exclude_lists[HS_VERSION_MAX + 1] = {
+ { NULL }, /* v0. */
+ { NULL }, /* v1. */
+ { NULL }, /* v2 */
+ { opts_exclude_v3 }, /* v3. */
+ };
+
+ optlist = exclude_lists[service->config.version].list;
+ if (optlist == NULL) {
+ /* No exclude options to look at for this version. */
+ goto end;
+ }
+ for (int i = 0; optlist[i]; i++) {
+ const char *opt = optlist[i];
+ for (line = line_; line; line = line->next) {
+ if (!strcasecmp(line->key, "HiddenServiceDir")) {
+ /* We just hit the next hidden service, stop right now. */
+ goto end;
+ }
+ if (!strcasecmp(line->key, opt)) {
+ log_warn(LD_CONFIG, "Hidden service option %s is incompatible with "
+ "version %" PRIu32 " of service in %s",
+ opt, service->config.version,
+ service->config.directory_path);
+ ret = 1;
+ /* Continue the loop so we can find all possible options. */
+ continue;
+ }
+ }
+ }
+ end:
+ return ret;
+}
+
+/* Validate service configuration. This is used when loading the configuration
+ * and once we've setup a service object, it's config object is passed to this
+ * function for further validation. This does not validate service key
+ * material. Return 0 if valid else -1 if invalid. */
+static int
+config_validate_service(const hs_service_config_t *config)
+{
+ tor_assert(config);
+
+ /* Amount of ports validation. */
+ if (!config->ports || smartlist_len(config->ports) == 0) {
+ log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.",
+ escaped(config->directory_path));
+ goto invalid;
+ }
+
+ /* Valid. */
+ return 0;
+ invalid:
+ return -1;
+}
+
+/* Configuration funcion for a version 3 service. The line_ must be pointing
+ * to the directive directly after a HiddenServiceDir. That way, when hitting
+ * the next HiddenServiceDir line or reaching the end of the list of lines, we
+ * know that we have to stop looking for more options. The given service
+ * object must be already allocated and passed through
+ * config_generic_service() prior to calling this function.
+ *
+ * Return 0 on success else a negative value. */
+static int
+config_service_v3(const config_line_t *line_,
+ hs_service_config_t *config)
+{
+ int have_num_ip = 0;
+ const char *dup_opt_seen = NULL;
+ const config_line_t *line;
+
+ tor_assert(config);
+
+ for (line = line_; line; line = line->next) {
+ int ok = 0;
+ if (!strcasecmp(line->key, "HiddenServiceDir")) {
+ /* We just hit the next hidden service, stop right now. */
+ break;
+ }
+ /* Number of introduction points. */
+ if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) {
+ config->num_intro_points =
+ (unsigned int) helper_parse_uint64(line->key, line->value,
+ NUM_INTRO_POINTS_DEFAULT,
+ HS_CONFIG_V3_MAX_INTRO_POINTS,
+ &ok);
+ if (!ok || have_num_ip) {
+ if (have_num_ip)
+ dup_opt_seen = line->key;
+ goto err;
+ }
+ have_num_ip = 1;
+ continue;
+ }
+ }
+
+ /* We do not load the key material for the service at this stage. This is
+ * done later once tor can confirm that it is in a running state. */
+
+ /* We are about to return a fully configured service so do one last pass of
+ * validation at it. */
+ if (config_validate_service(config) < 0) {
+ goto err;
+ }
+
+ return 0;
+ err:
+ if (dup_opt_seen) {
+ log_warn(LD_CONFIG, "Duplicate directive %s.", dup_opt_seen);
+ }
+ return -1;
+}
+
+/* Configure a service using the given options in line_ and options. This is
+ * called for any service regardless of its version which means that all
+ * directives in this function are generic to any service version. This
+ * function will also check the validity of the service directory path.
+ *
+ * The line_ must be pointing to the directive directly after a
+ * HiddenServiceDir. That way, when hitting the next HiddenServiceDir line or
+ * reaching the end of the list of lines, we know that we have to stop looking
+ * for more options.
+ *
+ * Return 0 on success else -1. */
+static int
+config_generic_service(const config_line_t *line_,
+ const or_options_t *options,
+ hs_service_t *service)
+{
+ int dir_seen = 0;
+ const config_line_t *line;
+ hs_service_config_t *config;
+ /* If this is set, we've seen a duplicate of this option. Keep the string
+ * so we can log the directive. */
+ const char *dup_opt_seen = NULL;
+ /* These variables will tell us if we ever have duplicate. */
+ int have_version = 0, have_allow_unknown_ports = 0;
+ int have_dir_group_read = 0, have_max_streams = 0;
+ int have_max_streams_close = 0;
+
+ tor_assert(line_);
+ tor_assert(options);
+ tor_assert(service);
+
+ /* Makes thing easier. */
+ config = &service->config;
+
+ /* The first line starts with HiddenServiceDir so we consider what's next is
+ * the configuration of the service. */
+ for (line = line_; line ; line = line->next) {
+ int ok = 0;
+
+ /* This indicate that we have a new service to configure. */
+ if (!strcasecmp(line->key, "HiddenServiceDir")) {
+ /* This function only configures one service at a time so if we've
+ * already seen one, stop right now. */
+ if (dir_seen) {
+ break;
+ }
+ /* Ok, we've seen one and we are about to configure it. */
+ dir_seen = 1;
+ config->directory_path = tor_strdup(line->value);
+ log_info(LD_CONFIG, "HiddenServiceDir=%s. Configuring...",
+ escaped(config->directory_path));
+ continue;
+ }
+ if (BUG(!dir_seen)) {
+ goto err;
+ }
+ /* Version of the service. */
+ if (!strcasecmp(line->key, "HiddenServiceVersion")) {
+ service->config.version =
+ (uint32_t) helper_parse_uint64(line->key, line->value, HS_VERSION_MIN,
+ HS_VERSION_MAX, &ok);
+ if (!ok || have_version) {
+ if (have_version)
+ dup_opt_seen = line->key;
+ goto err;
+ }
+ have_version = 1;
+ continue;
+ }
+ /* Virtual port. */
+ if (!strcasecmp(line->key, "HiddenServicePort")) {
+ char *err_msg = NULL;
+ /* XXX: Can we rename this? */
+ rend_service_port_config_t *portcfg =
+ rend_service_parse_port_config(line->value, " ", &err_msg);
+ if (!portcfg) {
+ if (err_msg) {
+ log_warn(LD_CONFIG, "%s", err_msg);
+ }
+ tor_free(err_msg);
+ goto err;
+ }
+ tor_assert(!err_msg);
+ smartlist_add(config->ports, portcfg);
+ log_info(LD_CONFIG, "HiddenServicePort=%s for %s",
+ line->value, escaped(config->directory_path));
+ continue;
+ }
+ /* Do we allow unknown ports. */
+ if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
+ config->allow_unknown_ports =
+ (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
+ if (!ok || have_allow_unknown_ports) {
+ if (have_allow_unknown_ports)
+ dup_opt_seen = line->key;
+ goto err;
+ }
+ have_allow_unknown_ports = 1;
+ continue;
+ }
+ /* Directory group readable. */
+ if (!strcasecmp(line->key, "HiddenServiceDirGroupReadable")) {
+ config->dir_group_readable =
+ (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
+ if (!ok || have_dir_group_read) {
+ if (have_dir_group_read)
+ dup_opt_seen = line->key;
+ goto err;
+ }
+ have_dir_group_read = 1;
+ continue;
+ }
+ /* Maximum streams per circuit. */
+ if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
+ config->max_streams_per_rdv_circuit =
+ helper_parse_uint64(line->key, line->value, 0,
+ HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT, &ok);
+ if (!ok || have_max_streams) {
+ if (have_max_streams)
+ dup_opt_seen = line->key;
+ goto err;
+ }
+ have_max_streams = 1;
+ continue;
+ }
+ /* Maximum amount of streams before we close the circuit. */
+ if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
+ config->max_streams_close_circuit =
+ (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
+ if (!ok || have_max_streams_close) {
+ if (have_max_streams_close)
+ dup_opt_seen = line->key;
+ goto err;
+ }
+ have_max_streams_close = 1;
+ continue;
+ }
+ }
+
+ /* Check if we are configured in non anonymous mode meaning every service
+ * becomes a single onion service. */
+ if (rend_service_non_anonymous_mode_enabled(options)) {
+ config->is_single_onion = 1;
+ /* We will add support for IPv6-only v3 single onion services in a future
+ * Tor version. This won't catch "ReachableAddresses reject *4", but that
+ * option doesn't work anyway. */
+ if (options->ClientUseIPv4 == 0 && config->version == HS_VERSION_THREE) {
+ log_warn(LD_CONFIG, "IPv6-only v3 single onion services are not "
+ "supported. Set HiddenServiceSingleHopMode 0 and "
+ "HiddenServiceNonAnonymousMode 0, or set ClientUseIPv4 1.");
+ goto err;
+ }
+ }
+
+ /* Success */
+ return 0;
+ err:
+ if (dup_opt_seen) {
+ log_warn(LD_CONFIG, "Duplicate directive %s.", dup_opt_seen);
+ }
+ return -1;
+}
+
+/* Configure a service using the given line and options. This function will
+ * call the corresponding configuration function for a specific service
+ * version and validate the service against the other ones. On success, add
+ * the service to the given list and return 0. On error, nothing is added to
+ * the list and a negative value is returned. */
+static int
+config_service(const config_line_t *line, const or_options_t *options,
+ smartlist_t *service_list)
+{
+ int ret;
+ hs_service_t *service = NULL;
+
+ tor_assert(line);
+ tor_assert(options);
+ tor_assert(service_list);
+
+ /* We have a new hidden service. */
+ service = hs_service_new(options);
+ /* We'll configure that service as a generic one and then pass it to a
+ * specific function according to the configured version number. */
+ if (config_generic_service(line, options, service) < 0) {
+ goto err;
+ }
+ tor_assert(service->config.version <= HS_VERSION_MAX);
+ /* Before we configure the service on a per-version basis, we'll make
+ * sure that this set of options for a service are valid that is for
+ * instance an option only for v2 is not used for v3. */
+ if (config_has_invalid_options(line->next, service)) {
+ goto err;
+ }
+ /* Check permission on service directory that was just parsed. And this must
+ * be done regardless of the service version. Do not ask for the directory
+ * to be created, this is done when the keys are loaded because we could be
+ * in validation mode right now. */
+ if (hs_check_service_private_dir(options->User,
+ service->config.directory_path,
+ service->config.dir_group_readable,
+ 0) < 0) {
+ goto err;
+ }
+ /* Different functions are in charge of specific options for a version. We
+ * start just after the service directory line so once we hit another
+ * directory line, the function knows that it has to stop parsing. */
+ switch (service->config.version) {
+ case HS_VERSION_TWO:
+ ret = rend_config_service(line->next, options, &service->config);
+ break;
+ case HS_VERSION_THREE:
+ ret = config_service_v3(line->next, &service->config);
+ break;
+ default:
+ /* We do validate before if we support the parsed version. */
+ tor_assert_nonfatal_unreached();
+ goto err;
+ }
+ if (ret < 0) {
+ goto err;
+ }
+ /* We'll check if this service can be kept depending on the others
+ * configured previously. */
+ if (service_is_duplicate_in_list(service_list, service)) {
+ goto err;
+ }
+ /* Passes, add it to the given list. */
+ smartlist_add(service_list, service);
+ return 0;
+
+ err:
+ hs_service_free(service);
+ return -1;
+}
+
+/* From a set of <b>options</b>, setup every hidden service found. Return 0 on
+ * success or -1 on failure. If <b>validate_only</b> is set, parse, warn and
+ * return as normal, but don't actually change the configured services. */
+int
+hs_config_service_all(const or_options_t *options, int validate_only)
+{
+ int dir_option_seen = 0, ret = -1;
+ const config_line_t *line;
+ smartlist_t *new_service_list = NULL;
+
+ tor_assert(options);
+
+ /* Newly configured service are put in that list which is then used for
+ * validation and staging for >= v3. */
+ new_service_list = smartlist_new();
+
+ for (line = options->RendConfigLines; line; line = line->next) {
+ /* Ignore all directives that aren't the start of a service. */
+ if (strcasecmp(line->key, "HiddenServiceDir")) {
+ if (!dir_option_seen) {
+ log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
+ line->key);
+ goto err;
+ }
+ continue;
+ }
+ /* Flag that we've seen a directory directive and we'll use it to make
+ * sure that the torrc options ordering is actually valid. */
+ dir_option_seen = 1;
+
+ /* Try to configure this service now. On success, it will be added to the
+ * list and validated against the service in that same list. */
+ if (config_service(line, options, new_service_list) < 0) {
+ goto err;
+ }
+ }
+
+ /* In non validation mode, we'll stage those services we just successfully
+ * configured. Service ownership is transfered from the list to the global
+ * state. If any service is invalid, it will be removed from the list and
+ * freed. All versions are handled in that function. */
+ if (!validate_only) {
+ stage_services(new_service_list);
+ } else {
+ /* We've just validated that we were able to build a clean working list of
+ * services. We don't need those objects anymore. */
+ SMARTLIST_FOREACH(new_service_list, hs_service_t *, s,
+ hs_service_free(s));
+ /* For the v2 subsystem, the configuration function adds the service
+ * object to the staging list and it is transferred in the main list
+ * through the prunning process. In validation mode, we thus have to purge
+ * the staging list so it's not kept in memory as valid service. */
+ rend_service_free_staging_list();
+ }
+
+ /* Success. Note that the service list has no ownership of its content. */
+ ret = 0;
+ goto end;
+
+ err:
+ SMARTLIST_FOREACH(new_service_list, hs_service_t *, s, hs_service_free(s));
+
+ end:
+ smartlist_free(new_service_list);
+ /* Tor main should call the free all function on error. */
+ return ret;
+}
+
diff --git a/src/or/hs_config.h b/src/or/hs_config.h
new file mode 100644
index 0000000000..6cd7aed460
--- /dev/null
+++ b/src/or/hs_config.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_config.h
+ * \brief Header file containing configuration ABI/API for the HS subsytem.
+ **/
+
+#ifndef TOR_HS_CONFIG_H
+#define TOR_HS_CONFIG_H
+
+#include "or.h"
+
+/* Max value for HiddenServiceMaxStreams */
+#define HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT 65535
+/* Maximum number of intro points per version 3 services. */
+#define HS_CONFIG_V3_MAX_INTRO_POINTS 20
+
+/* API */
+
+int hs_config_service_all(const or_options_t *options, int validate_only);
+
+#endif /* !defined(TOR_HS_CONFIG_H) */
+
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index 3ec02618bf..fef0607c1d 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -55,13 +55,14 @@
/* For unit tests.*/
#define HS_DESCRIPTOR_PRIVATE
-#include "hs_descriptor.h"
-
#include "or.h"
#include "ed25519_cert.h" /* Trunnel interface. */
+#include "hs_descriptor.h"
+#include "circuitbuild.h"
#include "parsecommon.h"
#include "rendcache.h"
#include "hs_cache.h"
+#include "hs_config.h"
#include "torcert.h" /* tor_cert_encode_ed22519() */
/* Constant string value used for the descriptor format. */
@@ -77,6 +78,7 @@
#define str_intro_auth_required "intro-auth-required"
#define str_single_onion "single-onion-service"
#define str_intro_point "introduction-point"
+#define str_ip_onion_key "onion-key"
#define str_ip_auth_key "auth-key"
#define str_ip_enc_key "enc-key"
#define str_ip_enc_key_cert "enc-key-cert"
@@ -135,6 +137,7 @@ static token_rule_t hs_desc_encrypted_v3_token_table[] = {
/* Descriptor ruleset for the introduction points section. */
static token_rule_t hs_desc_intro_point_v3_token_table[] = {
T1_START(str_intro_point, R3_INTRODUCTION_POINT, EQ(1), NO_OBJ),
+ T1N(str_ip_onion_key, R3_INTRO_ONION_KEY, GE(2), OBJ_OK),
T1(str_ip_auth_key, R3_INTRO_AUTH_KEY, NO_ARGS, NEED_OBJ),
T1(str_ip_enc_key, R3_INTRO_ENC_KEY, GE(2), OBJ_OK),
T1(str_ip_enc_key_cert, R3_INTRO_ENC_KEY_CERT, ARGS, OBJ_OK),
@@ -143,29 +146,6 @@ static token_rule_t hs_desc_intro_point_v3_token_table[] = {
END_OF_TABLE
};
-/* Free a descriptor intro point object. */
-STATIC void
-desc_intro_point_free(hs_desc_intro_point_t *ip)
-{
- if (!ip) {
- return;
- }
- if (ip->link_specifiers) {
- SMARTLIST_FOREACH(ip->link_specifiers, hs_desc_link_specifier_t *,
- ls, tor_free(ls));
- smartlist_free(ip->link_specifiers);
- }
- tor_cert_free(ip->auth_key_cert);
- tor_cert_free(ip->enc_key_cert);
- if (ip->legacy.key) {
- crypto_pk_free(ip->legacy.key);
- }
- if (ip->legacy.cert.encoded) {
- tor_free(ip->legacy.cert.encoded);
- }
- tor_free(ip);
-}
-
/* Free the content of the plaintext section of a descriptor. */
STATIC void
desc_plaintext_data_free_contents(hs_desc_plaintext_data_t *desc)
@@ -196,7 +176,7 @@ desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc)
}
if (desc->intro_points) {
SMARTLIST_FOREACH(desc->intro_points, hs_desc_intro_point_t *, ip,
- desc_intro_point_free(ip));
+ hs_desc_intro_point_free(ip));
smartlist_free(desc->intro_points);
}
memwipe(desc, 0, sizeof(*desc));
@@ -255,7 +235,7 @@ build_secret_input(const hs_descriptor_t *desc, uint8_t *dst, size_t dstlen)
memcpy(dst + offset, desc->subcredential, sizeof(desc->subcredential));
offset += sizeof(desc->subcredential);
/* Copy revision counter value. */
- set_uint64(dst + offset, tor_ntohll(desc->plaintext_data.revision_counter));
+ set_uint64(dst + offset, tor_htonll(desc->plaintext_data.revision_counter));
offset += sizeof(uint64_t);
tor_assert(HS_DESC_ENCRYPTED_SECRET_INPUT_LEN == offset);
}
@@ -351,42 +331,10 @@ encode_link_specifiers(const smartlist_t *specs)
SMARTLIST_FOREACH_BEGIN(specs, const hs_desc_link_specifier_t *,
spec) {
- link_specifier_t *ls = link_specifier_new();
- link_specifier_set_ls_type(ls, spec->type);
-
- switch (spec->type) {
- case LS_IPV4:
- link_specifier_set_un_ipv4_addr(ls,
- tor_addr_to_ipv4h(&spec->u.ap.addr));
- link_specifier_set_un_ipv4_port(ls, spec->u.ap.port);
- /* Four bytes IPv4 and two bytes port. */
- link_specifier_set_ls_len(ls, sizeof(spec->u.ap.addr.addr.in_addr) +
- sizeof(spec->u.ap.port));
- break;
- case LS_IPV6:
- {
- size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
- const uint8_t *in6_addr = tor_addr_to_in6_addr8(&spec->u.ap.addr);
- uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
- memcpy(ipv6_array, in6_addr, addr_len);
- link_specifier_set_un_ipv6_port(ls, spec->u.ap.port);
- /* Sixteen bytes IPv6 and two bytes port. */
- link_specifier_set_ls_len(ls, addr_len + sizeof(spec->u.ap.port));
- break;
- }
- case LS_LEGACY_ID:
- {
- size_t legacy_id_len = link_specifier_getlen_un_legacy_id(ls);
- uint8_t *legacy_id_array = link_specifier_getarray_un_legacy_id(ls);
- memcpy(legacy_id_array, spec->u.legacy_id, legacy_id_len);
- link_specifier_set_ls_len(ls, legacy_id_len);
- break;
+ link_specifier_t *ls = hs_desc_lspec_to_trunnel(spec);
+ if (ls) {
+ link_specifier_list_add_spec(lslist, ls);
}
- default:
- tor_assert(0);
- }
-
- link_specifier_list_add_spec(lslist, ls);
} SMARTLIST_FOREACH_END(spec);
{
@@ -478,6 +426,26 @@ encode_enc_key(const hs_desc_intro_point_t *ip)
return encoded;
}
+/* Encode an introduction point onion key. Return a newly allocated string
+ * with it. On failure, return NULL. */
+static char *
+encode_onion_key(const hs_desc_intro_point_t *ip)
+{
+ char *encoded = NULL;
+ char key_b64[CURVE25519_BASE64_PADDED_LEN + 1];
+
+ tor_assert(ip);
+
+ /* Base64 encode the encryption key for the "onion-key" field. */
+ if (curve25519_public_to_base64(key_b64, &ip->onion_key) < 0) {
+ goto done;
+ }
+ tor_asprintf(&encoded, "%s ntor %s", str_ip_onion_key, key_b64);
+
+ done:
+ return encoded;
+}
+
/* Encode an introduction point object and return a newly allocated string
* with it. On failure, return NULL. */
static char *
@@ -497,6 +465,16 @@ encode_intro_point(const ed25519_public_key_t *sig_key,
tor_free(ls_str);
}
+ /* Onion key encoding. */
+ {
+ char *encoded_onion_key = encode_onion_key(ip);
+ if (encoded_onion_key == NULL) {
+ goto err;
+ }
+ smartlist_add_asprintf(lines, "%s", encoded_onion_key);
+ tor_free(encoded_onion_key);
+ }
+
/* Authentication key encoding. */
{
char *encoded_cert;
@@ -987,6 +965,10 @@ desc_encode_v3(const hs_descriptor_t *desc,
tor_assert(encoded_out);
tor_assert(desc->plaintext_data.version == 3);
+ if (BUG(desc->subcredential == NULL)) {
+ goto err;
+ }
+
/* Build the non-encrypted values. */
{
char *encoded_cert;
@@ -1133,6 +1115,15 @@ decode_link_specifiers(const char *encoded)
memcpy(hs_spec->u.legacy_id, link_specifier_getarray_un_legacy_id(ls),
sizeof(hs_spec->u.legacy_id));
break;
+ case LS_ED25519_ID:
+ /* Both are known at compile time so let's make sure they are the same
+ * else we can copy memory out of bound. */
+ tor_assert(link_specifier_getlen_un_ed25519_id(ls) ==
+ sizeof(hs_spec->u.ed25519_id));
+ memcpy(hs_spec->u.ed25519_id,
+ link_specifier_getconstarray_un_ed25519_id(ls),
+ sizeof(hs_spec->u.ed25519_id));
+ break;
default:
goto err;
}
@@ -1242,7 +1233,8 @@ cert_is_valid(tor_cert_t *cert, uint8_t type, const char *log_obj_type)
/* The following will not only check if the signature matches but also the
* expiration date and overall validity. */
if (tor_cert_checksig(cert, &cert->signing_key, approx_time()) < 0) {
- log_warn(LD_REND, "Invalid signature for %s.", log_obj_type);
+ log_warn(LD_REND, "Invalid signature for %s: %s", log_obj_type,
+ tor_cert_describe_signature_status(cert));
goto err;
}
@@ -1311,13 +1303,17 @@ encrypted_data_length_is_valid(size_t len)
* <b>encrypted_blob_size</b>. Use the descriptor object <b>desc</b> to
* generate the right decryption keys; set <b>decrypted_out</b> to the
* plaintext. If <b>is_superencrypted_layer</b> is set, this is the outter
- * encrypted layer of the descriptor. */
-static size_t
-decrypt_desc_layer(const hs_descriptor_t *desc,
- const uint8_t *encrypted_blob,
- size_t encrypted_blob_size,
- int is_superencrypted_layer,
- char **decrypted_out)
+ * encrypted layer of the descriptor.
+ *
+ * On any error case, including an empty output, return 0 and set
+ * *<b>decrypted_out</b> to NULL.
+ */
+MOCK_IMPL(STATIC size_t,
+decrypt_desc_layer,(const hs_descriptor_t *desc,
+ const uint8_t *encrypted_blob,
+ size_t encrypted_blob_size,
+ int is_superencrypted_layer,
+ char **decrypted_out))
{
uint8_t *decrypted = NULL;
uint8_t secret_key[HS_DESC_ENCRYPTED_KEY_LEN], secret_iv[CIPHER_IV_LEN];
@@ -1391,6 +1387,11 @@ decrypt_desc_layer(const hs_descriptor_t *desc,
}
}
+ if (result_len == 0) {
+ /* Treat this as an error, so that somebody will free the output. */
+ goto err;
+ }
+
/* Make sure to NUL terminate the string. */
decrypted[encrypted_len] = '\0';
*decrypted_out = (char *) decrypted;
@@ -1625,6 +1626,50 @@ decode_intro_legacy_key(const directory_token_t *tok,
return -1;
}
+/* Dig into the descriptor <b>tokens</b> to find the onion key we should use
+ * for this intro point, and set it into <b>onion_key_out</b>. Return 0 if it
+ * was found and well-formed, otherwise return -1 in case of errors. */
+static int
+set_intro_point_onion_key(curve25519_public_key_t *onion_key_out,
+ const smartlist_t *tokens)
+{
+ int retval = -1;
+ smartlist_t *onion_keys = NULL;
+
+ tor_assert(onion_key_out);
+
+ onion_keys = find_all_by_keyword(tokens, R3_INTRO_ONION_KEY);
+ if (!onion_keys) {
+ log_warn(LD_REND, "Descriptor did not contain intro onion keys");
+ goto err;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(onion_keys, directory_token_t *, tok) {
+ /* This field is using GE(2) so for possible forward compatibility, we
+ * accept more fields but must be at least 2. */
+ tor_assert(tok->n_args >= 2);
+
+ /* Try to find an ntor key, it's the only recognized type right now */
+ if (!strcmp(tok->args[0], "ntor")) {
+ if (curve25519_public_from_base64(onion_key_out, tok->args[1]) < 0) {
+ log_warn(LD_REND, "Introduction point ntor onion-key is invalid");
+ goto err;
+ }
+ /* Got the onion key! Set the appropriate retval */
+ retval = 0;
+ }
+ } SMARTLIST_FOREACH_END(tok);
+
+ /* Log an error if we didn't find it :( */
+ if (retval < 0) {
+ log_warn(LD_REND, "Descriptor did not contain ntor onion keys");
+ }
+
+ err:
+ smartlist_free(onion_keys);
+ return retval;
+}
+
/* Given the start of a section and the end of it, decode a single
* introduction point from that section. Return a newly allocated introduction
* point object containing the decoded data. Return NULL if the section can't
@@ -1650,17 +1695,24 @@ decode_introduction_point(const hs_descriptor_t *desc, const char *start)
/* Ok we seem to have a well formed section containing enough tokens to
* parse. Allocate our IP object and try to populate it. */
- ip = tor_malloc_zero(sizeof(hs_desc_intro_point_t));
+ ip = hs_desc_intro_point_new();
/* "introduction-point" SP link-specifiers NL */
tok = find_by_keyword(tokens, R3_INTRODUCTION_POINT);
tor_assert(tok->n_args == 1);
+ /* Our constructor creates this list by default so free it. */
+ smartlist_free(ip->link_specifiers);
ip->link_specifiers = decode_link_specifiers(tok->args[0]);
if (!ip->link_specifiers) {
log_warn(LD_REND, "Introduction point has invalid link specifiers");
goto err;
}
+ /* "onion-key" SP ntor SP key NL */
+ if (set_intro_point_onion_key(&ip->onion_key, tokens) < 0) {
+ goto err;
+ }
+
/* "auth-key" NL certificate NL */
tok = find_by_keyword(tokens, R3_INTRO_AUTH_KEY);
tor_assert(tok->object_body);
@@ -1677,7 +1729,8 @@ decode_introduction_point(const hs_descriptor_t *desc, const char *start)
/* Validate authentication certificate with descriptor signing key. */
if (tor_cert_checksig(ip->auth_key_cert,
&desc->plaintext_data.signing_pubkey, 0) < 0) {
- log_warn(LD_REND, "Invalid authentication key signature");
+ log_warn(LD_REND, "Invalid authentication key signature: %s",
+ tor_cert_describe_signature_status(ip->auth_key_cert));
goto err;
}
@@ -1714,7 +1767,8 @@ decode_introduction_point(const hs_descriptor_t *desc, const char *start)
}
if (tor_cert_checksig(ip->enc_key_cert,
&desc->plaintext_data.signing_pubkey, 0) < 0) {
- log_warn(LD_REND, "Invalid encryption key signature");
+ log_warn(LD_REND, "Invalid encryption key signature: %s",
+ tor_cert_describe_signature_status(ip->enc_key_cert));
goto err;
}
/* It is successfully cross certified. Flag the object. */
@@ -1732,7 +1786,7 @@ decode_introduction_point(const hs_descriptor_t *desc, const char *start)
goto done;
err:
- desc_intro_point_free(ip);
+ hs_desc_intro_point_free(ip);
ip = NULL;
done:
@@ -1747,18 +1801,13 @@ decode_introduction_point(const hs_descriptor_t *desc, const char *start)
/* Given a descriptor string at <b>data</b>, decode all possible introduction
* points that we can find. Add the introduction point object to desc_enc as we
- * find them. Return 0 on success.
- *
- * On error, a negative value is returned. It is possible that some intro
- * point object have been added to the desc_enc, they should be considered
- * invalid. One single bad encoded introduction point will make this function
- * return an error. */
-STATIC int
+ * find them. This function can't fail and it is possible that zero
+ * introduction points can be decoded. */
+static void
decode_intro_points(const hs_descriptor_t *desc,
hs_desc_encrypted_data_t *desc_enc,
const char *data)
{
- int retval = -1;
smartlist_t *chunked_desc = smartlist_new();
smartlist_t *intro_points = smartlist_new();
@@ -1799,22 +1848,19 @@ decode_intro_points(const hs_descriptor_t *desc,
SMARTLIST_FOREACH_BEGIN(intro_points, const char *, intro_point) {
hs_desc_intro_point_t *ip = decode_introduction_point(desc, intro_point);
if (!ip) {
- /* Malformed introduction point section. Stop right away, this
- * descriptor shouldn't be used. */
- goto err;
+ /* Malformed introduction point section. We'll ignore this introduction
+ * point and continue parsing. New or unknown fields are possible for
+ * forward compatibility. */
+ continue;
}
smartlist_add(desc_enc->intro_points, ip);
} SMARTLIST_FOREACH_END(intro_point);
done:
- retval = 0;
-
- err:
SMARTLIST_FOREACH(chunked_desc, char *, a, tor_free(a));
smartlist_free(chunked_desc);
SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a));
smartlist_free(intro_points);
- return retval;
}
/* Return 1 iff the given base64 encoded signature in b64_sig from the encoded
* descriptor in encoded_desc validates the descriptor content. */
@@ -2041,14 +2087,14 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
/* Initialize the descriptor's introduction point list before we start
* decoding. Having 0 intro point is valid. Then decode them all. */
desc_encrypted_out->intro_points = smartlist_new();
- if (decode_intro_points(desc, desc_encrypted_out, message) < 0) {
- goto err;
- }
+ decode_intro_points(desc, desc_encrypted_out, message);
+
/* Validation of maximum introduction points allowed. */
- if (smartlist_len(desc_encrypted_out->intro_points) > MAX_INTRO_POINTS) {
+ if (smartlist_len(desc_encrypted_out->intro_points) >
+ HS_CONFIG_V3_MAX_INTRO_POINTS) {
log_warn(LD_REND, "Service descriptor contains too many introduction "
"points. Maximum allowed is %d but we have %d",
- MAX_INTRO_POINTS,
+ HS_CONFIG_V3_MAX_INTRO_POINTS,
smartlist_len(desc_encrypted_out->intro_points));
goto err;
}
@@ -2223,7 +2269,7 @@ hs_desc_decode_descriptor(const char *encoded,
const uint8_t *subcredential,
hs_descriptor_t **desc_out)
{
- int ret;
+ int ret = -1;
hs_descriptor_t *desc;
tor_assert(encoded);
@@ -2231,10 +2277,13 @@ hs_desc_decode_descriptor(const char *encoded,
desc = tor_malloc_zero(sizeof(hs_descriptor_t));
/* Subcredentials are optional. */
- if (subcredential) {
- memcpy(desc->subcredential, subcredential, sizeof(desc->subcredential));
+ if (BUG(!subcredential)) {
+ log_warn(LD_GENERAL, "Tried to decrypt without subcred. Impossible!");
+ goto err;
}
+ memcpy(desc->subcredential, subcredential, sizeof(desc->subcredential));
+
ret = hs_desc_decode_plaintext(encoded, &desc->plaintext_data);
if (ret < 0) {
goto err;
@@ -2280,10 +2329,10 @@ static int
*
* Return 0 on success and encoded_out is a valid pointer. On error, -1 is
* returned and encoded_out is set to NULL. */
-int
-hs_desc_encode_descriptor(const hs_descriptor_t *desc,
- const ed25519_keypair_t *signing_kp,
- char **encoded_out)
+MOCK_IMPL(int,
+hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
+ const ed25519_keypair_t *signing_kp,
+ char **encoded_out))
{
int ret = -1;
uint32_t version;
@@ -2360,3 +2409,197 @@ hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data)
data->superencrypted_blob_size);
}
+/* Return the size in bytes of the given encrypted data object. Used by OOM
+ * subsystem. */
+static size_t
+hs_desc_encrypted_obj_size(const hs_desc_encrypted_data_t *data)
+{
+ tor_assert(data);
+ size_t intro_size = 0;
+ if (data->intro_auth_types) {
+ intro_size +=
+ smartlist_len(data->intro_auth_types) * sizeof(intro_auth_types);
+ }
+ if (data->intro_points) {
+ /* XXX could follow pointers here and get more accurate size */
+ intro_size +=
+ smartlist_len(data->intro_points) * sizeof(hs_desc_intro_point_t);
+ }
+
+ return sizeof(*data) + intro_size;
+}
+
+/* Return the size in bytes of the given descriptor object. Used by OOM
+ * subsystem. */
+ size_t
+hs_desc_obj_size(const hs_descriptor_t *data)
+{
+ tor_assert(data);
+ return (hs_desc_plaintext_obj_size(&data->plaintext_data) +
+ hs_desc_encrypted_obj_size(&data->encrypted_data) +
+ sizeof(data->subcredential));
+}
+
+/* Return a newly allocated descriptor intro point. */
+hs_desc_intro_point_t *
+hs_desc_intro_point_new(void)
+{
+ hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip));
+ ip->link_specifiers = smartlist_new();
+ return ip;
+}
+
+/* Free a descriptor intro point object. */
+void
+hs_desc_intro_point_free(hs_desc_intro_point_t *ip)
+{
+ if (ip == NULL) {
+ return;
+ }
+ if (ip->link_specifiers) {
+ SMARTLIST_FOREACH(ip->link_specifiers, hs_desc_link_specifier_t *,
+ ls, hs_desc_link_specifier_free(ls));
+ smartlist_free(ip->link_specifiers);
+ }
+ tor_cert_free(ip->auth_key_cert);
+ tor_cert_free(ip->enc_key_cert);
+ crypto_pk_free(ip->legacy.key);
+ tor_free(ip->legacy.cert.encoded);
+ tor_free(ip);
+}
+
+/* Free the given descriptor link specifier. */
+void
+hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls)
+{
+ if (ls == NULL) {
+ return;
+ }
+ tor_free(ls);
+}
+
+/* Return a newly allocated descriptor link specifier using the given extend
+ * info and requested type. Return NULL on error. */
+hs_desc_link_specifier_t *
+hs_desc_link_specifier_new(const extend_info_t *info, uint8_t type)
+{
+ hs_desc_link_specifier_t *ls = NULL;
+
+ tor_assert(info);
+
+ ls = tor_malloc_zero(sizeof(*ls));
+ ls->type = type;
+ switch (ls->type) {
+ case LS_IPV4:
+ if (info->addr.family != AF_INET) {
+ goto err;
+ }
+ tor_addr_copy(&ls->u.ap.addr, &info->addr);
+ ls->u.ap.port = info->port;
+ break;
+ case LS_IPV6:
+ if (info->addr.family != AF_INET6) {
+ goto err;
+ }
+ tor_addr_copy(&ls->u.ap.addr, &info->addr);
+ ls->u.ap.port = info->port;
+ break;
+ case LS_LEGACY_ID:
+ /* Bug out if the identity digest is not set */
+ if (BUG(tor_mem_is_zero(info->identity_digest,
+ sizeof(info->identity_digest)))) {
+ goto err;
+ }
+ memcpy(ls->u.legacy_id, info->identity_digest, sizeof(ls->u.legacy_id));
+ break;
+ case LS_ED25519_ID:
+ /* ed25519 keys are optional for intro points */
+ if (ed25519_public_key_is_zero(&info->ed_identity)) {
+ goto err;
+ }
+ memcpy(ls->u.ed25519_id, info->ed_identity.pubkey,
+ sizeof(ls->u.ed25519_id));
+ break;
+ default:
+ /* Unknown type is code flow error. */
+ tor_assert(0);
+ }
+
+ return ls;
+ err:
+ tor_free(ls);
+ return NULL;
+}
+
+/* From the given descriptor, remove and free every introduction point. */
+void
+hs_descriptor_clear_intro_points(hs_descriptor_t *desc)
+{
+ smartlist_t *ips;
+
+ tor_assert(desc);
+
+ ips = desc->encrypted_data.intro_points;
+ if (ips) {
+ SMARTLIST_FOREACH(ips, hs_desc_intro_point_t *,
+ ip, hs_desc_intro_point_free(ip));
+ smartlist_clear(ips);
+ }
+}
+
+/* From a descriptor link specifier object spec, returned a newly allocated
+ * link specifier object that is the encoded representation of spec. Return
+ * NULL on error. */
+link_specifier_t *
+hs_desc_lspec_to_trunnel(const hs_desc_link_specifier_t *spec)
+{
+ tor_assert(spec);
+
+ link_specifier_t *ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, spec->type);
+
+ switch (spec->type) {
+ case LS_IPV4:
+ link_specifier_set_un_ipv4_addr(ls,
+ tor_addr_to_ipv4h(&spec->u.ap.addr));
+ link_specifier_set_un_ipv4_port(ls, spec->u.ap.port);
+ /* Four bytes IPv4 and two bytes port. */
+ link_specifier_set_ls_len(ls, sizeof(spec->u.ap.addr.addr.in_addr) +
+ sizeof(spec->u.ap.port));
+ break;
+ case LS_IPV6:
+ {
+ size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
+ const uint8_t *in6_addr = tor_addr_to_in6_addr8(&spec->u.ap.addr);
+ uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
+ memcpy(ipv6_array, in6_addr, addr_len);
+ link_specifier_set_un_ipv6_port(ls, spec->u.ap.port);
+ /* Sixteen bytes IPv6 and two bytes port. */
+ link_specifier_set_ls_len(ls, addr_len + sizeof(spec->u.ap.port));
+ break;
+ }
+ case LS_LEGACY_ID:
+ {
+ size_t legacy_id_len = link_specifier_getlen_un_legacy_id(ls);
+ uint8_t *legacy_id_array = link_specifier_getarray_un_legacy_id(ls);
+ memcpy(legacy_id_array, spec->u.legacy_id, legacy_id_len);
+ link_specifier_set_ls_len(ls, legacy_id_len);
+ break;
+ }
+ case LS_ED25519_ID:
+ {
+ size_t ed25519_id_len = link_specifier_getlen_un_ed25519_id(ls);
+ uint8_t *ed25519_id_array = link_specifier_getarray_un_ed25519_id(ls);
+ memcpy(ed25519_id_array, spec->u.ed25519_id, ed25519_id_len);
+ link_specifier_set_ls_len(ls, ed25519_id_len);
+ break;
+ }
+ default:
+ tor_assert_nonfatal_unreached();
+ link_specifier_free(ls);
+ ls = NULL;
+ }
+
+ return ls;
+}
+
diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h
index 136477ae3a..52bec8e244 100644
--- a/src/or/hs_descriptor.h
+++ b/src/or/hs_descriptor.h
@@ -18,17 +18,26 @@
#include "crypto_ed25519.h"
#include "torcert.h"
+/* Trunnel */
+struct link_specifier_t;
+
/* The earliest descriptor format version we support. */
#define HS_DESC_SUPPORTED_FORMAT_VERSION_MIN 3
/* The latest descriptor format version we support. */
#define HS_DESC_SUPPORTED_FORMAT_VERSION_MAX 3
+/* Default lifetime of a descriptor in seconds. The valus is set at 3 hours
+ * which is 180 minutes or 10800 seconds. */
+#define HS_DESC_DEFAULT_LIFETIME (3 * 60 * 60)
/* Maximum lifetime of a descriptor in seconds. The value is set at 12 hours
* which is 720 minutes or 43200 seconds. */
#define HS_DESC_MAX_LIFETIME (12 * 60 * 60)
/* Lifetime of certificate in the descriptor. This defines the lifetime of the
- * descriptor signing key and the cross certification cert of that key. */
-#define HS_DESC_CERT_LIFETIME (24 * 60 * 60)
+ * descriptor signing key and the cross certification cert of that key. It is
+ * set to 54 hours because a descriptor can be around for 48 hours and because
+ * consensuses are used after the hour, add an extra 6 hours to give some time
+ * for the service to stop using it. */
+#define HS_DESC_CERT_LIFETIME (54 * 60 * 60)
/* Length of the salt needed for the encrypted section of a descriptor. */
#define HS_DESC_ENCRYPTED_SALT_LEN 16
/* Length of the secret input needed for the KDF construction which derives
@@ -65,12 +74,14 @@ typedef struct hs_desc_link_specifier_t {
* specification. */
uint8_t type;
- /* It's either an address/port or a legacy identity fingerprint. */
+ /* It must be one of these types, can't be more than one. */
union {
/* IP address and port of the relay use to extend. */
tor_addr_port_t ap;
/* Legacy identity. A 20-byte SHA1 identity fingerprint. */
uint8_t legacy_id[DIGEST_LEN];
+ /* ed25519 identity. A 32-byte key. */
+ uint8_t ed25519_id[ED25519_PUBKEY_LEN];
} u;
} hs_desc_link_specifier_t;
@@ -80,6 +91,10 @@ typedef struct hs_desc_intro_point_t {
* contains hs_desc_link_specifier_t object. It MUST have at least one. */
smartlist_t *link_specifiers;
+ /* Onion key of the introduction point used to extend to it for the ntor
+ * handshake. */
+ curve25519_public_key_t onion_key;
+
/* Authentication key used to establish the introduction point circuit and
* cross-certifies the blinded public key for the replica thus signed by
* the blinded key and in turn signs it. */
@@ -197,9 +212,15 @@ void hs_descriptor_free(hs_descriptor_t *desc);
void hs_desc_plaintext_data_free(hs_desc_plaintext_data_t *desc);
void hs_desc_encrypted_data_free(hs_desc_encrypted_data_t *desc);
-int hs_desc_encode_descriptor(const hs_descriptor_t *desc,
- const ed25519_keypair_t *signing_kp,
- char **encoded_out);
+void hs_desc_link_specifier_free(hs_desc_link_specifier_t *ls);
+hs_desc_link_specifier_t *hs_desc_link_specifier_new(
+ const extend_info_t *info, uint8_t type);
+void hs_descriptor_clear_intro_points(hs_descriptor_t *desc);
+
+MOCK_DECL(int,
+ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
+ const ed25519_keypair_t *signing_kp,
+ char **encoded_out));
int hs_desc_decode_descriptor(const char *encoded,
const uint8_t *subcredential,
@@ -209,8 +230,15 @@ int hs_desc_decode_plaintext(const char *encoded,
int hs_desc_decode_encrypted(const hs_descriptor_t *desc,
hs_desc_encrypted_data_t *desc_out);
+size_t hs_desc_obj_size(const hs_descriptor_t *data);
size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data);
+hs_desc_intro_point_t *hs_desc_intro_point_new(void);
+void hs_desc_intro_point_free(hs_desc_intro_point_t *ip);
+
+link_specifier_t *hs_desc_lspec_to_trunnel(
+ const hs_desc_link_specifier_t *spec);
+
#ifdef HS_DESCRIPTOR_PRIVATE
/* Encoding. */
@@ -223,21 +251,23 @@ STATIC smartlist_t *decode_link_specifiers(const char *encoded);
STATIC hs_desc_intro_point_t *decode_introduction_point(
const hs_descriptor_t *desc,
const char *text);
-STATIC int decode_intro_points(const hs_descriptor_t *desc,
- hs_desc_encrypted_data_t *desc_enc,
- const char *data);
STATIC int encrypted_data_length_is_valid(size_t len);
STATIC int cert_is_valid(tor_cert_t *cert, uint8_t type,
const char *log_obj_type);
STATIC int desc_sig_is_valid(const char *b64_sig,
const ed25519_public_key_t *signing_pubkey,
const char *encoded_desc, size_t encoded_len);
-STATIC void desc_intro_point_free(hs_desc_intro_point_t *ip);
STATIC size_t decode_superencrypted(const char *message, size_t message_len,
uint8_t **encrypted_out);
STATIC void desc_plaintext_data_free_contents(hs_desc_plaintext_data_t *desc);
-#endif /* HS_DESCRIPTOR_PRIVATE */
+MOCK_DECL(STATIC size_t, decrypt_desc_layer,(const hs_descriptor_t *desc,
+ const uint8_t *encrypted_blob,
+ size_t encrypted_blob_size,
+ int is_superencrypted_layer,
+ char **decrypted_out));
+
+#endif /* defined(HS_DESCRIPTOR_PRIVATE) */
-#endif /* TOR_HS_DESCRIPTOR_H */
+#endif /* !defined(TOR_HS_DESCRIPTOR_H) */
diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c
new file mode 100644
index 0000000000..b0e4e36a9b
--- /dev/null
+++ b/src/or/hs_ident.c
@@ -0,0 +1,126 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_ident.c
+ * \brief Contains circuit and connection identifier code for the whole HS
+ * subsytem.
+ **/
+
+#include "hs_ident.h"
+
+/* Return a newly allocated circuit identifier. The given public key is copied
+ * identity_pk into the identifier. */
+hs_ident_circuit_t *
+hs_ident_circuit_new(const ed25519_public_key_t *identity_pk,
+ hs_ident_circuit_type_t circuit_type)
+{
+ tor_assert(circuit_type == HS_IDENT_CIRCUIT_INTRO ||
+ circuit_type == HS_IDENT_CIRCUIT_RENDEZVOUS);
+ hs_ident_circuit_t *ident = tor_malloc_zero(sizeof(*ident));
+ ed25519_pubkey_copy(&ident->identity_pk, identity_pk);
+ ident->circuit_type = circuit_type;
+ return ident;
+}
+
+/* Free the given circuit identifier. */
+void
+hs_ident_circuit_free(hs_ident_circuit_t *ident)
+{
+ if (ident == NULL) {
+ return;
+ }
+ memwipe(ident, 0, sizeof(hs_ident_circuit_t));
+ tor_free(ident);
+}
+
+/* For a given circuit identifier src, return a newly allocated copy of it.
+ * This can't fail. */
+hs_ident_circuit_t *
+hs_ident_circuit_dup(const hs_ident_circuit_t *src)
+{
+ hs_ident_circuit_t *ident = tor_malloc_zero(sizeof(*ident));
+ memcpy(ident, src, sizeof(*ident));
+ return ident;
+}
+
+/* For a given directory connection identifier src, return a newly allocated
+ * copy of it. This can't fail. */
+hs_ident_dir_conn_t *
+hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src)
+{
+ hs_ident_dir_conn_t *ident = tor_malloc_zero(sizeof(*ident));
+ memcpy(ident, src, sizeof(*ident));
+ return ident;
+}
+
+/* Free the given directory connection identifier. */
+void
+hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident)
+{
+ if (ident == NULL) {
+ return;
+ }
+ memwipe(ident, 0, sizeof(hs_ident_dir_conn_t));
+ tor_free(ident);
+}
+
+/* Initialized the allocated ident object with identity_pk and blinded_pk.
+ * None of them can be NULL since a valid directory connection identifier must
+ * have all fields set. */
+void
+hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ hs_ident_dir_conn_t *ident)
+{
+ tor_assert(identity_pk);
+ tor_assert(blinded_pk);
+ tor_assert(ident);
+
+ ed25519_pubkey_copy(&ident->identity_pk, identity_pk);
+ ed25519_pubkey_copy(&ident->blinded_pk, blinded_pk);
+}
+
+/* Return a newly allocated edge connection identifier. The given public key
+ * identity_pk is copied into the identifier. */
+hs_ident_edge_conn_t *
+hs_ident_edge_conn_new(const ed25519_public_key_t *identity_pk)
+{
+ hs_ident_edge_conn_t *ident = tor_malloc_zero(sizeof(*ident));
+ ed25519_pubkey_copy(&ident->identity_pk, identity_pk);
+ return ident;
+}
+
+/* Free the given edge connection identifier. */
+void
+hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident)
+{
+ if (ident == NULL) {
+ return;
+ }
+ memwipe(ident, 0, sizeof(hs_ident_edge_conn_t));
+ tor_free(ident);
+}
+
+/* Return true if the given ident is valid for an introduction circuit. */
+int
+hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident)
+{
+ if (ident == NULL) {
+ goto invalid;
+ }
+
+ if (ed25519_public_key_is_zero(&ident->identity_pk)) {
+ goto invalid;
+ }
+
+ if (ed25519_public_key_is_zero(&ident->intro_auth_pk)) {
+ goto invalid;
+ }
+
+ /* Valid. */
+ return 1;
+ invalid:
+ return 0;
+}
+
diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h
new file mode 100644
index 0000000000..03150d25ea
--- /dev/null
+++ b/src/or/hs_ident.h
@@ -0,0 +1,141 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_ident.h
+ * \brief Header file containing circuit and connection identifier data for
+ * the whole HS subsytem.
+ *
+ * \details
+ * This interface is used to uniquely identify a hidden service on a circuit
+ * or connection using the service identity public key. Once the circuit or
+ * connection subsystem calls in the hidden service one, we use those
+ * identifiers to lookup the corresponding objects like service, intro point
+ * and descriptor.
+ *
+ * Furthermore, the circuit identifier holds cryptographic material needed for
+ * the e2e encryption on the rendezvous circuit which is set once the
+ * rendezvous circuit has opened and ready to be used.
+ **/
+
+#ifndef TOR_HS_IDENT_H
+#define TOR_HS_IDENT_H
+
+#include "crypto.h"
+#include "crypto_ed25519.h"
+
+#include "hs_common.h"
+
+/* Length of the rendezvous cookie that is used to connect circuits at the
+ * rendezvous point. */
+#define HS_REND_COOKIE_LEN DIGEST_LEN
+
+/* Type of circuit an hs_ident_t object is associated with. */
+typedef enum {
+ HS_IDENT_CIRCUIT_INTRO = 1,
+ HS_IDENT_CIRCUIT_RENDEZVOUS = 2,
+} hs_ident_circuit_type_t;
+
+/* Client and service side circuit identifier that is used for hidden service
+ * circuit establishment. Not all fields contain data, it depends on the
+ * circuit purpose. This is attached to an origin_circuit_t. All fields are
+ * used by both client and service. */
+typedef struct hs_ident_circuit_t {
+ /* (All circuit) The public key used to uniquely identify the service. It is
+ * the one found in the onion address. */
+ ed25519_public_key_t identity_pk;
+
+ /* (All circuit) The type of circuit this identifier is attached to.
+ * Accessors of the fields in this object assert non fatal on this circuit
+ * type. In other words, if a rendezvous field is being accessed, the
+ * circuit type MUST BE of type HS_IDENT_CIRCUIT_RENDEZVOUS. This value is
+ * set when an object is initialized in its constructor. */
+ hs_ident_circuit_type_t circuit_type;
+
+ /* (All circuit) Introduction point authentication key. It's also needed on
+ * the rendezvous circuit for the ntor handshake. It's used as the unique key
+ * of the introduction point so it should not be shared between multiple
+ * intro points. */
+ ed25519_public_key_t intro_auth_pk;
+
+ /* (Only client rendezvous circuit) Introduction point encryption public
+ * key. We keep it in the rendezvous identifier for the ntor handshake. */
+ curve25519_public_key_t intro_enc_pk;
+
+ /* (Only rendezvous circuit) Rendezvous cookie sent from the client to the
+ * service with an INTRODUCE1 cell and used by the service in an
+ * RENDEZVOUS1 cell. */
+ uint8_t rendezvous_cookie[HS_REND_COOKIE_LEN];
+
+ /* (Only service rendezvous circuit) The HANDSHAKE_INFO needed in the
+ * RENDEZVOUS1 cell of the service. The construction is as follows:
+ * SERVER_PK [32 bytes]
+ * AUTH_MAC [32 bytes]
+ */
+ uint8_t rendezvous_handshake_info[CURVE25519_PUBKEY_LEN + DIGEST256_LEN];
+
+ /* (Only client rendezvous circuit) Client ephemeral keypair needed for the
+ * e2e encryption with the service. */
+ curve25519_keypair_t rendezvous_client_kp;
+
+ /* (Only rendezvous circuit) The NTOR_KEY_SEED needed for key derivation for
+ * the e2e encryption with the client on the circuit. */
+ uint8_t rendezvous_ntor_key_seed[DIGEST256_LEN];
+
+ /* (Only rendezvous circuit) Number of streams associated with this
+ * rendezvous circuit. We track this because there is a check on a maximum
+ * value. */
+ uint64_t num_rdv_streams;
+} hs_ident_circuit_t;
+
+/* Client and service side directory connection identifier used for a
+ * directory connection to identify which service is being queried. This is
+ * attached to a dir_connection_t. */
+typedef struct hs_ident_dir_conn_t {
+ /* The public key used to uniquely identify the service. It is the one found
+ * in the onion address. */
+ ed25519_public_key_t identity_pk;
+
+ /* The blinded public key used to uniquely identify the descriptor that this
+ * directory connection identifier is for. Only used by the service-side code
+ * to fine control descriptor uploads. */
+ ed25519_public_key_t blinded_pk;
+
+ /* XXX: Client authorization. */
+} hs_ident_dir_conn_t;
+
+/* Client and service side edge connection identifier used for an edge
+ * connection to identify which service is being queried. This is attached to
+ * a edge_connection_t. */
+typedef struct hs_ident_edge_conn_t {
+ /* The public key used to uniquely identify the service. It is the one found
+ * in the onion address. */
+ ed25519_public_key_t identity_pk;
+
+ /* XXX: Client authorization. */
+} hs_ident_edge_conn_t;
+
+/* Circuit identifier API. */
+hs_ident_circuit_t *hs_ident_circuit_new(
+ const ed25519_public_key_t *identity_pk,
+ hs_ident_circuit_type_t circuit_type);
+void hs_ident_circuit_free(hs_ident_circuit_t *ident);
+hs_ident_circuit_t *hs_ident_circuit_dup(const hs_ident_circuit_t *src);
+
+/* Directory connection identifier API. */
+hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src);
+void hs_ident_dir_conn_free(hs_ident_dir_conn_t *ident);
+void hs_ident_dir_conn_init(const ed25519_public_key_t *identity_pk,
+ const ed25519_public_key_t *blinded_pk,
+ hs_ident_dir_conn_t *ident);
+
+/* Edge connection identifier API. */
+hs_ident_edge_conn_t *hs_ident_edge_conn_new(
+ const ed25519_public_key_t *identity_pk);
+void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident);
+
+/* Validators */
+int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident);
+
+#endif /* !defined(TOR_HS_IDENT_H) */
+
diff --git a/src/or/hs_intropoint.c b/src/or/hs_intropoint.c
index 06f8a2c3ad..4a5202f614 100644
--- a/src/or/hs_intropoint.c
+++ b/src/or/hs_intropoint.c
@@ -24,6 +24,7 @@
#include "hs/cell_introduce1.h"
#include "hs_circuitmap.h"
+#include "hs_descriptor.h"
#include "hs_intropoint.h"
#include "hs_common.h"
@@ -595,3 +596,18 @@ hs_intro_received_introduce1(or_circuit_t *circ, const uint8_t *request,
return -1;
}
+/* Clear memory allocated by the given intropoint object ip (but don't free the
+ * object itself). */
+void
+hs_intropoint_clear(hs_intropoint_t *ip)
+{
+ if (ip == NULL) {
+ return;
+ }
+ tor_cert_free(ip->auth_key_cert);
+ SMARTLIST_FOREACH(ip->link_specifiers, hs_desc_link_specifier_t *, ls,
+ hs_desc_link_specifier_free(ls));
+ smartlist_free(ip->link_specifiers);
+ memset(ip, 0, sizeof(hs_intropoint_t));
+}
+
diff --git a/src/or/hs_intropoint.h b/src/or/hs_intropoint.h
index 163ed810e7..749d1530e1 100644
--- a/src/or/hs_intropoint.h
+++ b/src/or/hs_intropoint.h
@@ -9,12 +9,15 @@
#ifndef TOR_HS_INTRO_H
#define TOR_HS_INTRO_H
+#include "crypto_curve25519.h"
+#include "torcert.h"
+
/* Authentication key type in an ESTABLISH_INTRO cell. */
-enum hs_intro_auth_key_type {
+typedef enum {
HS_INTRO_AUTH_KEY_TYPE_LEGACY0 = 0x00,
HS_INTRO_AUTH_KEY_TYPE_LEGACY1 = 0x01,
HS_INTRO_AUTH_KEY_TYPE_ED25519 = 0x02,
-};
+} hs_intro_auth_key_type_t;
/* INTRODUCE_ACK status code. */
typedef enum {
@@ -24,6 +27,18 @@ typedef enum {
HS_INTRO_ACK_STATUS_CANT_RELAY = 0x0003,
} hs_intro_ack_status_t;
+/* Object containing introduction point common data between the service and
+ * the client side. */
+typedef struct hs_intropoint_t {
+ /* Does this intro point only supports legacy ID ?. */
+ unsigned int is_only_legacy : 1;
+
+ /* Authentication key certificate from the descriptor. */
+ tor_cert_t *auth_key_cert;
+ /* A list of link specifier. */
+ smartlist_t *link_specifiers;
+} hs_intropoint_t;
+
int hs_intro_received_establish_intro(or_circuit_t *circ,
const uint8_t *request,
size_t request_len);
@@ -35,6 +50,9 @@ MOCK_DECL(int, hs_intro_send_intro_established_cell,(or_circuit_t *circ));
/* also used by rendservice.c */
int hs_intro_circuit_is_suitable_for_establish_intro(const or_circuit_t *circ);
+hs_intropoint_t *hs_intro_new(void);
+void hs_intropoint_clear(hs_intropoint_t *ip);
+
#ifdef HS_INTROPOINT_PRIVATE
#include "hs/cell_establish_intro.h"
@@ -55,7 +73,7 @@ STATIC int handle_introduce1(or_circuit_t *client_circ,
STATIC int validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell);
STATIC int circuit_is_suitable_for_introduce1(const or_circuit_t *circ);
-#endif /* HS_INTROPOINT_PRIVATE */
+#endif /* defined(HS_INTROPOINT_PRIVATE) */
-#endif /* TOR_HS_INTRO_H */
+#endif /* !defined(TOR_HS_INTRO_H) */
diff --git a/src/or/hs_ntor.c b/src/or/hs_ntor.c
index 119899817e..a416bc46c3 100644
--- a/src/or/hs_ntor.c
+++ b/src/or/hs_ntor.c
@@ -578,49 +578,41 @@ hs_ntor_client_rendezvous2_mac_is_good(
/* Input length to KDF for key expansion */
#define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN)
-/* Output length of KDF for key expansion */
-#define NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN (DIGEST256_LEN*3+CIPHER256_KEY_LEN*2)
-
-/** Given the rendezvous key material in <b>hs_ntor_rend_cell_keys</b>, do the
- * circuit key expansion as specified by section '4.2.1. Key expansion' and
- * return a hs_ntor_rend_circuit_keys_t structure with the computed keys. */
-hs_ntor_rend_circuit_keys_t *
-hs_ntor_circuit_key_expansion(
- const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys)
+
+/** Given the rendezvous key seed in <b>ntor_key_seed</b> (of size
+ * DIGEST256_LEN), do the circuit key expansion as specified by section
+ * '4.2.1. Key expansion' and place the keys in <b>keys_out</b> (which must be
+ * of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN).
+ *
+ * Return 0 if things went well, else return -1. */
+int
+hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len,
+ uint8_t *keys_out, size_t keys_out_len)
{
uint8_t *ptr;
uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
- uint8_t keys[NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN];
crypto_xof_t *xof;
- hs_ntor_rend_circuit_keys_t *rend_circuit_keys = NULL;
+
+ /* Sanity checks on lengths to make sure we are good */
+ if (BUG(seed_len != DIGEST256_LEN)) {
+ return -1;
+ }
+ if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
+ return -1;
+ }
/* Let's build the input to the KDF */
ptr = kdf_input;
- APPEND(ptr, hs_ntor_rend_cell_keys->ntor_key_seed, DIGEST256_LEN);
+ APPEND(ptr, ntor_key_seed, DIGEST256_LEN);
APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
tor_assert(ptr == kdf_input + sizeof(kdf_input));
/* Generate the keys */
xof = crypto_xof_new();
crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input));
- crypto_xof_squeeze_bytes(xof, keys, sizeof(keys));
+ crypto_xof_squeeze_bytes(xof, keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN);
crypto_xof_free(xof);
- /* Generate keys structure and assign keys to it */
- rend_circuit_keys = tor_malloc_zero(sizeof(hs_ntor_rend_circuit_keys_t));
- ptr = keys;
- memcpy(rend_circuit_keys->KH, ptr, DIGEST256_LEN);
- ptr += DIGEST256_LEN;;
- memcpy(rend_circuit_keys->Df, ptr, DIGEST256_LEN);
- ptr += DIGEST256_LEN;
- memcpy(rend_circuit_keys->Db, ptr, DIGEST256_LEN);
- ptr += DIGEST256_LEN;
- memcpy(rend_circuit_keys->Kf, ptr, CIPHER256_KEY_LEN);
- ptr += CIPHER256_KEY_LEN;
- memcpy(rend_circuit_keys->Kb, ptr, CIPHER256_KEY_LEN);
- ptr += CIPHER256_KEY_LEN;
- tor_assert(ptr == keys + sizeof(keys));
-
- return rend_circuit_keys;
+ return 0;
}
diff --git a/src/or/hs_ntor.h b/src/or/hs_ntor.h
index cd75f46a4c..77e544a130 100644
--- a/src/or/hs_ntor.h
+++ b/src/or/hs_ntor.h
@@ -6,6 +6,10 @@
#include "or.h"
+/* Output length of KDF for key expansion */
+#define HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN \
+ (DIGEST256_LEN*2 + CIPHER256_KEY_LEN*2)
+
/* Key material needed to encode/decode INTRODUCE1 cells */
typedef struct {
/* Key used for encryption of encrypted INTRODUCE1 blob */
@@ -23,21 +27,6 @@ typedef struct {
uint8_t ntor_key_seed[DIGEST256_LEN];
} hs_ntor_rend_cell_keys_t;
-/* Key material resulting from key expansion as detailed in section "4.2.1. Key
- * expansion" of rend-spec-ng.txt. */
-typedef struct {
- /* Per-circuit key material used in ESTABLISH_INTRO cell */
- uint8_t KH[DIGEST256_LEN];
- /* Authentication key for outgoing RELAY cells */
- uint8_t Df[DIGEST256_LEN];
- /* Authentication key for incoming RELAY cells */
- uint8_t Db[DIGEST256_LEN];
- /* Encryption key for outgoing RELAY cells */
- uint8_t Kf[CIPHER256_KEY_LEN];
- /* Decryption key for incoming RELAY cells */
- uint8_t Kb[CIPHER256_KEY_LEN];
-} hs_ntor_rend_circuit_keys_t;
-
int hs_ntor_client_get_introduce1_keys(
const ed25519_public_key_t *intro_auth_pubkey,
const curve25519_public_key_t *intro_enc_pubkey,
@@ -66,12 +55,13 @@ int hs_ntor_service_get_rendezvous1_keys(
const curve25519_public_key_t *client_ephemeral_enc_pubkey,
hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out);
-hs_ntor_rend_circuit_keys_t *hs_ntor_circuit_key_expansion(
- const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys);
+int hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed,
+ size_t seed_len,
+ uint8_t *keys_out, size_t keys_out_len);
int hs_ntor_client_rendezvous2_mac_is_good(
const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys,
const uint8_t *rcvd_mac);
-#endif
+#endif /* !defined(TOR_HS_NTOR_H) */
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index b3eec13046..b9a1dfc36e 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -6,170 +6,3372 @@
* \brief Implement next generation hidden service functionality
**/
+#define HS_SERVICE_PRIVATE
+
#include "or.h"
-#include "relay.h"
-#include "rendservice.h"
-#include "circuitlist.h"
#include "circpathbias.h"
+#include "circuitbuild.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "config.h"
+#include "connection.h"
+#include "directory.h"
+#include "main.h"
#include "networkstatus.h"
+#include "nodelist.h"
+#include "relay.h"
+#include "rendservice.h"
+#include "router.h"
+#include "routerkeys.h"
+#include "routerlist.h"
+#include "shared_random_state.h"
+#include "statefile.h"
+#include "hs_circuit.h"
+#include "hs_common.h"
+#include "hs_config.h"
+#include "hs_circuit.h"
+#include "hs_descriptor.h"
+#include "hs_ident.h"
#include "hs_intropoint.h"
#include "hs_service.h"
-#include "hs_common.h"
-#include "hs/cell_establish_intro.h"
+/* Trunnel */
+#include "ed25519_cert.h"
#include "hs/cell_common.h"
+#include "hs/cell_establish_intro.h"
+
+/* Helper macro. Iterate over every service in the global map. The var is the
+ * name of the service pointer. */
+#define FOR_EACH_SERVICE_BEGIN(var) \
+ STMT_BEGIN \
+ hs_service_t **var##_iter, *var; \
+ HT_FOREACH(var##_iter, hs_service_ht, hs_service_map) { \
+ var = *var##_iter;
+#define FOR_EACH_SERVICE_END } STMT_END ;
+
+/* Helper macro. Iterate over both current and previous descriptor of a
+ * service. The var is the name of the descriptor pointer. This macro skips
+ * any descriptor object of the service that is NULL. */
+#define FOR_EACH_DESCRIPTOR_BEGIN(service, var) \
+ STMT_BEGIN \
+ hs_service_descriptor_t *var; \
+ for (int var ## _loop_idx = 0; var ## _loop_idx < 2; \
+ ++var ## _loop_idx) { \
+ (var ## _loop_idx == 0) ? (var = service->desc_current) : \
+ (var = service->desc_next); \
+ if (var == NULL) continue;
+#define FOR_EACH_DESCRIPTOR_END } STMT_END ;
+
+/* Onion service directory file names. */
+static const char fname_keyfile_prefix[] = "hs_ed25519";
+static const char fname_hostname[] = "hostname";
+static const char address_tld[] = "onion";
-/* XXX We don't currently use these functions, apart from generating unittest
- data. When we start implementing the service-side support for prop224 we
- should revisit these functions and use them. */
+/* Staging list of service object. When configuring service, we add them to
+ * this list considered a staging area and they will get added to our global
+ * map once the keys have been loaded. These two steps are seperated because
+ * loading keys requires that we are an actual running tor process. */
+static smartlist_t *hs_service_staging_list;
+
+/** True if the list of available router descriptors might have changed which
+ * might result in an altered hash ring. Check if the hash ring changed and
+ * reupload if needed */
+static int consider_republishing_hs_descriptors = 0;
+
+static void set_descriptor_revision_counter(hs_descriptor_t *hs_desc);
+static void move_descriptors(hs_service_t *src, hs_service_t *dst);
+
+/* Helper: Function to compare two objects in the service map. Return 1 if the
+ * two service have the same master public identity key. */
+static inline int
+hs_service_ht_eq(const hs_service_t *first, const hs_service_t *second)
+{
+ tor_assert(first);
+ tor_assert(second);
+ /* Simple key compare. */
+ return ed25519_pubkey_eq(&first->keys.identity_pk,
+ &second->keys.identity_pk);
+}
+
+/* Helper: Function for the service hash table code below. The key used is the
+ * master public identity key which is ultimately the onion address. */
+static inline unsigned int
+hs_service_ht_hash(const hs_service_t *service)
+{
+ tor_assert(service);
+ return (unsigned int) siphash24g(service->keys.identity_pk.pubkey,
+ sizeof(service->keys.identity_pk.pubkey));
+}
+
+/* This is _the_ global hash map of hidden services which indexed the service
+ * contained in it by master public identity key which is roughly the onion
+ * address of the service. */
+static struct hs_service_ht *hs_service_map;
+
+/* Register the service hash table. */
+HT_PROTOTYPE(hs_service_ht, /* Name of hashtable. */
+ hs_service_t, /* Object contained in the map. */
+ hs_service_node, /* The name of the HT_ENTRY member. */
+ hs_service_ht_hash, /* Hashing function. */
+ hs_service_ht_eq) /* Compare function for objects. */
+
+HT_GENERATE2(hs_service_ht, hs_service_t, hs_service_node,
+ hs_service_ht_hash, hs_service_ht_eq,
+ 0.6, tor_reallocarray, tor_free_)
+
+/* Query the given service map with a public key and return a service object
+ * if found else NULL. It is also possible to set a directory path in the
+ * search query. If pk is NULL, then it will be set to zero indicating the
+ * hash table to compare the directory path instead. */
+STATIC hs_service_t *
+find_service(hs_service_ht *map, const ed25519_public_key_t *pk)
+{
+ hs_service_t dummy_service;
+ tor_assert(map);
+ tor_assert(pk);
+ memset(&dummy_service, 0, sizeof(dummy_service));
+ ed25519_pubkey_copy(&dummy_service.keys.identity_pk, pk);
+ return HT_FIND(hs_service_ht, map, &dummy_service);
+}
-/** Given an ESTABLISH_INTRO <b>cell</b>, encode it and place its payload in
- * <b>buf_out</b> which has size <b>buf_out_len</b>. Return the number of
- * bytes written, or a negative integer if there was an error. */
-ssize_t
-get_establish_intro_payload(uint8_t *buf_out, size_t buf_out_len,
- const trn_cell_establish_intro_t *cell)
+/* Register the given service in the given map. If the service already exists
+ * in the map, -1 is returned. On success, 0 is returned and the service
+ * ownership has been transfered to the global map. */
+STATIC int
+register_service(hs_service_ht *map, hs_service_t *service)
{
- ssize_t bytes_used = 0;
+ tor_assert(map);
+ tor_assert(service);
+ tor_assert(!ed25519_public_key_is_zero(&service->keys.identity_pk));
- if (buf_out_len < RELAY_PAYLOAD_SIZE) {
+ if (find_service(map, &service->keys.identity_pk)) {
+ /* Existing service with the same key. Do not register it. */
return -1;
}
+ /* Taking ownership of the object at this point. */
+ HT_INSERT(hs_service_ht, map, service);
+ return 0;
+}
+
+/* Remove a given service from the given map. If service is NULL or the
+ * service key is unset, return gracefully. */
+STATIC void
+remove_service(hs_service_ht *map, hs_service_t *service)
+{
+ hs_service_t *elm;
+
+ tor_assert(map);
- bytes_used = trn_cell_establish_intro_encode(buf_out, buf_out_len,
- cell);
- return bytes_used;
+ /* Ignore if no service or key is zero. */
+ if (BUG(service == NULL) ||
+ BUG(ed25519_public_key_is_zero(&service->keys.identity_pk))) {
+ return;
+ }
+
+ elm = HT_REMOVE(hs_service_ht, map, service);
+ if (elm) {
+ tor_assert(elm == service);
+ } else {
+ log_warn(LD_BUG, "Could not find service in the global map "
+ "while removing service %s",
+ escaped(service->config.directory_path));
+ }
}
-/* Set the cell extensions of <b>cell</b>. */
+/* Set the default values for a service configuration object <b>c</b>. */
static void
-set_trn_cell_extensions(trn_cell_establish_intro_t *cell)
+set_service_default_config(hs_service_config_t *c,
+ const or_options_t *options)
{
- trn_cell_extension_t *trn_cell_extensions = trn_cell_extension_new();
+ (void) options;
+ tor_assert(c);
+ c->ports = smartlist_new();
+ c->directory_path = NULL;
+ c->max_streams_per_rdv_circuit = 0;
+ c->max_streams_close_circuit = 0;
+ c->num_intro_points = NUM_INTRO_POINTS_DEFAULT;
+ c->allow_unknown_ports = 0;
+ c->is_single_onion = 0;
+ c->dir_group_readable = 0;
+ c->is_ephemeral = 0;
+}
- /* For now, we don't use extensions at all. */
- trn_cell_extensions->num = 0; /* It's already zeroed, but be explicit. */
- trn_cell_establish_intro_set_extensions(cell, trn_cell_extensions);
+/* From a service configuration object config, clear everything from it
+ * meaning free allocated pointers and reset the values. */
+static void
+service_clear_config(hs_service_config_t *config)
+{
+ if (config == NULL) {
+ return;
+ }
+ tor_free(config->directory_path);
+ if (config->ports) {
+ SMARTLIST_FOREACH(config->ports, rend_service_port_config_t *, p,
+ rend_service_port_config_free(p););
+ smartlist_free(config->ports);
+ }
+ memset(config, 0, sizeof(*config));
}
-/** Given the circuit handshake info in <b>circuit_key_material</b>, create and
- * return an ESTABLISH_INTRO cell. Return NULL if something went wrong. The
- * returned cell is allocated on the heap and it's the responsibility of the
- * caller to free it. */
-trn_cell_establish_intro_t *
-generate_establish_intro_cell(const uint8_t *circuit_key_material,
- size_t circuit_key_material_len)
+/* Helper function to return a human readable description of the given intro
+ * point object.
+ *
+ * This function is not thread-safe. Each call to this invalidates the
+ * previous values returned by it. */
+static const char *
+describe_intro_point(const hs_service_intro_point_t *ip)
{
- trn_cell_establish_intro_t *cell = NULL;
- ssize_t encoded_len;
+ /* Hex identity digest of the IP prefixed by the $ sign and ends with NUL
+ * byte hence the plus two. */
+ static char buf[HEX_DIGEST_LEN + 2];
+ const char *legacy_id = NULL;
- log_warn(LD_GENERAL,
- "Generating ESTABLISH_INTRO cell (key_material_len: %u)",
- (unsigned) circuit_key_material_len);
+ SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
+ const hs_desc_link_specifier_t *, lspec) {
+ if (lspec->type == LS_LEGACY_ID) {
+ legacy_id = (const char *) lspec->u.legacy_id;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(lspec);
- /* Generate short-term keypair for use in ESTABLISH_INTRO */
- ed25519_keypair_t key_struct;
- if (ed25519_keypair_generate(&key_struct, 0) < 0) {
- goto err;
+ /* For now, we only print the identity digest but we could improve this with
+ * much more information such as the ed25519 identity has well. */
+ buf[0] = '$';
+ if (legacy_id) {
+ base16_encode(buf + 1, HEX_DIGEST_LEN + 1, legacy_id, DIGEST_LEN);
}
- cell = trn_cell_establish_intro_new();
+ return buf;
+}
- /* Set AUTH_KEY_TYPE: 2 means ed25519 */
- trn_cell_establish_intro_set_auth_key_type(cell,
- HS_INTRO_AUTH_KEY_TYPE_ED25519);
+/* Return the lower bound of maximum INTRODUCE2 cells per circuit before we
+ * rotate intro point (defined by a consensus parameter or the default
+ * value). */
+static int32_t
+get_intro_point_min_introduce2(void)
+{
+ /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ * in the future. */
+ return networkstatus_get_param(NULL, "hs_intro_min_introduce2",
+ INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS,
+ 0, INT32_MAX);
+}
- /* Set AUTH_KEY_LEN field */
- /* Must also set byte-length of AUTH_KEY to match */
- int auth_key_len = ED25519_PUBKEY_LEN;
- trn_cell_establish_intro_set_auth_key_len(cell, auth_key_len);
- trn_cell_establish_intro_setlen_auth_key(cell, auth_key_len);
+/* Return the upper bound of maximum INTRODUCE2 cells per circuit before we
+ * rotate intro point (defined by a consensus parameter or the default
+ * value). */
+static int32_t
+get_intro_point_max_introduce2(void)
+{
+ /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ * in the future. */
+ return networkstatus_get_param(NULL, "hs_intro_max_introduce2",
+ INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS,
+ 0, INT32_MAX);
+}
- /* Set AUTH_KEY field */
- uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell);
- memcpy(auth_key_ptr, key_struct.pubkey.pubkey, auth_key_len);
+/* Return the minimum lifetime in seconds of an introduction point defined by a
+ * consensus parameter or the default value. */
+static int32_t
+get_intro_point_min_lifetime(void)
+{
+#define MIN_INTRO_POINT_LIFETIME_TESTING 10
+ if (get_options()->TestingTorNetwork) {
+ return MIN_INTRO_POINT_LIFETIME_TESTING;
+ }
- /* No cell extensions needed */
- set_trn_cell_extensions(cell);
+ /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ * in the future. */
+ return networkstatus_get_param(NULL, "hs_intro_min_lifetime",
+ INTRO_POINT_LIFETIME_MIN_SECONDS,
+ 0, INT32_MAX);
+}
- /* Set signature size.
- We need to do this up here, because _encode() needs it and we need to call
- _encode() to calculate the MAC and signature.
- */
- int sig_len = ED25519_SIG_LEN;
- trn_cell_establish_intro_set_sig_len(cell, sig_len);
- trn_cell_establish_intro_setlen_sig(cell, sig_len);
+/* Return the maximum lifetime in seconds of an introduction point defined by a
+ * consensus parameter or the default value. */
+static int32_t
+get_intro_point_max_lifetime(void)
+{
+#define MAX_INTRO_POINT_LIFETIME_TESTING 30
+ if (get_options()->TestingTorNetwork) {
+ return MAX_INTRO_POINT_LIFETIME_TESTING;
+ }
- /* XXX How to make this process easier and nicer? */
+ /* The [0, 2147483647] range is quite large to accomodate anything we decide
+ * in the future. */
+ return networkstatus_get_param(NULL, "hs_intro_max_lifetime",
+ INTRO_POINT_LIFETIME_MAX_SECONDS,
+ 0, INT32_MAX);
+}
- /* Calculate the cell MAC (aka HANDSHAKE_AUTH). */
- {
- /* To calculate HANDSHAKE_AUTH, we dump the cell in bytes, and then derive
- the MAC from it. */
- uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0};
- uint8_t mac[TRUNNEL_SHA3_256_LEN];
-
- encoded_len = trn_cell_establish_intro_encode(cell_bytes_tmp,
- sizeof(cell_bytes_tmp),
- cell);
- if (encoded_len < 0) {
- log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell.");
+/* Return the number of extra introduction point defined by a consensus
+ * parameter or the default value. */
+static int32_t
+get_intro_point_num_extra(void)
+{
+ /* The [0, 128] range bounds the number of extra introduction point allowed.
+ * Above 128 intro points, it's getting a bit crazy. */
+ return networkstatus_get_param(NULL, "hs_intro_num_extra",
+ NUM_INTRO_POINTS_EXTRA, 0, 128);
+}
+
+/* Helper: Function that needs to return 1 for the HT for each loop which
+ * frees every service in an hash map. */
+static int
+ht_free_service_(struct hs_service_t *service, void *data)
+{
+ (void) data;
+ hs_service_free(service);
+ /* This function MUST return 1 so the given object is then removed from the
+ * service map leading to this free of the object being safe. */
+ return 1;
+}
+
+/* Free every service that can be found in the global map. Once done, clear
+ * and free the global map. */
+static void
+service_free_all(void)
+{
+ if (hs_service_map) {
+ /* The free helper function returns 1 so this is safe. */
+ hs_service_ht_HT_FOREACH_FN(hs_service_map, ht_free_service_, NULL);
+ HT_CLEAR(hs_service_ht, hs_service_map);
+ tor_free(hs_service_map);
+ hs_service_map = NULL;
+ }
+
+ if (hs_service_staging_list) {
+ /* Cleanup staging list. */
+ SMARTLIST_FOREACH(hs_service_staging_list, hs_service_t *, s,
+ hs_service_free(s));
+ smartlist_free(hs_service_staging_list);
+ hs_service_staging_list = NULL;
+ }
+}
+
+/* Free a given service intro point object. */
+STATIC void
+service_intro_point_free(hs_service_intro_point_t *ip)
+{
+ if (!ip) {
+ return;
+ }
+ memwipe(&ip->auth_key_kp, 0, sizeof(ip->auth_key_kp));
+ memwipe(&ip->enc_key_kp, 0, sizeof(ip->enc_key_kp));
+ crypto_pk_free(ip->legacy_key);
+ replaycache_free(ip->replay_cache);
+ hs_intropoint_clear(&ip->base);
+ tor_free(ip);
+}
+
+/* Helper: free an hs_service_intro_point_t object. This function is used by
+ * digest256map_free() which requires a void * pointer. */
+static void
+service_intro_point_free_(void *obj)
+{
+ service_intro_point_free(obj);
+}
+
+/* Return a newly allocated service intro point and fully initialized from the
+ * given extend_info_t ei if non NULL. If is_legacy is true, we also generate
+ * the legacy key. On error, NULL is returned.
+ *
+ * If ei is NULL, returns a hs_service_intro_point_t with an empty link
+ * specifier list and no onion key. (This is used for testing.)
+ *
+ * ei must be an extend_info_t containing an IPv4 address. (We will add supoort
+ * for IPv6 in a later release.) When calling extend_info_from_node(), pass
+ * 0 in for_direct_connection to make sure ei always has an IPv4 address. */
+STATIC hs_service_intro_point_t *
+service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy)
+{
+ hs_desc_link_specifier_t *ls;
+ hs_service_intro_point_t *ip;
+
+ ip = tor_malloc_zero(sizeof(*ip));
+ /* We'll create the key material. No need for extra strong, those are short
+ * term keys. */
+ ed25519_keypair_generate(&ip->auth_key_kp, 0);
+
+ { /* Set introduce2 max cells limit */
+ int32_t min_introduce2_cells = get_intro_point_min_introduce2();
+ int32_t max_introduce2_cells = get_intro_point_max_introduce2();
+ if (BUG(max_introduce2_cells < min_introduce2_cells)) {
+ goto err;
+ }
+ ip->introduce2_max = crypto_rand_int_range(min_introduce2_cells,
+ max_introduce2_cells);
+ }
+ { /* Set intro point lifetime */
+ int32_t intro_point_min_lifetime = get_intro_point_min_lifetime();
+ int32_t intro_point_max_lifetime = get_intro_point_max_lifetime();
+ if (BUG(intro_point_max_lifetime < intro_point_min_lifetime)) {
+ goto err;
+ }
+ ip->time_to_expire = time(NULL) +
+ crypto_rand_int_range(intro_point_min_lifetime,intro_point_max_lifetime);
+ }
+
+ ip->replay_cache = replaycache_new(0, 0);
+
+ /* Initialize the base object. We don't need the certificate object. */
+ ip->base.link_specifiers = smartlist_new();
+
+ /* Generate the encryption key for this intro point. */
+ curve25519_keypair_generate(&ip->enc_key_kp, 0);
+ /* Figure out if this chosen node supports v3 or is legacy only. */
+ if (is_legacy) {
+ ip->base.is_only_legacy = 1;
+ /* Legacy mode that is doesn't support v3+ with ed25519 auth key. */
+ ip->legacy_key = crypto_pk_new();
+ if (crypto_pk_generate_key(ip->legacy_key) < 0) {
goto err;
}
+ }
+
+ if (ei == NULL) {
+ goto done;
+ }
+
+ /* We'll try to add all link specifiers. Legacy is mandatory.
+ * IPv4 or IPv6 is required, and we always send IPv4. */
+ ls = hs_desc_link_specifier_new(ei, LS_IPV4);
+ /* It is impossible to have an extend info object without a v4. */
+ if (BUG(!ls)) {
+ goto err;
+ }
+ smartlist_add(ip->base.link_specifiers, ls);
+
+ ls = hs_desc_link_specifier_new(ei, LS_LEGACY_ID);
+ /* It is impossible to have an extend info object without an identity
+ * digest. */
+ if (BUG(!ls)) {
+ goto err;
+ }
+ smartlist_add(ip->base.link_specifiers, ls);
+
+ /* ed25519 identity key is optional for intro points */
+ ls = hs_desc_link_specifier_new(ei, LS_ED25519_ID);
+ if (ls) {
+ smartlist_add(ip->base.link_specifiers, ls);
+ }
+
+ /* IPv6 is not supported in this release. */
+
+ /* Finally, copy onion key from the extend_info_t object. */
+ memcpy(&ip->onion_key, &ei->curve25519_onion_key, sizeof(ip->onion_key));
+
+ done:
+ return ip;
+ err:
+ service_intro_point_free(ip);
+ return NULL;
+}
+
+/* Add the given intro point object to the given intro point map. The intro
+ * point MUST have its RSA encryption key set if this is a legacy type or the
+ * authentication key set otherwise. */
+STATIC void
+service_intro_point_add(digest256map_t *map, hs_service_intro_point_t *ip)
+{
+ hs_service_intro_point_t *old_ip_entry;
+
+ tor_assert(map);
+ tor_assert(ip);
+
+ old_ip_entry = digest256map_set(map, ip->auth_key_kp.pubkey.pubkey, ip);
+ /* Make sure we didn't just try to double-add an intro point */
+ tor_assert_nonfatal(!old_ip_entry);
+}
+
+/* For a given service, remove the intro point from that service's descriptors
+ * (check both current and next descriptor) */
+STATIC void
+service_intro_point_remove(const hs_service_t *service,
+ const hs_service_intro_point_t *ip)
+{
+ tor_assert(service);
+ tor_assert(ip);
+
+ /* Trying all descriptors. */
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ /* We'll try to remove the descriptor on both descriptors which is not
+ * very expensive to do instead of doing loopup + remove. */
+ digest256map_remove(desc->intro_points.map,
+ ip->auth_key_kp.pubkey.pubkey);
+ } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* For a given service and authentication key, return the intro point or NULL
+ * if not found. This will check both descriptors in the service. */
+STATIC hs_service_intro_point_t *
+service_intro_point_find(const hs_service_t *service,
+ const ed25519_public_key_t *auth_key)
+{
+ hs_service_intro_point_t *ip = NULL;
+
+ tor_assert(service);
+ tor_assert(auth_key);
+
+ /* Trying all descriptors to find the right intro point.
+ *
+ * Even if we use the same node as intro point in both descriptors, the node
+ * will have a different intro auth key for each descriptor since we generate
+ * a new one everytime we pick an intro point.
+ *
+ * After #22893 gets implemented, intro points will be moved to be
+ * per-service instead of per-descriptor so this function will need to
+ * change.
+ */
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ if ((ip = digest256map_get(desc->intro_points.map,
+ auth_key->pubkey)) != NULL) {
+ break;
+ }
+ } FOR_EACH_DESCRIPTOR_END;
+
+ return ip;
+}
+
+/* For a given service and intro point, return the descriptor for which the
+ * intro point is assigned to. NULL is returned if not found. */
+STATIC hs_service_descriptor_t *
+service_desc_find_by_intro(const hs_service_t *service,
+ const hs_service_intro_point_t *ip)
+{
+ hs_service_descriptor_t *descp = NULL;
+
+ tor_assert(service);
+ tor_assert(ip);
+
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ if (digest256map_get(desc->intro_points.map,
+ ip->auth_key_kp.pubkey.pubkey)) {
+ descp = desc;
+ break;
+ }
+ } FOR_EACH_DESCRIPTOR_END;
+
+ return descp;
+}
+
+/* From a circuit identifier, get all the possible objects associated with the
+ * ident. If not NULL, service, ip or desc are set if the object can be found.
+ * They are untouched if they can't be found.
+ *
+ * This is an helper function because we do those lookups often so it's more
+ * convenient to simply call this functions to get all the things at once. */
+STATIC void
+get_objects_from_ident(const hs_ident_circuit_t *ident,
+ hs_service_t **service, hs_service_intro_point_t **ip,
+ hs_service_descriptor_t **desc)
+{
+ hs_service_t *s;
+
+ tor_assert(ident);
+
+ /* Get service object from the circuit identifier. */
+ s = find_service(hs_service_map, &ident->identity_pk);
+ if (s && service) {
+ *service = s;
+ }
+
+ /* From the service object, get the intro point object of that circuit. The
+ * following will query both descriptors intro points list. */
+ if (s && ip) {
+ *ip = service_intro_point_find(s, &ident->intro_auth_pk);
+ }
+
+ /* Get the descriptor for this introduction point and service. */
+ if (s && ip && *ip && desc) {
+ *desc = service_desc_find_by_intro(s, *ip);
+ }
+}
+
+/* From a given intro point, return the first link specifier of type
+ * encountered in the link specifier list. Return NULL if it can't be found.
+ *
+ * The caller does NOT have ownership of the object, the intro point does. */
+static hs_desc_link_specifier_t *
+get_link_spec_by_type(const hs_service_intro_point_t *ip, uint8_t type)
+{
+ hs_desc_link_specifier_t *lnk_spec = NULL;
+
+ tor_assert(ip);
+
+ SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
+ hs_desc_link_specifier_t *, ls) {
+ if (ls->type == type) {
+ lnk_spec = ls;
+ goto end;
+ }
+ } SMARTLIST_FOREACH_END(ls);
+
+ end:
+ return lnk_spec;
+}
+
+/* Given a service intro point, return the node_t associated to it. This can
+ * return NULL if the given intro point has no legacy ID or if the node can't
+ * be found in the consensus. */
+STATIC const node_t *
+get_node_from_intro_point(const hs_service_intro_point_t *ip)
+{
+ const hs_desc_link_specifier_t *ls;
+
+ tor_assert(ip);
+
+ ls = get_link_spec_by_type(ip, LS_LEGACY_ID);
+ if (BUG(!ls)) {
+ return NULL;
+ }
+ /* XXX In the future, we want to only use the ed25519 ID (#22173). */
+ return node_get_by_id((const char *) ls->u.legacy_id);
+}
+
+/* Given a service intro point, return the extend_info_t for it. This can
+ * return NULL if the node can't be found for the intro point or the extend
+ * info can't be created for the found node. If direct_conn is set, the extend
+ * info is validated on if we can connect directly. */
+static extend_info_t *
+get_extend_info_from_intro_point(const hs_service_intro_point_t *ip,
+ unsigned int direct_conn)
+{
+ extend_info_t *info = NULL;
+ const node_t *node;
+
+ tor_assert(ip);
+
+ node = get_node_from_intro_point(ip);
+ if (node == NULL) {
+ /* This can happen if the relay serving as intro point has been removed
+ * from the consensus. In that case, the intro point will be removed from
+ * the descriptor during the scheduled events. */
+ goto end;
+ }
+
+ /* In the case of a direct connection (single onion service), it is possible
+ * our firewall policy won't allow it so this can return a NULL value. */
+ info = extend_info_from_node(node, direct_conn);
+
+ end:
+ return info;
+}
+
+/* Return the number of introduction points that are established for the
+ * given descriptor. */
+static unsigned int
+count_desc_circuit_established(const hs_service_descriptor_t *desc)
+{
+ unsigned int count = 0;
+
+ tor_assert(desc);
+
+ DIGEST256MAP_FOREACH(desc->intro_points.map, key,
+ const hs_service_intro_point_t *, ip) {
+ count += ip->circuit_established;
+ } DIGEST256MAP_FOREACH_END;
+
+ return count;
+}
+
+/* For a given service and descriptor of that service, close all active
+ * directory connections. */
+static void
+close_directory_connections(const hs_service_t *service,
+ const hs_service_descriptor_t *desc)
+{
+ unsigned int count = 0;
+ smartlist_t *dir_conns;
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ /* Close pending HS desc upload connections for the blinded key of 'desc'. */
+ dir_conns = connection_list_by_type_purpose(CONN_TYPE_DIR,
+ DIR_PURPOSE_UPLOAD_HSDESC);
+ SMARTLIST_FOREACH_BEGIN(dir_conns, connection_t *, conn) {
+ dir_connection_t *dir_conn = TO_DIR_CONN(conn);
+ if (ed25519_pubkey_eq(&dir_conn->hs_ident->identity_pk,
+ &service->keys.identity_pk) &&
+ ed25519_pubkey_eq(&dir_conn->hs_ident->blinded_pk,
+ &desc->blinded_kp.pubkey)) {
+ connection_mark_for_close(conn);
+ count++;
+ continue;
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ log_info(LD_REND, "Closed %u active service directory connections for "
+ "descriptor %s of service %s",
+ count, safe_str_client(ed25519_fmt(&desc->blinded_kp.pubkey)),
+ safe_str_client(service->onion_address));
+ /* We don't have ownership of the objects in this list. */
+ smartlist_free(dir_conns);
+}
+
+/* Close all rendezvous circuits for the given service. */
+static void
+close_service_rp_circuits(hs_service_t *service)
+{
+ origin_circuit_t *ocirc = NULL;
+
+ tor_assert(service);
+
+ /* The reason we go over all circuit instead of using the circuitmap API is
+ * because most hidden service circuits are rendezvous circuits so there is
+ * no real improvement at getting all rendezvous circuits from the
+ * circuitmap and then going over them all to find the right ones.
+ * Furthermore, another option would have been to keep a list of RP cookies
+ * for a service but it creates an engineering complexity since we don't
+ * have a "RP circuit closed" event to clean it up properly so we avoid a
+ * memory DoS possibility. */
+
+ while ((ocirc = circuit_get_next_service_rp_circ(ocirc))) {
+ /* Only close circuits that are v3 and for this service. */
+ if (ocirc->hs_ident != NULL &&
+ ed25519_pubkey_eq(&ocirc->hs_ident->identity_pk,
+ &service->keys.identity_pk)) {
+ /* Reason is FINISHED because service has been removed and thus the
+ * circuit is considered old/uneeded. When freed, it is removed from the
+ * hs circuitmap. */
+ circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
+ }
+ }
+}
+
+/* Close the circuit(s) for the given map of introduction points. */
+static void
+close_intro_circuits(hs_service_intropoints_t *intro_points)
+{
+ tor_assert(intro_points);
+
+ DIGEST256MAP_FOREACH(intro_points->map, key,
+ const hs_service_intro_point_t *, ip) {
+ origin_circuit_t *ocirc = hs_circ_service_get_intro_circ(ip);
+ if (ocirc) {
+ /* Reason is FINISHED because service has been removed and thus the
+ * circuit is considered old/uneeded. When freed, the circuit is removed
+ * from the HS circuitmap. */
+ circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
+ }
+ } DIGEST256MAP_FOREACH_END;
+}
+
+/* Close all introduction circuits for the given service. */
+static void
+close_service_intro_circuits(hs_service_t *service)
+{
+ tor_assert(service);
+
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ close_intro_circuits(&desc->intro_points);
+ } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* Close any circuits related to the given service. */
+static void
+close_service_circuits(hs_service_t *service)
+{
+ tor_assert(service);
+
+ /* Only support for version >= 3. */
+ if (BUG(service->config.version < HS_VERSION_THREE)) {
+ return;
+ }
+ /* Close intro points. */
+ close_service_intro_circuits(service);
+ /* Close rendezvous points. */
+ close_service_rp_circuits(service);
+}
+
+/* Move every ephemeral services from the src service map to the dst service
+ * map. It is possible that a service can't be register to the dst map which
+ * won't stop the process of moving them all but will trigger a log warn. */
+static void
+move_ephemeral_services(hs_service_ht *src, hs_service_ht *dst)
+{
+ hs_service_t **iter, **next;
+
+ tor_assert(src);
+ tor_assert(dst);
+
+ /* Iterate over the map to find ephemeral service and move them to the other
+ * map. We loop using this method to have a safe removal process. */
+ for (iter = HT_START(hs_service_ht, src); iter != NULL; iter = next) {
+ hs_service_t *s = *iter;
+ if (!s->config.is_ephemeral) {
+ /* Yeah, we are in a very manual loop :). */
+ next = HT_NEXT(hs_service_ht, src, iter);
+ continue;
+ }
+ /* Remove service from map and then register to it to the other map.
+ * Reminder that "*iter" and "s" are the same thing. */
+ next = HT_NEXT_RMV(hs_service_ht, src, iter);
+ if (register_service(dst, s) < 0) {
+ log_warn(LD_BUG, "Ephemeral service key is already being used. "
+ "Skipping.");
+ }
+ }
+}
+
+/* Return a const string of the directory path escaped. If this is an
+ * ephemeral service, it returns "[EPHEMERAL]". This can only be called from
+ * the main thread because escaped() uses a static variable. */
+static const char *
+service_escaped_dir(const hs_service_t *s)
+{
+ return (s->config.is_ephemeral) ? "[EPHEMERAL]" :
+ escaped(s->config.directory_path);
+}
+
+/** Move the hidden service state from <b>src</b> to <b>dst</b>. We do this
+ * when we receive a SIGHUP: <b>dst</b> is the post-HUP service */
+static void
+move_hs_state(hs_service_t *src_service, hs_service_t *dst_service)
+{
+ tor_assert(src_service);
+ tor_assert(dst_service);
+
+ hs_service_state_t *src = &src_service->state;
+ hs_service_state_t *dst = &dst_service->state;
+
+ /* Let's do a shallow copy */
+ dst->intro_circ_retry_started_time = src->intro_circ_retry_started_time;
+ dst->num_intro_circ_launched = src->num_intro_circ_launched;
+ /* Freeing a NULL replaycache triggers an info LD_BUG. */
+ if (dst->replay_cache_rend_cookie != NULL) {
+ replaycache_free(dst->replay_cache_rend_cookie);
+ }
+ dst->replay_cache_rend_cookie = src->replay_cache_rend_cookie;
+
+ src->replay_cache_rend_cookie = NULL; /* steal pointer reference */
+}
+
+/* Register services that are in the staging list. Once this function returns,
+ * the global service map will be set with the right content and all non
+ * surviving services will be cleaned up. */
+static void
+register_all_services(void)
+{
+ struct hs_service_ht *new_service_map;
+
+ tor_assert(hs_service_staging_list);
+
+ /* Allocate a new map that will replace the current one. */
+ new_service_map = tor_malloc_zero(sizeof(*new_service_map));
+ HT_INIT(hs_service_ht, new_service_map);
+
+ /* First step is to transfer all ephemeral services from the current global
+ * map to the new one we are constructing. We do not prune ephemeral
+ * services as the only way to kill them is by deleting it from the control
+ * port or stopping the tor daemon. */
+ move_ephemeral_services(hs_service_map, new_service_map);
+
+ SMARTLIST_FOREACH_BEGIN(hs_service_staging_list, hs_service_t *, snew) {
+ hs_service_t *s;
+
+ /* Check if that service is already in our global map and if so, we'll
+ * transfer the intro points to it. */
+ s = find_service(hs_service_map, &snew->keys.identity_pk);
+ if (s) {
+ /* Pass ownership of the descriptors from s (the current service) to
+ * snew (the newly configured one). */
+ move_descriptors(s, snew);
+ move_hs_state(s, snew);
+ /* Remove the service from the global map because after this, we need to
+ * go over the remaining service in that map that aren't surviving the
+ * reload to close their circuits. */
+ remove_service(hs_service_map, s);
+ hs_service_free(s);
+ }
+ /* Great, this service is now ready to be added to our new map. */
+ if (BUG(register_service(new_service_map, snew) < 0)) {
+ /* This should never happen because prior to registration, we validate
+ * every service against the entire set. Not being able to register a
+ * service means we failed to validate correctly. In that case, don't
+ * break tor and ignore the service but tell user. */
+ log_warn(LD_BUG, "Unable to register service with directory %s",
+ service_escaped_dir(snew));
+ SMARTLIST_DEL_CURRENT(hs_service_staging_list, snew);
+ hs_service_free(snew);
+ }
+ } SMARTLIST_FOREACH_END(snew);
+
+ /* Close any circuits associated with the non surviving services. Every
+ * service in the current global map are roaming. */
+ FOR_EACH_SERVICE_BEGIN(service) {
+ close_service_circuits(service);
+ } FOR_EACH_SERVICE_END;
+
+ /* Time to make the switch. We'll clear the staging list because its content
+ * has now changed ownership to the map. */
+ smartlist_clear(hs_service_staging_list);
+ service_free_all();
+ hs_service_map = new_service_map;
+}
+
+/* Write the onion address of a given service to the given filename fname_ in
+ * the service directory. Return 0 on success else -1 on error. */
+STATIC int
+write_address_to_file(const hs_service_t *service, const char *fname_)
+{
+ int ret = -1;
+ char *fname = NULL;
+ char *addr_buf = NULL;
+
+ tor_assert(service);
+ tor_assert(fname_);
+
+ /* Construct the full address with the onion tld and write the hostname file
+ * to disk. */
+ tor_asprintf(&addr_buf, "%s.%s\n", service->onion_address, address_tld);
+ /* Notice here that we use the given "fname_". */
+ fname = hs_path_from_filename(service->config.directory_path, fname_);
+ if (write_str_to_file(fname, addr_buf, 0) < 0) {
+ log_warn(LD_REND, "Could not write onion address to hostname file %s",
+ escaped(fname));
+ goto end;
+ }
+
+#ifndef _WIN32
+ if (service->config.dir_group_readable) {
+ /* Mode to 0640. */
+ if (chmod(fname, S_IRUSR | S_IWUSR | S_IRGRP) < 0) {
+ log_warn(LD_FS, "Unable to make onion service hostname file %s "
+ "group-readable.", escaped(fname));
+ }
+ }
+#endif /* !defined(_WIN32) */
- /* sanity check */
- tor_assert(encoded_len > ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN);
+ /* Success. */
+ ret = 0;
+ end:
+ tor_free(fname);
+ tor_free(addr_buf);
+ return ret;
+}
+
+/* Load and/or generate private keys for the given service. On success, the
+ * hostname file will be written to disk along with the master private key iff
+ * the service is not configured for offline keys. Return 0 on success else -1
+ * on failure. */
+static int
+load_service_keys(hs_service_t *service)
+{
+ int ret = -1;
+ char *fname = NULL;
+ ed25519_keypair_t *kp;
+ const hs_service_config_t *config;
+
+ tor_assert(service);
- /* Calculate MAC of all fields before HANDSHAKE_AUTH */
- crypto_mac_sha3_256(mac, sizeof(mac),
- circuit_key_material, circuit_key_material_len,
- cell_bytes_tmp,
- encoded_len -
- (ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN));
- /* Write the MAC to the cell */
- uint8_t *handshake_ptr =
- trn_cell_establish_intro_getarray_handshake_mac(cell);
- memcpy(handshake_ptr, mac, sizeof(mac));
+ config = &service->config;
+
+ /* Create and fix permission on service directory. We are about to write
+ * files to that directory so make sure it exists and has the right
+ * permissions. We do this here because at this stage we know that Tor is
+ * actually running and the service we have has been validated. */
+ if (BUG(hs_check_service_private_dir(get_options()->User,
+ config->directory_path,
+ config->dir_group_readable, 1) < 0)) {
+ goto end;
}
- /* Calculate the cell signature */
+ /* Try to load the keys from file or generate it if not found. */
+ fname = hs_path_from_filename(config->directory_path, fname_keyfile_prefix);
+ /* Don't ask for key creation, we want to know if we were able to load it or
+ * we had to generate it. Better logging! */
+ kp = ed_key_init_from_file(fname, INIT_ED_KEY_SPLIT, LOG_INFO, NULL, 0, 0,
+ 0, NULL);
+ if (!kp) {
+ log_info(LD_REND, "Unable to load keys from %s. Generating it...", fname);
+ /* We'll now try to generate the keys and for it we want the strongest
+ * randomness for it. The keypair will be written in different files. */
+ uint32_t key_flags = INIT_ED_KEY_CREATE | INIT_ED_KEY_EXTRA_STRONG |
+ INIT_ED_KEY_SPLIT;
+ kp = ed_key_init_from_file(fname, key_flags, LOG_WARN, NULL, 0, 0, 0,
+ NULL);
+ if (!kp) {
+ log_warn(LD_REND, "Unable to generate keys and save in %s.", fname);
+ goto end;
+ }
+ }
+
+ /* Copy loaded or generated keys to service object. */
+ ed25519_pubkey_copy(&service->keys.identity_pk, &kp->pubkey);
+ memcpy(&service->keys.identity_sk, &kp->seckey,
+ sizeof(service->keys.identity_sk));
+ /* This does a proper memory wipe. */
+ ed25519_keypair_free(kp);
+
+ /* Build onion address from the newly loaded keys. */
+ tor_assert(service->config.version <= UINT8_MAX);
+ hs_build_address(&service->keys.identity_pk,
+ (uint8_t) service->config.version,
+ service->onion_address);
+
+ /* Write onion address to hostname file. */
+ if (write_address_to_file(service, fname_hostname) < 0) {
+ goto end;
+ }
+
+ /* Succes. */
+ ret = 0;
+ end:
+ tor_free(fname);
+ return ret;
+}
+
+/* Free a given service descriptor object and all key material is wiped. */
+STATIC void
+service_descriptor_free(hs_service_descriptor_t *desc)
+{
+ if (!desc) {
+ return;
+ }
+ hs_descriptor_free(desc->desc);
+ memwipe(&desc->signing_kp, 0, sizeof(desc->signing_kp));
+ memwipe(&desc->blinded_kp, 0, sizeof(desc->blinded_kp));
+ /* Cleanup all intro points. */
+ digest256map_free(desc->intro_points.map, service_intro_point_free_);
+ digestmap_free(desc->intro_points.failed_id, tor_free_);
+ if (desc->previous_hsdirs) {
+ SMARTLIST_FOREACH(desc->previous_hsdirs, char *, s, tor_free(s));
+ smartlist_free(desc->previous_hsdirs);
+ }
+ tor_free(desc);
+}
+
+/* Return a newly allocated service descriptor object. */
+STATIC hs_service_descriptor_t *
+service_descriptor_new(void)
+{
+ hs_service_descriptor_t *sdesc = tor_malloc_zero(sizeof(*sdesc));
+ sdesc->desc = tor_malloc_zero(sizeof(hs_descriptor_t));
+ /* Initialize the intro points map. */
+ sdesc->intro_points.map = digest256map_new();
+ sdesc->intro_points.failed_id = digestmap_new();
+ sdesc->previous_hsdirs = smartlist_new();
+ return sdesc;
+}
+
+/* Move descriptor(s) from the src service to the dst service. We do this
+ * during SIGHUP when we re-create our hidden services. */
+static void
+move_descriptors(hs_service_t *src, hs_service_t *dst)
+{
+ tor_assert(src);
+ tor_assert(dst);
+
+ if (src->desc_current) {
+ /* Nothing should be there, but clean it up just in case */
+ if (BUG(dst->desc_current)) {
+ service_descriptor_free(dst->desc_current);
+ }
+ dst->desc_current = src->desc_current;
+ src->desc_current = NULL;
+ }
+
+ if (src->desc_next) {
+ /* Nothing should be there, but clean it up just in case */
+ if (BUG(dst->desc_next)) {
+ service_descriptor_free(dst->desc_next);
+ }
+ dst->desc_next = src->desc_next;
+ src->desc_next = NULL;
+ }
+}
+
+/* From the given service, remove all expired failing intro points for each
+ * descriptor. */
+static void
+remove_expired_failing_intro(hs_service_t *service, time_t now)
+{
+ tor_assert(service);
+
+ /* For both descriptors, cleanup the failing intro points list. */
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ DIGESTMAP_FOREACH_MODIFY(desc->intro_points.failed_id, key, time_t *, t) {
+ time_t failure_time = *t;
+ if ((failure_time + INTRO_CIRC_RETRY_PERIOD) <= now) {
+ MAP_DEL_CURRENT(key);
+ tor_free(t);
+ }
+ } DIGESTMAP_FOREACH_END;
+ } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* For the given descriptor desc, put all node_t object found from its failing
+ * intro point list and put them in the given node_list. */
+static void
+setup_intro_point_exclude_list(const hs_service_descriptor_t *desc,
+ smartlist_t *node_list)
+{
+ tor_assert(desc);
+ tor_assert(node_list);
+
+ DIGESTMAP_FOREACH(desc->intro_points.failed_id, key, time_t *, t) {
+ (void) t; /* Make gcc happy. */
+ const node_t *node = node_get_by_id(key);
+ if (node) {
+ smartlist_add(node_list, (void *) node);
+ }
+ } DIGESTMAP_FOREACH_END;
+}
+
+/* For the given failing intro point ip, we add its time of failure to the
+ * failed map and index it by identity digest (legacy ID) in the descriptor
+ * desc failed id map. */
+static void
+remember_failing_intro_point(const hs_service_intro_point_t *ip,
+ hs_service_descriptor_t *desc, time_t now)
+{
+ time_t *time_of_failure, *prev_ptr;
+ const hs_desc_link_specifier_t *legacy_ls;
+
+ tor_assert(ip);
+ tor_assert(desc);
+
+ time_of_failure = tor_malloc_zero(sizeof(time_t));
+ *time_of_failure = now;
+ legacy_ls = get_link_spec_by_type(ip, LS_LEGACY_ID);
+ tor_assert(legacy_ls);
+ prev_ptr = digestmap_set(desc->intro_points.failed_id,
+ (const char *) legacy_ls->u.legacy_id,
+ time_of_failure);
+ tor_free(prev_ptr);
+}
+
+/* Copy the descriptor link specifier object from src to dst. */
+static void
+link_specifier_copy(hs_desc_link_specifier_t *dst,
+ const hs_desc_link_specifier_t *src)
+{
+ tor_assert(dst);
+ tor_assert(src);
+ memcpy(dst, src, sizeof(hs_desc_link_specifier_t));
+}
+
+/* Using a given descriptor signing keypair signing_kp, a service intro point
+ * object ip and the time now, setup the content of an already allocated
+ * descriptor intro desc_ip.
+ *
+ * Return 0 on success else a negative value. */
+static int
+setup_desc_intro_point(const ed25519_keypair_t *signing_kp,
+ const hs_service_intro_point_t *ip,
+ time_t now, hs_desc_intro_point_t *desc_ip)
+{
+ int ret = -1;
+ time_t nearest_hour = now - (now % 3600);
+
+ tor_assert(signing_kp);
+ tor_assert(ip);
+ tor_assert(desc_ip);
+
+ /* Copy the onion key. */
+ memcpy(&desc_ip->onion_key, &ip->onion_key, sizeof(desc_ip->onion_key));
+
+ /* Key and certificate material. */
+ desc_ip->auth_key_cert = tor_cert_create(signing_kp,
+ CERT_TYPE_AUTH_HS_IP_KEY,
+ &ip->auth_key_kp.pubkey,
+ nearest_hour,
+ HS_DESC_CERT_LIFETIME,
+ CERT_FLAG_INCLUDE_SIGNING_KEY);
+ if (desc_ip->auth_key_cert == NULL) {
+ log_warn(LD_REND, "Unable to create intro point auth-key certificate");
+ goto done;
+ }
+
+ /* Copy link specifier(s). */
+ SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
+ const hs_desc_link_specifier_t *, ls) {
+ hs_desc_link_specifier_t *copy = tor_malloc_zero(sizeof(*copy));
+ link_specifier_copy(copy, ls);
+ smartlist_add(desc_ip->link_specifiers, copy);
+ } SMARTLIST_FOREACH_END(ls);
+
+ /* For a legacy intro point, we'll use an RSA/ed cross certificate. */
+ if (ip->base.is_only_legacy) {
+ desc_ip->legacy.key = crypto_pk_dup_key(ip->legacy_key);
+ /* Create cross certification cert. */
+ ssize_t cert_len = tor_make_rsa_ed25519_crosscert(
+ &signing_kp->pubkey,
+ desc_ip->legacy.key,
+ nearest_hour + HS_DESC_CERT_LIFETIME,
+ &desc_ip->legacy.cert.encoded);
+ if (cert_len < 0) {
+ log_warn(LD_REND, "Unable to create enc key legacy cross cert.");
+ goto done;
+ }
+ desc_ip->legacy.cert.len = cert_len;
+ }
+
+ /* Encryption key and its cross certificate. */
{
- /* To calculate the sig we follow the same procedure as above. We first
- dump the cell up to the sig, and then calculate the sig */
- uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0};
- ed25519_signature_t sig;
-
- encoded_len = trn_cell_establish_intro_encode(cell_bytes_tmp,
- sizeof(cell_bytes_tmp),
- cell);
- if (encoded_len < 0) {
- log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell (2).");
- goto err;
+ ed25519_public_key_t ed25519_pubkey;
+
+ /* Use the public curve25519 key. */
+ memcpy(&desc_ip->enc_key, &ip->enc_key_kp.pubkey,
+ sizeof(desc_ip->enc_key));
+ /* The following can't fail. */
+ ed25519_public_key_from_curve25519_public_key(&ed25519_pubkey,
+ &ip->enc_key_kp.pubkey,
+ 0);
+ desc_ip->enc_key_cert = tor_cert_create(signing_kp,
+ CERT_TYPE_CROSS_HS_IP_KEYS,
+ &ed25519_pubkey, nearest_hour,
+ HS_DESC_CERT_LIFETIME,
+ CERT_FLAG_INCLUDE_SIGNING_KEY);
+ if (desc_ip->enc_key_cert == NULL) {
+ log_warn(LD_REND, "Unable to create enc key curve25519 cross cert.");
+ goto done;
}
+ }
+ /* Success. */
+ ret = 0;
- tor_assert(encoded_len > ED25519_SIG_LEN);
+ done:
+ return ret;
+}
- if (ed25519_sign_prefixed(&sig,
- cell_bytes_tmp,
- encoded_len -
- (ED25519_SIG_LEN + sizeof(cell->sig_len)),
- ESTABLISH_INTRO_SIG_PREFIX,
- &key_struct)) {
- log_warn(LD_BUG, "Unable to gen signature for ESTABLISH_INTRO cell.");
- goto err;
+/* Using the given descriptor from the given service, build the descriptor
+ * intro point list so we can then encode the descriptor for publication. This
+ * function does not pick intro points, they have to be in the descriptor
+ * current map. Cryptographic material (keys) must be initialized in the
+ * descriptor for this function to make sense. */
+static void
+build_desc_intro_points(const hs_service_t *service,
+ hs_service_descriptor_t *desc, time_t now)
+{
+ hs_desc_encrypted_data_t *encrypted;
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ /* Ease our life. */
+ encrypted = &desc->desc->encrypted_data;
+ /* Cleanup intro points, we are about to set them from scratch. */
+ hs_descriptor_clear_intro_points(desc->desc);
+
+ DIGEST256MAP_FOREACH(desc->intro_points.map, key,
+ const hs_service_intro_point_t *, ip) {
+ hs_desc_intro_point_t *desc_ip = hs_desc_intro_point_new();
+ if (setup_desc_intro_point(&desc->signing_kp, ip, now, desc_ip) < 0) {
+ hs_desc_intro_point_free(desc_ip);
+ continue;
+ }
+ /* We have a valid descriptor intro point. Add it to the list. */
+ smartlist_add(encrypted->intro_points, desc_ip);
+ } DIGEST256MAP_FOREACH_END;
+}
+
+/* Populate the descriptor encrypted section fomr the given service object.
+ * This will generate a valid list of introduction points that can be used
+ * after for circuit creation. Return 0 on success else -1 on error. */
+static int
+build_service_desc_encrypted(const hs_service_t *service,
+ hs_service_descriptor_t *desc)
+{
+ hs_desc_encrypted_data_t *encrypted;
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ encrypted = &desc->desc->encrypted_data;
+
+ encrypted->create2_ntor = 1;
+ encrypted->single_onion_service = service->config.is_single_onion;
+
+ /* Setup introduction points from what we have in the service. */
+ if (encrypted->intro_points == NULL) {
+ encrypted->intro_points = smartlist_new();
+ }
+ /* We do NOT build introduction point yet, we only do that once the circuit
+ * have been opened. Until we have the right number of introduction points,
+ * we do not encode anything in the descriptor. */
+
+ /* XXX: Support client authorization (#20700). */
+ encrypted->intro_auth_types = NULL;
+ return 0;
+}
+
+/* Populare the descriptor plaintext section from the given service object.
+ * The caller must make sure that the keys in the descriptors are valid that
+ * is are non-zero. Return 0 on success else -1 on error. */
+static int
+build_service_desc_plaintext(const hs_service_t *service,
+ hs_service_descriptor_t *desc, time_t now)
+{
+ int ret = -1;
+ hs_desc_plaintext_data_t *plaintext;
+
+ tor_assert(service);
+ tor_assert(desc);
+ /* XXX: Use a "assert_desc_ok()" ? */
+ tor_assert(!tor_mem_is_zero((char *) &desc->blinded_kp,
+ sizeof(desc->blinded_kp)));
+ tor_assert(!tor_mem_is_zero((char *) &desc->signing_kp,
+ sizeof(desc->signing_kp)));
+
+ /* Set the subcredential. */
+ hs_get_subcredential(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
+ desc->desc->subcredential);
+
+ plaintext = &desc->desc->plaintext_data;
+
+ plaintext->version = service->config.version;
+ plaintext->lifetime_sec = HS_DESC_DEFAULT_LIFETIME;
+ plaintext->signing_key_cert =
+ tor_cert_create(&desc->blinded_kp, CERT_TYPE_SIGNING_HS_DESC,
+ &desc->signing_kp.pubkey, now, HS_DESC_CERT_LIFETIME,
+ CERT_FLAG_INCLUDE_SIGNING_KEY);
+ if (plaintext->signing_key_cert == NULL) {
+ log_warn(LD_REND, "Unable to create descriptor signing certificate for "
+ "service %s",
+ safe_str_client(service->onion_address));
+ goto end;
+ }
+ /* Copy public key material to go in the descriptor. */
+ ed25519_pubkey_copy(&plaintext->signing_pubkey, &desc->signing_kp.pubkey);
+ ed25519_pubkey_copy(&plaintext->blinded_pubkey, &desc->blinded_kp.pubkey);
+ /* Success. */
+ ret = 0;
+
+ end:
+ return ret;
+}
+
+/* For the given service and descriptor object, create the key material which
+ * is the blinded keypair and the descriptor signing keypair. Return 0 on
+ * success else -1 on error where the generated keys MUST be ignored. */
+static int
+build_service_desc_keys(const hs_service_t *service,
+ hs_service_descriptor_t *desc,
+ uint64_t time_period_num)
+{
+ int ret = 0;
+ ed25519_keypair_t kp;
+
+ tor_assert(desc);
+ tor_assert(!tor_mem_is_zero((char *) &service->keys.identity_pk,
+ ED25519_PUBKEY_LEN));
+
+ /* XXX: Support offline key feature (#18098). */
+
+ /* Copy the identity keys to the keypair so we can use it to create the
+ * blinded key. */
+ memcpy(&kp.pubkey, &service->keys.identity_pk, sizeof(kp.pubkey));
+ memcpy(&kp.seckey, &service->keys.identity_sk, sizeof(kp.seckey));
+ /* Build blinded keypair for this time period. */
+ hs_build_blinded_keypair(&kp, NULL, 0, time_period_num, &desc->blinded_kp);
+ /* Let's not keep too much traces of our keys in memory. */
+ memwipe(&kp, 0, sizeof(kp));
+
+ /* No need for extra strong, this is a temporary key only for this
+ * descriptor. Nothing long term. */
+ if (ed25519_keypair_generate(&desc->signing_kp, 0) < 0) {
+ log_warn(LD_REND, "Can't generate descriptor signing keypair for "
+ "service %s",
+ safe_str_client(service->onion_address));
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* Given a service and the current time, build a descriptor for the service.
+ * This function does not pick introduction point, this needs to be done by
+ * the update function. On success, desc_out will point to the newly allocated
+ * descriptor object.
+ *
+ * This can error if we are unable to create keys or certificate. */
+static void
+build_service_descriptor(hs_service_t *service, time_t now,
+ uint64_t time_period_num,
+ hs_service_descriptor_t **desc_out)
+{
+ char *encoded_desc;
+ hs_service_descriptor_t *desc;
+
+ tor_assert(service);
+ tor_assert(desc_out);
+
+ desc = service_descriptor_new();
+ desc->time_period_num = time_period_num;
+
+ /* Create the needed keys so we can setup the descriptor content. */
+ if (build_service_desc_keys(service, desc, time_period_num) < 0) {
+ goto err;
+ }
+ /* Setup plaintext descriptor content. */
+ if (build_service_desc_plaintext(service, desc, now) < 0) {
+ goto err;
+ }
+ /* Setup encrypted descriptor content. */
+ if (build_service_desc_encrypted(service, desc) < 0) {
+ goto err;
+ }
+
+ /* Set the revision counter for this descriptor */
+ set_descriptor_revision_counter(desc->desc);
+
+ /* Let's make sure that we've created a descriptor that can actually be
+ * encoded properly. This function also checks if the encoded output is
+ * decodable after. */
+ if (BUG(hs_desc_encode_descriptor(desc->desc, &desc->signing_kp,
+ &encoded_desc) < 0)) {
+ goto err;
+ }
+ tor_free(encoded_desc);
+
+ /* Assign newly built descriptor to the next slot. */
+ *desc_out = desc;
+ return;
+
+ err:
+ service_descriptor_free(desc);
+}
+
+/* Build both descriptors for the given service that has just booted up.
+ * Because it's a special case, it deserves its special function ;). */
+static void
+build_descriptors_for_new_service(hs_service_t *service, time_t now)
+{
+ uint64_t current_desc_tp, next_desc_tp;
+
+ tor_assert(service);
+ /* These are the conditions for a new service. */
+ tor_assert(!service->desc_current);
+ tor_assert(!service->desc_next);
+
+ /*
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | A B |
+ * +------------------------------------------------------------------+
+ *
+ * Case A: The service boots up before a new time period, the current time
+ * period is thus TP#1 and the next is TP#2 which for both we have access to
+ * their SRVs.
+ *
+ * Case B: The service boots up inside TP#2, we can't use the TP#3 for the
+ * next descriptor because we don't have the SRV#3 so the current should be
+ * TP#1 and next TP#2.
+ */
+
+ if (hs_in_period_between_tp_and_srv(NULL, now)) {
+ /* Case B from the above, inside of the new time period. */
+ current_desc_tp = hs_get_previous_time_period_num(0); /* TP#1 */
+ next_desc_tp = hs_get_time_period_num(0); /* TP#2 */
+ } else {
+ /* Case A from the above, outside of the new time period. */
+ current_desc_tp = hs_get_time_period_num(0); /* TP#1 */
+ next_desc_tp = hs_get_next_time_period_num(0); /* TP#2 */
+ }
+
+ /* Build descriptors. */
+ build_service_descriptor(service, now, current_desc_tp,
+ &service->desc_current);
+ build_service_descriptor(service, now, next_desc_tp,
+ &service->desc_next);
+ log_info(LD_REND, "Hidden service %s has just started. Both descriptors "
+ "built. Now scheduled for upload.",
+ safe_str_client(service->onion_address));
+}
+
+/* Build descriptors for each service if needed. There are conditions to build
+ * a descriptor which are details in the function. */
+STATIC void
+build_all_descriptors(time_t now)
+{
+ FOR_EACH_SERVICE_BEGIN(service) {
+
+ /* A service booting up will have both descriptors to NULL. No other cases
+ * makes both descriptor non existent. */
+ if (service->desc_current == NULL && service->desc_next == NULL) {
+ build_descriptors_for_new_service(service, now);
+ continue;
}
- /* And write the signature to the cell */
- uint8_t *sig_ptr = trn_cell_establish_intro_getarray_sig(cell);
- memcpy(sig_ptr, sig.sig, sig_len);
+ /* Reaching this point means we are pass bootup so at runtime. We should
+ * *never* have an empty current descriptor. If the next descriptor is
+ * empty, we'll try to build it for the next time period. This only
+ * happens when we rotate meaning that we are guaranteed to have a new SRV
+ * at that point for the next time period. */
+ tor_assert(service->desc_current);
+
+ if (service->desc_next == NULL) {
+ build_service_descriptor(service, now, hs_get_next_time_period_num(0),
+ &service->desc_next);
+ log_info(LD_REND, "Hidden service %s next descriptor successfully "
+ "built. Now scheduled for upload.",
+ safe_str_client(service->onion_address));
+ }
+ } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* Randomly pick a node to become an introduction point but not present in the
+ * given exclude_nodes list. The chosen node is put in the exclude list
+ * regardless of success or not because in case of failure, the node is simply
+ * unsusable from that point on.
+ *
+ * If direct_conn is set, try to pick a node that our local firewall/policy
+ * allows us to connect to directly. If we can't find any, return NULL.
+ * This function supports selecting dual-stack nodes for direct single onion
+ * service IPv6 connections. But it does not send IPv6 addresses in link
+ * specifiers. (Current clients don't use IPv6 addresses to extend, and
+ * direct client connections to intro points are not supported.)
+ *
+ * Return a newly allocated service intro point ready to be used for encoding.
+ * Return NULL on error. */
+static hs_service_intro_point_t *
+pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
+{
+ const node_t *node;
+ extend_info_t *info = NULL;
+ hs_service_intro_point_t *ip = NULL;
+ /* Normal 3-hop introduction point flags. */
+ router_crn_flags_t flags = CRN_NEED_UPTIME | CRN_NEED_DESC;
+ /* Single onion flags. */
+ router_crn_flags_t direct_flags = flags | CRN_PREF_ADDR | CRN_DIRECT_CONN;
+
+ node = router_choose_random_node(exclude_nodes, get_options()->ExcludeNodes,
+ direct_conn ? direct_flags : flags);
+ /* Unable to find a node. When looking for a node for a direct connection,
+ * we could try a 3-hop path instead. We'll add support for this in a later
+ * release. */
+ if (!node) {
+ goto err;
}
- /* We are done! Return the cell! */
- return cell;
+ /* We have a suitable node, add it to the exclude list. We do this *before*
+ * we can validate the extend information because even in case of failure,
+ * we don't want to use that node anymore. */
+ smartlist_add(exclude_nodes, (void *) node);
+ /* We do this to ease our life but also this call makes appropriate checks
+ * of the node object such as validating ntor support for instance.
+ *
+ * We must provide an extend_info for clients to connect over a 3-hop path,
+ * so we don't pass direct_conn here. */
+ info = extend_info_from_node(node, 0);
+ if (BUG(info == NULL)) {
+ goto err;
+ }
+
+ /* Let's do a basic sanity check here so that we don't end up advertising the
+ * ed25519 identity key of relays that don't actually support the link
+ * protocol */
+ if (!node_supports_ed25519_link_authentication(node)) {
+ tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity));
+ } else {
+ /* Make sure we *do* have an ed key if we support the link authentication.
+ * Sending an empty key would result in a failure to extend. */
+ tor_assert_nonfatal(!ed25519_public_key_is_zero(&info->ed_identity));
+ }
+
+ /* Create our objects and populate them with the node information. */
+ ip = service_intro_point_new(info, !node_supports_ed25519_hs_intro(node));
+ if (ip == NULL) {
+ goto err;
+ }
+
+ log_info(LD_REND, "Picked intro point: %s", extend_info_describe(info));
+ extend_info_free(info);
+ return ip;
err:
- trn_cell_establish_intro_free(cell);
+ service_intro_point_free(ip);
+ extend_info_free(info);
return NULL;
}
+/* For a given descriptor from the given service, pick any needed intro points
+ * and update the current map with those newly picked intro points. Return the
+ * number node that might have been added to the descriptor current map. */
+static unsigned int
+pick_needed_intro_points(hs_service_t *service,
+ hs_service_descriptor_t *desc)
+{
+ int i = 0, num_needed_ip;
+ smartlist_t *exclude_nodes = smartlist_new();
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ /* Compute how many intro points we actually need to open. */
+ num_needed_ip = service->config.num_intro_points -
+ digest256map_size(desc->intro_points.map);
+ if (BUG(num_needed_ip < 0)) {
+ /* Let's not make tor freak out here and just skip this. */
+ goto done;
+ }
+
+ /* We want to end up with config.num_intro_points intro points, but if we
+ * have no intro points at all (chances are they all cycled or we are
+ * starting up), we launch get_intro_point_num_extra() extra circuits and
+ * use the first config.num_intro_points that complete. See proposal #155,
+ * section 4 for the rationale of this which is purely for performance.
+ *
+ * The ones after the first config.num_intro_points will be converted to
+ * 'General' internal circuits and then we'll drop them from the list of
+ * intro points. */
+ if (digest256map_size(desc->intro_points.map) == 0) {
+ num_needed_ip += get_intro_point_num_extra();
+ }
+
+ /* Build an exclude list of nodes of our intro point(s). The expiring intro
+ * points are OK to pick again because this is afterall a concept of round
+ * robin so they are considered valid nodes to pick again. */
+ DIGEST256MAP_FOREACH(desc->intro_points.map, key,
+ hs_service_intro_point_t *, ip) {
+ const node_t *intro_node = get_node_from_intro_point(ip);
+ if (intro_node) {
+ smartlist_add(exclude_nodes, (void*)intro_node);
+ }
+ } DIGEST256MAP_FOREACH_END;
+ /* Also, add the failing intro points that our descriptor encounteered in
+ * the exclude node list. */
+ setup_intro_point_exclude_list(desc, exclude_nodes);
+
+ for (i = 0; i < num_needed_ip; i++) {
+ hs_service_intro_point_t *ip;
+
+ /* This function will add the picked intro point node to the exclude nodes
+ * list so we don't pick the same one at the next iteration. */
+ ip = pick_intro_point(service->config.is_single_onion, exclude_nodes);
+ if (ip == NULL) {
+ /* If we end up unable to pick an introduction point it is because we
+ * can't find suitable node and calling this again is highly unlikely to
+ * give us a valid node all of the sudden. */
+ log_info(LD_REND, "Unable to find a suitable node to be an "
+ "introduction point for service %s.",
+ safe_str_client(service->onion_address));
+ goto done;
+ }
+ /* Valid intro point object, add it to the descriptor current map. */
+ service_intro_point_add(desc->intro_points.map, ip);
+ }
+ /* We've successfully picked all our needed intro points thus none are
+ * missing which will tell our upload process to expect the number of
+ * circuits to be the number of configured intro points circuits and not the
+ * number of intro points object that we have. */
+ desc->missing_intro_points = 0;
+
+ /* Success. */
+ done:
+ /* We don't have ownership of the node_t object in this list. */
+ smartlist_free(exclude_nodes);
+ return i;
+}
+
+/** Clear previous cached HSDirs in <b>desc</b>. */
+static void
+service_desc_clear_previous_hsdirs(hs_service_descriptor_t *desc)
+{
+ if (BUG(!desc->previous_hsdirs)) {
+ return;
+ }
+
+ SMARTLIST_FOREACH(desc->previous_hsdirs, char*, s, tor_free(s));
+ smartlist_clear(desc->previous_hsdirs);
+}
+
+/** Note that we attempted to upload <b>desc</b> to <b>hsdir</b>. */
+static void
+service_desc_note_upload(hs_service_descriptor_t *desc, const node_t *hsdir)
+{
+ char b64_digest[BASE64_DIGEST_LEN+1] = {0};
+ digest_to_base64(b64_digest, hsdir->identity);
+
+ if (BUG(!desc->previous_hsdirs)) {
+ return;
+ }
+
+ if (!smartlist_contains_string(desc->previous_hsdirs, b64_digest)) {
+ smartlist_add_strdup(desc->previous_hsdirs, b64_digest);
+ }
+}
+
+/** Schedule an upload of <b>desc</b>. If <b>descriptor_changed</b> is set, it
+ * means that this descriptor is dirty. */
+STATIC void
+service_desc_schedule_upload(hs_service_descriptor_t *desc,
+ time_t now,
+ int descriptor_changed)
+
+{
+ desc->next_upload_time = now;
+
+ /* If the descriptor changed, clean up the old HSDirs list. We want to
+ * re-upload no matter what. */
+ if (descriptor_changed) {
+ service_desc_clear_previous_hsdirs(desc);
+ }
+}
+
+/* Update the given descriptor from the given service. The possible update
+ * actions includes:
+ * - Picking missing intro points if needed.
+ * - Incrementing the revision counter if needed.
+ */
+static void
+update_service_descriptor(hs_service_t *service,
+ hs_service_descriptor_t *desc, time_t now)
+{
+ unsigned int num_intro_points;
+
+ tor_assert(service);
+ tor_assert(desc);
+ tor_assert(desc->desc);
+
+ num_intro_points = digest256map_size(desc->intro_points.map);
+
+ /* Pick any missing introduction point(s). */
+ if (num_intro_points < service->config.num_intro_points) {
+ unsigned int num_new_intro_points = pick_needed_intro_points(service,
+ desc);
+ if (num_new_intro_points != 0) {
+ log_info(LD_REND, "Service %s just picked %u intro points and wanted "
+ "%u for %s descriptor. It currently has %d intro "
+ "points. Launching ESTABLISH_INTRO circuit shortly.",
+ safe_str_client(service->onion_address),
+ num_new_intro_points,
+ service->config.num_intro_points - num_intro_points,
+ (desc == service->desc_current) ? "current" : "next",
+ num_intro_points);
+ /* We'll build those introduction point into the descriptor once we have
+ * confirmation that the circuits are opened and ready. However,
+ * indicate that this descriptor should be uploaded from now on. */
+ service_desc_schedule_upload(desc, now, 1);
+ }
+ /* Were we able to pick all the intro points we needed? If not, we'll
+ * flag the descriptor that it's missing intro points because it
+ * couldn't pick enough which will trigger a descriptor upload. */
+ if ((num_new_intro_points + num_intro_points) <
+ service->config.num_intro_points) {
+ desc->missing_intro_points = 1;
+ }
+ }
+}
+
+/* Update descriptors for each service if needed. */
+STATIC void
+update_all_descriptors(time_t now)
+{
+ FOR_EACH_SERVICE_BEGIN(service) {
+ /* We'll try to update each descriptor that is if certain conditions apply
+ * in order for the descriptor to be updated. */
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ update_service_descriptor(service, desc, now);
+ } FOR_EACH_DESCRIPTOR_END;
+ } FOR_EACH_SERVICE_END;
+}
+
+/* Return true iff the given intro point has expired that is it has been used
+ * for too long or we've reached our max seen INTRODUCE2 cell. */
+STATIC int
+intro_point_should_expire(const hs_service_intro_point_t *ip,
+ time_t now)
+{
+ tor_assert(ip);
+
+ if (ip->introduce2_count >= ip->introduce2_max) {
+ goto expired;
+ }
+
+ if (ip->time_to_expire <= now) {
+ goto expired;
+ }
+
+ /* Not expiring. */
+ return 0;
+ expired:
+ return 1;
+}
+
+/* Go over the given set of intro points for each service and remove any
+ * invalid ones. The conditions for removal are:
+ *
+ * - The node doesn't exists anymore (not in consensus)
+ * OR
+ * - The intro point maximum circuit retry count has been reached and no
+ * circuit can be found associated with it.
+ * OR
+ * - The intro point has expired and we should pick a new one.
+ *
+ * If an intro point is removed, the circuit (if any) is immediately close.
+ * If a circuit can't be found, the intro point is kept if it hasn't reached
+ * its maximum circuit retry value and thus should be retried. */
+static void
+cleanup_intro_points(hs_service_t *service, time_t now)
+{
+ /* List of intro points to close. We can't mark the intro circuits for close
+ * in the modify loop because doing so calls
+ * hs_service_intro_circ_has_closed() which does a digest256map_get() on the
+ * intro points map (that we are iterating over). This can't be done in a
+ * single iteration after a MAP_DEL_CURRENT, the object will still be
+ * returned leading to a use-after-free. So, we close the circuits and free
+ * the intro points after the loop if any. */
+ smartlist_t *ips_to_free = smartlist_new();
+
+ tor_assert(service);
+
+ /* For both descriptors, cleanup the intro points. */
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ /* Go over the current intro points we have, make sure they are still
+ * valid and remove any of them that aren't. */
+ DIGEST256MAP_FOREACH_MODIFY(desc->intro_points.map, key,
+ hs_service_intro_point_t *, ip) {
+ const node_t *node = get_node_from_intro_point(ip);
+ int has_expired = intro_point_should_expire(ip, now);
+
+ /* We cleanup an intro point if it has expired or if we do not know the
+ * node_t anymore (removed from our latest consensus) or if we've
+ * reached the maximum number of retry with a non existing circuit. */
+ if (has_expired || node == NULL ||
+ ip->circuit_retries > MAX_INTRO_POINT_CIRCUIT_RETRIES) {
+ log_info(LD_REND, "Intro point %s%s (retried: %u times). "
+ "Removing it.",
+ describe_intro_point(ip),
+ has_expired ? " has expired" :
+ (node == NULL) ? " fell off the consensus" : "",
+ ip->circuit_retries);
+
+ /* We've retried too many times, remember it as a failed intro point
+ * so we don't pick it up again for INTRO_CIRC_RETRY_PERIOD sec. */
+ if (ip->circuit_retries > MAX_INTRO_POINT_CIRCUIT_RETRIES) {
+ remember_failing_intro_point(ip, desc, approx_time());
+ }
+
+ /* Remove intro point from descriptor map and add it to the list of
+ * ips to free for which we'll also try to close the intro circuit. */
+ MAP_DEL_CURRENT(key);
+ smartlist_add(ips_to_free, ip);
+ }
+ } DIGEST256MAP_FOREACH_END;
+ } FOR_EACH_DESCRIPTOR_END;
+
+ /* Go over the intro points to free and close their circuit if any. */
+ SMARTLIST_FOREACH_BEGIN(ips_to_free, hs_service_intro_point_t *, ip) {
+ /* See if we need to close the intro point circuit as well */
+
+ /* XXX: Legacy code does NOT close circuits like this: it keeps the circuit
+ * open until a new descriptor is uploaded and then closed all expiring
+ * intro point circuit. Here, we close immediately and because we just
+ * discarded the intro point, a new one will be selected, a new descriptor
+ * created and uploaded. There is no difference to an attacker between the
+ * timing of a new consensus and intro point rotation (possibly?). */
+ origin_circuit_t *ocirc = hs_circ_service_get_intro_circ(ip);
+ if (ocirc && !TO_CIRCUIT(ocirc)->marked_for_close) {
+ circuit_mark_for_close(TO_CIRCUIT(ocirc), END_CIRC_REASON_FINISHED);
+ }
+
+ /* Cleanup the intro point */
+ service_intro_point_free(ip);
+ } SMARTLIST_FOREACH_END(ip);
+
+ smartlist_free(ips_to_free);
+}
+
+/* Set the next rotation time of the descriptors for the given service for the
+ * time now. */
+static void
+set_rotation_time(hs_service_t *service, time_t now)
+{
+ time_t valid_after;
+ const networkstatus_t *ns = networkstatus_get_live_consensus(now);
+ if (ns) {
+ valid_after = ns->valid_after;
+ } else {
+ valid_after = now;
+ }
+
+ tor_assert(service);
+ service->state.next_rotation_time =
+ sr_state_get_start_time_of_current_protocol_run(valid_after) +
+ sr_state_get_protocol_run_duration();
+
+ {
+ char fmt_time[ISO_TIME_LEN + 1];
+ format_local_iso_time(fmt_time, service->state.next_rotation_time);
+ log_info(LD_REND, "Next descriptor rotation time set to %s for %s",
+ fmt_time, safe_str_client(service->onion_address));
+ }
+}
+
+/* Return true iff the service should rotate its descriptor. The time now is
+ * only used to fetch the live consensus and if none can be found, this
+ * returns false. */
+static unsigned int
+should_rotate_descriptors(hs_service_t *service, time_t now)
+{
+ const networkstatus_t *ns;
+
+ tor_assert(service);
+
+ ns = networkstatus_get_live_consensus(now);
+ if (ns == NULL) {
+ goto no_rotation;
+ }
+
+ if (ns->valid_after >= service->state.next_rotation_time) {
+ goto rotation;
+ }
+
+ no_rotation:
+ return 0;
+ rotation:
+ return 1;
+}
+
+/* Rotate the service descriptors of the given service. The current descriptor
+ * will be freed, the next one put in as the current and finally the next
+ * descriptor pointer is NULLified. */
+static void
+rotate_service_descriptors(hs_service_t *service, time_t now)
+{
+ if (service->desc_current) {
+ /* Close all IP circuits for the descriptor. */
+ close_intro_circuits(&service->desc_current->intro_points);
+ /* We don't need this one anymore, we won't serve any clients coming with
+ * this service descriptor. */
+ service_descriptor_free(service->desc_current);
+ }
+ /* The next one become the current one and emptying the next will trigger
+ * a descriptor creation for it. */
+ service->desc_current = service->desc_next;
+ service->desc_next = NULL;
+
+ /* We've just rotated, set the next time for the rotation. */
+ set_rotation_time(service, now);
+}
+
+/* Rotate descriptors for each service if needed. A non existing current
+ * descriptor will trigger a descriptor build for the next time period. */
+STATIC void
+rotate_all_descriptors(time_t now)
+{
+ /* XXX We rotate all our service descriptors at once. In the future it might
+ * be wise, to rotate service descriptors independently to hide that all
+ * those descriptors are on the same tor instance */
+
+ FOR_EACH_SERVICE_BEGIN(service) {
+
+ /* Note for a service booting up: Both descriptors are NULL in that case
+ * so this function might return true if we are in the timeframe for a
+ * rotation leading to basically swapping two NULL pointers which is
+ * harmless. However, the side effect is that triggering a rotation will
+ * update the service state and avoid doing anymore rotations after the
+ * two descriptors have been built. */
+ if (!should_rotate_descriptors(service, now)) {
+ continue;
+ }
+
+ tor_assert(service->desc_current);
+ tor_assert(service->desc_next);
+
+ log_info(LD_REND, "Time to rotate our descriptors (%p / %p) for %s",
+ service->desc_current, service->desc_next,
+ safe_str_client(service->onion_address));
+
+ rotate_service_descriptors(service, now);
+ } FOR_EACH_SERVICE_END;
+}
+
+/* Scheduled event run from the main loop. Make sure all our services are up
+ * to date and ready for the other scheduled events. This includes looking at
+ * the introduction points status and descriptor rotation time. */
+STATIC void
+run_housekeeping_event(time_t now)
+{
+ /* Note that nothing here opens circuit(s) nor uploads descriptor(s). We are
+ * simply moving things around or removing unneeded elements. */
+
+ FOR_EACH_SERVICE_BEGIN(service) {
+
+ /* If the service is starting off, set the rotation time. We can't do that
+ * at configure time because the get_options() needs to be set for setting
+ * that time that uses the voting interval. */
+ if (service->state.next_rotation_time == 0) {
+ /* Set the next rotation time of the descriptors. If it's Oct 25th
+ * 23:47:00, the next rotation time is when the next SRV is computed
+ * which is at Oct 26th 00:00:00 that is in 13 minutes. */
+ set_rotation_time(service, now);
+ }
+
+ /* Cleanup invalid intro points from the service descriptor. */
+ cleanup_intro_points(service, now);
+
+ /* Remove expired failing intro point from the descriptor failed list. We
+ * reset them at each INTRO_CIRC_RETRY_PERIOD. */
+ remove_expired_failing_intro(service, now);
+
+ /* At this point, the service is now ready to go through the scheduled
+ * events guaranteeing a valid state. Intro points might be missing from
+ * the descriptors after the cleanup but the update/build process will
+ * make sure we pick those missing ones. */
+ } FOR_EACH_SERVICE_END;
+}
+
+/* Scheduled event run from the main loop. Make sure all descriptors are up to
+ * date. Once this returns, each service descriptor needs to be considered for
+ * new introduction circuits and then for upload. */
+static void
+run_build_descriptor_event(time_t now)
+{
+ /* For v2 services, this step happens in the upload event. */
+
+ /* Run v3+ events. */
+ /* We start by rotating the descriptors only if needed. */
+ rotate_all_descriptors(now);
+
+ /* Then, we'll try to build new descriptors that we might need. The
+ * condition is that the next descriptor is non existing because it has
+ * been rotated or we just started up. */
+ build_all_descriptors(now);
+
+ /* Finally, we'll check if we should update the descriptors. Missing
+ * introduction points will be picked in this function which is useful for
+ * newly built descriptors. */
+ update_all_descriptors(now);
+}
+
+/* For the given service, launch any intro point circuits that could be
+ * needed. This considers every descriptor of the service. */
+static void
+launch_intro_point_circuits(hs_service_t *service)
+{
+ tor_assert(service);
+
+ /* For both descriptors, try to launch any missing introduction point
+ * circuits using the current map. */
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ /* Keep a ref on if we need a direct connection. We use this often. */
+ unsigned int direct_conn = service->config.is_single_onion;
+
+ DIGEST256MAP_FOREACH_MODIFY(desc->intro_points.map, key,
+ hs_service_intro_point_t *, ip) {
+ extend_info_t *ei;
+
+ /* Skip the intro point that already has an existing circuit
+ * (established or not). */
+ if (hs_circ_service_get_intro_circ(ip)) {
+ continue;
+ }
+
+ ei = get_extend_info_from_intro_point(ip, direct_conn);
+ if (ei == NULL) {
+ /* This is possible if we can get a node_t but not the extend info out
+ * of it. In this case, we remove the intro point and a new one will
+ * be picked at the next main loop callback. */
+ MAP_DEL_CURRENT(key);
+ service_intro_point_free(ip);
+ continue;
+ }
+
+ /* Launch a circuit to the intro point. */
+ ip->circuit_retries++;
+ if (hs_circ_launch_intro_point(service, ip, ei) < 0) {
+ log_info(LD_REND, "Unable to launch intro circuit to node %s "
+ "for service %s.",
+ safe_str_client(extend_info_describe(ei)),
+ safe_str_client(service->onion_address));
+ /* Intro point will be retried if possible after this. */
+ }
+ extend_info_free(ei);
+ } DIGEST256MAP_FOREACH_END;
+ } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* Don't try to build more than this many circuits before giving up for a
+ * while. Dynamically calculated based on the configured number of intro
+ * points for the given service and how many descriptor exists. The default
+ * use case of 3 introduction points and two descriptors will allow 28
+ * circuits for a retry period (((3 + 2) + (3 * 3)) * 2). */
+static unsigned int
+get_max_intro_circ_per_period(const hs_service_t *service)
+{
+ unsigned int count = 0;
+ unsigned int multiplier = 0;
+ unsigned int num_wanted_ip;
+
+ tor_assert(service);
+ tor_assert(service->config.num_intro_points <=
+ HS_CONFIG_V3_MAX_INTRO_POINTS);
+
+/* For a testing network, allow to do it for the maximum amount so circuit
+ * creation and rotation and so on can actually be tested without limit. */
+#define MAX_INTRO_POINT_CIRCUIT_RETRIES_TESTING -1
+ if (get_options()->TestingTorNetwork) {
+ return MAX_INTRO_POINT_CIRCUIT_RETRIES_TESTING;
+ }
+
+ num_wanted_ip = service->config.num_intro_points;
+
+ /* The calculation is as follow. We have a number of intro points that we
+ * want configured as a torrc option (num_intro_points). We then add an
+ * extra value so we can launch multiple circuits at once and pick the
+ * quickest ones. For instance, we want 3 intros, we add 2 extra so we'll
+ * pick 5 intros and launch 5 circuits. */
+ count += (num_wanted_ip + get_intro_point_num_extra());
+
+ /* Then we add the number of retries that is possible to do for each intro
+ * point. If we want 3 intros, we'll allow 3 times the number of possible
+ * retry. */
+ count += (num_wanted_ip * MAX_INTRO_POINT_CIRCUIT_RETRIES);
+
+ /* Then, we multiply by a factor of 2 if we have both descriptor or 0 if we
+ * have none. */
+ multiplier += (service->desc_current) ? 1 : 0;
+ multiplier += (service->desc_next) ? 1 : 0;
+
+ return (count * multiplier);
+}
+
+/* For the given service, return 1 if the service is allowed to launch more
+ * introduction circuits else 0 if the maximum has been reached for the retry
+ * period of INTRO_CIRC_RETRY_PERIOD. */
+STATIC int
+can_service_launch_intro_circuit(hs_service_t *service, time_t now)
+{
+ tor_assert(service);
+
+ /* Consider the intro circuit retry period of the service. */
+ if (now > (service->state.intro_circ_retry_started_time +
+ INTRO_CIRC_RETRY_PERIOD)) {
+ service->state.intro_circ_retry_started_time = now;
+ service->state.num_intro_circ_launched = 0;
+ goto allow;
+ }
+ /* Check if we can still launch more circuits in this period. */
+ if (service->state.num_intro_circ_launched <=
+ get_max_intro_circ_per_period(service)) {
+ goto allow;
+ }
+
+ /* Rate limit log that we've reached our circuit creation limit. */
+ {
+ char *msg;
+ time_t elapsed_time = now - service->state.intro_circ_retry_started_time;
+ static ratelim_t rlimit = RATELIM_INIT(INTRO_CIRC_RETRY_PERIOD);
+ if ((msg = rate_limit_log(&rlimit, now))) {
+ log_info(LD_REND, "Hidden service %s exceeded its circuit launch limit "
+ "of %u per %d seconds. It launched %u circuits in "
+ "the last %ld seconds. Will retry in %ld seconds.",
+ safe_str_client(service->onion_address),
+ get_max_intro_circ_per_period(service),
+ INTRO_CIRC_RETRY_PERIOD,
+ service->state.num_intro_circ_launched,
+ (long int) elapsed_time,
+ (long int) (INTRO_CIRC_RETRY_PERIOD - elapsed_time));
+ tor_free(msg);
+ }
+ }
+
+ /* Not allow. */
+ return 0;
+ allow:
+ return 1;
+}
+
+/* Scheduled event run from the main loop. Make sure we have all the circuits
+ * we need for each service. */
+static void
+run_build_circuit_event(time_t now)
+{
+ /* Make sure we can actually have enough information or able to build
+ * internal circuits as required by services. */
+ if (router_have_consensus_path() == CONSENSUS_PATH_UNKNOWN ||
+ !have_completed_a_circuit()) {
+ return;
+ }
+
+ /* Run v2 check. */
+ if (rend_num_services() > 0) {
+ rend_consider_services_intro_points(now);
+ }
+
+ /* Run v3+ check. */
+ FOR_EACH_SERVICE_BEGIN(service) {
+ /* For introduction circuit, we need to make sure we don't stress too much
+ * circuit creation so make sure this service is respecting that limit. */
+ if (can_service_launch_intro_circuit(service, now)) {
+ /* Launch intro point circuits if needed. */
+ launch_intro_point_circuits(service);
+ /* Once the circuits have opened, we'll make sure to update the
+ * descriptor intro point list and cleanup any extraneous. */
+ }
+ } FOR_EACH_SERVICE_END;
+}
+
+/* Encode and sign the service descriptor desc and upload it to the given
+ * hidden service directory. This does nothing if PublishHidServDescriptors
+ * is false. */
+static void
+upload_descriptor_to_hsdir(const hs_service_t *service,
+ hs_service_descriptor_t *desc, const node_t *hsdir)
+{
+ char version_str[4] = {0}, *encoded_desc = NULL;
+ directory_request_t *dir_req;
+ hs_ident_dir_conn_t ident;
+
+ tor_assert(service);
+ tor_assert(desc);
+ tor_assert(hsdir);
+
+ memset(&ident, 0, sizeof(ident));
+
+ /* Let's avoid doing that if tor is configured to not publish. */
+ if (!get_options()->PublishHidServDescriptors) {
+ log_info(LD_REND, "Service %s not publishing descriptor. "
+ "PublishHidServDescriptors is set to 1.",
+ safe_str_client(service->onion_address));
+ goto end;
+ }
+
+ /* First of all, we'll encode the descriptor. This should NEVER fail but
+ * just in case, let's make sure we have an actual usable descriptor. */
+ if (BUG(hs_desc_encode_descriptor(desc->desc, &desc->signing_kp,
+ &encoded_desc) < 0)) {
+ goto end;
+ }
+
+ /* Setup the connection identifier. */
+ hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
+ &ident);
+
+ /* This is our resource when uploading which is used to construct the URL
+ * with the version number: "/tor/hs/<version>/publish". */
+ tor_snprintf(version_str, sizeof(version_str), "%u",
+ service->config.version);
+
+ /* Build the directory request for this HSDir. */
+ dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC);
+ directory_request_set_routerstatus(dir_req, hsdir->rs);
+ directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS);
+ directory_request_set_resource(dir_req, version_str);
+ directory_request_set_payload(dir_req, encoded_desc,
+ strlen(encoded_desc));
+ /* The ident object is copied over the directory connection object once
+ * the directory request is initiated. */
+ directory_request_upload_set_hs_ident(dir_req, &ident);
+
+ /* Initiate the directory request to the hsdir.*/
+ directory_initiate_request(dir_req);
+ directory_request_free(dir_req);
+
+ /* Add this node to previous_hsdirs list */
+ service_desc_note_upload(desc, hsdir);
+
+ /* Logging so we know where it was sent. */
+ {
+ int is_next_desc = (service->desc_next == desc);
+ const uint8_t *idx = (is_next_desc) ? hsdir->hsdir_index->store_second:
+ hsdir->hsdir_index->store_first;
+ log_info(LD_REND, "Service %s %s descriptor of revision %" PRIu64
+ " initiated upload request to %s with index %s",
+ safe_str_client(service->onion_address),
+ (is_next_desc) ? "next" : "current",
+ desc->desc->plaintext_data.revision_counter,
+ safe_str_client(node_describe(hsdir)),
+ safe_str_client(hex_str((const char *) idx, 32)));
+ }
+
+ /* XXX: Inform control port of the upload event (#20699). */
+ end:
+ tor_free(encoded_desc);
+ return;
+}
+
+/** Return a newly-allocated string for our state file which contains revision
+ * counter information for <b>desc</b>. The format is:
+ *
+ * HidServRevCounter <blinded_pubkey> <rev_counter>
+ */
+STATIC char *
+encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc)
+{
+ char *state_str = NULL;
+ char blinded_pubkey_b64[ED25519_BASE64_LEN+1];
+ uint64_t rev_counter = desc->desc->plaintext_data.revision_counter;
+ const ed25519_public_key_t *blinded_pubkey = &desc->blinded_kp.pubkey;
+
+ /* Turn the blinded key into b64 so that we save it on state */
+ tor_assert(blinded_pubkey);
+ if (ed25519_public_to_base64(blinded_pubkey_b64, blinded_pubkey) < 0) {
+ goto done;
+ }
+
+ /* Format is: <blinded key> <rev counter> */
+ tor_asprintf(&state_str, "%s %" PRIu64, blinded_pubkey_b64, rev_counter);
+
+ log_info(LD_GENERAL, "[!] Adding rev counter %" PRIu64 " for %s!",
+ rev_counter, blinded_pubkey_b64);
+
+ done:
+ return state_str;
+}
+
+/** Update HS descriptor revision counters in our state by removing the old
+ * ones and writing down the ones that are currently active. */
+static void
+update_revision_counters_in_state(void)
+{
+ config_line_t *lines = NULL;
+ config_line_t **nextline = &lines;
+ or_state_t *state = get_or_state();
+
+ /* Prepare our state structure with the rev counters */
+ FOR_EACH_SERVICE_BEGIN(service) {
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ /* We don't want to save zero counters */
+ if (desc->desc->plaintext_data.revision_counter == 0) {
+ continue;
+ }
+
+ *nextline = tor_malloc_zero(sizeof(config_line_t));
+ (*nextline)->key = tor_strdup("HidServRevCounter");
+ (*nextline)->value = encode_desc_rev_counter_for_state(desc);
+ nextline = &(*nextline)->next;
+ } FOR_EACH_DESCRIPTOR_END;
+ } FOR_EACH_SERVICE_END;
+
+ /* Remove the old rev counters, and replace them with the new ones */
+ config_free_lines(state->HidServRevCounter);
+ state->HidServRevCounter = lines;
+
+ /* Set the state as dirty since we just edited it */
+ if (!get_options()->AvoidDiskWrites) {
+ or_state_mark_dirty(state, 0);
+ }
+}
+
+/** Scan the string <b>state_line</b> for the revision counter of the service
+ * with <b>blinded_pubkey</b>. Set <b>service_found_out</b> to True if the
+ * line is relevant to this service, and return the cached revision
+ * counter. Else set <b>service_found_out</b> to False. */
+STATIC uint64_t
+check_state_line_for_service_rev_counter(const char *state_line,
+ const ed25519_public_key_t *blinded_pubkey,
+ int *service_found_out)
+{
+ smartlist_t *items = NULL;
+ int ok;
+ ed25519_public_key_t pubkey_in_state;
+ uint64_t rev_counter = 0;
+
+ tor_assert(service_found_out);
+ tor_assert(state_line);
+ tor_assert(blinded_pubkey);
+
+ /* Assume that the line is not for this service */
+ *service_found_out = 0;
+
+ /* Start parsing the state line */
+ items = smartlist_new();
+ smartlist_split_string(items, state_line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+ if (smartlist_len(items) < 2) {
+ log_warn(LD_GENERAL, "Incomplete rev counter line. Ignoring.");
+ goto done;
+ }
+
+ char *b64_key_str = smartlist_get(items, 0);
+ char *saved_rev_counter_str = smartlist_get(items, 1);
+
+ /* Parse blinded key to check if it's for this hidden service */
+ if (ed25519_public_from_base64(&pubkey_in_state, b64_key_str) < 0) {
+ log_warn(LD_GENERAL, "Unable to base64 key in revcount line. Ignoring.");
+ goto done;
+ }
+ /* State line not for this hidden service */
+ if (!ed25519_pubkey_eq(&pubkey_in_state, blinded_pubkey)) {
+ goto done;
+ }
+
+ rev_counter = tor_parse_uint64(saved_rev_counter_str,
+ 10, 0, UINT64_MAX, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_GENERAL, "Unable to parse rev counter. Ignoring.");
+ goto done;
+ }
+
+ /* Since we got this far, the line was for this service */
+ *service_found_out = 1;
+
+ log_info(LD_GENERAL, "Found rev counter for %s: %" PRIu64,
+ b64_key_str, rev_counter);
+
+ done:
+ tor_assert(items);
+ SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+ smartlist_free(items);
+
+ return rev_counter;
+}
+
+/** Dig into our state file and find the current revision counter for the
+ * service with blinded key <b>blinded_pubkey</b>. If no revision counter is
+ * found, return 0. */
+static uint64_t
+get_rev_counter_for_service(const ed25519_public_key_t *blinded_pubkey)
+{
+ or_state_t *state = get_or_state();
+ config_line_t *line;
+
+ /* Set default value for rev counters (if not found) to 0 */
+ uint64_t final_rev_counter = 0;
+
+ for (line = state->HidServRevCounter ; line ; line = line->next) {
+ int service_found = 0;
+ uint64_t rev_counter = 0;
+
+ tor_assert(!strcmp(line->key, "HidServRevCounter"));
+
+ /* Scan all the HidServRevCounter lines till we find the line for this
+ service: */
+ rev_counter = check_state_line_for_service_rev_counter(line->value,
+ blinded_pubkey,
+ &service_found);
+ if (service_found) {
+ final_rev_counter = rev_counter;
+ goto done;
+ }
+ }
+
+ done:
+ return final_rev_counter;
+}
+
+/** Update the value of the revision counter for <b>hs_desc</b> and save it on
+ our state file. */
+static void
+increment_descriptor_revision_counter(hs_descriptor_t *hs_desc)
+{
+ /* Find stored rev counter if it exists */
+ uint64_t rev_counter =
+ get_rev_counter_for_service(&hs_desc->plaintext_data.blinded_pubkey);
+
+ /* Increment the revision counter of <b>hs_desc</b> so the next update (which
+ * will trigger an upload) will have the right value. We do this at this
+ * stage to only do it once because a descriptor can have many updates before
+ * being uploaded. By doing it at upload, we are sure to only increment by 1
+ * and thus avoid leaking how many operations we made on the descriptor from
+ * the previous one before uploading. */
+ rev_counter++;
+ hs_desc->plaintext_data.revision_counter = rev_counter;
+
+ update_revision_counters_in_state();
+}
+
+/** Set the revision counter in <b>hs_desc</b>, using the state file to find
+ * the current counter value if it exists. */
+static void
+set_descriptor_revision_counter(hs_descriptor_t *hs_desc)
+{
+ /* Find stored rev counter if it exists */
+ uint64_t rev_counter =
+ get_rev_counter_for_service(&hs_desc->plaintext_data.blinded_pubkey);
+
+ hs_desc->plaintext_data.revision_counter = rev_counter;
+}
+
+/* Encode and sign the service descriptor desc and upload it to the
+ * responsible hidden service directories. If for_next_period is true, the set
+ * of directories are selected using the next hsdir_index. This does nothing
+ * if PublishHidServDescriptors is false. */
+STATIC void
+upload_descriptor_to_all(const hs_service_t *service,
+ hs_service_descriptor_t *desc)
+{
+ smartlist_t *responsible_dirs = NULL;
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ /* We'll first cancel any directory request that are ongoing for this
+ * descriptor. It is possible that we can trigger multiple uploads in a
+ * short time frame which can lead to a race where the second upload arrives
+ * before the first one leading to a 400 malformed descriptor response from
+ * the directory. Closing all pending requests avoids that. */
+ close_directory_connections(service, desc);
+
+ /* Get our list of responsible HSDir. */
+ responsible_dirs = smartlist_new();
+ /* The parameter 0 means that we aren't a client so tell the function to use
+ * the spread store consensus paremeter. */
+ hs_get_responsible_hsdirs(&desc->blinded_kp.pubkey, desc->time_period_num,
+ service->desc_next == desc, 0, responsible_dirs);
+
+ /** Clear list of previous hsdirs since we are about to upload to a new
+ * list. Let's keep it up to date. */
+ service_desc_clear_previous_hsdirs(desc);
+
+ /* For each responsible HSDir we have, initiate an upload command. */
+ SMARTLIST_FOREACH_BEGIN(responsible_dirs, const routerstatus_t *,
+ hsdir_rs) {
+ const node_t *hsdir_node = node_get_by_id(hsdir_rs->identity_digest);
+ /* Getting responsible hsdir implies that the node_t object exists for the
+ * routerstatus_t found in the consensus else we have a problem. */
+ tor_assert(hsdir_node);
+ /* Upload this descriptor to the chosen directory. */
+ upload_descriptor_to_hsdir(service, desc, hsdir_node);
+ } SMARTLIST_FOREACH_END(hsdir_rs);
+
+ /* Set the next upload time for this descriptor. Even if we are configured
+ * to not upload, we still want to follow the right cycle of life for this
+ * descriptor. */
+ desc->next_upload_time =
+ (time(NULL) + crypto_rand_int_range(HS_SERVICE_NEXT_UPLOAD_TIME_MIN,
+ HS_SERVICE_NEXT_UPLOAD_TIME_MAX));
+ {
+ char fmt_next_time[ISO_TIME_LEN+1];
+ format_local_iso_time(fmt_next_time, desc->next_upload_time);
+ log_debug(LD_REND, "Service %s set to upload a descriptor at %s",
+ safe_str_client(service->onion_address), fmt_next_time);
+ }
+
+ /* Update the revision counter of this descriptor */
+ increment_descriptor_revision_counter(desc->desc);
+
+ smartlist_free(responsible_dirs);
+ return;
+}
+
+/** The set of HSDirs have changed: check if the change affects our descriptor
+ * HSDir placement, and if it does, reupload the desc. */
+STATIC int
+service_desc_hsdirs_changed(const hs_service_t *service,
+ const hs_service_descriptor_t *desc)
+{
+ int should_reupload = 0;
+ smartlist_t *responsible_dirs = smartlist_new();
+
+ /* No desc upload has happened yet: it will happen eventually */
+ if (!desc->previous_hsdirs || !smartlist_len(desc->previous_hsdirs)) {
+ goto done;
+ }
+
+ /* Get list of responsible hsdirs */
+ hs_get_responsible_hsdirs(&desc->blinded_kp.pubkey, desc->time_period_num,
+ service->desc_next == desc, 0, responsible_dirs);
+
+ /* Check if any new hsdirs have been added to the responsible hsdirs set:
+ * Iterate over the list of new hsdirs, and reupload if any of them is not
+ * present in the list of previous hsdirs.
+ */
+ SMARTLIST_FOREACH_BEGIN(responsible_dirs, const routerstatus_t *, hsdir_rs) {
+ char b64_digest[BASE64_DIGEST_LEN+1] = {0};
+ digest_to_base64(b64_digest, hsdir_rs->identity_digest);
+
+ if (!smartlist_contains_string(desc->previous_hsdirs, b64_digest)) {
+ should_reupload = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(hsdir_rs);
+
+ done:
+ smartlist_free(responsible_dirs);
+
+ return should_reupload;
+}
+
+/* Return 1 if the given descriptor from the given service can be uploaded
+ * else return 0 if it can not. */
+static int
+should_service_upload_descriptor(const hs_service_t *service,
+ const hs_service_descriptor_t *desc, time_t now)
+{
+ unsigned int num_intro_points;
+
+ tor_assert(service);
+ tor_assert(desc);
+
+ /* If this descriptors has missing intro points that is that it couldn't get
+ * them all when it was time to pick them, it means that we should upload
+ * instead of waiting an arbitrary amount of time breaking the service.
+ * Else, if we have no missing intro points, we use the value taken from the
+ * service configuration. */
+ if (desc->missing_intro_points) {
+ num_intro_points = digest256map_size(desc->intro_points.map);
+ } else {
+ num_intro_points = service->config.num_intro_points;
+ }
+
+ /* This means we tried to pick intro points but couldn't get any so do not
+ * upload descriptor in this case. We need at least one for the service to
+ * be reachable. */
+ if (desc->missing_intro_points && num_intro_points == 0) {
+ goto cannot;
+ }
+
+ /* Check if all our introduction circuit have been established for all the
+ * intro points we have selected. */
+ if (count_desc_circuit_established(desc) != num_intro_points) {
+ goto cannot;
+ }
+
+ /* Is it the right time to upload? */
+ if (desc->next_upload_time > now) {
+ goto cannot;
+ }
+
+ /* Don't upload desc if we don't have a live consensus */
+ if (!networkstatus_get_live_consensus(now)) {
+ goto cannot;
+ }
+
+ /* Do we know enough router descriptors to have adequate vision of the HSDir
+ hash ring? */
+ if (!router_have_minimum_dir_info()) {
+ goto cannot;
+ }
+
+ /* Can upload! */
+ return 1;
+ cannot:
+ return 0;
+}
+
+/* Scheduled event run from the main loop. Try to upload the descriptor for
+ * each service. */
+STATIC void
+run_upload_descriptor_event(time_t now)
+{
+ /* v2 services use the same function for descriptor creation and upload so
+ * we do everything here because the intro circuits were checked before. */
+ if (rend_num_services() > 0) {
+ rend_consider_services_upload(now);
+ rend_consider_descriptor_republication();
+ }
+
+ /* Run v3+ check. */
+ FOR_EACH_SERVICE_BEGIN(service) {
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ /* If we were asked to re-examine the hash ring, and it changed, then
+ schedule an upload */
+ if (consider_republishing_hs_descriptors &&
+ service_desc_hsdirs_changed(service, desc)) {
+ service_desc_schedule_upload(desc, now, 0);
+ }
+
+ /* Can this descriptor be uploaded? */
+ if (!should_service_upload_descriptor(service, desc, now)) {
+ continue;
+ }
+
+ log_info(LD_REND, "Initiating upload for hidden service %s descriptor "
+ "for service %s with %u/%u introduction points%s.",
+ (desc == service->desc_current) ? "current" : "next",
+ safe_str_client(service->onion_address),
+ digest256map_size(desc->intro_points.map),
+ service->config.num_intro_points,
+ (desc->missing_intro_points) ? " (couldn't pick more)" : "");
+
+ /* At this point, we have to upload the descriptor so start by building
+ * the intro points descriptor section which we are now sure to be
+ * accurate because all circuits have been established. */
+ build_desc_intro_points(service, desc, now);
+
+ upload_descriptor_to_all(service, desc);
+ } FOR_EACH_DESCRIPTOR_END;
+ } FOR_EACH_SERVICE_END;
+
+ /* We are done considering whether to republish rend descriptors */
+ consider_republishing_hs_descriptors = 0;
+}
+
+/* Called when the introduction point circuit is done building and ready to be
+ * used. */
+static void
+service_intro_circ_has_opened(origin_circuit_t *circ)
+{
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+ hs_service_descriptor_t *desc = NULL;
+
+ tor_assert(circ);
+
+ /* Let's do some basic sanity checking of the circ state */
+ if (BUG(!circ->cpath)) {
+ return;
+ }
+ if (BUG(TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)) {
+ return;
+ }
+ if (BUG(!circ->hs_ident)) {
+ return;
+ }
+
+ /* Get the corresponding service and intro point. */
+ get_objects_from_ident(circ->hs_ident, &service, &ip, &desc);
+
+ if (service == NULL) {
+ log_warn(LD_REND, "Unknown service identity key %s on the introduction "
+ "circuit %u. Can't find onion service.",
+ safe_str_client(ed25519_fmt(&circ->hs_ident->identity_pk)),
+ TO_CIRCUIT(circ)->n_circ_id);
+ goto err;
+ }
+ if (ip == NULL) {
+ log_warn(LD_REND, "Unknown introduction point auth key on circuit %u "
+ "for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto err;
+ }
+ /* We can't have an IP object without a descriptor. */
+ tor_assert(desc);
+
+ if (hs_circ_service_intro_has_opened(service, ip, desc, circ)) {
+ /* Getting here means that the circuit has been re-purposed because we
+ * have enough intro circuit opened. Remove the IP from the service. */
+ service_intro_point_remove(service, ip);
+ service_intro_point_free(ip);
+ }
+
+ goto done;
+
+ err:
+ /* Close circuit, we can't use it. */
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOSUCHSERVICE);
+ done:
+ return;
+}
+
+/* Called when a rendezvous circuit is done building and ready to be used. */
+static void
+service_rendezvous_circ_has_opened(origin_circuit_t *circ)
+{
+ hs_service_t *service = NULL;
+
+ tor_assert(circ);
+ tor_assert(circ->cpath);
+ /* Getting here means this is a v3 rendezvous circuit. */
+ tor_assert(circ->hs_ident);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
+
+ /* Declare the circuit dirty to avoid reuse, and for path-bias. We set the
+ * timestamp regardless of its content because that circuit could have been
+ * cannibalized so in any cases, we are about to use that circuit more. */
+ TO_CIRCUIT(circ)->timestamp_dirty = time(NULL);
+ pathbias_count_use_attempt(circ);
+
+ /* Get the corresponding service and intro point. */
+ get_objects_from_ident(circ->hs_ident, &service, NULL, NULL);
+ if (service == NULL) {
+ log_warn(LD_REND, "Unknown service identity key %s on the rendezvous "
+ "circuit %u with cookie %s. Can't find onion service.",
+ safe_str_client(ed25519_fmt(&circ->hs_ident->identity_pk)),
+ TO_CIRCUIT(circ)->n_circ_id,
+ hex_str((const char *) circ->hs_ident->rendezvous_cookie,
+ REND_COOKIE_LEN));
+ goto err;
+ }
+
+ /* If the cell can't be sent, the circuit will be closed within this
+ * function. */
+ hs_circ_service_rp_has_opened(service, circ);
+ goto done;
+
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOSUCHSERVICE);
+ done:
+ return;
+}
+
+/* We've been expecting an INTRO_ESTABLISHED cell on this circuit and it just
+ * arrived. Handle the INTRO_ESTABLISHED cell arriving on the given
+ * introduction circuit. Return 0 on success else a negative value. */
+static int
+service_handle_intro_established(origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len)
+{
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+
+ tor_assert(circ);
+ tor_assert(payload);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
+
+ /* We need the service and intro point for this cell. */
+ get_objects_from_ident(circ->hs_ident, &service, &ip, NULL);
+
+ /* Get service object from the circuit identifier. */
+ if (service == NULL) {
+ log_warn(LD_REND, "Unknown service identity key %s on the introduction "
+ "circuit %u. Can't find onion service.",
+ safe_str_client(ed25519_fmt(&circ->hs_ident->identity_pk)),
+ TO_CIRCUIT(circ)->n_circ_id);
+ goto err;
+ }
+ if (ip == NULL) {
+ /* We don't recognize the key. */
+ log_warn(LD_REND, "Introduction circuit established without an intro "
+ "point object on circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto err;
+ }
+
+ /* Try to parse the payload into a cell making sure we do actually have a
+ * valid cell. On success, the ip object and circuit purpose is updated to
+ * reflect the fact that the introduction circuit is established. */
+ if (hs_circ_handle_intro_established(service, ip, circ, payload,
+ payload_len) < 0) {
+ goto err;
+ }
+
+ /* Flag that we have an established circuit for this intro point. This value
+ * is what indicates the upload scheduled event if we are ready to build the
+ * intro point into the descriptor and upload. */
+ ip->circuit_established = 1;
+
+ log_info(LD_REND, "Successfully received an INTRO_ESTABLISHED cell "
+ "on circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ return 0;
+
+ err:
+ return -1;
+}
+
+/* We just received an INTRODUCE2 cell on the established introduction circuit
+ * circ. Handle the cell and return 0 on success else a negative value. */
+static int
+service_handle_introduce2(origin_circuit_t *circ, const uint8_t *payload,
+ size_t payload_len)
+{
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+ hs_service_descriptor_t *desc = NULL;
+
+ tor_assert(circ);
+ tor_assert(payload);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO);
+
+ /* We'll need every object associated with this circuit. */
+ get_objects_from_ident(circ->hs_ident, &service, &ip, &desc);
+
+ /* Get service object from the circuit identifier. */
+ if (service == NULL) {
+ log_warn(LD_BUG, "Unknown service identity key %s when handling "
+ "an INTRODUCE2 cell on circuit %u",
+ safe_str_client(ed25519_fmt(&circ->hs_ident->identity_pk)),
+ TO_CIRCUIT(circ)->n_circ_id);
+ goto err;
+ }
+ if (ip == NULL) {
+ /* We don't recognize the key. */
+ log_warn(LD_BUG, "Unknown introduction auth key when handling "
+ "an INTRODUCE2 cell on circuit %u for service %s",
+ TO_CIRCUIT(circ)->n_circ_id,
+ safe_str_client(service->onion_address));
+ goto err;
+ }
+ /* If we have an IP object, we MUST have a descriptor object. */
+ tor_assert(desc);
+
+ /* The following will parse, decode and launch the rendezvous point circuit.
+ * Both current and legacy cells are handled. */
+ if (hs_circ_handle_introduce2(service, circ, ip, desc->desc->subcredential,
+ payload, payload_len) < 0) {
+ goto err;
+ }
+
+ return 0;
+ err:
+ return -1;
+}
+
+/* Add to list every filename used by service. This is used by the sandbox
+ * subsystem. */
+static void
+service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
+{
+ const char *s_dir;
+ char fname[128] = {0};
+
+ tor_assert(service);
+ tor_assert(list);
+
+ /* Ease our life. */
+ s_dir = service->config.directory_path;
+ /* The hostname file. */
+ smartlist_add(list, hs_path_from_filename(s_dir, fname_hostname));
+ /* The key files splitted in two. */
+ tor_snprintf(fname, sizeof(fname), "%s_secret_key", fname_keyfile_prefix);
+ smartlist_add(list, hs_path_from_filename(s_dir, fname));
+ tor_snprintf(fname, sizeof(fname), "%s_public_key", fname_keyfile_prefix);
+ smartlist_add(list, hs_path_from_filename(s_dir, fname));
+}
+
+/* ========== */
+/* Public API */
+/* ========== */
+
+/* Return the number of service we have configured and usable. */
+unsigned int
+hs_service_get_num_services(void)
+{
+ if (hs_service_map == NULL) {
+ return 0;
+ }
+ return HT_SIZE(hs_service_map);
+}
+
+/* Called once an introduction circuit is closed. If the circuit doesn't have
+ * a v3 identifier, it is ignored. */
+void
+hs_service_intro_circ_has_closed(origin_circuit_t *circ)
+{
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+ hs_service_descriptor_t *desc = NULL;
+
+ tor_assert(circ);
+
+ if (circ->hs_ident == NULL) {
+ /* This is not a v3 circuit, ignore. */
+ goto end;
+ }
+
+ get_objects_from_ident(circ->hs_ident, &service, &ip, &desc);
+ if (service == NULL) {
+ log_warn(LD_REND, "Unable to find any hidden service associated "
+ "identity key %s on intro circuit %u.",
+ ed25519_fmt(&circ->hs_ident->identity_pk),
+ TO_CIRCUIT(circ)->n_circ_id);
+ goto end;
+ }
+ if (ip == NULL) {
+ /* The introduction point object has already been removed probably by our
+ * cleanup process so ignore. */
+ goto end;
+ }
+ /* Can't have an intro point object without a descriptor. */
+ tor_assert(desc);
+
+ /* Circuit disappeared so make sure the intro point is updated. By
+ * keeping the object in the descriptor, we'll be able to retry. */
+ ip->circuit_established = 0;
+
+ end:
+ return;
+}
+
+/* Given conn, a rendezvous edge connection acting as an exit stream, look up
+ * the hidden service for the circuit circ, and look up the port and address
+ * based on the connection port. Assign the actual connection address.
+ *
+ * Return 0 on success. Return -1 on failure and the caller should NOT close
+ * the circuit. Return -2 on failure and the caller MUST close the circuit for
+ * security reasons. */
+int
+hs_service_set_conn_addr_port(const origin_circuit_t *circ,
+ edge_connection_t *conn)
+{
+ hs_service_t *service = NULL;
+
+ tor_assert(circ);
+ tor_assert(conn);
+ tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
+ tor_assert(circ->hs_ident);
+
+ get_objects_from_ident(circ->hs_ident, &service, NULL, NULL);
+
+ if (service == NULL) {
+ log_warn(LD_REND, "Unable to find any hidden service associated "
+ "identity key %s on rendezvous circuit %u.",
+ ed25519_fmt(&circ->hs_ident->identity_pk),
+ TO_CIRCUIT(circ)->n_circ_id);
+ /* We want the caller to close the circuit because it's not a valid
+ * service so no danger. Attempting to bruteforce the entire key space by
+ * opening circuits to learn which service is being hosted here is
+ * impractical. */
+ goto err_close;
+ }
+
+ /* Enforce the streams-per-circuit limit, and refuse to provide a mapping if
+ * this circuit will exceed the limit. */
+ if (service->config.max_streams_per_rdv_circuit > 0 &&
+ (circ->hs_ident->num_rdv_streams >=
+ service->config.max_streams_per_rdv_circuit)) {
+#define MAX_STREAM_WARN_INTERVAL 600
+ static struct ratelim_t stream_ratelim =
+ RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
+ log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
+ "Maximum streams per circuit limit reached on "
+ "rendezvous circuit %u for service %s. Circuit has "
+ "%" PRIu64 " out of %" PRIu64 " streams. %s.",
+ TO_CIRCUIT(circ)->n_circ_id,
+ service->onion_address,
+ circ->hs_ident->num_rdv_streams,
+ service->config.max_streams_per_rdv_circuit,
+ service->config.max_streams_close_circuit ?
+ "Closing circuit" : "Ignoring open stream request");
+ if (service->config.max_streams_close_circuit) {
+ /* Service explicitly configured to close immediately. */
+ goto err_close;
+ }
+ /* Exceeding the limit makes tor silently ignore the stream creation
+ * request and keep the circuit open. */
+ goto err_no_close;
+ }
+
+ /* Find a virtual port of that service mathcing the one in the connection if
+ * succesful, set the address in the connection. */
+ if (hs_set_conn_addr_port(service->config.ports, conn) < 0) {
+ log_info(LD_REND, "No virtual port mapping exists for port %d for "
+ "hidden service %s.",
+ TO_CONN(conn)->port, service->onion_address);
+ if (service->config.allow_unknown_ports) {
+ /* Service explicitly allow connection to unknown ports so close right
+ * away because we do not care about port mapping. */
+ goto err_close;
+ }
+ /* If the service didn't explicitly allow it, we do NOT close the circuit
+ * here to raise the bar in terms of performance for port mapping. */
+ goto err_no_close;
+ }
+
+ /* Success. */
+ return 0;
+ err_close:
+ /* Indicate the caller that the circuit should be closed. */
+ return -2;
+ err_no_close:
+ /* Indicate the caller to NOT close the circuit. */
+ return -1;
+}
+
+/* Add to file_list every filename used by a configured hidden service, and to
+ * dir_list every directory path used by a configured hidden service. This is
+ * used by the sandbox subsystem to whitelist those. */
+void
+hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
+ smartlist_t *dir_list)
+{
+ tor_assert(file_list);
+ tor_assert(dir_list);
+
+ /* Add files and dirs for legacy services. */
+ rend_services_add_filenames_to_lists(file_list, dir_list);
+
+ /* Add files and dirs for v3+. */
+ FOR_EACH_SERVICE_BEGIN(service) {
+ /* Skip ephemeral service, they don't touch the disk. */
+ if (service->config.is_ephemeral) {
+ continue;
+ }
+ service_add_fnames_to_list(service, file_list);
+ smartlist_add_strdup(dir_list, service->config.directory_path);
+ } FOR_EACH_DESCRIPTOR_END;
+}
+
+/* Called when our internal view of the directory has changed. We might have
+ * received a new batch of descriptors which might affect the shape of the
+ * HSDir hash ring. Signal that we should reexamine the hash ring and
+ * re-upload our HS descriptors if needed. */
+void
+hs_service_dir_info_changed(void)
+{
+ if (hs_service_get_num_services() > 0) {
+ /* New directory information usually goes every consensus so rate limit
+ * every 30 minutes to not be too conservative. */
+ static struct ratelim_t dir_info_changed_ratelim = RATELIM_INIT(30 * 60);
+ log_fn_ratelim(&dir_info_changed_ratelim, LOG_INFO, LD_REND,
+ "New dirinfo arrived: consider reuploading descriptor");
+ consider_republishing_hs_descriptors = 1;
+ }
+}
+
+/* Called when we get an INTRODUCE2 cell on the circ. Respond to the cell and
+ * launch a circuit to the rendezvous point. */
+int
+hs_service_receive_introduce2(origin_circuit_t *circ, const uint8_t *payload,
+ size_t payload_len)
+{
+ int ret = -1;
+
+ tor_assert(circ);
+ tor_assert(payload);
+
+ /* Do some initial validation and logging before we parse the cell */
+ if (TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_S_INTRO) {
+ log_warn(LD_PROTOCOL, "Received an INTRODUCE2 cell on a "
+ "non introduction circuit of purpose %d",
+ TO_CIRCUIT(circ)->purpose);
+ goto done;
+ }
+
+ if (circ->hs_ident) {
+ ret = service_handle_introduce2(circ, payload, payload_len);
+ } else {
+ ret = rend_service_receive_introduction(circ, payload, payload_len);
+ }
+
+ done:
+ return ret;
+}
+
+/* Called when we get an INTRO_ESTABLISHED cell. Mark the circuit as an
+ * established introduction point. Return 0 on success else a negative value
+ * and the circuit is closed. */
+int
+hs_service_receive_intro_established(origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len)
+{
+ int ret = -1;
+
+ tor_assert(circ);
+ tor_assert(payload);
+
+ if (TO_CIRCUIT(circ)->purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) {
+ log_warn(LD_PROTOCOL, "Received an INTRO_ESTABLISHED cell on a "
+ "non introduction circuit of purpose %d",
+ TO_CIRCUIT(circ)->purpose);
+ goto err;
+ }
+
+ /* Handle both version. v2 uses rend_data and v3 uses the hs circuit
+ * identifier hs_ident. Can't be both. */
+ if (circ->hs_ident) {
+ ret = service_handle_intro_established(circ, payload, payload_len);
+ } else {
+ ret = rend_service_intro_established(circ, payload, payload_len);
+ }
+
+ if (ret < 0) {
+ goto err;
+ }
+ return 0;
+ err:
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
+ return -1;
+}
+
+/* Called when any kind of hidden service circuit is done building thus
+ * opened. This is the entry point from the circuit subsystem. */
+void
+hs_service_circuit_has_opened(origin_circuit_t *circ)
+{
+ tor_assert(circ);
+
+ /* Handle both version. v2 uses rend_data and v3 uses the hs circuit
+ * identifier hs_ident. Can't be both. */
+ switch (TO_CIRCUIT(circ)->purpose) {
+ case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
+ if (circ->hs_ident) {
+ service_intro_circ_has_opened(circ);
+ } else {
+ rend_service_intro_has_opened(circ);
+ }
+ break;
+ case CIRCUIT_PURPOSE_S_CONNECT_REND:
+ if (circ->hs_ident) {
+ service_rendezvous_circ_has_opened(circ);
+ } else {
+ rend_service_rendezvous_has_opened(circ);
+ }
+ break;
+ default:
+ tor_assert(0);
+ }
+}
+
+/* Load and/or generate keys for all onion services including the client
+ * authorization if any. Return 0 on success, -1 on failure. */
+int
+hs_service_load_all_keys(void)
+{
+ /* Load v2 service keys if we have v2. */
+ if (rend_num_services() != 0) {
+ if (rend_service_load_all_keys(NULL) < 0) {
+ goto err;
+ }
+ }
+
+ /* Load or/and generate them for v3+. */
+ SMARTLIST_FOREACH_BEGIN(hs_service_staging_list, hs_service_t *, service) {
+ /* Ignore ephemeral service, they already have their keys set. */
+ if (service->config.is_ephemeral) {
+ continue;
+ }
+ log_info(LD_REND, "Loading v3 onion service keys from %s",
+ service_escaped_dir(service));
+ if (load_service_keys(service) < 0) {
+ goto err;
+ }
+ /* XXX: Load/Generate client authorization keys. (#20700) */
+ } SMARTLIST_FOREACH_END(service);
+
+ /* Final step, the staging list contains service in a quiescent state that
+ * is ready to be used. Register them to the global map. Once this is over,
+ * the staging list will be cleaned up. */
+ register_all_services();
+
+ /* All keys have been loaded successfully. */
+ return 0;
+ err:
+ return -1;
+}
+
+/* Put all service object in the given service list. After this, the caller
+ * looses ownership of every elements in the list and responsible to free the
+ * list pointer. */
+void
+hs_service_stage_services(const smartlist_t *service_list)
+{
+ tor_assert(service_list);
+ /* This list is freed at registration time but this function can be called
+ * multiple time. */
+ if (hs_service_staging_list == NULL) {
+ hs_service_staging_list = smartlist_new();
+ }
+ /* Add all service object to our staging list. Caller is responsible for
+ * freeing the service_list. */
+ smartlist_add_all(hs_service_staging_list, service_list);
+}
+
+/* Allocate and initilize a service object. The service configuration will
+ * contain the default values. Return the newly allocated object pointer. This
+ * function can't fail. */
+hs_service_t *
+hs_service_new(const or_options_t *options)
+{
+ hs_service_t *service = tor_malloc_zero(sizeof(hs_service_t));
+ /* Set default configuration value. */
+ set_service_default_config(&service->config, options);
+ /* Set the default service version. */
+ service->config.version = HS_SERVICE_DEFAULT_VERSION;
+ /* Allocate the CLIENT_PK replay cache in service state. */
+ service->state.replay_cache_rend_cookie =
+ replaycache_new(REND_REPLAY_TIME_INTERVAL, REND_REPLAY_TIME_INTERVAL);
+
+ return service;
+}
+
+/* Free the given <b>service</b> object and all its content. This function
+ * also takes care of wiping service keys from memory. It is safe to pass a
+ * NULL pointer. */
+void
+hs_service_free(hs_service_t *service)
+{
+ if (service == NULL) {
+ return;
+ }
+
+ /* Free descriptors. Go over both descriptor with this loop. */
+ FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+ service_descriptor_free(desc);
+ } FOR_EACH_DESCRIPTOR_END;
+
+ /* Free service configuration. */
+ service_clear_config(&service->config);
+
+ /* Free replay cache from state. */
+ if (service->state.replay_cache_rend_cookie) {
+ replaycache_free(service->state.replay_cache_rend_cookie);
+ }
+
+ /* Wipe service keys. */
+ memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk));
+
+ tor_free(service);
+}
+
+/* Periodic callback. Entry point from the main loop to the HS service
+ * subsystem. This is call every second. This is skipped if tor can't build a
+ * circuit or the network is disabled. */
+void
+hs_service_run_scheduled_events(time_t now)
+{
+ /* First thing we'll do here is to make sure our services are in a
+ * quiescent state for the scheduled events. */
+ run_housekeeping_event(now);
+
+ /* Order matters here. We first make sure the descriptor object for each
+ * service contains the latest data. Once done, we check if we need to open
+ * new introduction circuit. Finally, we try to upload the descriptor for
+ * each service. */
+
+ /* Make sure descriptors are up to date. */
+ run_build_descriptor_event(now);
+ /* Make sure services have enough circuits. */
+ run_build_circuit_event(now);
+ /* Upload the descriptors if needed/possible. */
+ run_upload_descriptor_event(now);
+}
+
+/* Initialize the service HS subsystem. */
+void
+hs_service_init(void)
+{
+ /* Should never be called twice. */
+ tor_assert(!hs_service_map);
+ tor_assert(!hs_service_staging_list);
+
+ /* v2 specific. */
+ rend_service_init();
+
+ hs_service_map = tor_malloc_zero(sizeof(struct hs_service_ht));
+ HT_INIT(hs_service_ht, hs_service_map);
+
+ hs_service_staging_list = smartlist_new();
+}
+
+/* Release all global storage of the hidden service subsystem. */
+void
+hs_service_free_all(void)
+{
+ rend_service_free_all();
+ service_free_all();
+}
+
+#ifdef TOR_UNIT_TESTS
+
+/* Return the global service map size. Only used by unit test. */
+STATIC unsigned int
+get_hs_service_map_size(void)
+{
+ return HT_SIZE(hs_service_map);
+}
+
+/* Return the staging list size. Only used by unit test. */
+STATIC int
+get_hs_service_staging_list_size(void)
+{
+ return smartlist_len(hs_service_staging_list);
+}
+
+STATIC hs_service_ht *
+get_hs_service_map(void)
+{
+ return hs_service_map;
+}
+
+STATIC hs_service_t *
+get_first_service(void)
+{
+ hs_service_t **obj = HT_START(hs_service_ht, hs_service_map);
+ if (obj == NULL) {
+ return NULL;
+ }
+ return *obj;
+}
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
diff --git a/src/or/hs_service.h b/src/or/hs_service.h
index 3302592762..ed1053d850 100644
--- a/src/or/hs_service.h
+++ b/src/or/hs_service.h
@@ -3,25 +3,352 @@
/**
* \file hs_service.h
- * \brief Header file for hs_service.c.
+ * \brief Header file containing service data for the HS subsytem.
**/
#ifndef TOR_HS_SERVICE_H
#define TOR_HS_SERVICE_H
-#include "or.h"
+#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
+#include "replaycache.h"
+
+#include "hs_common.h"
+#include "hs_descriptor.h"
+#include "hs_ident.h"
+#include "hs_intropoint.h"
+
+/* Trunnel */
#include "hs/cell_establish_intro.h"
-/* These functions are only used by unit tests and we need to expose them else
- * hs_service.o ends up with no symbols in libor.a which makes clang throw a
- * warning at compile time. See #21825. */
+/* When loading and configuring a service, this is the default version it will
+ * be configured for as it is possible that no HiddenServiceVersion is
+ * present. */
+#define HS_SERVICE_DEFAULT_VERSION HS_VERSION_TWO
+
+/* As described in the specification, service publishes their next descriptor
+ * at a random time between those two values (in seconds). */
+#define HS_SERVICE_NEXT_UPLOAD_TIME_MIN (60 * 60)
+#define HS_SERVICE_NEXT_UPLOAD_TIME_MAX (120 * 60)
+
+/* Service side introduction point. */
+typedef struct hs_service_intro_point_t {
+ /* Top level intropoint "shared" data between client/service. */
+ hs_intropoint_t base;
+
+ /* Onion key of the introduction point used to extend to it for the ntor
+ * handshake. */
+ curve25519_public_key_t onion_key;
+
+ /* Authentication keypair used to create the authentication certificate
+ * which is published in the descriptor. */
+ ed25519_keypair_t auth_key_kp;
+
+ /* Encryption keypair for the "ntor" type. */
+ curve25519_keypair_t enc_key_kp;
+
+ /* Legacy key if that intro point doesn't support v3. This should be used if
+ * the base object legacy flag is set. */
+ crypto_pk_t *legacy_key;
+
+ /* Amount of INTRODUCE2 cell accepted from this intro point. */
+ uint64_t introduce2_count;
+
+ /* Maximum number of INTRODUCE2 cell this intro point should accept. */
+ uint64_t introduce2_max;
+
+ /* The time at which this intro point should expire and stop being used. */
+ time_t time_to_expire;
+
+ /* The amount of circuit creation we've made to this intro point. This is
+ * incremented every time we do a circuit relaunch on this intro point which
+ * is triggered when the circuit dies but the node is still in the
+ * consensus. After MAX_INTRO_POINT_CIRCUIT_RETRIES, we give up on it. */
+ uint32_t circuit_retries;
+
+ /* Set if this intro point has an established circuit. */
+ unsigned int circuit_established : 1;
+
+ /* Replay cache recording the encrypted part of an INTRODUCE2 cell that the
+ * circuit associated with this intro point has received. This is used to
+ * prevent replay attacks. */
+ replaycache_t *replay_cache;
+} hs_service_intro_point_t;
+
+/* Object handling introduction points of a service. */
+typedef struct hs_service_intropoints_t {
+ /* The time at which we've started our retry period to build circuits. We
+ * don't want to stress circuit creation so we can only retry for a certain
+ * time and then after we stop and wait. */
+ time_t retry_period_started;
+
+ /* Number of circuit we've launched during a single retry period. */
+ unsigned int num_circuits_launched;
+
+ /* Contains the current hs_service_intro_point_t objects indexed by
+ * authentication public key. */
+ digest256map_t *map;
+
+ /* Contains node's identity key digest that were introduction point for this
+ * descriptor but were retried to many times. We keep those so we avoid
+ * re-picking them over and over for a circuit retry period.
+ * XXX: Once we have #22173, change this to only use ed25519 identity. */
+ digestmap_t *failed_id;
+} hs_service_intropoints_t;
+
+/* Representation of a service descriptor. */
+typedef struct hs_service_descriptor_t {
+ /* Decoded descriptor. This object is used for encoding when the service
+ * publishes the descriptor. */
+ hs_descriptor_t *desc;
+
+ /* Descriptor signing keypair. */
+ ed25519_keypair_t signing_kp;
+
+ /* Blinded keypair derived from the master identity public key. */
+ ed25519_keypair_t blinded_kp;
+
+ /* When is the next time when we should upload the descriptor. */
+ time_t next_upload_time;
+
+ /* Introduction points assign to this descriptor which contains
+ * hs_service_intropoints_t object indexed by authentication key (the RSA
+ * key if the node is legacy). */
+ hs_service_intropoints_t intro_points;
+
+ /* The time period number this descriptor has been created for. */
+ uint64_t time_period_num;
+
+ /* True iff we have missing intro points for this descriptor because we
+ * couldn't pick any nodes. */
+ unsigned int missing_intro_points : 1;
+
+ /** List of the responsible HSDirs (their b64ed identity digest) last time we
+ * uploaded this descriptor. If the set of responsible HSDirs is different
+ * from this list, this means we received new dirinfo and we need to
+ * reupload our descriptor. */
+ smartlist_t *previous_hsdirs;
+} hs_service_descriptor_t;
+
+/* Service key material. */
+typedef struct hs_service_keys_t {
+ /* Master identify public key. */
+ ed25519_public_key_t identity_pk;
+ /* Master identity private key. */
+ ed25519_secret_key_t identity_sk;
+ /* True iff the key is kept offline which means the identity_sk MUST not be
+ * used in that case. */
+ unsigned int is_identify_key_offline : 1;
+} hs_service_keys_t;
+
+/* Service configuration. The following are set from the torrc options either
+ * set by the configuration file or by the control port. Nothing else should
+ * change those values. */
+typedef struct hs_service_config_t {
+ /* Protocol version of the service. Specified by HiddenServiceVersion
+ * option. */
+ uint32_t version;
+
+ /* List of rend_service_port_config_t */
+ smartlist_t *ports;
+
+ /* Path on the filesystem where the service persistent data is stored. NULL
+ * if the service is ephemeral. Specified by HiddenServiceDir option. */
+ char *directory_path;
+
+ /* The maximum number of simultaneous streams per rendezvous circuit that
+ * are allowed to be created. No limit if 0. Specified by
+ * HiddenServiceMaxStreams option. */
+ uint64_t max_streams_per_rdv_circuit;
+
+ /* If true, we close circuits that exceed the max_streams_per_rdv_circuit
+ * limit. Specified by HiddenServiceMaxStreamsCloseCircuit option. */
+ unsigned int max_streams_close_circuit : 1;
+
+ /* How many introduction points this service has. Specified by
+ * HiddenServiceNumIntroductionPoints option. */
+ unsigned int num_intro_points;
+
+ /* True iff we allow request made on unknown ports. Specified by
+ * HiddenServiceAllowUnknownPorts option. */
+ unsigned int allow_unknown_ports : 1;
+
+ /* If true, this service is a Single Onion Service. Specified by
+ * HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode options. */
+ unsigned int is_single_onion : 1;
+
+ /* If true, allow group read permissions on the directory_path. Specified by
+ * HiddenServiceDirGroupReadable option. */
+ unsigned int dir_group_readable : 1;
+
+ /* Is this service ephemeral? */
+ unsigned int is_ephemeral : 1;
+} hs_service_config_t;
+
+/* Service state. */
+typedef struct hs_service_state_t {
+ /* The time at which we've started our retry period to build circuits. We
+ * don't want to stress circuit creation so we can only retry for a certain
+ * time and then after we stop and wait. */
+ time_t intro_circ_retry_started_time;
+
+ /* Number of circuit we've launched during a single retry period. This
+ * should never go over MAX_INTRO_CIRCS_PER_PERIOD. */
+ unsigned int num_intro_circ_launched;
+
+ /* Replay cache tracking the REND_COOKIE found in INTRODUCE2 cell to detect
+ * repeats. Clients may send INTRODUCE1 cells for the same rendezvous point
+ * through two or more different introduction points; when they do, this
+ * keeps us from launching multiple simultaneous attempts to connect to the
+ * same rend point. */
+ replaycache_t *replay_cache_rend_cookie;
+
+ /* When is the next time we should rotate our descriptors. This is has to be
+ * done at the start time of the next SRV protocol run. */
+ time_t next_rotation_time;
+} hs_service_state_t;
+
+/* Representation of a service running on this tor instance. */
+typedef struct hs_service_t {
+ /* Onion address base32 encoded and NUL terminated. We keep it for logging
+ * purposes so we don't have to build it everytime. */
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+ /* Hashtable node: use to look up the service by its master public identity
+ * key in the service global map. */
+ HT_ENTRY(hs_service_t) hs_service_node;
+
+ /* Service state which contains various flags and counters. */
+ hs_service_state_t state;
+
+ /* Key material of the service. */
+ hs_service_keys_t keys;
+
+ /* Configuration of the service. */
+ hs_service_config_t config;
+
+ /* Current descriptor. */
+ hs_service_descriptor_t *desc_current;
+ /* Next descriptor. */
+ hs_service_descriptor_t *desc_next;
+
+ /* XXX: Credential (client auth.) #20700. */
+
+} hs_service_t;
+
+/* For the service global hash map, we define a specific type for it which
+ * will make it safe to use and specific to some controlled parameters such as
+ * the hashing function and how to compare services. */
+typedef HT_HEAD(hs_service_ht, hs_service_t) hs_service_ht;
+
+/* API */
+
+/* Global initializer and cleanup function. */
+void hs_service_init(void);
+void hs_service_free_all(void);
+
+/* Service new/free functions. */
+hs_service_t *hs_service_new(const or_options_t *options);
+void hs_service_free(hs_service_t *service);
+
+unsigned int hs_service_get_num_services(void);
+void hs_service_stage_services(const smartlist_t *service_list);
+int hs_service_load_all_keys(void);
+void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
+ smartlist_t *dir_list);
+int hs_service_set_conn_addr_port(const origin_circuit_t *circ,
+ edge_connection_t *conn);
+
+void hs_service_dir_info_changed(void);
+void hs_service_run_scheduled_events(time_t now);
+void hs_service_circuit_has_opened(origin_circuit_t *circ);
+int hs_service_receive_intro_established(origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len);
+int hs_service_receive_introduce2(origin_circuit_t *circ,
+ const uint8_t *payload,
+ size_t payload_len);
+
+void hs_service_intro_circ_has_closed(origin_circuit_t *circ);
+
+#ifdef HS_SERVICE_PRIVATE
+
+#ifdef TOR_UNIT_TESTS
+
+/* Useful getters for unit tests. */
+STATIC unsigned int get_hs_service_map_size(void);
+STATIC int get_hs_service_staging_list_size(void);
+STATIC hs_service_ht *get_hs_service_map(void);
+STATIC hs_service_t *get_first_service(void);
+
+/* Service accessors. */
+STATIC hs_service_t *find_service(hs_service_ht *map,
+ const ed25519_public_key_t *pk);
+STATIC void remove_service(hs_service_ht *map, hs_service_t *service);
+STATIC int register_service(hs_service_ht *map, hs_service_t *service);
+/* Service introduction point functions. */
+STATIC hs_service_intro_point_t *service_intro_point_new(
+ const extend_info_t *ei,
+ unsigned int is_legacy);
+STATIC void service_intro_point_free(hs_service_intro_point_t *ip);
+STATIC void service_intro_point_add(digest256map_t *map,
+ hs_service_intro_point_t *ip);
+STATIC void service_intro_point_remove(const hs_service_t *service,
+ const hs_service_intro_point_t *ip);
+STATIC hs_service_intro_point_t *service_intro_point_find(
+ const hs_service_t *service,
+ const ed25519_public_key_t *auth_key);
+STATIC hs_service_intro_point_t *service_intro_point_find_by_ident(
+ const hs_service_t *service,
+ const hs_ident_circuit_t *ident);
+/* Service descriptor functions. */
+STATIC hs_service_descriptor_t *service_descriptor_new(void);
+STATIC hs_service_descriptor_t *service_desc_find_by_intro(
+ const hs_service_t *service,
+ const hs_service_intro_point_t *ip);
+/* Helper functions. */
+STATIC void get_objects_from_ident(const hs_ident_circuit_t *ident,
+ hs_service_t **service,
+ hs_service_intro_point_t **ip,
+ hs_service_descriptor_t **desc);
+STATIC const node_t *
+get_node_from_intro_point(const hs_service_intro_point_t *ip);
+STATIC int can_service_launch_intro_circuit(hs_service_t *service,
+ time_t now);
+STATIC int intro_point_should_expire(const hs_service_intro_point_t *ip,
+ time_t now);
+STATIC void run_housekeeping_event(time_t now);
+STATIC void rotate_all_descriptors(time_t now);
+STATIC void build_all_descriptors(time_t now);
+STATIC void update_all_descriptors(time_t now);
+STATIC void run_upload_descriptor_event(time_t now);
+
+STATIC char *
+encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc);
+
+STATIC void service_descriptor_free(hs_service_descriptor_t *desc);
+
+STATIC uint64_t
+check_state_line_for_service_rev_counter(const char *state_line,
+ const ed25519_public_key_t *blinded_pubkey,
+ int *service_found_out);
+
+STATIC int
+write_address_to_file(const hs_service_t *service, const char *fname_);
+
+STATIC void upload_descriptor_to_all(const hs_service_t *service,
+ hs_service_descriptor_t *desc);
+
+STATIC void service_desc_schedule_upload(hs_service_descriptor_t *desc,
+ time_t now,
+ int descriptor_changed);
+
+STATIC int service_desc_hsdirs_changed(const hs_service_t *service,
+ const hs_service_descriptor_t *desc);
+
+#endif /* defined(TOR_UNIT_TESTS) */
-trn_cell_establish_intro_t *
-generate_establish_intro_cell(const uint8_t *circuit_key_material,
- size_t circuit_key_material_len);
-ssize_t
-get_establish_intro_payload(uint8_t *buf, size_t buf_len,
- const trn_cell_establish_intro_t *cell);
+#endif /* defined(HS_SERVICE_PRIVATE) */
-#endif /* TOR_HS_SERVICE_H */
+#endif /* !defined(TOR_HS_SERVICE_H) */
diff --git a/src/or/include.am b/src/or/include.am
index 7548ed0946..4938ae8e73 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -20,7 +20,6 @@ EXTRA_DIST+= src/or/ntmain.c src/or/Makefile.nmake
LIBTOR_A_SOURCES = \
src/or/addressmap.c \
src/or/bridges.c \
- src/or/buffers.c \
src/or/channel.c \
src/or/channelpadding.c \
src/or/channeltls.c \
@@ -51,16 +50,21 @@ LIBTOR_A_SOURCES = \
src/or/dos.c \
src/or/fp_pair.c \
src/or/geoip.c \
- src/or/hs_intropoint.c \
- src/or/hs_circuitmap.c \
- src/or/hs_ntor.c \
- src/or/hs_service.c \
src/or/entrynodes.c \
src/or/ext_orport.c \
src/or/hibernate.c \
src/or/hs_cache.c \
+ src/or/hs_cell.c \
+ src/or/hs_circuit.c \
+ src/or/hs_circuitmap.c \
+ src/or/hs_client.c \
src/or/hs_common.c \
+ src/or/hs_config.c \
src/or/hs_descriptor.c \
+ src/or/hs_ident.c \
+ src/or/hs_intropoint.c \
+ src/or/hs_ntor.c \
+ src/or/hs_service.c \
src/or/keypin.c \
src/or/main.c \
src/or/microdesc.c \
@@ -75,6 +79,11 @@ LIBTOR_A_SOURCES = \
src/or/parsecommon.c \
src/or/periodic.c \
src/or/protover.c \
+ src/or/proto_cell.c \
+ src/or/proto_control0.c \
+ src/or/proto_ext_or.c \
+ src/or/proto_http.c \
+ src/or/proto_socks.c \
src/or/policies.c \
src/or/reasons.c \
src/or/relay.c \
@@ -91,6 +100,8 @@ LIBTOR_A_SOURCES = \
src/or/routerparse.c \
src/or/routerset.c \
src/or/scheduler.c \
+ src/or/scheduler_kist.c \
+ src/or/scheduler_vanilla.c \
src/or/statefile.c \
src/or/status.c \
src/or/torcert.c \
@@ -123,10 +134,11 @@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a src/common/libor-ctime.a \
src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event.a src/trunnel/libor-trunnel.a \
src/trace/libor-trace.a \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
- @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \
- @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \
- $(rust_ldadd)
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \
+ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
if COVERAGE_ENABLED
src_or_tor_cov_SOURCES = src/or/tor_main.c
@@ -146,7 +158,6 @@ ORHEADERS = \
src/or/addressmap.h \
src/or/auth_dirs.inc \
src/or/bridges.h \
- src/or/buffers.h \
src/or/channel.h \
src/or/channelpadding.h \
src/or/channeltls.h \
@@ -183,12 +194,17 @@ ORHEADERS = \
src/or/entrynodes.h \
src/or/hibernate.h \
src/or/hs_cache.h \
+ src/or/hs_cell.h \
+ src/or/hs_config.h \
+ src/or/hs_circuit.h \
+ src/or/hs_circuitmap.h \
+ src/or/hs_client.h \
src/or/hs_common.h \
src/or/hs_descriptor.h \
- src/or/hs_intropoint.h \
- src/or/hs_circuitmap.h \
- src/or/hs_ntor.h \
- src/or/hs_service.h \
+ src/or/hs_ident.h \
+ src/or/hs_intropoint.h \
+ src/or/hs_ntor.h \
+ src/or/hs_service.h \
src/or/keypin.h \
src/or/main.h \
src/or/microdesc.h \
@@ -207,6 +223,11 @@ ORHEADERS = \
src/or/periodic.h \
src/or/policies.h \
src/or/protover.h \
+ src/or/proto_cell.h \
+ src/or/proto_control0.h \
+ src/or/proto_ext_or.h \
+ src/or/proto_http.h \
+ src/or/proto_socks.h \
src/or/reasons.h \
src/or/relay.h \
src/or/rendcache.h \
diff --git a/src/or/keypin.h b/src/or/keypin.h
index 2564f5befb..fbb77e5c35 100644
--- a/src/or/keypin.h
+++ b/src/or/keypin.h
@@ -41,7 +41,7 @@ STATIC keypin_ent_t * keypin_parse_journal_line(const char *cp);
STATIC int keypin_load_journal_impl(const char *data, size_t size);
MOCK_DECL(STATIC void, keypin_add_entry_to_map, (keypin_ent_t *ent));
-#endif
+#endif /* defined(KEYPIN_PRIVATE) */
-#endif
+#endif /* !defined(TOR_KEYPIN_H) */
diff --git a/src/or/main.c b/src/or/main.c
index e9723cb0b7..66b5920980 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -52,6 +52,7 @@
#include "backtrace.h"
#include "bridges.h"
#include "buffers.h"
+#include "buffers_tls.h"
#include "channel.h"
#include "channeltls.h"
#include "channelpadding.h"
@@ -81,6 +82,7 @@
#include "hibernate.h"
#include "hs_cache.h"
#include "hs_circuitmap.h"
+#include "hs_client.h"
#include "keypin.h"
#include "main.h"
#include "microdesc.h"
@@ -121,9 +123,9 @@
* Coverity. Here's a kludge to unconfuse it.
*/
# define __INCLUDE_LEVEL__ 2
-# endif
+#endif /* defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__) */
#include <systemd/sd-daemon.h>
-#endif
+#endif /* defined(HAVE_SYSTEMD) */
void evdns_shutdown(int);
@@ -741,7 +743,7 @@ conn_read_callback(evutil_socket_t fd, short event, void *_conn)
"(fd %d); removing",
conn_type_to_string(conn->type), (int)conn->s);
tor_fragile_assert();
-#endif
+#endif /* !defined(_WIN32) */
if (CONN_IS_EDGE(conn))
connection_edge_end_errno(TO_EDGE_CONN(conn));
connection_mark_for_close(conn);
@@ -836,7 +838,7 @@ conn_close_if_marked(int i)
(int)conn->outbuf_flushlen,
conn->marked_for_close_file, conn->marked_for_close);
if (conn->linked_conn) {
- retval = move_buf_to_buf(conn->linked_conn->inbuf, conn->outbuf,
+ retval = buf_move_to_buf(conn->linked_conn->inbuf, conn->outbuf,
&conn->outbuf_flushlen);
if (retval >= 0) {
/* The linked conn will notice that it has data when it notices that
@@ -850,12 +852,13 @@ conn_close_if_marked(int i)
connection_wants_to_flush(conn));
} else if (connection_speaks_cells(conn)) {
if (conn->state == OR_CONN_STATE_OPEN) {
- retval = flush_buf_tls(TO_OR_CONN(conn)->tls, conn->outbuf, sz,
+ retval = buf_flush_to_tls(conn->outbuf, TO_OR_CONN(conn)->tls, sz,
&conn->outbuf_flushlen);
} else
retval = -1; /* never flush non-open broken tls connections */
} else {
- retval = flush_buf(conn->s, conn->outbuf, sz, &conn->outbuf_flushlen);
+ retval = buf_flush_to_socket(conn->outbuf, conn->s, sz,
+ &conn->outbuf_flushlen);
}
if (retval >= 0 && /* Technically, we could survive things like
TLS_WANT_WRITE here. But don't bother for now. */
@@ -1143,7 +1146,7 @@ signewnym_impl(time_t now)
circuit_mark_all_dirty_circs_as_unusable();
addressmap_clear_transient();
- rend_client_purge_state();
+ hs_client_purge_state();
time_of_last_signewnym = now;
signewnym_is_pending = 0;
@@ -1195,6 +1198,7 @@ CALLBACK(heartbeat);
CALLBACK(clean_consdiffmgr);
CALLBACK(reset_padding_counts);
CALLBACK(check_canonical_channels);
+CALLBACK(hs_service);
#undef CALLBACK
@@ -1230,6 +1234,7 @@ static periodic_event_item_t periodic_events[] = {
CALLBACK(clean_consdiffmgr),
CALLBACK(reset_padding_counts),
CALLBACK(check_canonical_channels),
+ CALLBACK(hs_service),
END_OF_PERIODIC_EVENTS
};
#undef CALLBACK
@@ -1462,12 +1467,6 @@ run_scheduled_events(time_t now)
/* 6. And remove any marked circuits... */
circuit_close_all_marked();
- /* 7. And upload service descriptors if necessary. */
- if (have_completed_a_circuit() && !net_is_disabled()) {
- rend_consider_services_upload(now);
- rend_consider_descriptor_republication();
- }
-
/* 8. and blow away any connections that need to die. have to do this now,
* because if we marked a conn for close and left its socket -1, then
* we'll pass it to poll/select and bad things will happen.
@@ -1714,7 +1713,7 @@ check_expired_networkstatus_callback(time_t now, const or_options_t *options)
* networkstatus_get_reasonably_live_consensus(), but that value is way
* way too high. Arma: is the bridge issue there resolved yet? -NM */
#define NS_EXPIRY_SLOP (24*60*60)
- if (ns && ns->valid_until < now+NS_EXPIRY_SLOP &&
+ if (ns && ns->valid_until < (now - NS_EXPIRY_SLOP) &&
router_have_minimum_dir_info()) {
router_dir_info_changed();
}
@@ -1832,8 +1831,8 @@ clean_caches_callback(time_t now, const or_options_t *options)
{
/* Remove old information from rephist and the rend cache. */
rep_history_clean(now - options->RephistTrackTime);
- rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
rend_cache_clean(now, REND_CACHE_TYPE_SERVICE);
+ hs_cache_clean_as_client(now);
hs_cache_clean_as_dir(now);
microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
@@ -1852,6 +1851,7 @@ rend_cache_failure_clean_callback(time_t now, const or_options_t *options)
* clean it as soon as we can since we want to make sure the client waits
* as little as possible for reachability reasons. */
rend_cache_failure_clean(now);
+ hs_cache_client_intro_state_clean(now);
return 30;
}
@@ -2041,7 +2041,8 @@ check_fw_helper_app_callback(time_t now, const or_options_t *options)
{
if (net_is_disabled() ||
! server_mode(options) ||
- ! options->PortForwarding) {
+ ! options->PortForwarding ||
+ options->NoExec) {
return PERIODIC_EVENT_NO_UPDATE;
}
/* 11. check the port forwarding app */
@@ -2061,6 +2062,9 @@ check_fw_helper_app_callback(time_t now, const or_options_t *options)
/**
* Periodic callback: write the heartbeat message in the logs.
+ *
+ * If writing the heartbeat message to the logs fails for some reason, retry
+ * again after <b>MIN_HEARTBEAT_PERIOD</b> seconds.
*/
static int
heartbeat_callback(time_t now, const or_options_t *options)
@@ -2072,14 +2076,20 @@ heartbeat_callback(time_t now, const or_options_t *options)
return PERIODIC_EVENT_NO_UPDATE;
}
- /* Write the heartbeat message */
+ /* Skip the first one. */
if (first) {
- first = 0; /* Skip the first one. */
- } else {
- log_heartbeat(now);
+ first = 0;
+ return options->HeartbeatPeriod;
}
- return options->HeartbeatPeriod;
+ /* Write the heartbeat message */
+ if (log_heartbeat(now) == 0) {
+ return options->HeartbeatPeriod;
+ } else {
+ /* If we couldn't write the heartbeat log message, try again in the minimum
+ * interval of time. */
+ return MIN_HEARTBEAT_PERIOD;
+ }
}
#define CDM_CLEAN_CALLBACK_INTERVAL 600
@@ -2093,6 +2103,29 @@ clean_consdiffmgr_callback(time_t now, const or_options_t *options)
return CDM_CLEAN_CALLBACK_INTERVAL;
}
+/*
+ * Periodic callback: Run scheduled events for HS service. This is called
+ * every second.
+ */
+static int
+hs_service_callback(time_t now, const or_options_t *options)
+{
+ (void) options;
+
+ /* We need to at least be able to build circuits and that we actually have
+ * a working network. */
+ if (!have_completed_a_circuit() || net_is_disabled() ||
+ networkstatus_get_live_consensus(now) == NULL) {
+ goto end;
+ }
+
+ hs_service_run_scheduled_events(now);
+
+ end:
+ /* Every 1 second. */
+ return 1;
+}
+
/** Timer: used to invoke second_elapsed_callback() once per second. */
static periodic_timer_t *second_timer = NULL;
/** Number of libevent errors in the last second: we die if we get too many. */
@@ -2195,7 +2228,7 @@ systemd_watchdog_callback(periodic_timer_t *timer, void *arg)
(void)arg;
sd_notify(0, "WATCHDOG=1");
}
-#endif
+#endif /* defined(HAVE_SYSTEMD_209) */
/** Timer: used to invoke refill_callback(). */
static periodic_timer_t *refill_timer = NULL;
@@ -2259,7 +2292,7 @@ got_libevent_error(void)
}
return 0;
}
-#endif
+#endif /* !defined(_WIN32) */
#define UPTIME_CUTOFF_FOR_NEW_BANDWIDTH_TEST (6*60*60)
@@ -2356,7 +2389,7 @@ do_hup(void)
tor_free(msg);
}
}
- if (authdir_mode_handles_descs(options, -1)) {
+ if (authdir_mode(options)) {
/* reload the approved-routers file */
if (dirserv_load_fingerprint_file() < 0) {
/* warnings are logged from dirserv_load_fingerprint_file() directly */
@@ -2500,9 +2533,6 @@ do_main_loop(void)
}
}
- /* Initialize relay-side HS circuitmap */
- hs_circuitmap_init();
-
/* set up once-a-second callback. */
if (! second_timer) {
struct timeval one_second;
@@ -2536,7 +2566,7 @@ do_main_loop(void)
tor_assert(systemd_watchdog_timer);
}
}
-#endif
+#endif /* defined(HAVE_SYSTEMD_209) */
if (!refill_timer) {
struct timeval refill_interval;
@@ -2564,7 +2594,7 @@ do_main_loop(void)
log_info(LD_GENERAL, "Systemd NOTIFY_SOCKET not present.");
}
}
-#endif
+#endif /* defined(HAVE_SYSTEMD) */
return run_main_loop_until_done();
}
@@ -2617,7 +2647,7 @@ run_main_loop_once(void)
log_warn(LD_NET, "EINVAL from libevent: should you upgrade libevent?");
if (got_libevent_error())
return -1;
-#endif
+#endif /* !defined(_WIN32) */
} else {
tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e));
log_debug(LD_NET,"libevent call interrupted.");
@@ -2878,7 +2908,6 @@ dumpstats(int severity)
rep_hist_dump_stats(now,severity);
rend_service_dump_stats(severity);
- dump_pk_ops(severity);
dump_distinct_digest_count(severity);
}
@@ -2974,7 +3003,7 @@ handle_signals(int is_parent)
#ifdef SIGXFSZ
sigaction(SIGXFSZ, &action, NULL);
#endif
-#endif
+#endif /* !defined(_WIN32) */
}
}
@@ -3015,9 +3044,10 @@ tor_init(int argc, char *argv[])
rep_hist_init();
/* Initialize the service cache. */
rend_cache_init();
- hs_cache_init();
addressmap_init(); /* Init the client dns cache. Do it always, since it's
* cheap. */
+ /* Initialize the HS subsystem. */
+ hs_init();
{
/* We search for the "quiet" option first, since it decides whether we
@@ -3217,10 +3247,8 @@ tor_free_all(int postfork)
networkstatus_free_all();
addressmap_free_all();
dirserv_free_all();
- rend_service_free_all();
rend_cache_free_all();
rend_service_authorization_free_all();
- hs_cache_free_all();
rep_hist_free_all();
dns_free_all();
clear_pending_onions();
@@ -3233,7 +3261,6 @@ tor_free_all(int postfork)
connection_edge_free_all();
scheduler_free_all();
nodelist_free_all();
- hs_circuitmap_free_all();
microdesc_free_all();
routerparse_free_all();
ext_orport_free_all();
@@ -3242,6 +3269,7 @@ tor_free_all(int postfork)
protover_free_all();
bridges_free_all();
consdiffmgr_free_all();
+ hs_free_all();
dos_free_all();
if (!postfork) {
config_free_all();
@@ -3480,7 +3508,7 @@ sandbox_init_filter(void)
if (options->BridgeAuthoritativeDir)
OPEN_DATADIR_SUFFIX("networkstatus-bridges", ".tmp");
- if (authdir_mode_handles_descs(options, -1))
+ if (authdir_mode(options))
OPEN_DATADIR("approved-routers");
if (options->ServerDNSResolvConfFile)
@@ -3552,7 +3580,7 @@ sandbox_init_filter(void)
{
smartlist_t *files = smartlist_new();
smartlist_t *dirs = smartlist_new();
- rend_services_add_filenames_to_lists(files, dirs);
+ hs_service_lists_fnames_for_sandbox(files, dirs);
SMARTLIST_FOREACH(files, char *, file_name, {
char *tmp_name = NULL;
tor_asprintf(&tmp_name, "%s.tmp", file_name);
@@ -3704,7 +3732,7 @@ tor_main(int argc, char *argv[])
setdeppolicy(3);
}
}
-#endif
+#endif /* defined(_WIN32) */
configure_backtrace_handler(get_version());
@@ -3720,14 +3748,14 @@ tor_main(int argc, char *argv[])
int r = crypto_use_tor_alloc_functions();
tor_assert(r == 0);
}
-#endif
+#endif /* defined(USE_DMALLOC) */
#ifdef NT_SERVICE
{
int done = 0;
result = nt_service_parse_options(argc, argv, &done);
if (done) return result;
}
-#endif
+#endif /* defined(NT_SERVICE) */
if (tor_init(argc, argv)<0)
return -1;
@@ -3756,6 +3784,10 @@ tor_main(int argc, char *argv[])
case CMD_KEYGEN:
result = load_ed_keys(get_options(), time(NULL)) < 0;
break;
+ case CMD_KEY_EXPIRATION:
+ init_keys();
+ result = log_cert_expiration();
+ break;
case CMD_LIST_FINGERPRINT:
result = do_list_fingerprint();
break;
diff --git a/src/or/main.h b/src/or/main.h
index 57aa372750..132ab12bbb 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -95,7 +95,7 @@ STATIC void teardown_periodic_events(void);
#ifdef TOR_UNIT_TESTS
extern smartlist_t *connection_array;
#endif
-#endif
+#endif /* defined(MAIN_PRIVATE) */
-#endif
+#endif /* !defined(TOR_MAIN_H) */
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 32242d0054..fe327c6c82 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -977,7 +977,7 @@ update_microdesc_downloads(time_t now)
smartlist_free(missing);
}
-/** For every microdescriptor listed in the current microdecriptor consensus,
+/** For every microdescriptor listed in the current microdescriptor consensus,
* update its last_listed field to be at least as recent as the publication
* time of the current microdescriptor consensus.
*/
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index fdaa469faf..3d99dd9eec 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -62,22 +62,17 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "scheduler.h"
#include "shared_random.h"
#include "transports.h"
#include "torcert.h"
#include "channelpadding.h"
-/** Map from lowercase nickname to identity digest of named server, if any. */
-static strmap_t *named_server_map = NULL;
-/** Map from lowercase nickname to (void*)1 for all names that are listed
- * as unnamed for some server in the consensus. */
-static strmap_t *unnamed_server_map = NULL;
-
/** Most recently received and validated v3 "ns"-flavored consensus network
* status. */
STATIC networkstatus_t *current_ns_consensus = NULL;
-/** Most recently received and validated v3 "microdec"-flavored consensus
+/** Most recently received and validated v3 "microdesc"-flavored consensus
* network status. */
STATIC networkstatus_t *current_md_consensus = NULL;
@@ -143,7 +138,6 @@ static int have_warned_about_old_version = 0;
* listed by the authorities. */
static int have_warned_about_new_version = 0;
-static void routerstatus_list_update_named_server_map(void);
static void update_consensus_bootstrap_multiple_downloads(
time_t now,
const or_options_t *options);
@@ -251,13 +245,6 @@ router_reload_consensus_networkstatus(void)
}
}
- if (!networkstatus_get_latest_consensus()) {
- if (!named_server_map)
- named_server_map = strmap_new();
- if (!unnamed_server_map)
- unnamed_server_map = strmap_new();
- }
-
update_certificate_downloads(time(NULL));
routers_update_all_from_networkstatus(time(NULL), 3);
@@ -795,41 +782,6 @@ router_get_consensus_status_by_id(const char *digest)
return router_get_mutable_consensus_status_by_id(digest);
}
-/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
- * the corresponding routerstatus_t, or NULL if none exists. Warn the
- * user if <b>warn_if_unnamed</b> is set, and they have specified a router by
- * nickname, but the Named flag isn't set for that router. */
-const routerstatus_t *
-router_get_consensus_status_by_nickname(const char *nickname,
- int warn_if_unnamed)
-{
- const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed);
- if (node)
- return node->rs;
- else
- return NULL;
-}
-
-/** Return the identity digest that's mapped to officially by
- * <b>nickname</b>. */
-const char *
-networkstatus_get_router_digest_by_nickname(const char *nickname)
-{
- if (!named_server_map)
- return NULL;
- return strmap_get_lc(named_server_map, nickname);
-}
-
-/** Return true iff <b>nickname</b> is disallowed from being the nickname
- * of any server. */
-int
-networkstatus_nickname_is_unnamed(const char *nickname)
-{
- if (!unnamed_server_map)
- return 0;
- return strmap_get_lc(unnamed_server_map, nickname) != NULL;
-}
-
/** How frequently do directory authorities re-download fresh networkstatus
* documents? */
#define AUTHORITY_NS_CACHE_INTERVAL (10*60)
@@ -1258,7 +1210,9 @@ should_delay_dir_fetches(const or_options_t *options, const char **msg_out)
}
if (options->UseBridges) {
- if (!any_bridge_descriptors_known()) {
+ /* If we know that none of our bridges can possibly work, avoid fetching
+ * directory documents. But if some of them might work, try again. */
+ if (num_bridges_usable(1) == 0) {
if (msg_out) {
*msg_out = "No running bridges";
}
@@ -1394,14 +1348,21 @@ networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f))
MOCK_IMPL(networkstatus_t *,
networkstatus_get_live_consensus,(time_t now))
{
- if (networkstatus_get_latest_consensus() &&
- networkstatus_get_latest_consensus()->valid_after <= now &&
- now <= networkstatus_get_latest_consensus()->valid_until)
- return networkstatus_get_latest_consensus();
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
+ if (ns && networkstatus_is_live(ns, now))
+ return ns;
else
return NULL;
}
+/** Given a consensus in <b>ns</b>, return true iff currently live and
+ * unexpired. */
+int
+networkstatus_is_live(const networkstatus_t *ns, time_t now)
+{
+ return (ns->valid_after <= now && now <= ns->valid_until);
+}
+
/** Determine if <b>consensus</b> is valid or expired recently enough that
* we can still use it.
*
@@ -1604,15 +1565,23 @@ notify_control_networkstatus_changed(const networkstatus_t *old_c,
smartlist_free(changed);
}
-/* Called when the consensus has changed from old_c to new_c. */
+/* Called before the consensus changes from old_c to new_c. */
static void
-notify_networkstatus_changed(const networkstatus_t *old_c,
- const networkstatus_t *new_c)
+notify_before_networkstatus_changes(const networkstatus_t *old_c,
+ const networkstatus_t *new_c)
{
notify_control_networkstatus_changed(old_c, new_c);
dos_consensus_has_changed(new_c);
}
+/* Called after a new consensus has been put in the global state. It is safe
+ * to use the consensus getters in this function. */
+static void
+notify_after_networkstatus_changes(void)
+{
+ scheduler_notify_networkstatus_changed();
+}
+
/** Copy all the ancillary information (like router download status and so on)
* from <b>old_c</b> to <b>new_c</b>. */
static void
@@ -1670,7 +1639,7 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
}
return current_md_consensus ? 0 : -1;
}
-#endif //TOR_UNIT_TESTS
+#endif /* defined(TOR_UNIT_TESTS) */
/**
* Return true if any option is set in <b>options</b> to make us behave
@@ -1685,7 +1654,8 @@ any_client_port_set(const or_options_t *options)
return (options->SocksPort_set ||
options->TransPort_set ||
options->NATDPort_set ||
- options->DNSPort_set);
+ options->DNSPort_set ||
+ options->HTTPTunnelPort_set);
}
/**
@@ -1792,7 +1762,7 @@ networkstatus_set_current_consensus(const char *consensus,
if (from_cache && !was_waiting_for_certs) {
/* We previously stored this; check _now_ to make sure that version-kills
- * really work. This happens even before we check signatures: we did so
+ * really work. This happens even before we check signatures: we did so
* before when we stored this to disk. This does mean an attacker who can
* write to the datadir can make us not start: such an attacker could
* already harm us by replacing our guards, which would be worse. */
@@ -1935,8 +1905,11 @@ networkstatus_set_current_consensus(const char *consensus,
const int is_usable_flavor = flav == usable_consensus_flavor();
+ /* Before we switch to the new consensus, notify that we are about to change
+ * it using the old consensus and the new one. */
if (is_usable_flavor) {
- notify_networkstatus_changed(networkstatus_get_latest_consensus(), c);
+ notify_before_networkstatus_changes(networkstatus_get_latest_consensus(),
+ c);
}
if (flav == FLAV_NS) {
if (current_ns_consensus) {
@@ -1979,14 +1952,21 @@ networkstatus_set_current_consensus(const char *consensus,
}
if (is_usable_flavor) {
+ /* Notify that we just changed the consensus so the current global value
+ * can be looked at. */
+ notify_after_networkstatus_changes();
+
+ /* The "current" consensus has just been set and it is a usable flavor so
+ * the first thing we need to do is recalculate the voting schedule static
+ * object so we can use the timings in there needed by some subsystems
+ * such as hidden service and shared random. */
+ dirvote_recalculate_timing(options, now);
+
nodelist_set_consensus(c);
/* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/
update_consensus_networkstatus_fetch_time(now);
- dirvote_recalculate_timing(options, now);
- routerstatus_list_update_named_server_map();
-
/* Update ewma and adjust policy if needed; first cache the old value */
old_ewma_enabled = cell_ewma_enabled();
/* Change the cell EWMA settings */
@@ -2039,14 +2019,16 @@ networkstatus_set_current_consensus(const char *consensus,
char tbuf[ISO_TIME_LEN+1];
char dbuf[64];
long delta = now - c->valid_after;
+ char *flavormsg = NULL;
format_iso_time(tbuf, c->valid_after);
format_time_interval(dbuf, sizeof(dbuf), delta);
log_warn(LD_GENERAL, "Our clock is %s behind the time published in the "
"consensus network status document (%s UTC). Tor needs an "
"accurate clock to work correctly. Please check your time and "
"date settings!", dbuf, tbuf);
- control_event_general_status(LOG_WARN,
- "CLOCK_SKEW MIN_SKEW=%ld SOURCE=CONSENSUS", delta);
+ tor_asprintf(&flavormsg, "%s flavor consensus", flavor);
+ clock_skew_warning(NULL, delta, 1, LD_GENERAL, flavormsg, "CONSENSUS");
+ tor_free(flavormsg);
}
/* We got a new consesus. Reset our md fetch fail cache */
@@ -2157,31 +2139,6 @@ routers_update_all_from_networkstatus(time_t now, int dir_version)
}
}
-/** Update our view of the list of named servers from the most recently
- * retrieved networkstatus consensus. */
-static void
-routerstatus_list_update_named_server_map(void)
-{
- networkstatus_t *ns = networkstatus_get_latest_consensus();
- if (!ns)
- return;
-
- strmap_free(named_server_map, tor_free_);
- named_server_map = strmap_new();
- strmap_free(unnamed_server_map, NULL);
- unnamed_server_map = strmap_new();
- smartlist_t *rslist = ns->routerstatus_list;
- SMARTLIST_FOREACH_BEGIN(rslist, const routerstatus_t *, rs) {
- if (rs->is_named) {
- strmap_set_lc(named_server_map, rs->nickname,
- tor_memdup(rs->identity_digest, DIGEST_LEN));
- }
- if (rs->is_unnamed) {
- strmap_set_lc(unnamed_server_map, rs->nickname, (void*)1);
- }
- } SMARTLIST_FOREACH_END(rs);
-}
-
/** Given a list <b>routers</b> of routerinfo_t *, update each status field
* according to our current consensus networkstatus. May re-order
* <b>routers</b>. */
@@ -2390,9 +2347,9 @@ get_net_param_from_list(smartlist_t *net_params, const char *param_name,
* Make sure the value parsed from the consensus is at least
* <b>min_val</b> and at most <b>max_val</b> and raise/cap the parsed value
* if necessary. */
-int32_t
-networkstatus_get_param(const networkstatus_t *ns, const char *param_name,
- int32_t default_val, int32_t min_val, int32_t max_val)
+MOCK_IMPL(int32_t,
+networkstatus_get_param, (const networkstatus_t *ns, const char *param_name,
+ int32_t default_val, int32_t min_val, int32_t max_val))
{
if (!ns) /* if they pass in null, go find it ourselves */
ns = networkstatus_get_latest_consensus();
@@ -2559,7 +2516,8 @@ getinfo_helper_networkstatus(control_connection_t *conn,
}
status = router_get_consensus_status_by_id(d);
} else if (!strcmpstart(question, "ns/name/")) {
- status = router_get_consensus_status_by_nickname(question+8, 0);
+ const node_t *n = node_get_by_nickname(question+8, 0);
+ status = n ? n->rs : NULL;
} else if (!strcmpstart(question, "ns/purpose/")) {
*answer = networkstatus_getinfo_by_purpose(question+11, time(NULL));
return *answer ? 0 : -1;
@@ -2666,8 +2624,5 @@ networkstatus_free_all(void)
}
tor_free(waiting->body);
}
-
- strmap_free(named_server_map, tor_free_);
- strmap_free(unnamed_server_map, NULL);
}
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index e774c4d266..39a0f753d8 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -62,11 +62,6 @@ const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
MOCK_DECL(routerstatus_t *,
router_get_mutable_consensus_status_by_descriptor_digest,
(networkstatus_t *consensus, const char *digest));
-const routerstatus_t *router_get_consensus_status_by_nickname(
- const char *nickname,
- int warn_if_unnamed);
-const char *networkstatus_get_router_digest_by_nickname(const char *nickname);
-int networkstatus_nickname_is_unnamed(const char *nickname);
int we_want_to_fetch_flavor(const or_options_t *options, int flavor);
int we_want_to_fetch_unknown_auth_certs(const or_options_t *options);
void networkstatus_consensus_download_failed(int status_code,
@@ -81,6 +76,7 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus,(void));
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
(consensus_flavor_t f));
MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now));
+int networkstatus_is_live(const networkstatus_t *ns, time_t now);
int networkstatus_consensus_reasonably_live(const networkstatus_t *consensus,
time_t now);
int networkstatus_valid_until_is_reasonably_live(time_t valid_until,
@@ -113,10 +109,9 @@ void signed_descs_update_status_from_consensus_networkstatus(
char *networkstatus_getinfo_helper_single(const routerstatus_t *rs);
char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now);
void networkstatus_dump_bridge_status_to_file(time_t now);
-int32_t networkstatus_get_param(const networkstatus_t *ns,
- const char *param_name,
- int32_t default_val, int32_t min_val,
- int32_t max_val);
+MOCK_DECL(int32_t, networkstatus_get_param,
+ (const networkstatus_t *ns, const char *param_name,
+ int32_t default_val, int32_t min_val, int32_t max_val));
int32_t networkstatus_get_overridable_param(const networkstatus_t *ns,
int32_t torrc_value,
const char *param_name,
@@ -142,8 +137,8 @@ STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
const char *flavor);
extern networkstatus_t *current_ns_consensus;
extern networkstatus_t *current_md_consensus;
-#endif
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
+#endif /* defined(NETWORKSTATUS_PRIVATE) */
-#endif
+#endif /* !defined(TOR_NETWORKSTATUS_H) */
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index aaec39f7b8..ac94498558 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -38,6 +38,8 @@
* used for authorities and fallback directories.)
*/
+#define NODELIST_PRIVATE
+
#include "or.h"
#include "address.h"
#include "address_set.h"
@@ -46,6 +48,8 @@
#include "dirserv.h"
#include "entrynodes.h"
#include "geoip.h"
+#include "hs_common.h"
+#include "hs_client.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -55,6 +59,7 @@
#include "rendservice.h"
#include "router.h"
#include "routerlist.h"
+#include "routerparse.h"
#include "routerset.h"
#include "torcert.h"
@@ -92,7 +97,14 @@ typedef struct nodelist_t {
smartlist_t *nodes;
/* Hash table to map from node ID digest to node. */
HT_HEAD(nodelist_map, node_t) nodes_by_id;
-
+ /* Hash table to map from node Ed25519 ID to node.
+ *
+ * Whenever a node's routerinfo or microdescriptor is about to change,
+ * you should remove it from this map with node_remove_from_ed25519_map().
+ * Whenever a node's routerinfo or microdescriptor has just chaned,
+ * you should add it to this map with node_add_to_ed25519_map().
+ */
+ HT_HEAD(nodelist_ed_map, node_t) nodes_by_ed_id;
/* Set of addresses that belong to nodes we believe in. */
address_set_t *node_addrs;
} nodelist_t;
@@ -113,6 +125,23 @@ HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq)
HT_GENERATE2(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq,
0.6, tor_reallocarray_, tor_free_)
+static inline unsigned int
+node_ed_id_hash(const node_t *node)
+{
+ return (unsigned) siphash24g(node->ed25519_id.pubkey, ED25519_PUBKEY_LEN);
+}
+
+static inline unsigned int
+node_ed_id_eq(const node_t *node1, const node_t *node2)
+{
+ return ed25519_pubkey_eq(&node1->ed25519_id, &node2->ed25519_id);
+}
+
+HT_PROTOTYPE(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash,
+ node_ed_id_eq)
+HT_GENERATE2(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash,
+ node_ed_id_eq, 0.6, tor_reallocarray_, tor_free_)
+
/** The global nodelist. */
static nodelist_t *the_nodelist=NULL;
@@ -123,6 +152,7 @@ init_nodelist(void)
if (PREDICT_UNLIKELY(the_nodelist == NULL)) {
the_nodelist = tor_malloc_zero(sizeof(nodelist_t));
HT_INIT(nodelist_map, &the_nodelist->nodes_by_id);
+ HT_INIT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id);
the_nodelist->nodes = smartlist_new();
}
}
@@ -140,6 +170,21 @@ node_get_mutable_by_id(const char *identity_digest)
return node;
}
+/** As node_get_by_ed25519_id, but returns a non-const pointer */
+node_t *
+node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id)
+{
+ node_t search, *node;
+ if (PREDICT_UNLIKELY(the_nodelist == NULL))
+ return NULL;
+ if (BUG(ed_id == NULL) || BUG(ed25519_public_key_is_zero(ed_id)))
+ return NULL;
+
+ memcpy(&search.ed25519_id, ed_id, sizeof(search.ed25519_id));
+ node = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, &search);
+ return node;
+}
+
/** Return the node_t whose identity is <b>identity_digest</b>, or NULL
* if no such node exists. */
MOCK_IMPL(const node_t *,
@@ -148,6 +193,14 @@ node_get_by_id,(const char *identity_digest))
return node_get_mutable_by_id(identity_digest);
}
+/** Return the node_t whose ed25519 identity is <b>ed_id</b>, or NULL
+ * if no such node exists. */
+MOCK_IMPL(const node_t *,
+node_get_by_ed25519_id,(const ed25519_public_key_t *ed_id))
+{
+ return node_get_mutable_by_ed25519_id(ed_id);
+}
+
/** Internal: return the node_t whose identity_digest is
* <b>identity_digest</b>. If none exists, create a new one, add it to the
* nodelist, and return it.
@@ -168,12 +221,181 @@ node_get_or_create(const char *identity_digest)
smartlist_add(the_nodelist->nodes, node);
node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1;
+ node->hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t));
node->country = -1;
return node;
}
+/** Remove <b>node</b> from the ed25519 map (if it present), and
+ * set its ed25519_id field to zero. */
+static int
+node_remove_from_ed25519_map(node_t *node)
+{
+ tor_assert(the_nodelist);
+ tor_assert(node);
+
+ if (ed25519_public_key_is_zero(&node->ed25519_id)) {
+ return 0;
+ }
+
+ int rv = 0;
+ node_t *search =
+ HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ if (BUG(search != node)) {
+ goto clear_and_return;
+ }
+
+ search = HT_REMOVE(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ tor_assert(search == node);
+ rv = 1;
+
+ clear_and_return:
+ memset(&node->ed25519_id, 0, sizeof(node->ed25519_id));
+ return rv;
+}
+
+/** If <b>node</b> has an ed25519 id, and it is not already in the ed25519 id
+ * map, set its ed25519_id field, and add it to the ed25519 map.
+ */
+static int
+node_add_to_ed25519_map(node_t *node)
+{
+ tor_assert(the_nodelist);
+ tor_assert(node);
+
+ if (! ed25519_public_key_is_zero(&node->ed25519_id)) {
+ return 0;
+ }
+
+ const ed25519_public_key_t *key = node_get_ed25519_id(node);
+ if (!key) {
+ return 0;
+ }
+
+ node_t *old;
+ memcpy(&node->ed25519_id, key, sizeof(node->ed25519_id));
+ old = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ if (BUG(old)) {
+ /* XXXX order matters here, and this may mean that authorities aren't
+ * pinning. */
+ if (old != node)
+ memset(&node->ed25519_id, 0, sizeof(node->ed25519_id));
+ return 0;
+ }
+
+ HT_INSERT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node);
+ return 1;
+}
+
+/* For a given <b>node</b> for the consensus <b>ns</b>, set the hsdir index
+ * for the node, both current and next if possible. This can only fails if the
+ * node_t ed25519 identity key can't be found which would be a bug. */
+STATIC void
+node_set_hsdir_index(node_t *node, const networkstatus_t *ns)
+{
+ time_t now = approx_time();
+ const ed25519_public_key_t *node_identity_pk;
+ uint8_t *fetch_srv = NULL, *store_first_srv = NULL, *store_second_srv = NULL;
+ uint64_t next_time_period_num, current_time_period_num;
+ uint64_t fetch_tp, store_first_tp, store_second_tp;
+
+ tor_assert(node);
+ tor_assert(ns);
+
+ if (!networkstatus_is_live(ns, now)) {
+ static struct ratelim_t live_consensus_ratelim = RATELIM_INIT(30 * 60);
+ log_fn_ratelim(&live_consensus_ratelim, LOG_INFO, LD_GENERAL,
+ "Not setting hsdir index with a non-live consensus.");
+ goto done;
+ }
+
+ node_identity_pk = node_get_ed25519_id(node);
+ if (node_identity_pk == NULL) {
+ log_debug(LD_GENERAL, "ed25519 identity public key not found when "
+ "trying to build the hsdir indexes for node %s",
+ node_describe(node));
+ goto done;
+ }
+
+ /* Get the current and next time period number. */
+ current_time_period_num = hs_get_time_period_num(0);
+ next_time_period_num = hs_get_next_time_period_num(0);
+
+ /* We always use the current time period for fetching descs */
+ fetch_tp = current_time_period_num;
+
+ /* Now extract the needed SRVs and time periods for building hsdir indices */
+ if (hs_in_period_between_tp_and_srv(ns, now)) {
+ fetch_srv = hs_get_current_srv(fetch_tp, ns);
+
+ store_first_tp = hs_get_previous_time_period_num(0);
+ store_second_tp = current_time_period_num;
+ } else {
+ fetch_srv = hs_get_previous_srv(fetch_tp, ns);
+
+ store_first_tp = current_time_period_num;
+ store_second_tp = next_time_period_num;
+ }
+
+ /* We always use the old SRV for storing the first descriptor and the latest
+ * SRV for storing the second descriptor */
+ store_first_srv = hs_get_previous_srv(store_first_tp, ns);
+ store_second_srv = hs_get_current_srv(store_second_tp, ns);
+
+ /* Build the fetch index. */
+ hs_build_hsdir_index(node_identity_pk, fetch_srv, fetch_tp,
+ node->hsdir_index->fetch);
+
+ /* If we are in the time segment between SRV#N and TP#N, the fetch index is
+ the same as the first store index */
+ if (!hs_in_period_between_tp_and_srv(ns, now)) {
+ memcpy(node->hsdir_index->store_first, node->hsdir_index->fetch,
+ sizeof(node->hsdir_index->store_first));
+ } else {
+ hs_build_hsdir_index(node_identity_pk, store_first_srv, store_first_tp,
+ node->hsdir_index->store_first);
+ }
+
+ /* If we are in the time segment between TP#N and SRV#N+1, the fetch index is
+ the same as the second store index */
+ if (hs_in_period_between_tp_and_srv(ns, now)) {
+ memcpy(node->hsdir_index->store_second, node->hsdir_index->fetch,
+ sizeof(node->hsdir_index->store_second));
+ } else {
+ hs_build_hsdir_index(node_identity_pk, store_second_srv, store_second_tp,
+ node->hsdir_index->store_second);
+ }
+
+ done:
+ tor_free(fetch_srv);
+ tor_free(store_first_srv);
+ tor_free(store_second_srv);
+ return;
+}
+
+/** Recompute all node hsdir indices. */
+void
+nodelist_recompute_all_hsdir_indices(void)
+{
+ networkstatus_t *consensus;
+ if (!the_nodelist) {
+ return;
+ }
+
+ /* Get a live consensus. Abort if not found */
+ consensus = networkstatus_get_live_consensus(approx_time());
+ if (!consensus) {
+ return;
+ }
+
+ /* Recompute all hsdir indices */
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ node_set_hsdir_index(node, consensus);
+ } SMARTLIST_FOREACH_END(node);
+}
+
/** Called when a node's address changes. */
static void
node_addrs_changed(node_t *node)
@@ -242,6 +464,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
id_digest = ri->cache_info.identity_digest;
node = node_get_or_create(id_digest);
+ node_remove_from_ed25519_map(node);
+
if (node->ri) {
if (!routers_have_same_or_addrs(node->ri, ri)) {
node_addrs_changed(node);
@@ -255,6 +479,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
}
node->ri = ri;
+ node_add_to_ed25519_map(node);
+
if (node->country == -1)
node_set_country(node);
@@ -264,6 +490,14 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
dirserv_set_node_flags_from_authoritative_status(node, status);
}
+ /* Setting the HSDir index requires the ed25519 identity key which can
+ * only be found either in the ri or md. This is why this is called here.
+ * Only nodes supporting HSDir=2 protocol version needs this index. */
+ if (node->rs && node->rs->supports_v3_hsdir) {
+ node_set_hsdir_index(node,
+ networkstatus_get_latest_consensus());
+ }
+
node_add_to_address_set(node);
return node;
@@ -293,10 +527,20 @@ nodelist_add_microdesc(microdesc_t *md)
node = node_get_mutable_by_id(rs->identity_digest);
if (node == NULL)
return NULL;
+
+ node_remove_from_ed25519_map(node);
if (node->md)
node->md->held_by_nodes--;
+
node->md = md;
md->held_by_nodes++;
+ /* Setting the HSDir index requires the ed25519 identity key which can
+ * only be found either in the ri or md. This is why this is called here.
+ * Only nodes supporting HSDir=2 protocol version needs this index. */
+ if (rs->supports_v3_hsdir) {
+ node_set_hsdir_index(node, ns);
+ }
+ node_add_to_ed25519_map(node);
node_add_to_address_set(node);
return node;
@@ -343,15 +587,20 @@ nodelist_set_consensus(networkstatus_t *ns)
if (ns->flavor == FLAV_MICRODESC) {
if (node->md == NULL ||
tor_memneq(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) {
+ node_remove_from_ed25519_map(node);
if (node->md)
node->md->held_by_nodes--;
node->md = microdesc_cache_lookup_by_digest256(NULL,
rs->descriptor_digest);
if (node->md)
node->md->held_by_nodes++;
+ node_add_to_ed25519_map(node);
}
}
+ if (rs->supports_v3_hsdir) {
+ node_set_hsdir_index(node, ns);
+ }
node_set_country(node);
/* If we're not an authdir, believe others. */
@@ -415,6 +664,9 @@ nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md)
if (node && node->md == md) {
node->md = NULL;
md->held_by_nodes--;
+ if (! node_get_ed25519_id(node)) {
+ node_remove_from_ed25519_map(node);
+ }
}
}
@@ -443,6 +695,7 @@ nodelist_drop_node(node_t *node, int remove_from_ht)
tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node);
tor_assert(tmp == node);
}
+ node_remove_from_ed25519_map(node);
idx = node->nodelist_idx;
tor_assert(idx >= 0);
@@ -484,6 +737,7 @@ node_free(node_t *node)
if (node->md)
node->md->held_by_nodes--;
tor_assert(node->nodelist_idx == -1);
+ tor_free(node->hsdir_index);
tor_free(node);
}
@@ -525,6 +779,7 @@ nodelist_free_all(void)
return;
HT_CLEAR(nodelist_map, &the_nodelist->nodes_by_id);
+ HT_CLEAR(nodelist_ed_map, &the_nodelist->nodes_by_ed_id);
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
node->nodelist_idx = -1;
node_free(node);
@@ -592,9 +847,27 @@ nodelist_assert_ok(void)
tor_assert(node_sl_idx == node->nodelist_idx);
} SMARTLIST_FOREACH_END(node);
+ /* Every node listed with an ed25519 identity should be listed by that
+ * identity.
+ */
+ SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
+ if (!ed25519_public_key_is_zero(&node->ed25519_id)) {
+ tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id));
+ }
+ } SMARTLIST_FOREACH_END(node);
+
+ node_t **idx;
+ HT_FOREACH(idx, nodelist_ed_map, &the_nodelist->nodes_by_ed_id) {
+ node_t *node = *idx;
+ tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id));
+ }
+
tor_assert((long)smartlist_len(the_nodelist->nodes) ==
(long)HT_SIZE(&the_nodelist->nodes_by_id));
+ tor_assert((long)smartlist_len(the_nodelist->nodes) >=
+ (long)HT_SIZE(&the_nodelist->nodes_by_ed_id));
+
digestmap_free(dm, NULL);
}
@@ -611,28 +884,23 @@ nodelist_get_list,(void))
/** Given a hex-encoded nickname of the format DIGEST, $DIGEST, $DIGEST=name,
* or $DIGEST~name, return the node with the matching identity digest and
* nickname (if any). Return NULL if no such node exists, or if <b>hex_id</b>
- * is not well-formed. */
+ * is not well-formed. DOCDOC flags */
const node_t *
-node_get_by_hex_id(const char *hex_id)
+node_get_by_hex_id(const char *hex_id, unsigned flags)
{
char digest_buf[DIGEST_LEN];
char nn_buf[MAX_NICKNAME_LEN+1];
char nn_char='\0';
+ (void) flags; // XXXX
+
if (hex_digest_nickname_decode(hex_id, digest_buf, &nn_char, nn_buf)==0) {
const node_t *node = node_get_by_id(digest_buf);
if (!node)
return NULL;
- if (nn_char) {
- const char *real_name = node_get_nickname(node);
- if (!real_name || strcasecmp(real_name, nn_buf))
- return NULL;
- if (nn_char == '=') {
- const char *named_id =
- networkstatus_get_router_digest_by_nickname(nn_buf);
- if (!named_id || tor_memneq(named_id, digest_buf, DIGEST_LEN))
- return NULL;
- }
+ if (nn_char == '=') {
+ /* "=" indicates a Named relay, but there aren't any of those now. */
+ return NULL;
}
return node;
}
@@ -641,42 +909,27 @@ node_get_by_hex_id(const char *hex_id)
}
/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
- * the corresponding node_t, or NULL if none exists. Warn the user if
- * <b>warn_if_unnamed</b> is set, and they have specified a router by
- * nickname, but the Named flag isn't set for that router. */
+ * the corresponding node_t, or NULL if none exists. Warn the user if they
+ * have specified a router by nickname, unless the NNF_NO_WARN_UNNAMED bit is
+ * set in <b>flags</b>. */
MOCK_IMPL(const node_t *,
-node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
+node_get_by_nickname,(const char *nickname, unsigned flags))
{
+ const int warn_if_unnamed = !(flags & NNF_NO_WARN_UNNAMED);
+
if (!the_nodelist)
return NULL;
/* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
{
const node_t *node;
- if ((node = node_get_by_hex_id(nickname)) != NULL)
+ if ((node = node_get_by_hex_id(nickname, flags)) != NULL)
return node;
}
if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
return NULL;
- /* Okay, so if we get here, the nickname is just a nickname. Is there
- * a binding for it in the consensus? */
- {
- const char *named_id =
- networkstatus_get_router_digest_by_nickname(nickname);
- if (named_id)
- return node_get_by_id(named_id);
- }
-
- /* Is it marked as owned-by-someone-else? */
- if (networkstatus_nickname_is_unnamed(nickname)) {
- log_info(LD_GENERAL, "The name %s is listed as Unnamed: there is some "
- "router that holds it, but not one listed in the current "
- "consensus.", escaped(nickname));
- return NULL;
- }
-
/* Okay, so the name is not canonical for anybody. */
{
smartlist_t *matches = smartlist_new();
@@ -707,11 +960,9 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
if (! node->name_lookup_warned) {
base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN);
log_warn(LD_CONFIG,
- "You specified a server \"%s\" by name, but the directory "
- "authorities do not have any key registered for this "
- "nickname -- so it could be used by any server, not just "
- "the one you meant. "
- "To make sure you get the same server in the future, refer "
+ "You specified a relay \"%s\" by name, but nicknames can be "
+ "used by any relay, not just the one you meant. "
+ "To make sure you get the same relay in the future, refer "
"to it by key, as \"$%s\".", nickname, fp);
node->name_lookup_warned = 1;
}
@@ -730,22 +981,37 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
const ed25519_public_key_t *
node_get_ed25519_id(const node_t *node)
{
+ const ed25519_public_key_t *ri_pk = NULL;
+ const ed25519_public_key_t *md_pk = NULL;
if (node->ri) {
if (node->ri->cache_info.signing_key_cert) {
- const ed25519_public_key_t *pk =
- &node->ri->cache_info.signing_key_cert->signing_key;
- if (BUG(ed25519_public_key_is_zero(pk)))
- goto try_the_md;
- return pk;
+ ri_pk = &node->ri->cache_info.signing_key_cert->signing_key;
+ if (BUG(ed25519_public_key_is_zero(ri_pk)))
+ ri_pk = NULL;
}
}
- try_the_md:
+
if (node->md) {
if (node->md->ed25519_identity_pkey) {
- return node->md->ed25519_identity_pkey;
+ md_pk = node->md->ed25519_identity_pkey;
}
}
- return NULL;
+
+ if (ri_pk && md_pk) {
+ if (ed25519_pubkey_eq(ri_pk, md_pk)) {
+ return ri_pk;
+ } else {
+ /* This can happen if the relay gets flagged NoEdConsensus which will be
+ * triggered on all relays of the network. Thus a protocol warning. */
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Inconsistent ed25519 identities in the nodelist");
+ return NULL;
+ }
+ } else if (ri_pk) {
+ return ri_pk;
+ } else {
+ return md_pk;
+ }
}
/** Return true iff this node's Ed25519 identity matches <b>id</b>.
@@ -784,6 +1050,79 @@ node_supports_ed25519_link_authentication(const node_t *node)
return 0;
}
+/** Return true iff <b>node</b> supports the hidden service directory version
+ * 3 protocol (proposal 224). */
+int
+node_supports_v3_hsdir(const node_t *node)
+{
+ tor_assert(node);
+
+ if (node->rs) {
+ return node->rs->supports_v3_hsdir;
+ }
+ if (node->ri) {
+ if (node->ri->protocol_list == NULL) {
+ return 0;
+ }
+ /* Bug #22447 forces us to filter on tor version:
+ * If platform is a Tor version, and older than 0.3.0.8, return False.
+ * Else, obey the protocol list. */
+ if (node->ri->platform) {
+ if (!strcmpstart(node->ri->platform, "Tor ") &&
+ !tor_version_as_new_as(node->ri->platform, "0.3.0.8")) {
+ return 0;
+ }
+ }
+ return protocol_list_supports_protocol(node->ri->protocol_list,
+ PRT_HSDIR, PROTOVER_HSDIR_V3);
+ }
+ tor_assert_nonfatal_unreached_once();
+ return 0;
+}
+
+/** Return true iff <b>node</b> supports ed25519 authentication as an hidden
+ * service introduction point.*/
+int
+node_supports_ed25519_hs_intro(const node_t *node)
+{
+ tor_assert(node);
+
+ if (node->rs) {
+ return node->rs->supports_ed25519_hs_intro;
+ }
+ if (node->ri) {
+ if (node->ri->protocol_list == NULL) {
+ return 0;
+ }
+ return protocol_list_supports_protocol(node->ri->protocol_list,
+ PRT_HSINTRO, PROTOVER_HS_INTRO_V3);
+ }
+ tor_assert_nonfatal_unreached_once();
+ return 0;
+}
+
+/** Return true iff <b>node</b> supports to be a rendezvous point for hidden
+ * service version 3 (HSRend=2). */
+int
+node_supports_v3_rendezvous_point(const node_t *node)
+{
+ tor_assert(node);
+
+ if (node->rs) {
+ return node->rs->supports_v3_rendezvous_point;
+ }
+ if (node->ri) {
+ if (node->ri->protocol_list == NULL) {
+ return 0;
+ }
+ return protocol_list_supports_protocol(node->ri->protocol_list,
+ PRT_HSREND,
+ PROTOVER_HS_RENDEZVOUS_POINT_V3);
+ }
+ tor_assert_nonfatal_unreached_once();
+ return 0;
+}
+
/** Return the RSA ID key's SHA1 digest for the provided node. */
const uint8_t *
node_get_rsa_id_digest(const node_t *node)
@@ -805,21 +1144,6 @@ node_get_nickname(const node_t *node)
return NULL;
}
-/** Return true iff the nickname of <b>node</b> is canonical, based on the
- * latest consensus. */
-int
-node_is_named(const node_t *node)
-{
- const char *named_id;
- const char *nickname = node_get_nickname(node);
- if (!nickname)
- return 0;
- named_id = networkstatus_get_router_digest_by_nickname(nickname);
- if (!named_id)
- return 0;
- return tor_memeq(named_id, node->identity, DIGEST_LEN);
-}
-
/** Return true iff <b>node</b> appears to be a directory authority or
* directory cache */
int
@@ -867,13 +1191,12 @@ node_get_verbose_nickname(const node_t *node,
char *verbose_name_out)
{
const char *nickname = node_get_nickname(node);
- int is_named = node_is_named(node);
verbose_name_out[0] = '$';
base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity,
DIGEST_LEN);
if (!nickname)
return;
- verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+ verbose_name_out[1+HEX_DIGEST_LEN] = '~';
strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1);
}
@@ -1433,8 +1756,7 @@ node_nickname_matches(const node_t *node, const char *nickname)
return 1;
return hex_digest_nickname_matches(nickname,
node->identity,
- n,
- node_is_named(node));
+ n);
}
/** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */
@@ -1536,7 +1858,7 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node)
SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) {
const node_t *node2;
const smartlist_t *family2;
- if (!(node2 = node_get_by_nickname(name, 0)))
+ if (!(node2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED)))
continue;
if (!(family2 = node_get_declared_family(node2)))
continue;
@@ -1688,8 +2010,8 @@ static char dir_info_status[512] = "";
* no exits in the consensus."
* To obtain the final weighted bandwidth, we multiply the
* weighted bandwidth fraction for each position (guard, middle, exit). */
-int
-router_have_minimum_dir_info(void)
+MOCK_IMPL(int,
+router_have_minimum_dir_info,(void))
{
static int logged_delay=0;
const char *delay_fetches_msg = NULL;
@@ -1736,6 +2058,8 @@ router_dir_info_changed(void)
{
need_to_update_have_min_dir_info = 1;
rend_hsdir_routers_changed();
+ hs_service_dir_info_changed();
+ hs_client_dir_info_changed();
}
/** Return a string describing what we're missing before we have enough
@@ -2045,6 +2369,7 @@ update_router_have_minimum_dir_info(void)
{
time_t now = time(NULL);
int res;
+ int num_present=0, num_usable=0;
const or_options_t *options = get_options();
const networkstatus_t *consensus =
networkstatus_get_reasonably_live_consensus(now,usable_consensus_flavor());
@@ -2063,17 +2388,9 @@ update_router_have_minimum_dir_info(void)
using_md = consensus->flavor == FLAV_MICRODESC;
- if (! entry_guards_have_enough_dir_info_to_build_circuits()) {
- strlcpy(dir_info_status, "We're missing descriptors for some of our "
- "primary entry guards", sizeof(dir_info_status));
- res = 0;
- goto done;
- }
-
/* Check fraction of available paths */
{
char *status = NULL;
- int num_present=0, num_usable=0;
double paths = compute_frac_paths_available(consensus, options, now,
&num_present, &num_usable,
&status);
@@ -2094,6 +2411,18 @@ update_router_have_minimum_dir_info(void)
res = 1;
}
+ { /* Check entry guard dirinfo status */
+ char *guard_error = entry_guards_get_err_str_if_dir_info_missing(using_md,
+ num_present,
+ num_usable);
+ if (guard_error) {
+ strlcpy(dir_info_status, guard_error, sizeof(dir_info_status));
+ tor_free(guard_error);
+ res = 0;
+ goto done;
+ }
+ }
+
done:
/* If paths have just become available in this update. */
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 9cd66f60a2..8a0c79f86d 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -18,7 +18,14 @@
node_t *node_get_mutable_by_id(const char *identity_digest);
MOCK_DECL(const node_t *, node_get_by_id, (const char *identity_digest));
-const node_t *node_get_by_hex_id(const char *identity_digest);
+node_t *node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id);
+MOCK_DECL(const node_t *, node_get_by_ed25519_id,
+ (const ed25519_public_key_t *ed_id));
+
+#define NNF_NO_WARN_UNNAMED (1u<<0)
+
+const node_t *node_get_by_hex_id(const char *identity_digest,
+ unsigned flags);
node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out);
node_t *nodelist_add_microdesc(microdesc_t *md);
void nodelist_set_consensus(networkstatus_t *ns);
@@ -29,16 +36,17 @@ void nodelist_remove_routerinfo(routerinfo_t *ri);
void nodelist_purge(void);
smartlist_t *nodelist_find_nodes_with_microdesc(const microdesc_t *md);
+void nodelist_recompute_all_hsdir_indices(void);
+
void nodelist_free_all(void);
void nodelist_assert_ok(void);
MOCK_DECL(const node_t *, node_get_by_nickname,
- (const char *nickname, int warn_if_unnamed));
+ (const char *nickname, unsigned flags));
void node_get_verbose_nickname(const node_t *node,
char *verbose_name_out);
void node_get_verbose_nickname_by_id(const char *id_digest,
char *verbose_name_out);
-int node_is_named(const node_t *node);
int node_is_dir(const node_t *node);
int node_has_descriptor(const node_t *node);
int node_get_purpose(const node_t *node);
@@ -59,6 +67,9 @@ const ed25519_public_key_t *node_get_ed25519_id(const node_t *node);
int node_ed25519_id_matches(const node_t *node,
const ed25519_public_key_t *id);
int node_supports_ed25519_link_authentication(const node_t *node);
+int node_supports_v3_hsdir(const node_t *node);
+int node_supports_ed25519_hs_intro(const node_t *node);
+int node_supports_v3_rendezvous_point(const node_t *node);
const uint8_t *node_get_rsa_id_digest(const node_t *node);
int node_has_ipv6_addr(const node_t *node);
@@ -104,7 +115,7 @@ int addrs_in_same_network_family(const tor_addr_t *a1,
* no exits in the consensus, we wait for enough info to create internal
* paths, and should avoid creating exit paths, as they will simply fail.
* We make sure we create all available circuit types at the same time. */
-int router_have_minimum_dir_info(void);
+MOCK_DECL(int, router_have_minimum_dir_info,(void));
/** Set to CONSENSUS_PATH_EXIT if there is at least one exit node
* in the consensus. We update this flag in compute_frac_paths_available if
@@ -132,7 +143,18 @@ void router_dir_info_changed(void);
const char *get_dir_info_status_string(void);
int count_loading_descriptors_progress(void);
+#ifdef NODELIST_PRIVATE
+
+#ifdef TOR_UNIT_TESTS
+
+STATIC void
+node_set_hsdir_index(node_t *node, const networkstatus_t *ns);
+
+#endif /* defined(TOR_UNIT_TESTS) */
+
+#endif /* defined(NODELIST_PRIVATE) */
+
MOCK_DECL(int, get_estimated_address_per_node, (void));
-#endif
+#endif /* !defined(TOR_NODELIST_H) */
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index d0d5276c48..508e5844eb 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -329,9 +329,10 @@ nt_service_main(void)
case CMD_VERIFY_CONFIG:
case CMD_DUMP_CONFIG:
case CMD_KEYGEN:
+ case CMD_KEY_EXPIRATION:
log_err(LD_CONFIG, "Unsupported command (--list-fingerprint, "
- "--hash-password, --keygen, --dump-config, or --verify-config) "
- "in NT service.");
+ "--hash-password, --keygen, --dump-config, --verify-config, "
+ "or --key-expiration) in NT service.");
break;
case CMD_RUN_UNITTESTS:
default:
@@ -501,7 +502,7 @@ nt_service_command_line(int *using_default_torrc)
tor_exe_ascii[sizeof(tor_exe_ascii)-1] = '\0';
#else
strlcpy(tor_exe_ascii, tor_exe, sizeof(tor_exe_ascii));
-#endif
+#endif /* defined(UNICODE) */
/* Allocate a string for the NT service command line and */
/* Format the service command */
@@ -777,5 +778,5 @@ nt_service_parse_options(int argc, char **argv, int *should_exit)
return 0;
}
-#endif
+#endif /* defined(_WIN32) */
diff --git a/src/or/ntmain.h b/src/or/ntmain.h
index 4b771b1828..81b7159855 100644
--- a/src/or/ntmain.h
+++ b/src/or/ntmain.h
@@ -22,7 +22,7 @@ int nt_service_is_stopping(void);
void nt_service_set_state(DWORD state);
#else
#define nt_service_is_stopping() 0
-#endif
+#endif /* defined(NT_SERVICE) */
-#endif
+#endif /* !defined(TOR_NTMAIN_H) */
diff --git a/src/or/onion.c b/src/or/onion.c
index a98b97cb1d..7e1e89df1b 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -1219,7 +1219,7 @@ extend_cell_format(uint8_t *command_out, uint16_t *len_out,
*command_out = RELAY_COMMAND_EXTEND;
*len_out = 6 + TAP_ONIONSKIN_CHALLENGE_LEN + DIGEST_LEN;
set_uint32(p, tor_addr_to_ipv4n(&cell_in->orport_ipv4.addr));
- set_uint16(p+4, ntohs(cell_in->orport_ipv4.port));
+ set_uint16(p+4, htons(cell_in->orport_ipv4.port));
if (cell_in->create_cell.handshake_type == ONION_HANDSHAKE_TYPE_NTOR) {
memcpy(p+6, NTOR_CREATE_MAGIC, 16);
memcpy(p+22, cell_in->create_cell.onionskin, NTOR_ONIONSKIN_LEN);
diff --git a/src/or/onion.h b/src/or/onion.h
index 37a7b08cb6..95544dfac1 100644
--- a/src/or/onion.h
+++ b/src/or/onion.h
@@ -119,5 +119,5 @@ int extend_cell_format(uint8_t *command_out, uint16_t *len_out,
int extended_cell_format(uint8_t *command_out, uint16_t *len_out,
uint8_t *payload_out, const extended_cell_t *cell_in);
-#endif
+#endif /* !defined(TOR_ONION_H) */
diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h
index b31f8e9492..3a5aefea3f 100644
--- a/src/or/onion_fast.h
+++ b/src/or/onion_fast.h
@@ -35,5 +35,5 @@ int fast_client_handshake(const fast_handshake_state_t *handshake_state,
size_t key_out_len,
const char **msg_out);
-#endif
+#endif /* !defined(TOR_ONION_FAST_H) */
diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h
index 158c499de4..02dea2dfc1 100644
--- a/src/or/onion_ntor.h
+++ b/src/or/onion_ntor.h
@@ -55,7 +55,7 @@ struct ntor_handshake_state_t {
curve25519_public_key_t pubkey_X;
/** @} */
};
-#endif
+#endif /* defined(ONION_NTOR_PRIVATE) */
-#endif
+#endif /* !defined(TOR_ONION_NTOR_H) */
diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c
index 294fc0df6d..c71fa236ed 100644
--- a/src/or/onion_tap.c
+++ b/src/or/onion_tap.c
@@ -72,10 +72,8 @@ onion_skin_TAP_create(crypto_pk_t *dest_router_key,
if (crypto_dh_get_public(dh, challenge, dhbytes))
goto err;
- note_crypto_pk_op(ENC_ONIONSKIN);
-
/* set meeting point, meeting cookie, etc here. Leave zero for now. */
- if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out,
+ if (crypto_pk_obsolete_public_hybrid_encrypt(dest_router_key, onion_skin_out,
TAP_ONIONSKIN_CHALLENGE_LEN,
challenge, DH_KEY_LEN,
PK_PKCS1_OAEP_PADDING, 1)<0)
@@ -124,8 +122,7 @@ onion_skin_TAP_server_handshake(
k = i==0?private_key:prev_private_key;
if (!k)
break;
- note_crypto_pk_op(DEC_ONIONSKIN);
- len = crypto_pk_private_hybrid_decrypt(k, challenge,
+ len = crypto_pk_obsolete_private_hybrid_decrypt(k, challenge,
TAP_ONIONSKIN_CHALLENGE_LEN,
onion_skin,
TAP_ONIONSKIN_CHALLENGE_LEN,
diff --git a/src/or/onion_tap.h b/src/or/onion_tap.h
index bd625231f4..713c1d7391 100644
--- a/src/or/onion_tap.h
+++ b/src/or/onion_tap.h
@@ -34,5 +34,5 @@ int onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state,
size_t key_out_len,
const char **msg_out);
-#endif
+#endif /* !defined(TOR_ONION_TAP_H) */
diff --git a/src/or/or.h b/src/or/or.h
index 9e7833386c..5128fd2197 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -64,7 +64,7 @@
#include <process.h>
#include <direct.h>
#include <windows.h>
-#endif
+#endif /* defined(_WIN32) */
#include "crypto.h"
#include "crypto_format.h"
@@ -226,8 +226,10 @@ typedef enum {
#define CONN_TYPE_EXT_OR 16
/** Type for sockets listening for Extended ORPort connections. */
#define CONN_TYPE_EXT_OR_LISTENER 17
+/** Type for sockets listening for HTTP CONNECT tunnel connections. */
+#define CONN_TYPE_AP_HTTP_CONNECT_LISTENER 18
-#define CONN_TYPE_MAX_ 17
+#define CONN_TYPE_MAX_ 19
/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in
* connection_t. */
@@ -348,7 +350,9 @@ typedef enum {
/** State for a transparent natd connection: waiting for original
* destination. */
#define AP_CONN_STATE_NATD_WAIT 12
-#define AP_CONN_STATE_MAX_ 12
+/** State for an HTTP tunnel: waiting for an HTTP CONNECT command. */
+#define AP_CONN_STATE_HTTP_CONNECT_WAIT 13
+#define AP_CONN_STATE_MAX_ 13
/** True iff the AP_CONN_STATE_* value <b>s</b> means that the corresponding
* edge connection is not attached to any circuit. */
@@ -421,15 +425,23 @@ typedef enum {
#define DIR_PURPOSE_FETCH_RENDDESC_V2 18
/** A connection to a directory server: download a microdescriptor. */
#define DIR_PURPOSE_FETCH_MICRODESC 19
-#define DIR_PURPOSE_MAX_ 19
+/** A connection to a hidden service directory: upload a v3 descriptor. */
+#define DIR_PURPOSE_UPLOAD_HSDESC 20
+/** A connection to a hidden service directory: fetch a v3 descriptor. */
+#define DIR_PURPOSE_FETCH_HSDESC 21
+/** A connection to a directory server: set after a hidden service descriptor
+ * is downloaded. */
+#define DIR_PURPOSE_HAS_FETCHED_HSDESC 22
+#define DIR_PURPOSE_MAX_ 22
/** True iff <b>p</b> is a purpose corresponding to uploading
* data to a directory server. */
#define DIR_PURPOSE_IS_UPLOAD(p) \
((p)==DIR_PURPOSE_UPLOAD_DIR || \
(p)==DIR_PURPOSE_UPLOAD_VOTE || \
- (p)==DIR_PURPOSE_UPLOAD_SIGNATURES || \
- (p)==DIR_PURPOSE_UPLOAD_RENDDESC_V2)
+ (p)==DIR_PURPOSE_UPLOAD_SIGNATURES || \
+ (p)==DIR_PURPOSE_UPLOAD_RENDDESC_V2 || \
+ (p)==DIR_PURPOSE_UPLOAD_HSDESC)
#define EXIT_PURPOSE_MIN_ 1
/** This exit stream wants to do an ordinary connect. */
@@ -640,6 +652,10 @@ typedef enum {
/** The target address is in a private network (like 127.0.0.1 or 10.0.0.1);
* you don't want to do that over a randomly chosen exit */
#define END_STREAM_REASON_PRIVATE_ADDR 262
+/** This is an HTTP tunnel connection and the client used or misused HTTP in a
+ * way we can't handle.
+ */
+#define END_STREAM_REASON_HTTPPROTOCOL 263
/** Bitwise-and this value with endreason to mask out all flags. */
#define END_STREAM_REASON_MASK 511
@@ -731,15 +747,15 @@ typedef enum {
#define REND_NUMBER_OF_CONSECUTIVE_REPLICAS 3
/** Length of v2 descriptor ID (32 base32 chars = 160 bits). */
-#define REND_DESC_ID_V2_LEN_BASE32 32
+#define REND_DESC_ID_V2_LEN_BASE32 BASE32_DIGEST_LEN
/** Length of the base32-encoded secret ID part of versioned hidden service
* descriptors. */
-#define REND_SECRET_ID_PART_LEN_BASE32 32
+#define REND_SECRET_ID_PART_LEN_BASE32 BASE32_DIGEST_LEN
/** Length of the base32-encoded hash of an introduction point's
* identity key. */
-#define REND_INTRO_POINT_ID_LEN_BASE32 32
+#define REND_INTRO_POINT_ID_LEN_BASE32 BASE32_DIGEST_LEN
/** Length of the descriptor cookie that is used for client authorization
* to hidden services. */
@@ -846,6 +862,13 @@ rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d)
return DOWNCAST(rend_data_v2_t, d);
}
+/* Stub because we can't include hs_ident.h. */
+struct hs_ident_edge_conn_t;
+struct hs_ident_dir_conn_t;
+struct hs_ident_circuit_t;
+/* Stub because we can't include hs_common.h. */
+struct hsdir_index_t;
+
/** Time interval for tracking replays of DH public keys received in
* INTRODUCE2 cells. Used only to avoid launching multiple
* simultaneous attempts to connect to the same rendezvous point. */
@@ -1179,11 +1202,8 @@ typedef struct {
uint16_t length; /**< How long is the payload body? */
} relay_header_t;
-typedef struct buf_t buf_t;
typedef struct socks_request_t socks_request_t;
-#define buf_t buf_t
-
typedef struct entry_port_cfg_t {
/* Client port types (socks, dns, trans, natd) only: */
uint8_t isolation_flags; /**< Zero or more isolation flags */
@@ -1243,6 +1263,8 @@ typedef struct server_port_cfg_t {
#define CONTROL_CONNECTION_MAGIC 0x8abc765du
#define LISTENER_CONNECTION_MAGIC 0x1a1ac741u
+struct buf_t;
+
/** Description of a connection to another host or process, and associated
* data.
*
@@ -1314,8 +1336,9 @@ typedef struct connection_t {
struct event *read_event; /**< Libevent event structure. */
struct event *write_event; /**< Libevent event structure. */
- buf_t *inbuf; /**< Buffer holding data read over this connection. */
- buf_t *outbuf; /**< Buffer holding data to write over this connection. */
+ struct buf_t *inbuf; /**< Buffer holding data read over this connection. */
+ struct buf_t *outbuf; /**< Buffer holding data to write over this
+ * connection. */
size_t outbuf_flushlen; /**< How much data should we try to flush from the
* outbuf? */
time_t timestamp_lastread; /**< When was the last time libevent said we could
@@ -1410,7 +1433,7 @@ typedef struct listener_connection_t {
* session as described in RFC 5705.
*
* Not used by today's tors, since everything that supports this
- * also supports ED25519_SHA3_5705, which is better.
+ * also supports ED25519_SHA256_5705, which is better.
**/
#define AUTHTYPE_RSA_SHA256_RFC5705 2
/** As AUTHTYPE_RSA_SHA256_RFC5705, but uses an Ed25519 identity key to
@@ -1652,6 +1675,11 @@ typedef struct edge_connection_t {
* an exit)? */
rend_data_t *rend_data;
+ /* Hidden service connection identifier for edge connections. Used by the HS
+ * client-side code to identify client SOCKS connections and by the
+ * service-side code to match HS circuits with their streams. */
+ struct hs_ident_edge_conn_t *hs_ident;
+
uint32_t address_ttl; /**< TTL for address-to-addr mapping on exit
* connection. Exit connections only. */
uint32_t begincell_flags; /** Flags sent or received in the BEGIN cell
@@ -1721,11 +1749,11 @@ typedef struct entry_connection_t {
/** For AP connections only: buffer for data that we have sent
* optimistically, which we might need to re-send if we have to
* retry this connection. */
- buf_t *pending_optimistic_data;
+ struct buf_t *pending_optimistic_data;
/* For AP connections only: buffer for data that we previously sent
* optimistically which we are currently re-sending as we retry this
* connection. */
- buf_t *sending_optimistic_data;
+ struct buf_t *sending_optimistic_data;
/** If this is a DNSPort connection, this field holds the pending DNS
* request that we're going to try to answer. */
@@ -1802,6 +1830,11 @@ typedef struct dir_connection_t {
/** What rendezvous service are we querying for? */
rend_data_t *rend_data;
+ /* Hidden service connection identifier for dir connections: Used by HS
+ client-side code to fetch HS descriptors, and by the service-side code to
+ upload descriptors. */
+ struct hs_ident_dir_conn_t *hs_ident;
+
/** If this is a one-hop connection, tracks the state of the directory guard
* for this connection (if any). */
struct circuit_guard_state_t *guard_state;
@@ -1820,7 +1853,7 @@ typedef struct dir_connection_t {
/** Number of RELAY_DATA cells sent. */
uint32_t data_cells_sent;
-#endif
+#endif /* defined(MEASUREMENTS_21206) */
} dir_connection_t;
/** Subtype of connection_t for an connection to a controller. */
@@ -2077,7 +2110,9 @@ typedef struct download_status_t {
* or after each failure? */
download_schedule_backoff_bitfield_t backoff : 1; /**< do we use the
* deterministic schedule, or random
- * exponential backoffs? */
+ * exponential backoffs?
+ * Increment on failure schedules
+ * always use exponential backoff. */
uint8_t last_backoff_position; /**< number of attempts/failures, depending
* on increment_on, when we last recalculated
* the delay. Only updated if backoff
@@ -2313,6 +2348,11 @@ typedef struct routerstatus_t {
* requires HSDir=2. */
unsigned int supports_v3_hsdir : 1;
+ /** True iff this router has a protocol list that allows it to be an hidden
+ * service rendezvous point supporting version 3 as seen in proposal 224.
+ * This requires HSRend=2. */
+ unsigned int supports_v3_rendezvous_point: 1;
+
unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
unsigned int bw_is_unmeasured:1; /**< This is a consensus entry, with
@@ -2438,6 +2478,8 @@ typedef struct node_t {
/** Used to look up the node_t by its identity digest. */
HT_ENTRY(node_t) ht_ent;
+ /** Used to look up the node_t by its ed25519 identity digest. */
+ HT_ENTRY(node_t) ed_ht_ent;
/** Position of the node within the list of nodes */
int nodelist_idx;
@@ -2445,6 +2487,13 @@ typedef struct node_t {
* identity may exist at a time. */
char identity[DIGEST_LEN];
+ /** The ed25519 identity of this node_t. This field is nonzero iff we
+ * currently have an ed25519 identity for this node in either md or ri,
+ * _and_ this node has been inserted to the ed25519-to-node map in the
+ * nodelist.
+ */
+ ed25519_public_key_t ed25519_id;
+
microdesc_t *md;
routerinfo_t *ri;
routerstatus_t *rs;
@@ -2492,6 +2541,10 @@ typedef struct node_t {
time_t last_reachable; /* IPv4. */
time_t last_reachable6; /* IPv6. */
+ /* Hidden service directory index data. This is used by a service or client
+ * in order to know what's the hs directory index for this node at the time
+ * the consensus is set. */
+ struct hsdir_index_t *hsdir_index;
} node_t;
/** Linked list of microdesc hash lines for a single router in a directory
@@ -3205,6 +3258,10 @@ typedef struct origin_circuit_t {
/** Holds all rendezvous data on either client or service side. */
rend_data_t *rend_data;
+ /** Holds hidden service identifier on either client or service side. This
+ * is for both introduction and rendezvous circuit. */
+ struct hs_ident_circuit_t *hs_ident;
+
/** Holds the data that the entry guard system uses to track the
* status of the guard this circuit is using, and thereby to determine
* whether this circuit can be used. */
@@ -3435,9 +3492,6 @@ typedef struct or_circuit_t {
/* We have already received an INTRODUCE1 cell on this circuit. */
unsigned int already_received_introduce1 : 1;
- /** True iff this circuit was made with a CREATE_FAST cell. */
- unsigned int is_first_hop : 1;
-
/** If set, this circuit carries HS traffic. Consider it in any HS
* statistics. */
unsigned int circuit_carries_hs_traffic_stats : 1;
@@ -3586,7 +3640,8 @@ typedef struct {
enum {
CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD,
CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG,
- CMD_KEYGEN
+ CMD_KEYGEN,
+ CMD_KEY_EXPIRATION,
} command;
char *command_arg; /**< Argument for command-line option. */
@@ -3668,8 +3723,8 @@ typedef struct {
config_line_t *SocksPort_lines;
/** Ports to listen on for transparent pf/netfilter connections. */
config_line_t *TransPort_lines;
- const char *TransProxyType; /**< What kind of transparent proxy
- * implementation are we using? */
+ char *TransProxyType; /**< What kind of transparent proxy
+ * implementation are we using? */
/** Parsed value of TransProxyType. */
enum {
TPT_DEFAULT,
@@ -3679,6 +3734,8 @@ typedef struct {
} TransProxyType_parsed;
config_line_t *NATDPort_lines; /**< Ports to listen on for transparent natd
* connections. */
+ /** Ports to listen on for HTTP Tunnel connections. */
+ config_line_t *HTTPTunnelPort_lines;
config_line_t *ControlPort_lines; /**< Ports to listen on for control
* connections. */
config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on
@@ -3705,7 +3762,8 @@ typedef struct {
* configured in one of the _lines options above.
* For client ports, also true if there is a unix socket configured.
* If you are checking for client ports, you may want to use:
- * SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set
+ * SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set ||
+ * HTTPTunnelPort_set
* rather than SocksPort_set.
*
* @{
@@ -3718,6 +3776,7 @@ typedef struct {
unsigned int DirPort_set : 1;
unsigned int DNSPort_set : 1;
unsigned int ExtORPort_set : 1;
+ unsigned int HTTPTunnelPort_set : 1;
/**@}*/
int AssumeReachable; /**< Whether to publish our descriptor regardless. */
@@ -3730,6 +3789,10 @@ typedef struct {
int BridgeAuthoritativeDir; /**< Boolean: is this an authoritative directory
* that aggregates bridge descriptors? */
+ /** If set on a bridge relay, it will include this value on a new
+ * "bridge-distribution-request" line in its bridge descriptor. */
+ char *BridgeDistribution;
+
/** If set on a bridge authority, it will answer requests on its dirport
* for bridge statuses -- but only if the requests use this password. */
char *BridgePassword;
@@ -4039,8 +4102,6 @@ typedef struct {
int Sandbox; /**< Boolean: should sandboxing be enabled? */
int SafeSocks; /**< Boolean: should we outright refuse application
* connections that use socks4 or socks5-with-local-dns? */
-#define LOG_PROTOCOL_WARN (get_options()->ProtocolWarnings ? \
- LOG_WARN : LOG_INFO)
int ProtocolWarnings; /**< Boolean: when other parties screw up the Tor
* protocol, is it a warn or an info in our logs? */
int TestSocks; /**< Boolean: when we get a socks connection, do we loudly
@@ -4126,13 +4187,6 @@ typedef struct {
* if we are a cache). For authorities, this is always true. */
int DownloadExtraInfo;
- /** If true, we convert "www.google.com.foo.exit" addresses on the
- * socks/trans/natd ports into "www.google.com" addresses that
- * exit from the node "foo". Disabled by default since attacking
- * websites and exit relays can use it to manipulate your path
- * selection. */
- int AllowDotExit;
-
/** If true, we're configured to collect statistics on clients
* requesting network statuses from us as directory. */
int DirReqStatistics_option;
@@ -4297,6 +4351,10 @@ typedef struct {
* altered on testing networks. */
smartlist_t *TestingBridgeDownloadSchedule;
+ /** Schedule for when clients should download bridge descriptors when they
+ * have no running bridges. Only altered on testing networks. */
+ smartlist_t *TestingBridgeBootstrapDownloadSchedule;
+
/** When directory clients have only a few descriptors to request, they
* batch them until they have more, or until this amount of time has
* passed. Only altered on testing networks. */
@@ -4501,19 +4559,6 @@ typedef struct {
/** How long (seconds) do we keep a guard before picking a new one? */
int GuardLifetime;
- /** Low-water mark for global scheduler - start sending when estimated
- * queued size falls below this threshold.
- */
- uint64_t SchedulerLowWaterMark__;
- /** High-water mark for global scheduler - stop sending when estimated
- * queued size exceeds this threshold.
- */
- uint64_t SchedulerHighWaterMark__;
- /** Flush size for global scheduler - flush this many cells at a time
- * when sending.
- */
- int SchedulerMaxFlushCells__;
-
/** Is this an exit node? This is a tristate, where "1" means "yes, and use
* the default exit policy if none is given" and "0" means "no; exit policy
* is 'reject *'" and "auto" (-1) means "same as 1, but warn the user."
@@ -4583,6 +4628,25 @@ typedef struct {
* use the default. */
int MaxConsensusAgeForDiffs;
+ /** Bool (default: 0). Tells Tor to never try to exec another program.
+ */
+ int NoExec;
+
+ /** Have the KIST scheduler run every X milliseconds. If less than zero, do
+ * not use the KIST scheduler but use the old vanilla scheduler instead. If
+ * zero, do what the consensus says and fall back to using KIST as if this is
+ * set to "10 msec" if the consensus doesn't say anything. */
+ int KISTSchedRunInterval;
+
+ /** A multiplier for the KIST per-socket limit calculation. */
+ double KISTSockBufSizeFactor;
+
+ /** The list of scheduler type string ordered by priority that is first one
+ * has to be tried first. Default: KIST,KISTLite,Vanilla */
+ smartlist_t *Schedulers;
+ /* An ordered list of scheduler_types mapped from Schedulers. */
+ smartlist_t *SchedulerTypes_;
+
/** Autobool: Is the circuit creation DoS mitigation subsystem enabled? */
int DoSCircuitCreationEnabled;
/** Minimum concurrent connection needed from one single address before any
@@ -4613,6 +4677,8 @@ typedef struct {
int DoSRefuseSingleHopClientRendezvous;
} or_options_t;
+#define LOG_PROTOCOL_WARN (get_protocol_warning_severity_level())
+
/** Persistent state for an onion router, as saved to disk. */
typedef struct {
uint32_t magic_;
@@ -4642,6 +4708,9 @@ typedef struct {
config_line_t *TransportProxies;
+ /** Cached revision counters for active hidden services on this host */
+ config_line_t *HidServRevCounter;
+
/** These fields hold information on the history of bandwidth usage for
* servers. The "Ends" fields hold the time when we last updated the
* bandwidth usage. The "Interval" fields hold the granularity, in seconds,
@@ -4669,8 +4738,8 @@ typedef struct {
/** Build time histogram */
config_line_t * BuildtimeHistogram;
- unsigned int TotalBuildTimes;
- unsigned int CircuitBuildAbandonedCount;
+ int TotalBuildTimes;
+ int CircuitBuildAbandonedCount;
/** What version of Tor wrote this state file? */
char *TorVersion;
@@ -5031,7 +5100,7 @@ typedef struct measured_bw_line_t {
long int bw_kb;
} measured_bw_line_t;
-#endif
+#endif /* defined(DIRSERV_PRIVATE) */
/********************************* dirvote.c ************************/
@@ -5386,7 +5455,10 @@ typedef enum {
CRN_PREF_ADDR = 1<<7,
/* On clients, only provide nodes that we can connect to directly, based on
* our firewall rules */
- CRN_DIRECT_CONN = 1<<8
+ CRN_DIRECT_CONN = 1<<8,
+ /* On clients, only provide nodes with HSRend >= 2 protocol version which
+ * is required for hidden service version >= 3. */
+ CRN_RENDEZVOUS_V3 = 1<<9,
} router_crn_flags_t;
/** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */
@@ -5441,5 +5513,5 @@ typedef struct tor_version_t {
char git_tag[DIGEST_LEN];
} tor_version_t;
-#endif
+#endif /* !defined(TOR_OR_H) */
diff --git a/src/or/parsecommon.c b/src/or/parsecommon.c
index 7959867875..6c3dd3100e 100644
--- a/src/or/parsecommon.c
+++ b/src/or/parsecommon.c
@@ -161,6 +161,7 @@ get_token_arguments(memarea_t *area, directory_token_t *tok,
char *cp = mem;
int j = 0;
char *args[MAX_ARGS];
+ memset(args, 0, sizeof(args));
while (*cp) {
if (j == MAX_ARGS)
return -1;
@@ -436,7 +437,7 @@ find_opt_by_keyword(smartlist_t *s, directory_keyword keyword)
* in the same order in which they occur in <b>s</b>. Otherwise return
* NULL. */
smartlist_t *
-find_all_by_keyword(smartlist_t *s, directory_keyword k)
+find_all_by_keyword(const smartlist_t *s, directory_keyword k)
{
smartlist_t *out = NULL;
SMARTLIST_FOREACH(s, directory_token_t *, t,
diff --git a/src/or/parsecommon.h b/src/or/parsecommon.h
index b9f1613457..903d94478b 100644
--- a/src/or/parsecommon.h
+++ b/src/or/parsecommon.h
@@ -160,6 +160,7 @@ typedef enum {
R3_INTRO_AUTH_REQUIRED,
R3_SINGLE_ONION_SERVICE,
R3_INTRODUCTION_POINT,
+ R3_INTRO_ONION_KEY,
R3_INTRO_AUTH_KEY,
R3_INTRO_ENC_KEY,
R3_INTRO_ENC_KEY_CERT,
@@ -315,7 +316,7 @@ directory_token_t *find_by_keyword_(smartlist_t *s,
directory_token_t *find_opt_by_keyword(smartlist_t *s,
directory_keyword keyword);
-smartlist_t * find_all_by_keyword(smartlist_t *s, directory_keyword k);
+smartlist_t * find_all_by_keyword(const smartlist_t *s, directory_keyword k);
-#endif /* TOR_PARSECOMMON_H */
+#endif /* !defined(TOR_PARSECOMMON_H) */
diff --git a/src/or/periodic.h b/src/or/periodic.h
index 88d00cc7e9..8baf3994eb 100644
--- a/src/or/periodic.h
+++ b/src/or/periodic.h
@@ -33,5 +33,5 @@ void periodic_event_setup(periodic_event_item_t *event);
void periodic_event_destroy(periodic_event_item_t *event);
void periodic_event_reschedule(periodic_event_item_t *event);
-#endif
+#endif /* !defined(TOR_PERIODIC_H) */
diff --git a/src/or/policies.c b/src/or/policies.c
index 9e43fd78bb..3bfea3a57c 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -2188,21 +2188,16 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port)
}
/** Return true iff <b>ri</b> is "useful as an exit node", meaning
- * it allows exit to at least one /8 address space for at least
- * two of ports 80, 443, and 6667. */
+ * it allows exit to at least one /8 address space for each of ports 80
+ * and 443. */
int
exit_policy_is_general_exit(smartlist_t *policy)
{
- static const int ports[] = { 80, 443, 6667 };
- int n_allowed = 0;
- int i;
if (!policy) /*XXXX disallow NULL policies? */
return 0;
- for (i = 0; i < 3; ++i) {
- n_allowed += exit_policy_is_general_exit_helper(policy, ports[i]);
- }
- return n_allowed >= 2;
+ return (exit_policy_is_general_exit_helper(policy, 80) &&
+ exit_policy_is_general_exit_helper(policy, 443));
}
/** Return false if <b>policy</b> might permit access to some addr:port;
@@ -2733,7 +2728,7 @@ parse_short_policy(const char *summary)
}
{
- size_t size = STRUCT_OFFSET(short_policy_t, entries) +
+ size_t size = offsetof(short_policy_t, entries) +
sizeof(short_policy_entry_t)*(n_entries);
result = tor_malloc_zero(size);
diff --git a/src/or/policies.h b/src/or/policies.h
index ce08d497e9..52ff4e2f99 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -141,7 +141,7 @@ STATIC const tor_addr_port_t * fascist_firewall_choose_address(
firewall_connection_t fw_connection,
int pref_only, int pref_ipv6);
-#endif
+#endif /* defined(POLICIES_PRIVATE) */
-#endif
+#endif /* !defined(TOR_POLICIES_H) */
diff --git a/src/or/proto_cell.c b/src/or/proto_cell.c
new file mode 100644
index 0000000000..75eb2a7e7f
--- /dev/null
+++ b/src/or/proto_cell.c
@@ -0,0 +1,84 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "buffers.h"
+#include "proto_cell.h"
+
+#include "connection_or.h"
+
+/** True iff the cell command <b>command</b> is one that implies a
+ * variable-length cell in Tor link protocol <b>linkproto</b>. */
+static inline int
+cell_command_is_var_length(uint8_t command, int linkproto)
+{
+ /* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells
+ * work as implemented here. If it's 1, there are no variable-length cells.
+ * Tor does not support other versions right now, and so can't negotiate
+ * them.
+ */
+ switch (linkproto) {
+ case 1:
+ /* Link protocol version 1 has no variable-length cells. */
+ return 0;
+ case 2:
+ /* In link protocol version 2, VERSIONS is the only variable-length cell */
+ return command == CELL_VERSIONS;
+ case 0:
+ case 3:
+ default:
+ /* In link protocol version 3 and later, and in version "unknown",
+ * commands 128 and higher indicate variable-length. VERSIONS is
+ * grandfathered in. */
+ return command == CELL_VERSIONS || command >= 128;
+ }
+}
+
+/** Check <b>buf</b> for a variable-length cell according to the rules of link
+ * protocol version <b>linkproto</b>. If one is found, pull it off the buffer
+ * and assign a newly allocated var_cell_t to *<b>out</b>, and return 1.
+ * Return 0 if whatever is on the start of buf_t is not a variable-length
+ * cell. Return 1 and set *<b>out</b> to NULL if there seems to be the start
+ * of a variable-length cell on <b>buf</b>, but the whole thing isn't there
+ * yet. */
+int
+fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
+{
+ char hdr[VAR_CELL_MAX_HEADER_SIZE];
+ var_cell_t *result;
+ uint8_t command;
+ uint16_t length;
+ const int wide_circ_ids = linkproto >= MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS;
+ const int circ_id_len = get_circ_id_size(wide_circ_ids);
+ const unsigned header_len = get_var_cell_header_size(wide_circ_ids);
+ *out = NULL;
+ if (buf_datalen(buf) < header_len)
+ return 0;
+ buf_peek(buf, hdr, header_len);
+
+ command = get_uint8(hdr + circ_id_len);
+ if (!(cell_command_is_var_length(command, linkproto)))
+ return 0;
+
+ length = ntohs(get_uint16(hdr + circ_id_len + 1));
+ if (buf_datalen(buf) < (size_t)(header_len+length))
+ return 1;
+
+ result = var_cell_new(length);
+ result->command = command;
+ if (wide_circ_ids)
+ result->circ_id = ntohl(get_uint32(hdr));
+ else
+ result->circ_id = ntohs(get_uint16(hdr));
+
+ buf_drain(buf, header_len);
+ buf_peek(buf, (char*) result->payload, length);
+ buf_drain(buf, length);
+
+ *out = result;
+ return 1;
+}
+
diff --git a/src/or/proto_cell.h b/src/or/proto_cell.h
new file mode 100644
index 0000000000..bbc14b9a02
--- /dev/null
+++ b/src/or/proto_cell.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PROTO_CELL_H
+#define TOR_PROTO_CELL_H
+
+struct buf_t;
+struct var_cell_t;
+
+int fetch_var_cell_from_buf(struct buf_t *buf, struct var_cell_t **out,
+ int linkproto);
+
+#endif /* !defined(TOR_PROTO_CELL_H) */
+
diff --git a/src/or/proto_control0.c b/src/or/proto_control0.c
new file mode 100644
index 0000000000..c17ba34948
--- /dev/null
+++ b/src/or/proto_control0.c
@@ -0,0 +1,26 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "buffers.h"
+#include "proto_control0.h"
+
+/** Return 1 iff buf looks more like it has an (obsolete) v0 controller
+ * command on it than any valid v1 controller command. */
+int
+peek_buf_has_control0_command(buf_t *buf)
+{
+ if (buf_datalen(buf) >= 4) {
+ char header[4];
+ uint16_t cmd;
+ buf_peek(buf, header, sizeof(header));
+ cmd = ntohs(get_uint16(header+2));
+ if (cmd <= 0x14)
+ return 1; /* This is definitely not a v1 control command. */
+ }
+ return 0;
+}
+
diff --git a/src/or/proto_control0.h b/src/or/proto_control0.h
new file mode 100644
index 0000000000..0cc8eacad0
--- /dev/null
+++ b/src/or/proto_control0.h
@@ -0,0 +1,14 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PROTO_CONTROL0_H
+#define TOR_PROTO_CONTROL0_H
+
+struct buf_t;
+int peek_buf_has_control0_command(struct buf_t *buf);
+
+#endif /* !defined(TOR_PROTO_CONTROL0_H) */
+
diff --git a/src/or/proto_ext_or.c b/src/or/proto_ext_or.c
new file mode 100644
index 0000000000..057cf109ec
--- /dev/null
+++ b/src/or/proto_ext_or.c
@@ -0,0 +1,40 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "buffers.h"
+#include "ext_orport.h"
+#include "proto_ext_or.h"
+
+/** The size of the header of an Extended ORPort message: 2 bytes for
+ * COMMAND, 2 bytes for BODYLEN */
+#define EXT_OR_CMD_HEADER_SIZE 4
+
+/** Read <b>buf</b>, which should contain an Extended ORPort message
+ * from a transport proxy. If well-formed, create and populate
+ * <b>out</b> with the Extended ORport message. Return 0 if the
+ * buffer was incomplete, 1 if it was well-formed and -1 if we
+ * encountered an error while parsing it. */
+int
+fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out)
+{
+ char hdr[EXT_OR_CMD_HEADER_SIZE];
+ uint16_t len;
+
+ if (buf_datalen(buf) < EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ buf_peek(buf, hdr, sizeof(hdr));
+ len = ntohs(get_uint16(hdr+2));
+ if (buf_datalen(buf) < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
+ return 0;
+ *out = ext_or_cmd_new(len);
+ (*out)->cmd = ntohs(get_uint16(hdr));
+ (*out)->len = len;
+ buf_drain(buf, EXT_OR_CMD_HEADER_SIZE);
+ buf_get_bytes(buf, (*out)->body, len);
+ return 1;
+}
+
diff --git a/src/or/proto_ext_or.h b/src/or/proto_ext_or.h
new file mode 100644
index 0000000000..cc504d18e3
--- /dev/null
+++ b/src/or/proto_ext_or.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PROTO_EXT_OR_H
+#define TOR_PROTO_EXT_OR_H
+
+struct buf_t;
+struct ext_or_cmt_t;
+
+int fetch_ext_or_command_from_buf(struct buf_t *buf,
+ struct ext_or_cmd_t **out);
+
+#endif /* !defined(TOR_PROTO_EXT_OR_H) */
+
diff --git a/src/or/proto_http.c b/src/or/proto_http.c
new file mode 100644
index 0000000000..3762429e1e
--- /dev/null
+++ b/src/or/proto_http.c
@@ -0,0 +1,171 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define PROTO_HTTP_PRIVATE
+#include "or.h"
+#include "buffers.h"
+#include "proto_http.h"
+
+/** Return true if <b>cmd</b> looks like a HTTP (proxy) request. */
+int
+peek_buf_has_http_command(const buf_t *buf)
+{
+ if (buf_peek_startswith(buf, "CONNECT ") ||
+ buf_peek_startswith(buf, "DELETE ") ||
+ buf_peek_startswith(buf, "GET ") ||
+ buf_peek_startswith(buf, "POST ") ||
+ buf_peek_startswith(buf, "PUT " ))
+ return 1;
+ return 0;
+}
+
+/** There is a (possibly incomplete) http statement on <b>buf</b>, of the
+ * form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain NULs.)
+ * If a) the headers include a Content-Length field and all bytes in
+ * the body are present, or b) there's no Content-Length field and
+ * all headers are present, then:
+ *
+ * - strdup headers into <b>*headers_out</b>, and NUL-terminate it.
+ * - memdup body into <b>*body_out</b>, and NUL-terminate it.
+ * - Then remove them from <b>buf</b>, and return 1.
+ *
+ * - If headers or body is NULL, discard that part of the buf.
+ * - If a headers or body doesn't fit in the arg, return -1.
+ * (We ensure that the headers or body don't exceed max len,
+ * _even if_ we're planning to discard them.)
+ * - If force_complete is true, then succeed even if not all of the
+ * content has arrived.
+ *
+ * Else, change nothing and return 0.
+ */
+int
+fetch_from_buf_http(buf_t *buf,
+ char **headers_out, size_t max_headerlen,
+ char **body_out, size_t *body_used, size_t max_bodylen,
+ int force_complete)
+{
+ const char *headers;
+ size_t headerlen, bodylen, contentlen=0;
+ int crlf_offset;
+ int r;
+
+ if (buf_datalen(buf) == 0)
+ return 0;
+
+ crlf_offset = buf_find_string_offset(buf, "\r\n\r\n", 4);
+ if (crlf_offset > (int)max_headerlen ||
+ (crlf_offset < 0 && buf_datalen(buf) > max_headerlen)) {
+ log_debug(LD_HTTP,"headers too long.");
+ return -1;
+ } else if (crlf_offset < 0) {
+ log_debug(LD_HTTP,"headers not all here yet.");
+ return 0;
+ }
+ /* Okay, we have a full header. Make sure it all appears in the first
+ * chunk. */
+ headerlen = crlf_offset + 4;
+ size_t headers_in_chunk = 0;
+ buf_pullup(buf, headerlen, &headers, &headers_in_chunk);
+
+ bodylen = buf_datalen(buf) - headerlen;
+ log_debug(LD_HTTP,"headerlen %d, bodylen %d.", (int)headerlen, (int)bodylen);
+
+ if (max_headerlen <= headerlen) {
+ log_warn(LD_HTTP,"headerlen %d larger than %d. Failing.",
+ (int)headerlen, (int)max_headerlen-1);
+ return -1;
+ }
+ if (max_bodylen <= bodylen) {
+ log_warn(LD_HTTP,"bodylen %d larger than %d. Failing.",
+ (int)bodylen, (int)max_bodylen-1);
+ return -1;
+ }
+
+ r = buf_http_find_content_length(headers, headerlen, &contentlen);
+ if (r == -1) {
+ log_warn(LD_PROTOCOL, "Content-Length is bogus; maybe "
+ "someone is trying to crash us.");
+ return -1;
+ } else if (r == 1) {
+ /* if content-length is malformed, then our body length is 0. fine. */
+ log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen);
+ if (bodylen < contentlen) {
+ if (!force_complete) {
+ log_debug(LD_HTTP,"body not all here yet.");
+ return 0; /* not all there yet */
+ }
+ }
+ if (bodylen > contentlen) {
+ bodylen = contentlen;
+ log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen);
+ }
+ } else {
+ tor_assert(r == 0);
+ /* Leave bodylen alone */
+ }
+
+ /* all happy. copy into the appropriate places, and return 1 */
+ if (headers_out) {
+ *headers_out = tor_malloc(headerlen+1);
+ buf_get_bytes(buf, *headers_out, headerlen);
+ (*headers_out)[headerlen] = 0; /* NUL terminate it */
+ }
+ if (body_out) {
+ tor_assert(body_used);
+ *body_used = bodylen;
+ *body_out = tor_malloc(bodylen+1);
+ buf_get_bytes(buf, *body_out, bodylen);
+ (*body_out)[bodylen] = 0; /* NUL terminate it */
+ }
+ return 1;
+}
+
+/**
+ * Scan the HTTP headers in the <b>headerlen</b>-byte memory range at
+ * <b>headers</b>, looking for a "Content-Length" header. Try to set
+ * *<b>result_out</b> to the numeric value of that header if possible.
+ * Return -1 if the header was malformed, 0 if it was missing, and 1 if
+ * it was present and well-formed.
+ */
+STATIC int
+buf_http_find_content_length(const char *headers, size_t headerlen,
+ size_t *result_out)
+{
+ const char *p, *newline;
+ char *len_str, *eos=NULL;
+ size_t remaining, result;
+ int ok;
+ *result_out = 0; /* The caller shouldn't look at this unless the
+ * return value is 1, but let's prevent confusion */
+
+#define CONTENT_LENGTH "\r\nContent-Length: "
+ p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH);
+ if (p == NULL)
+ return 0;
+
+ tor_assert(p >= headers && p < headers+headerlen);
+ remaining = (headers+headerlen)-p;
+ p += strlen(CONTENT_LENGTH);
+ remaining -= strlen(CONTENT_LENGTH);
+
+ newline = memchr(p, '\n', remaining);
+ if (newline == NULL)
+ return -1;
+
+ len_str = tor_memdup_nulterm(p, newline-p);
+ /* We limit the size to INT_MAX because other parts of the buffer.c
+ * code don't like buffers to be any bigger than that. */
+ result = (size_t) tor_parse_uint64(len_str, 10, 0, INT_MAX, &ok, &eos);
+ if (eos && !tor_strisspace(eos)) {
+ ok = 0;
+ } else {
+ *result_out = result;
+ }
+ tor_free(len_str);
+
+ return ok ? 1 : -1;
+}
+
diff --git a/src/or/proto_http.h b/src/or/proto_http.h
new file mode 100644
index 0000000000..805686070f
--- /dev/null
+++ b/src/or/proto_http.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PROTO_HTTP_H
+#define TOR_PROTO_HTTP_H
+
+struct buf_t;
+
+int fetch_from_buf_http(struct buf_t *buf,
+ char **headers_out, size_t max_headerlen,
+ char **body_out, size_t *body_used, size_t max_bodylen,
+ int force_complete);
+int peek_buf_has_http_command(const struct buf_t *buf);
+
+#ifdef PROTO_HTTP_PRIVATE
+STATIC int buf_http_find_content_length(const char *headers, size_t headerlen,
+ size_t *result_out);
+#endif
+
+#endif /* !defined(TOR_PROTO_HTTP_H) */
+
diff --git a/src/or/proto_socks.c b/src/or/proto_socks.c
new file mode 100644
index 0000000000..7649fcc4be
--- /dev/null
+++ b/src/or/proto_socks.c
@@ -0,0 +1,710 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "addressmap.h"
+#include "buffers.h"
+#include "control.h"
+#include "config.h"
+#include "ext_orport.h"
+#include "proto_socks.h"
+#include "reasons.h"
+
+static void socks_request_set_socks5_error(socks_request_t *req,
+ socks5_reply_status_t reason);
+
+static int parse_socks(const char *data, size_t datalen, socks_request_t *req,
+ int log_sockstype, int safe_socks, ssize_t *drain_out,
+ size_t *want_length_out);
+static int parse_socks_client(const uint8_t *data, size_t datalen,
+ int state, char **reason,
+ ssize_t *drain_out);
+/**
+ * Wait this many seconds before warning the user about using SOCKS unsafely
+ * again. */
+#define SOCKS_WARN_INTERVAL 5
+
+/** Warn that the user application has made an unsafe socks request using
+ * protocol <b>socks_protocol</b> on port <b>port</b>. Don't warn more than
+ * once per SOCKS_WARN_INTERVAL, unless <b>safe_socks</b> is set. */
+static void
+log_unsafe_socks_warning(int socks_protocol, const char *address,
+ uint16_t port, int safe_socks)
+{
+ static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL);
+
+ if (safe_socks) {
+ log_fn_ratelim(&socks_ratelim, LOG_WARN, LD_APP,
+ "Your application (using socks%d to port %d) is giving "
+ "Tor only an IP address. Applications that do DNS resolves "
+ "themselves may leak information. Consider using Socks4A "
+ "(e.g. via privoxy or socat) instead. For more information, "
+ "please see https://wiki.torproject.org/TheOnionRouter/"
+ "TorFAQ#SOCKSAndDNS.%s",
+ socks_protocol,
+ (int)port,
+ safe_socks ? " Rejecting." : "");
+ }
+ control_event_client_status(LOG_WARN,
+ "DANGEROUS_SOCKS PROTOCOL=SOCKS%d ADDRESS=%s:%d",
+ socks_protocol, address, (int)port);
+}
+
+/** Do not attempt to parse socks messages longer than this. This value is
+ * actually significantly higher than the longest possible socks message. */
+#define MAX_SOCKS_MESSAGE_LEN 512
+
+/** Return a new socks_request_t. */
+socks_request_t *
+socks_request_new(void)
+{
+ return tor_malloc_zero(sizeof(socks_request_t));
+}
+
+/** Free all storage held in the socks_request_t <b>req</b>. */
+void
+socks_request_free(socks_request_t *req)
+{
+ if (!req)
+ return;
+ if (req->username) {
+ memwipe(req->username, 0x10, req->usernamelen);
+ tor_free(req->username);
+ }
+ if (req->password) {
+ memwipe(req->password, 0x04, req->passwordlen);
+ tor_free(req->password);
+ }
+ memwipe(req, 0xCC, sizeof(socks_request_t));
+ tor_free(req);
+}
+
+/** There is a (possibly incomplete) socks handshake on <b>buf</b>, of one
+ * of the forms
+ * - socks4: "socksheader username\\0"
+ * - socks4a: "socksheader username\\0 destaddr\\0"
+ * - socks5 phase one: "version #methods methods"
+ * - socks5 phase two: "version command 0 addresstype..."
+ * If it's a complete and valid handshake, and destaddr fits in
+ * MAX_SOCKS_ADDR_LEN bytes, then pull the handshake off the buf,
+ * assign to <b>req</b>, and return 1.
+ *
+ * If it's invalid or too big, return -1.
+ *
+ * Else it's not all there yet, leave buf alone and return 0.
+ *
+ * If you want to specify the socks reply, write it into <b>req->reply</b>
+ * and set <b>req->replylen</b>, else leave <b>req->replylen</b> alone.
+ *
+ * If <b>log_sockstype</b> is non-zero, then do a notice-level log of whether
+ * the connection is possibly leaking DNS requests locally or not.
+ *
+ * If <b>safe_socks</b> is true, then reject unsafe socks protocols.
+ *
+ * If returning 0 or -1, <b>req->address</b> and <b>req->port</b> are
+ * undefined.
+ */
+int
+fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
+ int log_sockstype, int safe_socks)
+{
+ int res;
+ ssize_t n_drain;
+ size_t want_length = 128;
+ const char *head = NULL;
+ size_t datalen = 0;
+
+ if (buf_datalen(buf) < 2) /* version and another byte */
+ return 0;
+
+ do {
+ n_drain = 0;
+ buf_pullup(buf, want_length, &head, &datalen);
+ tor_assert(head && datalen >= 2);
+ want_length = 0;
+
+ res = parse_socks(head, datalen, req, log_sockstype,
+ safe_socks, &n_drain, &want_length);
+
+ if (n_drain < 0)
+ buf_clear(buf);
+ else if (n_drain > 0)
+ buf_drain(buf, n_drain);
+
+ } while (res == 0 && head && want_length < buf_datalen(buf) &&
+ buf_datalen(buf) >= 2);
+
+ return res;
+}
+
+/** Create a SOCKS5 reply message with <b>reason</b> in its REP field and
+ * have Tor send it as error response to <b>req</b>.
+ */
+static void
+socks_request_set_socks5_error(socks_request_t *req,
+ socks5_reply_status_t reason)
+{
+ req->replylen = 10;
+ memset(req->reply,0,10);
+
+ req->reply[0] = 0x05; // VER field.
+ req->reply[1] = reason; // REP field.
+ req->reply[3] = 0x01; // ATYP field.
+}
+
+static const char SOCKS_PROXY_IS_NOT_AN_HTTP_PROXY_MSG[] =
+ "HTTP/1.0 501 Tor is not an HTTP Proxy\r\n"
+ "Content-Type: text/html; charset=iso-8859-1\r\n\r\n"
+ "<html>\n"
+ "<head>\n"
+ "<title>This is a SOCKS Proxy, Not An HTTP Proxy</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<h1>This is a SOCKs proxy, not an HTTP proxy.</h1>\n"
+ "<p>\n"
+ "It appears you have configured your web browser to use this Tor port as\n"
+ "an HTTP proxy.\n"
+ "</p><p>\n"
+ "This is not correct: This port is configured as a SOCKS proxy, not\n"
+ "an HTTP proxy. If you need an HTTP proxy tunnel, use the HTTPTunnelPort\n"
+ "configuration option in place of, or in addition to, SOCKSPort.\n"
+ "Please configure your client accordingly.\n"
+ "</p>\n"
+ "<p>\n"
+ "See <a href=\"https://www.torproject.org/documentation.html\">"
+ "https://www.torproject.org/documentation.html</a> for more "
+ "information.\n"
+ "</p>\n"
+ "</body>\n"
+ "</html>\n";
+
+/** Implementation helper to implement fetch_from_*_socks. Instead of looking
+ * at a buffer's contents, we look at the <b>datalen</b> bytes of data in
+ * <b>data</b>. Instead of removing data from the buffer, we set
+ * <b>drain_out</b> to the amount of data that should be removed (or -1 if the
+ * buffer should be cleared). Instead of pulling more data into the first
+ * chunk of the buffer, we set *<b>want_length_out</b> to the number of bytes
+ * we'd like to see in the input buffer, if they're available. */
+static int
+parse_socks(const char *data, size_t datalen, socks_request_t *req,
+ int log_sockstype, int safe_socks, ssize_t *drain_out,
+ size_t *want_length_out)
+{
+ unsigned int len;
+ char tmpbuf[TOR_ADDR_BUF_LEN+1];
+ tor_addr_t destaddr;
+ uint32_t destip;
+ uint8_t socksver;
+ char *next, *startaddr;
+ unsigned char usernamelen, passlen;
+ struct in_addr in;
+
+ if (datalen < 2) {
+ /* We always need at least 2 bytes. */
+ *want_length_out = 2;
+ return 0;
+ }
+
+ if (req->socks_version == 5 && !req->got_auth) {
+ /* See if we have received authentication. Strictly speaking, we should
+ also check whether we actually negotiated username/password
+ authentication. But some broken clients will send us authentication
+ even if we negotiated SOCKS_NO_AUTH. */
+ if (*data == 1) { /* username/pass version 1 */
+ /* Format is: authversion [1 byte] == 1
+ usernamelen [1 byte]
+ username [usernamelen bytes]
+ passlen [1 byte]
+ password [passlen bytes] */
+ usernamelen = (unsigned char)*(data + 1);
+ if (datalen < 2u + usernamelen + 1u) {
+ *want_length_out = 2u + usernamelen + 1u;
+ return 0;
+ }
+ passlen = (unsigned char)*(data + 2u + usernamelen);
+ if (datalen < 2u + usernamelen + 1u + passlen) {
+ *want_length_out = 2u + usernamelen + 1u + passlen;
+ return 0;
+ }
+ req->replylen = 2; /* 2 bytes of response */
+ req->reply[0] = 1; /* authversion == 1 */
+ req->reply[1] = 0; /* authentication successful */
+ log_debug(LD_APP,
+ "socks5: Accepted username/password without checking.");
+ if (usernamelen) {
+ req->username = tor_memdup(data+2u, usernamelen);
+ req->usernamelen = usernamelen;
+ }
+ if (passlen) {
+ req->password = tor_memdup(data+3u+usernamelen, passlen);
+ req->passwordlen = passlen;
+ }
+ *drain_out = 2u + usernamelen + 1u + passlen;
+ req->got_auth = 1;
+ *want_length_out = 7; /* Minimal socks5 command. */
+ return 0;
+ } else if (req->auth_type == SOCKS_USER_PASS) {
+ /* unknown version byte */
+ log_warn(LD_APP, "Socks5 username/password version %d not recognized; "
+ "rejecting.", (int)*data);
+ return -1;
+ }
+ }
+
+ socksver = *data;
+
+ switch (socksver) { /* which version of socks? */
+ case 5: /* socks5 */
+
+ if (req->socks_version != 5) { /* we need to negotiate a method */
+ unsigned char nummethods = (unsigned char)*(data+1);
+ int have_user_pass, have_no_auth;
+ int r=0;
+ tor_assert(!req->socks_version);
+ if (datalen < 2u+nummethods) {
+ *want_length_out = 2u+nummethods;
+ return 0;
+ }
+ if (!nummethods)
+ return -1;
+ req->replylen = 2; /* 2 bytes of response */
+ req->reply[0] = 5; /* socks5 reply */
+ have_user_pass = (memchr(data+2, SOCKS_USER_PASS, nummethods) !=NULL);
+ have_no_auth = (memchr(data+2, SOCKS_NO_AUTH, nummethods) !=NULL);
+ if (have_user_pass && !(have_no_auth && req->socks_prefer_no_auth)) {
+ req->auth_type = SOCKS_USER_PASS;
+ req->reply[1] = SOCKS_USER_PASS; /* tell client to use "user/pass"
+ auth method */
+ req->socks_version = 5; /* remember we've already negotiated auth */
+ log_debug(LD_APP,"socks5: accepted method 2 (username/password)");
+ r=0;
+ } else if (have_no_auth) {
+ req->reply[1] = SOCKS_NO_AUTH; /* tell client to use "none" auth
+ method */
+ req->socks_version = 5; /* remember we've already negotiated auth */
+ log_debug(LD_APP,"socks5: accepted method 0 (no authentication)");
+ r=0;
+ } else {
+ log_warn(LD_APP,
+ "socks5: offered methods don't include 'no auth' or "
+ "username/password. Rejecting.");
+ req->reply[1] = '\xFF'; /* reject all methods */
+ r=-1;
+ }
+ /* Remove packet from buf. Some SOCKS clients will have sent extra
+ * junk at this point; let's hope it's an authentication message. */
+ *drain_out = 2u + nummethods;
+
+ return r;
+ }
+ if (req->auth_type != SOCKS_NO_AUTH && !req->got_auth) {
+ log_warn(LD_APP,
+ "socks5: negotiated authentication, but none provided");
+ return -1;
+ }
+ /* we know the method; read in the request */
+ log_debug(LD_APP,"socks5: checking request");
+ if (datalen < 7) {/* basic info plus >=1 for addr plus 2 for port */
+ *want_length_out = 7;
+ return 0; /* not yet */
+ }
+ req->command = (unsigned char) *(data+1);
+ if (req->command != SOCKS_COMMAND_CONNECT &&
+ req->command != SOCKS_COMMAND_RESOLVE &&
+ req->command != SOCKS_COMMAND_RESOLVE_PTR) {
+ /* not a connect or resolve or a resolve_ptr? we don't support it. */
+ socks_request_set_socks5_error(req,SOCKS5_COMMAND_NOT_SUPPORTED);
+
+ log_warn(LD_APP,"socks5: command %d not recognized. Rejecting.",
+ req->command);
+ return -1;
+ }
+ switch (*(data+3)) { /* address type */
+ case 1: /* IPv4 address */
+ case 4: /* IPv6 address */ {
+ const int is_v6 = *(data+3) == 4;
+ const unsigned addrlen = is_v6 ? 16 : 4;
+ log_debug(LD_APP,"socks5: ipv4 address type");
+ if (datalen < 6+addrlen) {/* ip/port there? */
+ *want_length_out = 6+addrlen;
+ return 0; /* not yet */
+ }
+
+ if (is_v6)
+ tor_addr_from_ipv6_bytes(&destaddr, data+4);
+ else
+ tor_addr_from_ipv4n(&destaddr, get_uint32(data+4));
+
+ tor_addr_to_str(tmpbuf, &destaddr, sizeof(tmpbuf), 1);
+
+ if (BUG(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN)) {
+ /* LCOV_EXCL_START -- This branch is unreachable, given the
+ * size of tmpbuf and the actual value of MAX_SOCKS_ADDR_LEN */
+ socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
+ log_warn(LD_APP,
+ "socks5 IP takes %d bytes, which doesn't fit in %d. "
+ "Rejecting.",
+ (int)strlen(tmpbuf)+1,(int)MAX_SOCKS_ADDR_LEN);
+ return -1;
+ /* LCOV_EXCL_STOP */
+ }
+ strlcpy(req->address,tmpbuf,sizeof(req->address));
+ req->port = ntohs(get_uint16(data+4+addrlen));
+ *drain_out = 6+addrlen;
+ if (req->command != SOCKS_COMMAND_RESOLVE_PTR &&
+ !addressmap_have_mapping(req->address,0)) {
+ log_unsafe_socks_warning(5, req->address, req->port, safe_socks);
+ if (safe_socks) {
+ socks_request_set_socks5_error(req, SOCKS5_NOT_ALLOWED);
+ return -1;
+ }
+ }
+ return 1;
+ }
+ case 3: /* fqdn */
+ log_debug(LD_APP,"socks5: fqdn address type");
+ if (req->command == SOCKS_COMMAND_RESOLVE_PTR) {
+ socks_request_set_socks5_error(req,
+ SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED);
+ log_warn(LD_APP, "socks5 received RESOLVE_PTR command with "
+ "hostname type. Rejecting.");
+ return -1;
+ }
+ len = (unsigned char)*(data+4);
+ if (datalen < 7+len) { /* addr/port there? */
+ *want_length_out = 7+len;
+ return 0; /* not yet */
+ }
+ if (BUG(len+1 > MAX_SOCKS_ADDR_LEN)) {
+ /* LCOV_EXCL_START -- unreachable, since len is at most 255,
+ * and MAX_SOCKS_ADDR_LEN is 256. */
+ socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
+ log_warn(LD_APP,
+ "socks5 hostname is %d bytes, which doesn't fit in "
+ "%d. Rejecting.", len+1,MAX_SOCKS_ADDR_LEN);
+ return -1;
+ /* LCOV_EXCL_STOP */
+ }
+ memcpy(req->address,data+5,len);
+ req->address[len] = 0;
+ req->port = ntohs(get_uint16(data+5+len));
+ *drain_out = 5+len+2;
+
+ if (!string_is_valid_hostname(req->address)) {
+ socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR);
+
+ log_warn(LD_PROTOCOL,
+ "Your application (using socks5 to port %d) gave Tor "
+ "a malformed hostname: %s. Rejecting the connection.",
+ req->port, escaped_safe_str_client(req->address));
+ return -1;
+ }
+ if (log_sockstype)
+ log_notice(LD_APP,
+ "Your application (using socks5 to port %d) instructed "
+ "Tor to take care of the DNS resolution itself if "
+ "necessary. This is good.", req->port);
+ return 1;
+ default: /* unsupported */
+ socks_request_set_socks5_error(req,
+ SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED);
+ log_warn(LD_APP,"socks5: unsupported address type %d. Rejecting.",
+ (int) *(data+3));
+ return -1;
+ }
+ tor_assert(0);
+ break;
+ case 4: { /* socks4 */
+ enum {socks4, socks4a} socks4_prot = socks4a;
+ const char *authstart, *authend;
+ /* http://ss5.sourceforge.net/socks4.protocol.txt */
+ /* http://ss5.sourceforge.net/socks4A.protocol.txt */
+
+ req->socks_version = 4;
+ if (datalen < SOCKS4_NETWORK_LEN) {/* basic info available? */
+ *want_length_out = SOCKS4_NETWORK_LEN;
+ return 0; /* not yet */
+ }
+ // buf_pullup(buf, 1280);
+ req->command = (unsigned char) *(data+1);
+ if (req->command != SOCKS_COMMAND_CONNECT &&
+ req->command != SOCKS_COMMAND_RESOLVE) {
+ /* not a connect or resolve? we don't support it. (No resolve_ptr with
+ * socks4.) */
+ log_warn(LD_APP,"socks4: command %d not recognized. Rejecting.",
+ req->command);
+ return -1;
+ }
+
+ req->port = ntohs(get_uint16(data+2));
+ destip = ntohl(get_uint32(data+4));
+ if ((!req->port && req->command!=SOCKS_COMMAND_RESOLVE) || !destip) {
+ log_warn(LD_APP,"socks4: Port or DestIP is zero. Rejecting.");
+ return -1;
+ }
+ if (destip >> 8) {
+ log_debug(LD_APP,"socks4: destip not in form 0.0.0.x.");
+ in.s_addr = htonl(destip);
+ tor_inet_ntoa(&in,tmpbuf,sizeof(tmpbuf));
+ if (BUG(strlen(tmpbuf)+1 > MAX_SOCKS_ADDR_LEN)) {
+ /* LCOV_EXCL_START -- This branch is unreachable, given the
+ * size of tmpbuf and the actual value of MAX_SOCKS_ADDR_LEN */
+ log_debug(LD_APP,"socks4 addr (%d bytes) too long. Rejecting.",
+ (int)strlen(tmpbuf));
+ return -1;
+ /* LCOV_EXCL_STOP */
+ }
+ log_debug(LD_APP,
+ "socks4: successfully read destip (%s)",
+ safe_str_client(tmpbuf));
+ socks4_prot = socks4;
+ }
+
+ authstart = data + SOCKS4_NETWORK_LEN;
+ next = memchr(authstart, 0,
+ datalen-SOCKS4_NETWORK_LEN);
+ if (!next) {
+ if (datalen >= 1024) {
+ log_debug(LD_APP, "Socks4 user name too long; rejecting.");
+ return -1;
+ }
+ log_debug(LD_APP,"socks4: Username not here yet.");
+ *want_length_out = datalen+1024; /* More than we need, but safe */
+ return 0;
+ }
+ authend = next;
+ tor_assert(next < data+datalen);
+
+ startaddr = NULL;
+ if (socks4_prot != socks4a &&
+ !addressmap_have_mapping(tmpbuf,0)) {
+ log_unsafe_socks_warning(4, tmpbuf, req->port, safe_socks);
+
+ if (safe_socks)
+ return -1;
+ }
+ if (socks4_prot == socks4a) {
+ if (next+1 == data+datalen) {
+ log_debug(LD_APP,"socks4: No part of destaddr here yet.");
+ *want_length_out = datalen + 1024; /* More than we need, but safe */
+ return 0;
+ }
+ startaddr = next+1;
+ next = memchr(startaddr, 0, data + datalen - startaddr);
+ if (!next) {
+ if (datalen >= 1024) {
+ log_debug(LD_APP,"socks4: Destaddr too long.");
+ return -1;
+ }
+ log_debug(LD_APP,"socks4: Destaddr not all here yet.");
+ *want_length_out = datalen + 1024; /* More than we need, but safe */
+ return 0;
+ }
+ if (MAX_SOCKS_ADDR_LEN <= next-startaddr) {
+ log_warn(LD_APP,"socks4: Destaddr too long. Rejecting.");
+ return -1;
+ }
+ // tor_assert(next < buf->cur+buf_datalen(buf));
+
+ if (log_sockstype)
+ log_notice(LD_APP,
+ "Your application (using socks4a to port %d) instructed "
+ "Tor to take care of the DNS resolution itself if "
+ "necessary. This is good.", req->port);
+ }
+ log_debug(LD_APP,"socks4: Everything is here. Success.");
+ strlcpy(req->address, startaddr ? startaddr : tmpbuf,
+ sizeof(req->address));
+ if (!string_is_valid_hostname(req->address)) {
+ log_warn(LD_PROTOCOL,
+ "Your application (using socks4 to port %d) gave Tor "
+ "a malformed hostname: %s. Rejecting the connection.",
+ req->port, escaped_safe_str_client(req->address));
+ return -1;
+ }
+ if (authend != authstart) {
+ req->got_auth = 1;
+ req->usernamelen = authend - authstart;
+ req->username = tor_memdup(authstart, authend - authstart);
+ }
+ /* next points to the final \0 on inbuf */
+ *drain_out = next - data + 1;
+ return 1;
+ }
+ case 'G': /* get */
+ case 'H': /* head */
+ case 'P': /* put/post */
+ case 'C': /* connect */
+ strlcpy((char*)req->reply, SOCKS_PROXY_IS_NOT_AN_HTTP_PROXY_MSG,
+ MAX_SOCKS_REPLY_LEN);
+ req->replylen = strlen((char*)req->reply)+1;
+ /* fall through */
+ default: /* version is not socks4 or socks5 */
+ log_warn(LD_APP,
+ "Socks version %d not recognized. (This port is not an "
+ "HTTP proxy; did you want to use HTTPTunnelPort?)",
+ *(data));
+ {
+ /* Tell the controller the first 8 bytes. */
+ char *tmp = tor_strndup(data, datalen < 8 ? datalen : 8);
+ control_event_client_status(LOG_WARN,
+ "SOCKS_UNKNOWN_PROTOCOL DATA=\"%s\"",
+ escaped(tmp));
+ tor_free(tmp);
+ }
+ return -1;
+ }
+}
+
+/** Inspect a reply from SOCKS server stored in <b>buf</b> according
+ * to <b>state</b>, removing the protocol data upon success. Return 0 on
+ * incomplete response, 1 on success and -1 on error, in which case
+ * <b>reason</b> is set to a descriptive message (free() when finished
+ * with it).
+ *
+ * As a special case, 2 is returned when user/pass is required
+ * during SOCKS5 handshake and user/pass is configured.
+ */
+int
+fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
+{
+ ssize_t drain = 0;
+ int r;
+ const char *head = NULL;
+ size_t datalen = 0;
+
+ if (buf_datalen(buf) < 2)
+ return 0;
+
+ buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, &head, &datalen);
+ tor_assert(head && datalen >= 2);
+
+ r = parse_socks_client((uint8_t*)head, datalen,
+ state, reason, &drain);
+ if (drain > 0)
+ buf_drain(buf, drain);
+ else if (drain < 0)
+ buf_clear(buf);
+
+ return r;
+}
+
+/** Implementation logic for fetch_from_*_socks_client. */
+static int
+parse_socks_client(const uint8_t *data, size_t datalen,
+ int state, char **reason,
+ ssize_t *drain_out)
+{
+ unsigned int addrlen;
+ *drain_out = 0;
+ if (datalen < 2)
+ return 0;
+
+ switch (state) {
+ case PROXY_SOCKS4_WANT_CONNECT_OK:
+ /* Wait for the complete response */
+ if (datalen < 8)
+ return 0;
+
+ if (data[1] != 0x5a) {
+ *reason = tor_strdup(socks4_response_code_to_string(data[1]));
+ return -1;
+ }
+
+ /* Success */
+ *drain_out = 8;
+ return 1;
+
+ case PROXY_SOCKS5_WANT_AUTH_METHOD_NONE:
+ /* we don't have any credentials */
+ if (data[1] != 0x00) {
+ *reason = tor_strdup("server doesn't support any of our "
+ "available authentication methods");
+ return -1;
+ }
+
+ log_info(LD_NET, "SOCKS 5 client: continuing without authentication");
+ *drain_out = -1;
+ return 1;
+
+ case PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929:
+ /* we have a username and password. return 1 if we can proceed without
+ * providing authentication, or 2 otherwise. */
+ switch (data[1]) {
+ case 0x00:
+ log_info(LD_NET, "SOCKS 5 client: we have auth details but server "
+ "doesn't require authentication.");
+ *drain_out = -1;
+ return 1;
+ case 0x02:
+ log_info(LD_NET, "SOCKS 5 client: need authentication.");
+ *drain_out = -1;
+ return 2;
+ /* fall through */
+ }
+
+ *reason = tor_strdup("server doesn't support any of our available "
+ "authentication methods");
+ return -1;
+
+ case PROXY_SOCKS5_WANT_AUTH_RFC1929_OK:
+ /* handle server reply to rfc1929 authentication */
+ if (data[1] != 0x00) {
+ *reason = tor_strdup("authentication failed");
+ return -1;
+ }
+
+ log_info(LD_NET, "SOCKS 5 client: authentication successful.");
+ *drain_out = -1;
+ return 1;
+
+ case PROXY_SOCKS5_WANT_CONNECT_OK:
+ /* response is variable length. BND.ADDR, etc, isn't needed
+ * (don't bother with buf_pullup()), but make sure to eat all
+ * the data used */
+
+ /* wait for address type field to arrive */
+ if (datalen < 4)
+ return 0;
+
+ switch (data[3]) {
+ case 0x01: /* ip4 */
+ addrlen = 4;
+ break;
+ case 0x04: /* ip6 */
+ addrlen = 16;
+ break;
+ case 0x03: /* fqdn (can this happen here?) */
+ if (datalen < 5)
+ return 0;
+ addrlen = 1 + data[4];
+ break;
+ default:
+ *reason = tor_strdup("invalid response to connect request");
+ return -1;
+ }
+
+ /* wait for address and port */
+ if (datalen < 6 + addrlen)
+ return 0;
+
+ if (data[1] != 0x00) {
+ *reason = tor_strdup(socks5_response_code_to_string(data[1]));
+ return -1;
+ }
+
+ *drain_out = 6 + addrlen;
+ return 1;
+ }
+
+ /* LCOV_EXCL_START */
+ /* shouldn't get here if the input state is one we know about... */
+ tor_assert(0);
+
+ return -1;
+ /* LCOV_EXCL_STOP */
+}
+
diff --git a/src/or/proto_socks.h b/src/or/proto_socks.h
new file mode 100644
index 0000000000..a714151414
--- /dev/null
+++ b/src/or/proto_socks.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_PROTO_SOCKS_H
+#define TOR_PROTO_SOCKS_H
+
+struct socks_request_t;
+struct buf_t;
+
+struct socks_request_t *socks_request_new(void);
+void socks_request_free(struct socks_request_t *req);
+int fetch_from_buf_socks(struct buf_t *buf, socks_request_t *req,
+ int log_sockstype, int safe_socks);
+int fetch_from_buf_socks_client(buf_t *buf, int state, char **reason);
+
+#endif /* !defined(TOR_PROTO_SOCKS_H) */
+
diff --git a/src/or/protover.h b/src/or/protover.h
index 22667bed79..657977279e 100644
--- a/src/or/protover.h
+++ b/src/or/protover.h
@@ -17,6 +17,13 @@
/* This is a guess. */
#define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha"
+/** The protover version number that signifies HSDir support for HSv3 */
+#define PROTOVER_HSDIR_V3 2
+/** The protover version number that signifies HSv3 intro point support */
+#define PROTOVER_HS_INTRO_V3 4
+/** The protover version number that signifies HSv3 rendezvous point support */
+#define PROTOVER_HS_RENDEZVOUS_POINT_V3 2
+
/** List of recognized subprotocols. */
typedef enum protocol_type_t {
PRT_LINK,
@@ -68,7 +75,7 @@ STATIC void proto_entry_free(proto_entry_t *entry);
STATIC char *encode_protocol_list(const smartlist_t *sl);
STATIC const char *protocol_type_to_str(protocol_type_t pr);
STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out);
-#endif
+#endif /* defined(PROTOVER_PRIVATE) */
-#endif
+#endif /* !defined(TOR_PROTOVER_H) */
diff --git a/src/or/reasons.c b/src/or/reasons.c
index e6c325f1b3..03d49418da 100644
--- a/src/or/reasons.c
+++ b/src/or/reasons.c
@@ -45,6 +45,8 @@ stream_end_reason_to_control_string(int reason)
case END_STREAM_REASON_CANT_ATTACH: return "CANT_ATTACH";
case END_STREAM_REASON_NET_UNREACHABLE: return "NET_UNREACHABLE";
case END_STREAM_REASON_SOCKSPROTOCOL: return "SOCKS_PROTOCOL";
+ // XXXX Controlspec
+ case END_STREAM_REASON_HTTPPROTOCOL: return "HTTP_PROTOCOL";
case END_STREAM_REASON_PRIVATE_ADDR: return "PRIVATE_ADDR";
@@ -138,6 +140,11 @@ stream_end_reason_to_socks5_response(int reason)
return SOCKS5_NET_UNREACHABLE;
case END_STREAM_REASON_SOCKSPROTOCOL:
return SOCKS5_GENERAL_ERROR;
+ case END_STREAM_REASON_HTTPPROTOCOL:
+ // LCOV_EXCL_START
+ tor_assert_nonfatal_unreached();
+ return SOCKS5_GENERAL_ERROR;
+ // LCOV_EXCL_STOP
case END_STREAM_REASON_PRIVATE_ADDR:
return SOCKS5_GENERAL_ERROR;
@@ -160,7 +167,7 @@ stream_end_reason_to_socks5_response(int reason)
#else
#define E_CASE(s) case s
#define S_CASE(s) case s
-#endif
+#endif /* defined(_WIN32) */
/** Given an errno from a failed exit connection, return a reason code
* appropriate for use in a RELAY END cell. */
@@ -442,3 +449,48 @@ bandwidth_weight_rule_to_string(bandwidth_weight_rule_t rule)
}
}
+/** Given a RELAY_END reason value, convert it to an HTTP response to be
+ * send over an HTTP tunnel connection. */
+const char *
+end_reason_to_http_connect_response_line(int endreason)
+{
+ endreason &= END_STREAM_REASON_MASK;
+ /* XXXX these are probably all wrong. Should they all be 502? */
+ switch (endreason) {
+ case 0:
+ return "HTTP/1.0 200 OK\r\n\r\n";
+ case END_STREAM_REASON_MISC:
+ return "HTTP/1.0 500 Internal Server Error\r\n\r\n";
+ case END_STREAM_REASON_RESOLVEFAILED:
+ return "HTTP/1.0 404 Not Found (resolve failed)\r\n\r\n";
+ case END_STREAM_REASON_NOROUTE:
+ return "HTTP/1.0 404 Not Found (no route)\r\n\r\n";
+ case END_STREAM_REASON_CONNECTREFUSED:
+ return "HTTP/1.0 403 Forbidden (connection refused)\r\n\r\n";
+ case END_STREAM_REASON_EXITPOLICY:
+ return "HTTP/1.0 403 Forbidden (exit policy)\r\n\r\n";
+ case END_STREAM_REASON_DESTROY:
+ return "HTTP/1.0 502 Bad Gateway (destroy cell received)\r\n\r\n";
+ case END_STREAM_REASON_DONE:
+ return "HTTP/1.0 502 Bad Gateway (unexpected close)\r\n\r\n";
+ case END_STREAM_REASON_TIMEOUT:
+ return "HTTP/1.0 504 Gateway Timeout\r\n\r\n";
+ case END_STREAM_REASON_HIBERNATING:
+ return "HTTP/1.0 502 Bad Gateway (hibernating server)\r\n\r\n";
+ case END_STREAM_REASON_INTERNAL:
+ return "HTTP/1.0 502 Bad Gateway (internal error)\r\n\r\n";
+ case END_STREAM_REASON_RESOURCELIMIT:
+ return "HTTP/1.0 502 Bad Gateway (resource limit)\r\n\r\n";
+ case END_STREAM_REASON_CONNRESET:
+ return "HTTP/1.0 403 Forbidden (connection reset)\r\n\r\n";
+ case END_STREAM_REASON_TORPROTOCOL:
+ return "HTTP/1.0 502 Bad Gateway (tor protocol violation)\r\n\r\n";
+ case END_STREAM_REASON_ENTRYPOLICY:
+ return "HTTP/1.0 403 Forbidden (entry policy violation)\r\n\r\n";
+ case END_STREAM_REASON_NOTDIRECTORY: /* Fall Through */
+ default:
+ tor_assert_nonfatal_unreached();
+ return "HTTP/1.0 500 Internal Server Error (weird end reason)\r\n\r\n";
+ }
+}
+
diff --git a/src/or/reasons.h b/src/or/reasons.h
index 1cadf4e89e..3d6ba8fc83 100644
--- a/src/or/reasons.h
+++ b/src/or/reasons.h
@@ -26,6 +26,7 @@ const char *socks4_response_code_to_string(uint8_t code);
const char *socks5_response_code_to_string(uint8_t code);
const char *bandwidth_weight_rule_to_string(enum bandwidth_weight_rule_t rule);
+const char *end_reason_to_http_connect_response_line(int endreason);
-#endif
+#endif /* !defined(TOR_REASONS_H) */
diff --git a/src/or/relay.c b/src/or/relay.c
index 2451f45c79..defbf63b79 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -185,18 +185,12 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell)
/** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b>
* (in place).
*
- * If <b>encrypt_mode</b> is 1 then encrypt, else decrypt.
- *
- * Returns 0.
+ * Note that we use the same operation for encrypting and for decrypting.
*/
-static int
-relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in,
- int encrypt_mode)
+static void
+relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in)
{
- (void)encrypt_mode;
crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
-
- return 0;
}
/**
@@ -450,8 +444,8 @@ relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
do { /* Remember: cpath is in forward order, that is, first hop first. */
tor_assert(thishop);
- if (relay_crypt_one_payload(thishop->b_crypto, cell->payload, 0) < 0)
- return -1;
+ /* decrypt one layer */
+ relay_crypt_one_payload(thishop->b_crypto, cell->payload);
relay_header_unpack(&rh, cell->payload);
if (rh.recognized == 0) {
@@ -468,19 +462,14 @@ relay_crypt(circuit_t *circ, cell_t *cell, cell_direction_t cell_direction,
log_fn(LOG_PROTOCOL_WARN, LD_OR,
"Incoming cell at client not recognized. Closing.");
return -1;
- } else { /* we're in the middle. Just one crypt. */
- if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->p_crypto,
- cell->payload, 1) < 0)
- return -1;
-// log_fn(LOG_DEBUG,"Skipping recognized check, because we're not "
-// "the client.");
+ } else {
+ /* We're in the middle. Encrypt one layer. */
+ relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->p_crypto, cell->payload);
}
} else /* cell_direction == CELL_DIRECTION_OUT */ {
- /* we're in the middle. Just one crypt. */
+ /* We're in the middle. Decrypt one layer. */
- if (relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->n_crypto,
- cell->payload, 0) < 0)
- return -1;
+ relay_crypt_one_payload(TO_OR_CIRCUIT(circ)->n_crypto, cell->payload);
relay_header_unpack(&rh, cell->payload);
if (rh.recognized == 0) {
@@ -516,7 +505,15 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
chan = circ->n_chan;
if (!chan) {
log_warn(LD_BUG,"outgoing relay cell sent from %s:%d has n_chan==NULL."
- " Dropping.", filename, lineno);
+ " Dropping. Circuit is in state %s (%d), and is "
+ "%smarked for close. (%s:%d, %d)", filename, lineno,
+ circuit_state_to_string(circ->state), circ->state,
+ circ->marked_for_close ? "" : "not ",
+ circ->marked_for_close_file?circ->marked_for_close_file:"",
+ circ->marked_for_close, circ->marked_for_close_reason);
+ if (CIRCUIT_IS_ORIGIN(circ)) {
+ circuit_log_path(LOG_WARN, LD_BUG, TO_ORIGIN_CIRCUIT(circ));
+ }
log_backtrace(LOG_WARN,LD_BUG,"");
return 0; /* just drop it */
}
@@ -533,11 +530,8 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
/* moving from farthest to nearest hop */
do {
tor_assert(thishop);
- /* XXXX RD This is a bug, right? */
- log_debug(LD_OR,"crypting a layer of the relay cell.");
- if (relay_crypt_one_payload(thishop->f_crypto, cell->payload, 1) < 0) {
- return -1;
- }
+ log_debug(LD_OR,"encrypting a layer of the relay cell.");
+ relay_crypt_one_payload(thishop->f_crypto, cell->payload);
thishop = thishop->prev;
} while (thishop != TO_ORIGIN_CIRCUIT(circ)->cpath->prev);
@@ -554,8 +548,8 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
or_circ = TO_OR_CIRCUIT(circ);
chan = or_circ->p_chan;
relay_set_digest(or_circ->p_digest, cell);
- if (relay_crypt_one_payload(or_circ->p_crypto, cell->payload, 1) < 0)
- return -1;
+ /* encrypt one layer */
+ relay_crypt_one_payload(or_circ->p_crypto, cell->payload);
}
++stats_n_relay_cells_relayed;
@@ -843,7 +837,7 @@ connection_edge_send_command(edge_connection_t *fromconn,
if (linked_conn && linked_conn->type == CONN_TYPE_DIR) {
++(TO_DIR_CONN(linked_conn)->data_cells_sent);
}
-#endif
+#endif /* defined(MEASUREMENTS_21206) */
return relay_send_command_from_edge(fromconn->stream_id, circ,
relay_command, payload,
@@ -1496,8 +1490,9 @@ connection_edge_process_relay_cell_not_open(
circuit_log_path(LOG_INFO,LD_APP,TO_ORIGIN_CIRCUIT(circ));
/* don't send a socks reply to transparent conns */
tor_assert(entry_conn->socks_request != NULL);
- if (!entry_conn->socks_request->has_finished)
+ if (!entry_conn->socks_request->has_finished) {
connection_ap_handshake_socks_reply(entry_conn, NULL, 0, 0);
+ }
/* Was it a linked dir conn? If so, a dir request just started to
* fetch something; this could be a bootstrap status milestone. */
@@ -1699,7 +1694,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
}
stats_n_data_bytes_received += rh.length;
- connection_write_to_buf((char*)(cell->payload + RELAY_HEADER_SIZE),
+ connection_buf_add((char*)(cell->payload + RELAY_HEADER_SIZE),
rh.length, TO_CONN(conn));
#ifdef MEASUREMENTS_21206
@@ -1710,7 +1705,7 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ,
if (linked_conn && linked_conn->type == CONN_TYPE_DIR) {
++(TO_DIR_CONN(linked_conn)->data_cells_received);
}
-#endif
+#endif /* defined(MEASUREMENTS_21206) */
if (!optimistic_data) {
/* Only send a SENDME if we're not getting optimistic data; otherwise
@@ -2067,13 +2062,13 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
/* XXXX We could be more efficient here by sometimes packing
* previously-sent optimistic data in the same cell with data
* from the inbuf. */
- fetch_from_buf(payload, length, entry_conn->sending_optimistic_data);
+ buf_get_bytes(entry_conn->sending_optimistic_data, payload, length);
if (!buf_datalen(entry_conn->sending_optimistic_data)) {
buf_free(entry_conn->sending_optimistic_data);
entry_conn->sending_optimistic_data = NULL;
}
} else {
- connection_fetch_from_buf(payload, length, TO_CONN(conn));
+ connection_buf_get_bytes(payload, length, TO_CONN(conn));
}
log_debug(domain,TOR_SOCKET_T_FORMAT": Packaging %d bytes (%d waiting).",
@@ -2085,7 +2080,7 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
retry */
if (!entry_conn->pending_optimistic_data)
entry_conn->pending_optimistic_data = buf_new();
- write_to_buf(payload, length, entry_conn->pending_optimistic_data);
+ buf_add(entry_conn->pending_optimistic_data, payload, length);
}
if (connection_edge_send_command(conn, RELAY_COMMAND_DATA,
@@ -2408,7 +2403,7 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
assert_circuit_mux_okay(chan)
#else
#define assert_cmux_ok_paranoid(chan)
-#endif
+#endif /* defined(ACTIVE_CIRCUITS_PARANOIA) */
/** The total number of cells we have allocated. */
static size_t total_cells_allocated = 0;
@@ -2961,7 +2956,7 @@ get_max_middle_cells(void)
{
return ORCIRC_MAX_MIDDLE_CELLS;
}
-#endif
+#endif /* 0 */
/** Add <b>cell</b> to the queue of <b>circ</b> writing to <b>chan</b>
* transmitting in <b>direction</b>. */
@@ -3071,7 +3066,7 @@ append_cell_to_circuit_queue(circuit_t *circ, channel_t *chan,
}
}
}
-#endif
+#endif /* 0 */
cell_queue_append_packed_copy(circ, queue, exitward, cell,
chan->wide_circ_ids, 1);
diff --git a/src/or/relay.h b/src/or/relay.h
index 9dc0b5d3a2..4cc1a0fbdb 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -112,7 +112,7 @@ STATIC packed_cell_t *cell_queue_pop(cell_queue_t *queue);
STATIC destroy_cell_t *destroy_cell_queue_pop(destroy_cell_queue_t *queue);
STATIC size_t cell_queues_get_total_allocation(void);
STATIC int cell_queues_check_size(void);
-#endif
+#endif /* defined(RELAY_PRIVATE) */
-#endif
+#endif /* !defined(TOR_RELAY_H) */
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index 11b60b36a1..b98b2bccfa 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -303,7 +303,7 @@ void
rend_cache_purge(void)
{
if (rend_cache) {
- log_info(LD_REND, "Purging HS descriptor cache");
+ log_info(LD_REND, "Purging HS v2 descriptor cache");
strmap_free(rend_cache, rend_cache_entry_free_);
}
rend_cache = strmap_new();
@@ -315,7 +315,7 @@ void
rend_cache_failure_purge(void)
{
if (rend_cache_failure) {
- log_info(LD_REND, "Purging HS failure cache");
+ log_info(LD_REND, "Purging HS v2 failure cache");
strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
}
rend_cache_failure = strmap_new();
@@ -512,7 +512,7 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
tor_assert(rend_cache);
tor_assert(query);
- if (!rend_valid_service_id(query)) {
+ if (!rend_valid_v2_service_id(query)) {
ret = -EINVAL;
goto end;
}
@@ -558,7 +558,7 @@ rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e)
tor_assert(rend_cache_local_service);
tor_assert(query);
- if (!rend_valid_service_id(query)) {
+ if (!rend_valid_v2_service_id(query)) {
ret = -EINVAL;
goto end;
}
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 1bd3be2243..5b13eadfa1 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -115,8 +115,8 @@ extern strmap_t *rend_cache;
extern strmap_t *rend_cache_failure;
extern digestmap_t *rend_cache_v2_dir;
extern size_t rend_cache_total_allocation;
-#endif
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
+#endif /* defined(RENDCACHE_PRIVATE) */
-#endif /* TOR_RENDCACHE_H */
+#endif /* !defined(TOR_RENDCACHE_H) */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 9e2daf0380..3274819241 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -17,6 +17,8 @@
#include "connection_edge.h"
#include "directory.h"
#include "hs_common.h"
+#include "hs_circuit.h"
+#include "hs_client.h"
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
@@ -41,7 +43,7 @@ rend_client_purge_state(void)
rend_cache_purge();
rend_cache_failure_purge();
rend_client_cancel_descriptor_fetches();
- rend_client_purge_last_hid_serv_requests();
+ hs_purge_last_hid_serv_requests();
}
/** Called when we've established a circuit to an introduction point:
@@ -88,46 +90,6 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ)
return 0;
}
-/** Extend the introduction circuit <b>circ</b> to another valid
- * introduction point for the hidden service it is trying to connect
- * to, or mark it and launch a new circuit if we can't extend it.
- * Return 0 on success or possible success. Return -1 and mark the
- * introduction circuit for close on permanent failure.
- *
- * On failure, the caller is responsible for marking the associated
- * rendezvous circuit for close. */
-static int
-rend_client_reextend_intro_circuit(origin_circuit_t *circ)
-{
- extend_info_t *extend_info;
- int result;
- extend_info = rend_client_get_random_intro(circ->rend_data);
- if (!extend_info) {
- log_warn(LD_REND,
- "No usable introduction points left for %s. Closing.",
- safe_str_client(rend_data_get_address(circ->rend_data)));
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL);
- return -1;
- }
- // XXX: should we not re-extend if hs_circ_has_timed_out?
- if (circ->remaining_relay_early_cells) {
- log_info(LD_REND,
- "Re-extending circ %u, this time to %s.",
- (unsigned)circ->base_.n_circ_id,
- safe_str_client(extend_info_describe(extend_info)));
- result = circuit_extend_to_new_exit(circ, extend_info);
- } else {
- log_info(LD_REND,
- "Closing intro circ %u (out of RELAY_EARLY cells).",
- (unsigned)circ->base_.n_circ_id);
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_FINISHED);
- /* connection_ap_handshake_attach_circuit will launch a new intro circ. */
- result = 0;
- }
- extend_info_free(extend_info);
- return result;
-}
-
/** Called when we're trying to connect an ap conn; sends an INTRODUCE1 cell
* down introcirc if possible.
*/
@@ -201,7 +163,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
introcirc->build_state->chosen_exit)),
smartlist_len(entry->parsed->intro_nodes));
- if (rend_client_reextend_intro_circuit(introcirc)) {
+ if (hs_client_reextend_intro_circuit(introcirc)) {
status = -2;
goto perm_err;
} else {
@@ -290,10 +252,9 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
goto perm_err;
}
- note_crypto_pk_op(REND_CLIENT);
- /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
+ /*XXX maybe give crypto_pk_obsolete_public_hybrid_encrypt a max_len arg,
* to avoid buffer overflows? */
- r = crypto_pk_public_hybrid_encrypt(intro_key, payload+DIGEST_LEN,
+ r = crypto_pk_obsolete_public_hybrid_encrypt(intro_key, payload+DIGEST_LEN,
sizeof(payload)-DIGEST_LEN,
tmp,
(int)(dh_offset+DH_KEY_LEN),
@@ -396,23 +357,11 @@ rend_client_introduction_acked(origin_circuit_t *circ,
origin_circuit_t *rendcirc;
(void) request; // XXXX Use this.
- if (circ->base_.purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
- log_warn(LD_PROTOCOL,
- "Received REND_INTRODUCE_ACK on unexpected circuit %u.",
- (unsigned)circ->base_.n_circ_id);
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
- return -1;
- }
-
tor_assert(circ->build_state);
tor_assert(circ->build_state->chosen_exit);
assert_circ_anonymity_ok(circ, options);
tor_assert(circ->rend_data);
- /* For path bias: This circuit was used successfully. Valid
- * nacks and acks count. */
- pathbias_mark_use_success(circ);
-
if (request_len == 0) {
/* It's an ACK; the introduction point relayed our introduction request. */
/* Locate the rend circ which is waiting to hear about this ack,
@@ -454,7 +403,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
INTRO_POINT_FAILURE_GENERIC)>0) {
/* There are introduction points left. Re-extend the circuit to
* another intro point and try again. */
- int result = rend_client_reextend_intro_circuit(circ);
+ int result = hs_client_reextend_intro_circuit(circ);
/* XXXX If that call failed, should we close the rend circuit,
* too? */
return result;
@@ -470,230 +419,6 @@ rend_client_introduction_acked(origin_circuit_t *circ,
return 0;
}
-/** The period for which a hidden service directory cannot be queried for
- * the same descriptor ID again. */
-#define REND_HID_SERV_DIR_REQUERY_PERIOD (15 * 60)
-/** Test networks generate a new consensus every 5 or 10 seconds.
- * So allow them to requery HSDirs much faster. */
-#define REND_HID_SERV_DIR_REQUERY_PERIOD_TESTING (5)
-
-/** Return the period for which a hidden service directory cannot be queried
- * for the same descriptor ID again, taking TestingTorNetwork into account. */
-static time_t
-hsdir_requery_period(const or_options_t *options)
-{
- tor_assert(options);
-
- if (options->TestingTorNetwork) {
- return REND_HID_SERV_DIR_REQUERY_PERIOD_TESTING;
- } else {
- return REND_HID_SERV_DIR_REQUERY_PERIOD;
- }
-}
-
-/** Contains the last request times to hidden service directories for
- * certain queries; each key is a string consisting of the
- * concatenation of a base32-encoded HS directory identity digest and
- * base32-encoded HS descriptor ID; each value is a pointer to a time_t
- * holding the time of the last request for that descriptor ID to that
- * HS directory. */
-static strmap_t *last_hid_serv_requests_ = NULL;
-
-/** Returns last_hid_serv_requests_, initializing it to a new strmap if
- * necessary. */
-static strmap_t *
-get_last_hid_serv_requests(void)
-{
- if (!last_hid_serv_requests_)
- last_hid_serv_requests_ = strmap_new();
- return last_hid_serv_requests_;
-}
-
-#define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \
- REND_DESC_ID_V2_LEN_BASE32)
-
-/** Look up the last request time to hidden service directory <b>hs_dir</b>
- * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero,
- * assign the current time <b>now</b> and return that. Otherwise, return the
- * most recent request time, or 0 if no such request has been sent before.
- */
-static time_t
-lookup_last_hid_serv_request(routerstatus_t *hs_dir,
- const char *desc_id_base32,
- time_t now, int set)
-{
- char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- char hsdir_desc_comb_id[LAST_HID_SERV_REQUEST_KEY_LEN + 1];
- time_t *last_request_ptr;
- strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
- base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
- hs_dir->identity_digest, DIGEST_LEN);
- tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
- hsdir_id_base32,
- desc_id_base32);
- /* XXX++?? tor_assert(strlen(hsdir_desc_comb_id) ==
- LAST_HID_SERV_REQUEST_KEY_LEN); */
- if (set) {
- time_t *oldptr;
- last_request_ptr = tor_malloc_zero(sizeof(time_t));
- *last_request_ptr = now;
- oldptr = strmap_set(last_hid_serv_requests, hsdir_desc_comb_id,
- last_request_ptr);
- tor_free(oldptr);
- } else
- last_request_ptr = strmap_get_lc(last_hid_serv_requests,
- hsdir_desc_comb_id);
- return (last_request_ptr) ? *last_request_ptr : 0;
-}
-
-/** Clean the history of request times to hidden service directories, so that
- * it does not contain requests older than REND_HID_SERV_DIR_REQUERY_PERIOD
- * seconds any more. */
-static void
-directory_clean_last_hid_serv_requests(time_t now)
-{
- strmap_iter_t *iter;
- time_t cutoff = now - hsdir_requery_period(get_options());
- strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
- for (iter = strmap_iter_init(last_hid_serv_requests);
- !strmap_iter_done(iter); ) {
- const char *key;
- void *val;
- time_t *ent;
- strmap_iter_get(iter, &key, &val);
- ent = (time_t *) val;
- if (*ent < cutoff) {
- iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
- tor_free(ent);
- } else {
- iter = strmap_iter_next(last_hid_serv_requests, iter);
- }
- }
-}
-
-/** Remove all requests related to the descriptor ID <b>desc_id</b> from the
- * history of times of requests to hidden service directories.
- * <b>desc_id</b> is an unencoded descriptor ID of size DIGEST_LEN.
- *
- * This is called from rend_client_note_connection_attempt_ended(), which
- * must be idempotent, so any future changes to this function must leave it
- * idempotent too. */
-static void
-purge_hid_serv_from_last_hid_serv_requests(const char *desc_id)
-{
- strmap_iter_t *iter;
- strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
-
- /* Key is stored with the base32 encoded desc_id. */
- base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
- DIGEST_LEN);
- for (iter = strmap_iter_init(last_hid_serv_requests);
- !strmap_iter_done(iter); ) {
- const char *key;
- void *val;
- strmap_iter_get(iter, &key, &val);
- /* XXX++?? tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */
- if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN -
- REND_DESC_ID_V2_LEN_BASE32,
- desc_id_base32,
- REND_DESC_ID_V2_LEN_BASE32)) {
- iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
- tor_free(val);
- } else {
- iter = strmap_iter_next(last_hid_serv_requests, iter);
- }
- }
-}
-
-/** Purge the history of request times to hidden service directories,
- * so that future lookups of an HS descriptor will not fail because we
- * accessed all of the HSDir relays responsible for the descriptor
- * recently. */
-void
-rend_client_purge_last_hid_serv_requests(void)
-{
- /* Don't create the table if it doesn't exist yet (and it may very
- * well not exist if the user hasn't accessed any HSes)... */
- strmap_t *old_last_hid_serv_requests = last_hid_serv_requests_;
- /* ... and let get_last_hid_serv_requests re-create it for us if
- * necessary. */
- last_hid_serv_requests_ = NULL;
-
- if (old_last_hid_serv_requests != NULL) {
- log_info(LD_REND, "Purging client last-HS-desc-request-time table");
- strmap_free(old_last_hid_serv_requests, tor_free_);
- }
-}
-
-/** This returns a good valid hs dir that should be used for the given
- * descriptor id.
- *
- * Return NULL on error else the hsdir node pointer. */
-static routerstatus_t *
-pick_hsdir(const char *desc_id, const char *desc_id_base32)
-{
- smartlist_t *responsible_dirs = smartlist_new();
- smartlist_t *usable_responsible_dirs = smartlist_new();
- const or_options_t *options = get_options();
- routerstatus_t *hs_dir;
- time_t now = time(NULL);
- int excluded_some;
-
- tor_assert(desc_id);
- tor_assert(desc_id_base32);
-
- /* Determine responsible dirs. Even if we can't get all we want, work with
- * the ones we have. If it's empty, we'll notice below. */
- hid_serv_get_responsible_directories(responsible_dirs, desc_id);
-
- /* Clean request history first. */
- directory_clean_last_hid_serv_requests(now);
-
- /* Only select those hidden service directories to which we did not send a
- * request recently and for which we have a router descriptor here. */
- SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
- time_t last = lookup_last_hid_serv_request(dir, desc_id_base32,
- 0, 0);
- const node_t *node = node_get_by_id(dir->identity_digest);
- if (last + hsdir_requery_period(options) >= now ||
- !node || !node_has_descriptor(node)) {
- SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
- continue;
- }
- if (!routerset_contains_node(options->ExcludeNodes, node)) {
- smartlist_add(usable_responsible_dirs, dir);
- }
- } SMARTLIST_FOREACH_END(dir);
-
- excluded_some =
- smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs);
-
- hs_dir = smartlist_choose(usable_responsible_dirs);
- if (!hs_dir && !options->StrictNodes) {
- hs_dir = smartlist_choose(responsible_dirs);
- }
-
- smartlist_free(responsible_dirs);
- smartlist_free(usable_responsible_dirs);
- if (!hs_dir) {
- log_info(LD_REND, "Could not pick one of the responsible hidden "
- "service directories, because we requested them all "
- "recently without success.");
- if (options->StrictNodes && excluded_some) {
- log_warn(LD_REND, "Could not pick a hidden service directory for the "
- "requested hidden service: they are all either down or "
- "excluded, and StrictNodes is set.");
- }
- } else {
- /* Remember that we are requesting a descriptor from this hidden service
- * directory now. */
- lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
- }
-
- return hs_dir;
-}
-
/** Determine the responsible hidden service directories for <b>desc_id</b>
* and fetch the descriptor with that ID from one of them. Only
* send a request to a hidden service directory that we have not yet tried
@@ -715,7 +440,7 @@ directory_get_from_hs_dir(const char *desc_id,
const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
#else
const int how_to_fetch = DIRIND_ANONYMOUS;
-#endif
+#endif /* defined(ENABLE_TOR2WEB_MODE) */
tor_assert(desc_id);
tor_assert(rend_query);
@@ -726,7 +451,12 @@ directory_get_from_hs_dir(const char *desc_id,
/* Automatically pick an hs dir if none given. */
if (!rs_hsdir) {
- hs_dir = pick_hsdir(desc_id, desc_id_base32);
+ /* Determine responsible dirs. Even if we can't get all we want, work with
+ * the ones we have. If it's empty, we'll notice in hs_pick_hsdir(). */
+ smartlist_t *responsible_dirs = smartlist_new();
+ hid_serv_get_responsible_directories(responsible_dirs, desc_id);
+
+ hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32);
if (!hs_dir) {
/* No suitable hs dir can be found, stop right now. */
control_event_hs_descriptor_failed(rend_query, NULL, "QUERY_NO_HSDIR");
@@ -791,6 +521,20 @@ directory_get_from_hs_dir(const char *desc_id,
return 1;
}
+/** Remove tracked HSDir requests from our history for this hidden service
+ * descriptor <b>desc_id</b> (of size DIGEST_LEN) */
+static void
+purge_v2_hidserv_req(const char *desc_id)
+{
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+
+ /* The hsdir request tracker stores v2 keys using the base32 encoded
+ desc_id. Do it: */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ hs_purge_hid_serv_from_last_hid_serv_requests(desc_id_base32);
+}
+
/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are
* given, they will be used instead.
*
@@ -865,8 +609,7 @@ fetch_v2_desc_by_addr(rend_data_t *rend_query, smartlist_t *hsdirs)
sizeof(descriptor_id)) != 0) {
/* Not equal from what we currently have so purge the last hid serv
* request cache and update the descriptor ID with the new value. */
- purge_hid_serv_from_last_hid_serv_requests(
- rend_data->descriptor_id[chosen_replica]);
+ purge_v2_hidserv_req(rend_data->descriptor_id[chosen_replica]);
memcpy(rend_data->descriptor_id[chosen_replica], descriptor_id,
sizeof(rend_data->descriptor_id[chosen_replica]));
}
@@ -1112,118 +855,24 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
return 1;
}
-/** Called when we receive a RENDEZVOUS_ESTABLISHED cell; changes the state of
- * the circuit to C_REND_READY.
- */
-int
-rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
- size_t request_len)
-{
- (void) request;
- (void) request_len;
- /* we just got an ack for our establish-rendezvous. switch purposes. */
- if (circ->base_.purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
- log_warn(LD_PROTOCOL,"Got a rendezvous ack when we weren't expecting one. "
- "Closing circ.");
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
- return -1;
- }
- log_info(LD_REND,"Got rendezvous ack. This circuit is now ready for "
- "rendezvous.");
- circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_READY);
- /* Set timestamp_dirty, because circuit_expire_building expects it
- * to specify when a circuit entered the _C_REND_READY state. */
- circ->base_.timestamp_dirty = time(NULL);
-
- /* From a path bias point of view, this circuit is now successfully used.
- * Waiting any longer opens us up to attacks from malicious hidden services.
- * They could induce the client to attempt to connect to their hidden
- * service and never reply to the client's rend requests */
- pathbias_mark_use_success(circ);
-
- /* XXXX++ This is a pretty brute-force approach. It'd be better to
- * attach only the connections that are waiting on this circuit, rather
- * than trying to attach them all. See comments bug 743. */
- /* If we already have the introduction circuit built, make sure we send
- * the INTRODUCE cell _now_ */
- connection_ap_attach_pending(1);
- return 0;
-}
-
/** The service sent us a rendezvous cell; join the circuits. */
int
rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request,
size_t request_len)
{
- crypt_path_t *hop;
- char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
-
- if ((circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY &&
- circ->base_.purpose != CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED)
- || !circ->build_state->pending_final_cpath) {
- log_warn(LD_PROTOCOL,"Got rendezvous2 cell from hidden service, but not "
- "expecting it. Closing.");
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
- return -1;
- }
-
if (request_len != DH_KEY_LEN+DIGEST_LEN) {
log_warn(LD_PROTOCOL,"Incorrect length (%d) on RENDEZVOUS2 cell.",
(int)request_len);
goto err;
}
- log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service.");
-
- /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh
- * handshake...*/
- tor_assert(circ->build_state);
- tor_assert(circ->build_state->pending_final_cpath);
- hop = circ->build_state->pending_final_cpath;
- tor_assert(hop->rend_dh_handshake_state);
- if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN,
- hop->rend_dh_handshake_state, (char*)request,
- DH_KEY_LEN,
- keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) {
- log_warn(LD_GENERAL, "Couldn't complete DH handshake.");
+ if (hs_circuit_setup_e2e_rend_circ_legacy_client(circ, request) < 0) {
+ log_warn(LD_GENERAL, "Failed to setup circ");
goto err;
}
- /* ... and set up cpath. */
- if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, 0)<0)
- goto err;
-
- /* Check whether the digest is right... */
- if (tor_memneq(keys, request+DH_KEY_LEN, DIGEST_LEN)) {
- log_warn(LD_PROTOCOL, "Incorrect digest of key material.");
- goto err;
- }
-
- crypto_dh_free(hop->rend_dh_handshake_state);
- hop->rend_dh_handshake_state = NULL;
-
- /* All is well. Extend the circuit. */
- circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED);
- hop->state = CPATH_STATE_OPEN;
- /* set the windows to default. these are the windows
- * that the client thinks the service has.
- */
- hop->package_window = circuit_initial_package_window();
- hop->deliver_window = CIRCWINDOW_START;
-
- /* Now that this circuit has finished connecting to its destination,
- * make sure circuit_get_open_circ_or_launch is willing to return it
- * so we can actually use it. */
- circ->hs_circ_has_timed_out = 0;
-
- onion_append_to_cpath(&circ->cpath, hop);
- circ->build_state->pending_final_cpath = NULL; /* prevent double-free */
-
- circuit_try_attaching_streams(circ);
-
- memwipe(keys, 0, sizeof(keys));
return 0;
+
err:
- memwipe(keys, 0, sizeof(keys));
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
return -1;
}
@@ -1310,14 +959,14 @@ rend_client_note_connection_attempt_ended(const rend_data_t *rend_data)
for (replica = 0; replica < ARRAY_LENGTH(rend_data_v2->descriptor_id);
replica++) {
const char *desc_id = rend_data_v2->descriptor_id[replica];
- purge_hid_serv_from_last_hid_serv_requests(desc_id);
+ purge_v2_hidserv_req(desc_id);
}
log_info(LD_REND, "Connection attempt for %s has ended; "
"cleaning up temporary state.",
safe_str_client(onion_address));
} else {
/* We only have an ID for a fetch. Probably used by HSFETCH. */
- purge_hid_serv_from_last_hid_serv_requests(rend_data_v2->desc_id_fetch);
+ purge_v2_hidserv_req(rend_data_v2->desc_id_fetch);
}
}
@@ -1516,7 +1165,7 @@ rend_parse_service_authorization(const or_options_t *options,
goto err;
}
strlcpy(auth->onion_address, onion_address, REND_SERVICE_ID_LEN_BASE32+1);
- if (!rend_valid_service_id(auth->onion_address)) {
+ if (!rend_valid_v2_service_id(auth->onion_address)) {
log_warn(LD_CONFIG, "Onion address has wrong format: '%s'",
onion_address);
goto err;
@@ -1569,7 +1218,7 @@ rend_client_allow_non_anonymous_connection(const or_options_t *options)
#else
(void)options;
return 0;
-#endif
+#endif /* defined(NON_ANONYMOUS_MODE_ENABLED) */
}
/* At compile-time, was non-anonymous mode enabled via
@@ -1584,6 +1233,6 @@ rend_client_non_anonymous_mode_enabled(const or_options_t *options)
return 1;
#else
return 0;
-#endif
+#endif /* defined(NON_ANONYMOUS_MODE_ENABLED) */
}
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index ff0f4084fd..e8495ce09c 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -24,15 +24,11 @@ int rend_client_introduction_acked(origin_circuit_t *circ,
void rend_client_refetch_v2_renddesc(rend_data_t *rend_query);
int rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs);
void rend_client_cancel_descriptor_fetches(void);
-void rend_client_purge_last_hid_serv_requests(void);
int rend_client_report_intro_point_failure(extend_info_t *failed_intro,
rend_data_t *rend_data,
unsigned int failure_type);
-int rend_client_rendezvous_acked(origin_circuit_t *circ,
- const uint8_t *request,
- size_t request_len);
int rend_client_receive_rendezvous(origin_circuit_t *circ,
const uint8_t *request,
size_t request_len);
@@ -54,5 +50,5 @@ void rend_service_authorization_free_all(void);
int rend_client_allow_non_anonymous_connection(const or_options_t *options);
int rend_client_non_anonymous_mode_enabled(const or_options_t *options);
-#endif
+#endif /* !defined(TOR_RENDCLIENT_H) */
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index d18356e67a..458a90058f 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -8,6 +8,8 @@
* introducers, services, clients, and rendezvous points.
**/
+#define RENDCOMMON_PRIVATE
+
#include "or.h"
#include "circuitbuild.h"
#include "config.h"
@@ -17,6 +19,7 @@
#include "rendcommon.h"
#include "rendmid.h"
#include "hs_intropoint.h"
+#include "hs_client.h"
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
@@ -395,7 +398,7 @@ rend_encrypt_v2_intro_points_stealth(char **encrypted_out,
/** Attempt to parse the given <b>desc_str</b> and return true if this
* succeeds, false otherwise. */
-static int
+STATIC int
rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc)
{
rend_service_descriptor_t *test_parsed = NULL;
@@ -696,7 +699,7 @@ rend_get_service_id(crypto_pk_t *pk, char *out)
/** Return true iff <b>query</b> is a syntactically valid service ID (as
* generated by rend_get_service_id). */
int
-rend_valid_service_id(const char *query)
+rend_valid_v2_service_id(const char *query)
{
if (strlen(query) != REND_SERVICE_ID_LEN_BASE32)
return 0;
@@ -778,11 +781,11 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
break;
case RELAY_COMMAND_INTRODUCE2:
if (origin_circ)
- r = rend_service_receive_introduction(origin_circ,payload,length);
+ r = hs_service_receive_introduce2(origin_circ,payload,length);
break;
case RELAY_COMMAND_INTRODUCE_ACK:
if (origin_circ)
- r = rend_client_introduction_acked(origin_circ,payload,length);
+ r = hs_client_receive_introduce_ack(origin_circ,payload,length);
break;
case RELAY_COMMAND_RENDEZVOUS1:
if (or_circ)
@@ -790,15 +793,15 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
break;
case RELAY_COMMAND_RENDEZVOUS2:
if (origin_circ)
- r = rend_client_receive_rendezvous(origin_circ,payload,length);
+ r = hs_client_receive_rendezvous2(origin_circ,payload,length);
break;
case RELAY_COMMAND_INTRO_ESTABLISHED:
if (origin_circ)
- r = rend_service_intro_established(origin_circ,payload,length);
+ r = hs_service_receive_intro_established(origin_circ,payload,length);
break;
case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED:
if (origin_circ)
- r = rend_client_rendezvous_acked(origin_circ,payload,length);
+ r = hs_client_receive_rendezvous_acked(origin_circ,payload,length);
break;
default:
tor_fragile_assert();
@@ -991,7 +994,7 @@ rend_non_anonymous_mode_enabled(const or_options_t *options)
* service.
*/
void
-assert_circ_anonymity_ok(origin_circuit_t *circ,
+assert_circ_anonymity_ok(const origin_circuit_t *circ,
const or_options_t *options)
{
tor_assert(options);
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index 94c2480d86..c35d7272fd 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -30,7 +30,7 @@ void rend_encoded_v2_service_descriptor_free(
rend_encoded_v2_service_descriptor_t *desc);
void rend_intro_point_free(rend_intro_point_t *intro);
-int rend_valid_service_id(const char *query);
+int rend_valid_v2_service_id(const char *query);
int rend_valid_descriptor_id(const char *query);
int rend_valid_client_name(const char *client_name);
int rend_encode_v2_descriptors(smartlist_t *descs_out,
@@ -60,8 +60,15 @@ int rend_auth_decode_cookie(const char *cookie_in,
int rend_allow_non_anonymous_connection(const or_options_t* options);
int rend_non_anonymous_mode_enabled(const or_options_t *options);
-void assert_circ_anonymity_ok(origin_circuit_t *circ,
+void assert_circ_anonymity_ok(const origin_circuit_t *circ,
const or_options_t *options);
-#endif
+#ifdef RENDCOMMON_PRIVATE
+
+STATIC int
+rend_desc_v2_is_parsable(rend_encoded_v2_service_descriptor_t *desc);
+
+#endif /* defined(RENDCOMMON_PRIVATE) */
+
+#endif /* !defined(TOR_RENDCOMMON_H) */
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index 89739e1291..c4a34ca62c 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -73,7 +73,6 @@ rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request,
goto err;
}
/* Rest of body: signature of previous data */
- note_crypto_pk_op(REND_MID);
if (crypto_pk_public_checksig_digest(pk,
(char*)request, 2+asn1len+DIGEST_LEN,
(char*)(request+2+DIGEST_LEN+asn1len),
diff --git a/src/or/rendmid.h b/src/or/rendmid.h
index daf9e2885e..6cc1fc8d95 100644
--- a/src/or/rendmid.h
+++ b/src/or/rendmid.h
@@ -21,5 +21,5 @@ int rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request,
int rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
size_t request_len);
-#endif
+#endif /* !defined(TOR_RENDMID_H) */
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 2a3594918e..fed8c97ebb 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -18,6 +18,7 @@
#include "control.h"
#include "directory.h"
#include "hs_common.h"
+#include "hs_config.h"
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
@@ -82,22 +83,6 @@ static smartlist_t* rend_get_service_list_mutable(
smartlist_t* substitute_service_list);
static int rend_max_intro_circs_per_period(unsigned int n_intro_points_wanted);
-/** Represents the mapping from a virtual port of a rendezvous service to
- * a real port on some IP.
- */
-struct rend_service_port_config_s {
- /* The incoming HS virtual port we're mapping */
- uint16_t virtual_port;
- /* Is this an AF_UNIX port? */
- unsigned int is_unix_addr:1;
- /* The outgoing TCP port to use, if !is_unix_addr */
- uint16_t real_port;
- /* The outgoing IPv4 or IPv6 address to use, if !is_unix_addr */
- tor_addr_t real_addr;
- /* The socket path to connect to, if is_unix_addr */
- char unix_addr[FLEXIBLE_ARRAY_MEMBER];
-};
-
/* Hidden service directory file names:
* new file names should be added to rend_service_add_filenames_to_list()
* for sandboxing purposes. */
@@ -163,7 +148,7 @@ rend_service_escaped_dir(const struct rend_service_t *s)
/** Return the number of rendezvous services we have configured. */
int
-num_rend_services(void)
+rend_num_services(void)
{
if (!rend_service_list)
return 0;
@@ -231,18 +216,41 @@ rend_service_free(rend_service_t *service)
tor_free(service);
}
-/** Release all the storage held in rend_service_list.
- */
+/* Release all the storage held in rend_service_staging_list. */
+void
+rend_service_free_staging_list(void)
+{
+ if (rend_service_staging_list) {
+ SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t*, ptr,
+ rend_service_free(ptr));
+ smartlist_free(rend_service_staging_list);
+ rend_service_staging_list = NULL;
+ }
+}
+
+/** Release all the storage held in both rend_service_list and
+ * rend_service_staging_list. */
void
rend_service_free_all(void)
{
- if (!rend_service_list)
- return;
+ if (rend_service_list) {
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
+ rend_service_free(ptr));
+ smartlist_free(rend_service_list);
+ rend_service_list = NULL;
+ }
+ rend_service_free_staging_list();
+}
+
+/* Initialize the subsystem. */
+void
+rend_service_init(void)
+{
+ tor_assert(!rend_service_list);
+ tor_assert(!rend_service_staging_list);
- SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
- rend_service_free(ptr));
- smartlist_free(rend_service_list);
- rend_service_list = NULL;
+ rend_service_list = smartlist_new();
+ rend_service_staging_list = smartlist_new();
}
/* Validate a <b>service</b>. Use the <b>service_list</b> to make sure there
@@ -252,8 +260,6 @@ static int
rend_validate_service(const smartlist_t *service_list,
const rend_service_t *service)
{
- int dupe = 0;
-
tor_assert(service_list);
tor_assert(service);
@@ -286,34 +292,6 @@ rend_validate_service(const smartlist_t *service_list,
goto invalid;
}
- /* XXX This duplicate check has two problems:
- *
- * a) It's O(n^2), but the same comment from the bottom of
- * rend_config_services() should apply.
- *
- * b) We only compare directory paths as strings, so we can't
- * detect two distinct paths that specify the same directory
- * (which can arise from symlinks, case-insensitivity, bind
- * mounts, etc.).
- *
- * It also can't detect that two separate Tor instances are trying
- * to use the same HiddenServiceDir; for that, we would need a
- * lock file. But this is enough to detect a simple mistake that
- * at least one person has actually made.
- */
- if (!rend_service_is_ephemeral(service)) {
- /* Skip dupe for ephemeral services. */
- SMARTLIST_FOREACH(service_list, rend_service_t *, ptr,
- dupe = dupe ||
- !strcmp(ptr->directory, service->directory));
- if (dupe) {
- log_warn(LD_REND, "Another hidden service is already configured for "
- "directory %s.",
- rend_service_escaped_dir(service));
- goto invalid;
- }
- }
-
/* Valid. */
return 0;
invalid:
@@ -335,6 +313,7 @@ rend_add_service(smartlist_t *service_list, rend_service_t *service)
/* We must have a service list, even if it's a temporary one, so we can
* check for duplicate services */
if (BUG(!s_list)) {
+ rend_service_free(service);
return -1;
}
@@ -496,39 +475,28 @@ rend_service_port_config_free(rend_service_port_config_t *p)
tor_free(p);
}
-/* Check the directory for <b>service</b>, and add the service to
- * <b>service_list</b>, or to the global list if <b>service_list</b> is NULL.
- * Only add the service to the list if <b>validate_only</b> is false.
- * If <b>validate_only</b> is true, free the service.
- * If <b>service</b> is NULL, ignore it, and return 0.
- * Returns 0 on success, and -1 on failure.
- * Takes ownership of <b>service</b>, either freeing it, or adding it to the
- * global service list.
- */
-STATIC int
-rend_service_check_dir_and_add(smartlist_t *service_list,
- const or_options_t *options,
- rend_service_t *service,
- int validate_only)
+/* Copy relevant data from service src to dst while pruning the service lists.
+ * This should only be called during the pruning process which takes existing
+ * services and copy their data to the newly configured services. The src
+ * service replaycache will be set to NULL after this call. */
+static void
+copy_service_on_prunning(rend_service_t *dst, rend_service_t *src)
{
- if (!service) {
- /* It is ok for a service to be NULL, this means there are no services */
- return 0;
- }
-
- if (rend_service_check_private_dir(options, service, !validate_only)
- < 0) {
- rend_service_free(service);
- return -1;
- }
-
- smartlist_t *s_list = rend_get_service_list_mutable(service_list);
- /* We must have a service list, even if it's a temporary one, so we can
- * check for duplicate services */
- if (BUG(!s_list)) {
- return -1;
- }
- return rend_add_service(s_list, service);
+ tor_assert(dst);
+ tor_assert(src);
+
+ /* Keep the timestamps for when the content changed and the next upload
+ * time so we can properly upload the descriptor if needed for the new
+ * service object. */
+ dst->desc_is_dirty = src->desc_is_dirty;
+ dst->next_upload_time = src->next_upload_time;
+ /* Move the replaycache to the new object. */
+ dst->accepted_intro_dh_parts = src->accepted_intro_dh_parts;
+ src->accepted_intro_dh_parts = NULL;
+ /* Copy intro point information to destination service. */
+ dst->intro_period_started = src->intro_period_started;
+ dst->n_intro_circuits_launched = src->n_intro_circuits_launched;
+ dst->n_intro_points_wanted = src->n_intro_points_wanted;
}
/* Helper: Actual implementation of the pruning on reload which we've
@@ -604,6 +572,10 @@ rend_service_prune_list_impl_(void)
smartlist_clear(old->intro_nodes);
smartlist_add_all(new->expiring_nodes, old->expiring_nodes);
smartlist_clear(old->expiring_nodes);
+
+ /* Copy needed information from old to new. */
+ copy_service_on_prunning(new, old);
+
/* This regular service will survive the closing IPs step after. */
smartlist_add(surviving_services, old);
break;
@@ -614,7 +586,10 @@ rend_service_prune_list_impl_(void)
* matching surviving configured service. If not, close the circuit. */
while ((ocirc = circuit_get_next_service_intro_circ(ocirc))) {
int keep_it = 0;
- tor_assert(ocirc->rend_data);
+ if (ocirc->rend_data == NULL) {
+ /* This is a v3 circuit, ignore it. */
+ continue;
+ }
SMARTLIST_FOREACH_BEGIN(surviving_services, const rend_service_t *, s) {
if (rend_circuit_pk_digest_eq(ocirc, (uint8_t *) s->pk_digest)) {
/* Keep this circuit as we have a matching configured service. */
@@ -643,10 +618,11 @@ void
rend_service_prune_list(void)
{
smartlist_t *old_service_list = rend_service_list;
- /* Don't try to prune anything if we have no staging list. */
+
if (!rend_service_staging_list) {
- return;
+ rend_service_staging_list = smartlist_new();
}
+
rend_service_prune_list_impl_();
if (old_service_list) {
/* Every remaining service in the old list have been removed from the
@@ -657,19 +633,54 @@ rend_service_prune_list(void)
}
}
-/** Set up rend_service_list, based on the values of HiddenServiceDir and
- * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
- * failure. (If <b>validate_only</b> is set, parse, warn and return as
- * normal, but don't actually change the configured services.)
- */
+/* Copy all the relevant data that the hs_service object contains over to the
+ * rend_service_t object. The reason to do so is because when configuring a
+ * service, we go through a generic handler that creates an hs_service_t
+ * object which so we have to copy the parsed values to a rend service object
+ * which is version 2 specific. */
+static void
+service_config_shadow_copy(rend_service_t *service,
+ hs_service_config_t *config)
+{
+ tor_assert(service);
+ tor_assert(config);
+
+ service->directory = tor_strdup(config->directory_path);
+ service->dir_group_readable = config->dir_group_readable;
+ service->allow_unknown_ports = config->allow_unknown_ports;
+ /* This value can't go above HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT (65535)
+ * if the code flow is right so this cast is safe. But just in case, we'll
+ * check it. */
+ service->max_streams_per_circuit = (int) config->max_streams_per_rdv_circuit;
+ if (BUG(config->max_streams_per_rdv_circuit >
+ HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT)) {
+ service->max_streams_per_circuit = HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT;
+ }
+ service->max_streams_close_circuit = config->max_streams_close_circuit;
+ service->n_intro_points_wanted = config->num_intro_points;
+ /* Switching ownership of the ports to the rend service object. */
+ smartlist_add_all(service->ports, config->ports);
+ smartlist_free(config->ports);
+ config->ports = NULL;
+}
+
+/* Parse the hidden service configuration starting at <b>line_</b> using the
+ * already configured generic service configuration in <b>config</b>. This
+ * function will translate the config object to a rend_service_t and add it to
+ * the temporary list if valid. If <b>validate_only</b> is set, parse, warn
+ * and return as normal but don't actually add the service to the list. */
int
-rend_config_services(const or_options_t *options, int validate_only)
+rend_config_service(const config_line_t *line_,
+ const or_options_t *options,
+ hs_service_config_t *config)
{
- config_line_t *line;
+ const config_line_t *line;
rend_service_t *service = NULL;
- rend_service_port_config_t *portcfg;
- int ok = 0;
- int rv = -1;
+
+ /* line_ can be NULL which would mean that the service configuration only
+ * have one line that is the directory directive. */
+ tor_assert(options);
+ tor_assert(config);
/* Use the staging service list so that we can check then do the pruning
* process using the main list at the end. */
@@ -677,100 +688,23 @@ rend_config_services(const or_options_t *options, int validate_only)
rend_service_staging_list = smartlist_new();
}
- for (line = options->RendConfigLines; line; line = line->next) {
+ /* Initialize service. */
+ service = tor_malloc_zero(sizeof(rend_service_t));
+ service->intro_period_started = time(NULL);
+ service->ports = smartlist_new();
+ /* From the hs_service object which has been used to load the generic
+ * options, we'll copy over the useful data to the rend_service_t object. */
+ service_config_shadow_copy(service, config);
+
+ for (line = line_; line; line = line->next) {
if (!strcasecmp(line->key, "HiddenServiceDir")) {
- if (service) {
- /* Validate and register the service we just finished parsing this
- * code registers every service except the last one parsed, which is
- * validated and registered below the loop. */
- if (rend_validate_service(rend_service_staging_list, service) < 0) {
- goto free_and_return;
- }
- if (rend_service_check_dir_and_add(rend_service_staging_list, options,
- service, validate_only) < 0) {
- /* The above frees the service on error so nullify the pointer. */
- service = NULL;
- goto free_and_return;
- }
- }
- service = tor_malloc_zero(sizeof(rend_service_t));
- service->directory = tor_strdup(line->value);
- service->ports = smartlist_new();
- service->intro_period_started = time(NULL);
- service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
- continue;
- }
- if (!service) {
- log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
- line->key);
- goto free_and_return;
+ /* We just hit the next hidden service, stop right now. */
+ break;
}
- if (!strcasecmp(line->key, "HiddenServicePort")) {
- char *err_msg = NULL;
- portcfg = rend_service_parse_port_config(line->value, " ", &err_msg);
- if (!portcfg) {
- if (err_msg)
- log_warn(LD_CONFIG, "%s", err_msg);
- tor_free(err_msg);
- goto free_and_return;
- }
- tor_assert(!err_msg);
- smartlist_add(service->ports, portcfg);
- } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
- service->allow_unknown_ports = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s",
- line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts=%d for %s",
- (int)service->allow_unknown_ports,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key,
- "HiddenServiceDirGroupReadable")) {
- service->dir_group_readable = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceDirGroupReadable should be 0 or 1, not %s",
- line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceDirGroupReadable=%d for %s",
- service->dir_group_readable,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
- service->max_streams_per_circuit = (int)tor_parse_long(line->value,
- 10, 0, 65535, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceMaxStreams should be between 0 and %d, not %s",
- 65535, line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceMaxStreams=%d for %s",
- service->max_streams_per_circuit,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
- service->max_streams_close_circuit = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, "
- "not %s",
- line->value);
- goto free_and_return;
- }
- log_info(LD_CONFIG,
- "HiddenServiceMaxStreamsCloseCircuit=%d for %s",
- (int)service->max_streams_close_circuit,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) {
+ /* Number of introduction points. */
+ if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) {
+ int ok = 0;
+ /* Those are specific defaults for version 2. */
service->n_intro_points_wanted =
(unsigned int) tor_parse_long(line->value, 10,
0, NUM_INTRO_POINTS_MAX, &ok, NULL);
@@ -779,22 +713,22 @@ rend_config_services(const or_options_t *options, int validate_only)
"HiddenServiceNumIntroductionPoints "
"should be between %d and %d, not %s",
0, NUM_INTRO_POINTS_MAX, line->value);
- goto free_and_return;
+ goto err;
}
log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s",
- service->n_intro_points_wanted,
- rend_service_escaped_dir(service));
- } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
+ service->n_intro_points_wanted, escaped(service->directory));
+ continue;
+ }
+ if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
/* Parse auth type and comma-separated list of client names and add a
* rend_authorized_client_t for each client to the service's list
* of authorized clients. */
smartlist_t *type_names_split, *clients;
const char *authname;
- int num_clients;
if (service->auth_type != REND_NO_AUTH) {
log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
"lines for a single service.");
- goto free_and_return;
+ goto err;
}
type_names_split = smartlist_new();
smartlist_split_string(type_names_split, line->value, " ", 0, 2);
@@ -802,7 +736,8 @@ rend_config_services(const or_options_t *options, int validate_only)
log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
"should have been prevented when parsing the "
"configuration.");
- goto free_and_return;
+ smartlist_free(type_names_split);
+ goto err;
}
authname = smartlist_get(type_names_split, 0);
if (!strcasecmp(authname, "basic")) {
@@ -816,7 +751,7 @@ rend_config_services(const or_options_t *options, int validate_only)
(char *) smartlist_get(type_names_split, 0));
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
smartlist_free(type_names_split);
- goto free_and_return;
+ goto err;
}
service->clients = smartlist_new();
if (smartlist_len(type_names_split) < 2) {
@@ -833,14 +768,15 @@ rend_config_services(const or_options_t *options, int validate_only)
SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
smartlist_free(type_names_split);
/* Remove duplicate client names. */
- num_clients = smartlist_len(clients);
- smartlist_sort_strings(clients);
- smartlist_uniq_strings(clients);
- if (smartlist_len(clients) < num_clients) {
- log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
- "duplicate client name(s); removing.",
- num_clients - smartlist_len(clients));
- num_clients = smartlist_len(clients);
+ {
+ int num_clients = smartlist_len(clients);
+ smartlist_sort_strings(clients);
+ smartlist_uniq_strings(clients);
+ if (smartlist_len(clients) < num_clients) {
+ log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
+ "duplicate client name(s); removing.",
+ num_clients - smartlist_len(clients));
+ }
}
SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name)
{
@@ -853,7 +789,7 @@ rend_config_services(const or_options_t *options, int validate_only)
client_name, REND_CLIENTNAME_MAX_LEN);
SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
smartlist_free(clients);
- goto free_and_return;
+ goto err;
}
client = tor_malloc_zero(sizeof(rend_authorized_client_t));
client->client_name = tor_strdup(client_name);
@@ -875,56 +811,29 @@ rend_config_services(const or_options_t *options, int validate_only)
smartlist_len(service->clients),
service->auth_type == REND_BASIC_AUTH ? 512 : 16,
service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
- goto free_and_return;
- }
- } else {
- tor_assert(!strcasecmp(line->key, "HiddenServiceVersion"));
- if (strcmp(line->value, "2")) {
- log_warn(LD_CONFIG,
- "The only supported HiddenServiceVersion is 2.");
- goto free_and_return;
+ goto err;
}
+ continue;
}
}
- /* Validate the last service that we just parsed. */
- if (service &&
- rend_validate_service(rend_service_staging_list, service) < 0) {
- goto free_and_return;
- }
- /* register the final service after we have finished parsing all services
- * this code only registers the last service, other services are registered
- * within the loop. It is ok for this service to be NULL, it is ignored. */
- if (rend_service_check_dir_and_add(rend_service_staging_list, options,
- service, validate_only) < 0) {
- /* Service object is freed on error so nullify pointer. */
- service = NULL;
- goto free_and_return;
+ /* Validate the service just parsed. */
+ if (rend_validate_service(rend_service_staging_list, service) < 0) {
+ /* Service is in the staging list so don't try to free it. */
+ goto err;
}
- /* The service is in the staging list so nullify pointer to avoid double
- * free of this object in case of error because we lost ownership of it at
- * this point. */
- service = NULL;
- /* Free the newly added services if validating */
- if (validate_only) {
- rv = 0;
- goto free_and_return;
+ /* Add it to the temporary list which we will use to prune our current
+ * list if any after configuring all services. */
+ if (rend_add_service(rend_service_staging_list, service) < 0) {
+ /* The object has been freed on error already. */
+ service = NULL;
+ goto err;
}
- /* This could be a reload of configuration so try to prune the main list
- * using the staging one. And we know we are not in validate mode here.
- * After this, the main and staging list will point to the right place and
- * be in a quiescent usable state. */
- rend_service_prune_list();
-
return 0;
- free_and_return:
+ err:
rend_service_free(service);
- SMARTLIST_FOREACH(rend_service_staging_list, rend_service_t *, ptr,
- rend_service_free(ptr));
- smartlist_free(rend_service_staging_list);
- rend_service_staging_list = NULL;
- return rv;
+ return -1;
}
/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, using
@@ -1013,7 +922,7 @@ int
rend_service_del_ephemeral(const char *service_id)
{
rend_service_t *s;
- if (!rend_valid_service_id(service_id)) {
+ if (!rend_valid_v2_service_id(service_id)) {
log_warn(LD_CONFIG, "Requested malformed Onion Service id for removal.");
return -1;
}
@@ -1038,8 +947,8 @@ rend_service_del_ephemeral(const char *service_id)
(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
- tor_assert(oc->rend_data);
- if (!rend_circuit_pk_digest_eq(oc, (uint8_t *) s->pk_digest)) {
+ if (oc->rend_data == NULL ||
+ !rend_circuit_pk_digest_eq(oc, (uint8_t *) s->pk_digest)) {
continue;
}
log_debug(LD_REND, "Closing intro point %s for service %s.",
@@ -1177,15 +1086,8 @@ rend_service_update_descriptor(rend_service_t *service)
static char *
rend_service_path(const rend_service_t *service, const char *file_name)
{
- char *file_path = NULL;
-
tor_assert(service->directory);
-
- /* Can never fail: asserts rather than leaving file_path NULL. */
- tor_asprintf(&file_path, "%s%s%s",
- service->directory, PATH_SEPARATOR, file_name);
-
- return file_path;
+ return hs_path_from_filename(service->directory, file_name);
}
/* Allocate and return a string containing the path to the single onion
@@ -1555,9 +1457,9 @@ rend_service_load_keys(rend_service_t *s)
char *fname = NULL;
char buf[128];
- /* Make sure the directory was created and single onion poisoning was
- * checked before calling this function */
- if (BUG(rend_service_check_private_dir(get_options(), s, 0) < 0))
+ /* Create the directory if needed which will also poison it in case of
+ * single onion service. */
+ if (rend_service_check_private_dir(get_options(), s, 1) < 0)
goto err;
/* Load key */
@@ -1586,7 +1488,7 @@ rend_service_load_keys(rend_service_t *s)
log_warn(LD_FS,"Unable to make hidden hostname file %s group-readable.",
fname);
}
-#endif
+#endif /* !defined(_WIN32) */
/* If client authorization is configured, load or generate keys. */
if (s->auth_type != REND_NO_AUTH) {
@@ -1815,24 +1717,6 @@ rend_service_get_by_service_id(const char *id)
return NULL;
}
-/** Return 1 if any virtual port in <b>service</b> wants a circuit
- * to have good uptime. Else return 0.
- */
-static int
-rend_service_requires_uptime(rend_service_t *service)
-{
- int i;
- rend_service_port_config_t *p;
-
- for (i=0; i < smartlist_len(service->ports); ++i) {
- p = smartlist_get(service->ports, i);
- if (smartlist_contains_int_as_string(get_options()->LongLivedPorts,
- p->virtual_port))
- return 1;
- }
- return 0;
-}
-
/** Check client authorization of a given <b>descriptor_cookie</b> of
* length <b>cookie_len</b> for <b>service</b>. Return 1 for success
* and 0 for failure. */
@@ -2152,7 +2036,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
goto err;
}
- circ_needs_uptime = rend_service_requires_uptime(service);
+ circ_needs_uptime = hs_service_requires_uptime_circ(service->ports);
/* help predict this next time */
rep_hist_note_used_internal(now, circ_needs_uptime, 1);
@@ -2205,7 +2089,9 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
cpath->rend_dh_handshake_state = dh;
dh = NULL;
- if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
+ if (circuit_init_cpath_crypto(cpath,
+ keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN,
+ 1, 0)<0)
goto err;
memcpy(cpath->rend_circ_nonce, keys, DIGEST_LEN);
@@ -2268,7 +2154,7 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
if (intro->version == 0 || intro->version == 1) {
rp_nickname = (const char *)(intro->u.v0_v1.rp);
- node = node_get_by_nickname(rp_nickname, 0);
+ node = node_get_by_nickname(rp_nickname, NNF_NO_WARN_UNNAMED);
if (!node) {
if (err_msg_out) {
tor_asprintf(&err_msg,
@@ -2860,10 +2746,8 @@ rend_service_decrypt_intro(
}
/* Decrypt the encrypted part */
-
- note_crypto_pk_op(REND_SERVER);
result =
- crypto_pk_private_hybrid_decrypt(
+ crypto_pk_obsolete_private_hybrid_decrypt(
key, (char *)buf, sizeof(buf),
(const char *)(intro->ciphertext), intro->ciphertext_len,
PK_PKCS1_OAEP_PADDING, 1);
@@ -3056,35 +2940,6 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
cpath_build_state_t *newstate, *oldstate;
tor_assert(oldcirc->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
-
- /* Don't relaunch the same rend circ twice. */
- if (oldcirc->hs_service_side_rend_circ_has_been_relaunched) {
- log_info(LD_REND, "Rendezvous circuit to %s has already been relaunched; "
- "not relaunching it again.",
- oldcirc->build_state ?
- safe_str(extend_info_describe(oldcirc->build_state->chosen_exit))
- : "*unknown*");
- return;
- }
- oldcirc->hs_service_side_rend_circ_has_been_relaunched = 1;
-
- /* We check failure_count >= hs_get_service_max_rend_failures()-1 below, and
- * the -1 is because we increment the failure count for our current failure
- * *after* this clause. */
- int max_rend_failures = hs_get_service_max_rend_failures() - 1;
-
- if (!oldcirc->build_state ||
- oldcirc->build_state->failure_count >= max_rend_failures ||
- oldcirc->build_state->expiry_time < time(NULL)) {
- log_info(LD_REND,
- "Attempt to build circuit to %s for rendezvous has failed "
- "too many times or expired; giving up.",
- oldcirc->build_state ?
- safe_str(extend_info_describe(oldcirc->build_state->chosen_exit))
- : "*unknown*");
- return;
- }
-
oldstate = oldcirc->build_state;
tor_assert(oldstate);
@@ -3252,10 +3107,11 @@ count_intro_point_circuits(const rend_service_t *service)
crypto material. On success, fill <b>cell_body_out</b> and return the number
of bytes written. On fail, return -1.
*/
-STATIC ssize_t
-encode_establish_intro_cell_legacy(char *cell_body_out,
- size_t cell_body_out_len,
- crypto_pk_t *intro_key, char *rend_circ_nonce)
+ssize_t
+rend_service_encode_establish_intro_cell(char *cell_body_out,
+ size_t cell_body_out_len,
+ crypto_pk_t *intro_key,
+ const char *rend_circ_nonce)
{
int retval = -1;
int r;
@@ -3280,7 +3136,6 @@ encode_establish_intro_cell_legacy(char *cell_body_out,
if (crypto_digest(cell_body_out+len, auth, DIGEST_LEN+9))
goto err;
len += 20;
- note_crypto_pk_op(REND_SERVER);
r = crypto_pk_private_sign_digest(intro_key, cell_body_out+len,
cell_body_out_len - len,
cell_body_out, len);
@@ -3393,7 +3248,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
/* Send the ESTABLISH_INTRO cell */
{
ssize_t len;
- len = encode_establish_intro_cell_legacy(buf, sizeof(buf),
+ len = rend_service_encode_establish_intro_cell(buf, sizeof(buf),
circuit->intro_key,
circuit->cpath->prev->rend_circ_nonce);
if (len < 0) {
@@ -3511,9 +3366,10 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
NULL);
rend_cookie = circuit->rend_data->rend_cookie;
- /* Declare the circuit dirty to avoid reuse, and for path-bias */
- if (!circuit->base_.timestamp_dirty)
- circuit->base_.timestamp_dirty = time(NULL);
+ /* Declare the circuit dirty to avoid reuse, and for path-bias. We set the
+ * timestamp regardless of its content because that circuit could have been
+ * cannibalized so in any cases, we are about to use that circuit more. */
+ circuit->base_.timestamp_dirty = time(NULL);
/* This may be redundant */
pathbias_count_use_attempt(circuit);
@@ -3573,7 +3429,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
/* Send the cell */
if (relay_send_command_from_edge(0, TO_CIRCUIT(circuit),
RELAY_COMMAND_RENDEZVOUS1,
- buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN,
+ buf, HS_LEGACY_RENDEZVOUS_CELL_SIZE,
circuit->cpath->prev)<0) {
log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell.");
goto done;
@@ -4129,10 +3985,9 @@ rend_max_intro_circs_per_period(unsigned int n_intro_points_wanted)
* This is called once a second by the main loop.
*/
void
-rend_consider_services_intro_points(void)
+rend_consider_services_intro_points(time_t now)
{
int i;
- time_t now;
const or_options_t *options = get_options();
/* Are we in single onion mode? */
const int allow_direct = rend_service_allow_non_anonymous_connection(
@@ -4149,7 +4004,6 @@ rend_consider_services_intro_points(void)
exclude_nodes = smartlist_new();
retry_nodes = smartlist_new();
- now = time(NULL);
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, service) {
int r;
@@ -4427,60 +4281,6 @@ rend_service_dump_stats(int severity)
}
}
-#ifdef HAVE_SYS_UN_H
-
-/** Given <b>ports</b>, a smarlist containing rend_service_port_config_t,
- * add the given <b>p</b>, a AF_UNIX port to the list. Return 0 on success
- * else return -ENOSYS if AF_UNIX is not supported (see function in the
- * #else statement below). */
-static int
-add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
-{
- tor_assert(ports);
- tor_assert(p);
- tor_assert(p->is_unix_addr);
-
- smartlist_add(ports, p);
- return 0;
-}
-
-/** Given <b>conn</b> set it to use the given port <b>p</b> values. Return 0
- * on success else return -ENOSYS if AF_UNIX is not supported (see function
- * in the #else statement below). */
-static int
-set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
-{
- tor_assert(conn);
- tor_assert(p);
- tor_assert(p->is_unix_addr);
-
- conn->base_.socket_family = AF_UNIX;
- tor_addr_make_unspec(&conn->base_.addr);
- conn->base_.port = 1;
- conn->base_.address = tor_strdup(p->unix_addr);
- return 0;
-}
-
-#else /* defined(HAVE_SYS_UN_H) */
-
-static int
-set_unix_port(edge_connection_t *conn, rend_service_port_config_t *p)
-{
- (void) conn;
- (void) p;
- return -ENOSYS;
-}
-
-static int
-add_unix_port(smartlist_t *ports, rend_service_port_config_t *p)
-{
- (void) ports;
- (void) p;
- return -ENOSYS;
-}
-
-#endif /* HAVE_SYS_UN_H */
-
/** Given <b>conn</b>, a rendezvous exit stream, look up the hidden service for
* 'circ', and look up the port and address based on conn-\>port.
* Assign the actual conn-\>addr and conn-\>port. Return -2 on failure
@@ -4493,15 +4293,11 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
{
rend_service_t *service;
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
- smartlist_t *matching_ports;
- rend_service_port_config_t *chosen_port;
- unsigned int warn_once = 0;
const char *rend_pk_digest;
tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_S_REND_JOINED);
tor_assert(circ->rend_data);
log_debug(LD_REND,"beginning to hunt for addr/port");
- /* XXX: This is version 2 specific (only one supported). */
rend_pk_digest = (char *) rend_data_get_pk_digest(circ->rend_data, NULL);
base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1,
rend_pk_digest, REND_SERVICE_ID_LEN);
@@ -4531,41 +4327,9 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
return service->max_streams_close_circuit ? -2 : -1;
}
}
- matching_ports = smartlist_new();
- SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
- {
- if (conn->base_.port != p->virtual_port) {
- continue;
- }
- if (!(p->is_unix_addr)) {
- smartlist_add(matching_ports, p);
- } else {
- if (add_unix_port(matching_ports, p)) {
- if (!warn_once) {
- /* Unix port not supported so warn only once. */
- log_warn(LD_REND,
- "Saw AF_UNIX virtual port mapping for port %d on service "
- "%s, which is unsupported on this platform. Ignoring it.",
- conn->base_.port, serviceid);
- }
- warn_once++;
- }
- }
- });
- chosen_port = smartlist_choose(matching_ports);
- smartlist_free(matching_ports);
- if (chosen_port) {
- if (!(chosen_port->is_unix_addr)) {
- /* Get a non-AF_UNIX connection ready for connection_exit_connect() */
- tor_addr_copy(&conn->base_.addr, &chosen_port->real_addr);
- conn->base_.port = chosen_port->real_port;
- } else {
- if (set_unix_port(conn, chosen_port)) {
- /* Simply impossible to end up here else we were able to add a Unix
- * port without AF_UNIX support... ? */
- tor_assert(0);
- }
- }
+
+ if (hs_set_conn_addr_port(service->ports, conn) == 0) {
+ /* Successfully set the port to the connection. We are done. */
return 0;
}
@@ -4641,5 +4405,5 @@ set_rend_rend_service_staging_list(smartlist_t *new_list)
rend_service_staging_list = new_list;
}
-#endif /* TOR_UNIT_TESTS */
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 1583a6010b..5946e31861 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -13,11 +13,9 @@
#define TOR_RENDSERVICE_H
#include "or.h"
+#include "hs_service.h"
typedef struct rend_intro_cell_s rend_intro_cell_t;
-typedef struct rend_service_port_config_s rend_service_port_config_t;
-
-#ifdef RENDSERVICE_PRIVATE
/* This can be used for both INTRODUCE1 and INTRODUCE2 */
@@ -63,6 +61,8 @@ struct rend_intro_cell_s {
uint8_t dh[DH_KEY_LEN];
};
+#ifdef RENDSERVICE_PRIVATE
+
/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */
@@ -119,37 +119,32 @@ typedef struct rend_service_t {
STATIC void rend_service_free(rend_service_t *service);
STATIC char *rend_service_sos_poison_path(const rend_service_t *service);
-STATIC int rend_service_check_dir_and_add(smartlist_t *service_list,
- const or_options_t *options,
- rend_service_t *service,
- int validate_only);
STATIC int rend_service_verify_single_onion_poison(
const rend_service_t *s,
const or_options_t *options);
STATIC int rend_service_poison_new_single_onion_dir(
const rend_service_t *s,
const or_options_t* options);
-STATIC ssize_t encode_establish_intro_cell_legacy(char *cell_body_out,
- size_t cell_body_out_len,
- crypto_pk_t *intro_key,
- char *rend_circ_nonce);
#ifdef TOR_UNIT_TESTS
STATIC void set_rend_service_list(smartlist_t *new_list);
STATIC void set_rend_rend_service_staging_list(smartlist_t *new_list);
STATIC void rend_service_prune_list_impl_(void);
-#endif /* TOR_UNIT_TESTS */
+#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* RENDSERVICE_PRIVATE */
+#endif /* defined(RENDSERVICE_PRIVATE) */
-int num_rend_services(void);
-int rend_config_services(const or_options_t *options, int validate_only);
+int rend_num_services(void);
+int rend_config_service(const config_line_t *line_,
+ const or_options_t *options,
+ hs_service_config_t *config);
void rend_service_prune_list(void);
+void rend_service_free_staging_list(void);
int rend_service_load_all_keys(const smartlist_t *service_list);
void rend_services_add_filenames_to_lists(smartlist_t *open_lst,
smartlist_t *stat_lst);
-void rend_consider_services_intro_points(void);
+void rend_consider_services_intro_points(time_t now);
void rend_consider_services_upload(time_t now);
void rend_hsdir_routers_changed(void);
void rend_consider_descriptor_republication(void);
@@ -172,6 +167,10 @@ rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request,
char **err_msg_out);
int rend_service_parse_intro_plaintext(rend_intro_cell_t *intro,
char **err_msg_out);
+ssize_t rend_service_encode_establish_intro_cell(char *cell_body_out,
+ size_t cell_body_out_len,
+ crypto_pk_t *intro_key,
+ const char *rend_circ_nonce);
int rend_service_validate_intro_late(const rend_intro_cell_t *intro,
char **err_msg_out);
void rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc);
@@ -179,6 +178,7 @@ int rend_service_set_connection_addr_port(edge_connection_t *conn,
origin_circuit_t *circ);
void rend_service_dump_stats(int severity);
void rend_service_free_all(void);
+void rend_service_init(void);
rend_service_port_config_t *rend_service_parse_port_config(const char *string,
const char *sep,
@@ -214,5 +214,5 @@ int rend_service_allow_non_anonymous_connection(const or_options_t *options);
int rend_service_reveal_startup_time(const or_options_t *options);
int rend_service_non_anonymous_mode_enabled(const or_options_t *options);
-#endif
+#endif /* !defined(TOR_RENDSERVICE_H) */
diff --git a/src/or/rephist.c b/src/or/rephist.c
index ffc1867955..345722d8ce 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -1811,7 +1811,7 @@ static time_t last_prediction_add_time=0;
int
predicted_ports_prediction_time_remaining(time_t now)
{
- time_t idle_delta = now - last_prediction_add_time;
+ time_t idle_delta;
/* Protect against overflow of return value. This can happen if the clock
* jumps backwards in time. Update the last prediction time (aka last
@@ -1821,6 +1821,8 @@ predicted_ports_prediction_time_remaining(time_t now)
if (last_prediction_add_time > now) {
last_prediction_add_time = now;
idle_delta = 0;
+ } else {
+ idle_delta = now - last_prediction_add_time;
}
/* Protect against underflow of the return value. This can happen for very
@@ -2064,105 +2066,6 @@ rep_hist_circbuilding_dormant(time_t now)
return 1;
}
-/** Structure to track how many times we've done each public key operation. */
-static struct {
- /** How many directory objects have we signed? */
- unsigned long n_signed_dir_objs;
- /** How many routerdescs have we signed? */
- unsigned long n_signed_routerdescs;
- /** How many directory objects have we verified? */
- unsigned long n_verified_dir_objs;
- /** How many routerdescs have we verified */
- unsigned long n_verified_routerdescs;
- /** How many onionskins have we encrypted to build circuits? */
- unsigned long n_onionskins_encrypted;
- /** How many onionskins have we decrypted to do circuit build requests? */
- unsigned long n_onionskins_decrypted;
- /** How many times have we done the TLS handshake as a client? */
- unsigned long n_tls_client_handshakes;
- /** How many times have we done the TLS handshake as a server? */
- unsigned long n_tls_server_handshakes;
- /** How many PK operations have we done as a hidden service client? */
- unsigned long n_rend_client_ops;
- /** How many PK operations have we done as a hidden service midpoint? */
- unsigned long n_rend_mid_ops;
- /** How many PK operations have we done as a hidden service provider? */
- unsigned long n_rend_server_ops;
-} pk_op_counts = {0,0,0,0,0,0,0,0,0,0,0};
-
-/** Increment the count of the number of times we've done <b>operation</b>. */
-void
-note_crypto_pk_op(pk_op_t operation)
-{
- switch (operation)
- {
- case SIGN_DIR:
- pk_op_counts.n_signed_dir_objs++;
- break;
- case SIGN_RTR:
- pk_op_counts.n_signed_routerdescs++;
- break;
- case VERIFY_DIR:
- pk_op_counts.n_verified_dir_objs++;
- break;
- case VERIFY_RTR:
- pk_op_counts.n_verified_routerdescs++;
- break;
- case ENC_ONIONSKIN:
- pk_op_counts.n_onionskins_encrypted++;
- break;
- case DEC_ONIONSKIN:
- pk_op_counts.n_onionskins_decrypted++;
- break;
- case TLS_HANDSHAKE_C:
- pk_op_counts.n_tls_client_handshakes++;
- break;
- case TLS_HANDSHAKE_S:
- pk_op_counts.n_tls_server_handshakes++;
- break;
- case REND_CLIENT:
- pk_op_counts.n_rend_client_ops++;
- break;
- case REND_MID:
- pk_op_counts.n_rend_mid_ops++;
- break;
- case REND_SERVER:
- pk_op_counts.n_rend_server_ops++;
- break;
- default:
- log_warn(LD_BUG, "Unknown pk operation %d", operation);
- }
-}
-
-/** Log the number of times we've done each public/private-key operation. */
-void
-dump_pk_ops(int severity)
-{
- tor_log(severity, LD_HIST,
- "PK operations: %lu directory objects signed, "
- "%lu directory objects verified, "
- "%lu routerdescs signed, "
- "%lu routerdescs verified, "
- "%lu onionskins encrypted, "
- "%lu onionskins decrypted, "
- "%lu client-side TLS handshakes, "
- "%lu server-side TLS handshakes, "
- "%lu rendezvous client operations, "
- "%lu rendezvous middle operations, "
- "%lu rendezvous server operations.",
- pk_op_counts.n_signed_dir_objs,
- pk_op_counts.n_verified_dir_objs,
- pk_op_counts.n_signed_routerdescs,
- pk_op_counts.n_verified_routerdescs,
- pk_op_counts.n_onionskins_encrypted,
- pk_op_counts.n_onionskins_decrypted,
- pk_op_counts.n_tls_client_handshakes,
- pk_op_counts.n_tls_server_handshakes,
- pk_op_counts.n_rend_client_ops,
- pk_op_counts.n_rend_mid_ops,
- pk_op_counts.n_rend_server_ops);
-}
-
/*** Exit port statistics ***/
/* Some constants */
@@ -2651,7 +2554,7 @@ rep_hist_format_buffer_stats(time_t now)
processed_cells_string,
queued_cells_string,
time_in_queue_string,
- (number_of_circuits + SHARES - 1) / SHARES);
+ CEIL_DIV(number_of_circuits, SHARES));
tor_free(processed_cells_string);
tor_free(queued_cells_string);
tor_free(time_in_queue_string);
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 2b1c2e7ec7..496e366865 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -62,9 +62,6 @@ int any_predicted_circuits(time_t now);
int rep_hist_circbuilding_dormant(time_t now);
int predicted_ports_prediction_time_remaining(time_t now);
-void note_crypto_pk_op(pk_op_t operation);
-void dump_pk_ops(int severity);
-
void rep_hist_exit_stats_init(time_t now);
void rep_hist_reset_exit_stats(time_t now);
void rep_hist_exit_stats_term(void);
@@ -146,5 +143,5 @@ void rep_hist_reset_padding_counts(void);
void rep_hist_prep_published_padding_counts(time_t now);
void rep_hist_padding_count_timers(uint64_t num_timers);
-#endif
+#endif /* !defined(TOR_REPHIST_H) */
diff --git a/src/or/replaycache.h b/src/or/replaycache.h
index 0d637939a4..1cae3497ae 100644
--- a/src/or/replaycache.h
+++ b/src/or/replaycache.h
@@ -29,7 +29,7 @@ struct replaycache_s {
digest256map_t *digests_seen;
};
-#endif /* REPLAYCACHE_PRIVATE */
+#endif /* defined(REPLAYCACHE_PRIVATE) */
/* replaycache_t free/new */
@@ -51,7 +51,7 @@ STATIC int replaycache_add_and_test_internal(
STATIC void replaycache_scrub_if_needed_internal(
time_t present, replaycache_t *r);
-#endif /* REPLAYCACHE_PRIVATE */
+#endif /* defined(REPLAYCACHE_PRIVATE) */
/*
* replaycache_t methods
@@ -62,5 +62,5 @@ int replaycache_add_test_and_elapsed(
replaycache_t *r, const void *data, size_t len, time_t *elapsed);
void replaycache_scrub_if_needed(replaycache_t *r);
-#endif
+#endif /* !defined(TOR_REPLAYCACHE_H) */
diff --git a/src/or/router.c b/src/or/router.c
index 5405d31260..cacd1132a7 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -1080,7 +1080,7 @@ init_keys(void)
/* 4. Build our router descriptor. */
/* Must be called after keys are initialized. */
mydesc = router_get_my_descriptor();
- if (authdir_mode_handles_descs(options, ROUTER_PURPOSE_GENERAL)) {
+ if (authdir_mode_v3(options)) {
const char *m = NULL;
routerinfo_t *ri;
/* We need to add our own fingerprint so it gets recognized. */
@@ -1611,32 +1611,19 @@ authdir_mode_v3(const or_options_t *options)
{
return authdir_mode(options) && options->V3AuthoritativeDir != 0;
}
-/** Return true iff we are a v3 directory authority. */
-int
-authdir_mode_any_main(const or_options_t *options)
-{
- return options->V3AuthoritativeDir;
-}
-/** Return true if we believe ourselves to be any kind of
- * authoritative directory beyond just a hidserv authority. */
-int
-authdir_mode_any_nonhidserv(const or_options_t *options)
-{
- return options->BridgeAuthoritativeDir ||
- authdir_mode_any_main(options);
-}
/** Return true iff we are an authoritative directory server that is
* authoritative about receiving and serving descriptors of type
- * <b>purpose</b> on its dirport. Use -1 for "any purpose". */
+ * <b>purpose</b> on its dirport.
+ */
int
authdir_mode_handles_descs(const or_options_t *options, int purpose)
{
- if (purpose < 0)
- return authdir_mode_any_nonhidserv(options);
+ if (BUG(purpose < 0)) /* Deprecated. */
+ return authdir_mode(options);
else if (purpose == ROUTER_PURPOSE_GENERAL)
- return authdir_mode_any_main(options);
+ return authdir_mode_v3(options);
else if (purpose == ROUTER_PURPOSE_BRIDGE)
- return (options->BridgeAuthoritativeDir);
+ return authdir_mode_bridge(options);
else
return 0;
}
@@ -1648,7 +1635,7 @@ authdir_mode_publishes_statuses(const or_options_t *options)
{
if (authdir_mode_bridge(options))
return 0;
- return authdir_mode_any_nonhidserv(options);
+ return authdir_mode(options);
}
/** Return true iff we are an authoritative directory server that
* tests reachability of the descriptors it learns about.
@@ -1656,7 +1643,7 @@ authdir_mode_publishes_statuses(const or_options_t *options)
int
authdir_mode_tests_reachability(const or_options_t *options)
{
- return authdir_mode_handles_descs(options, -1);
+ return authdir_mode(options);
}
/** Return true iff we believe ourselves to be a bridge authoritative
* directory server.
@@ -1879,12 +1866,12 @@ static routerinfo_t *desc_routerinfo = NULL;
static extrainfo_t *desc_extrainfo = NULL;
/** Why did we most recently decide to regenerate our descriptor? Used to
* tell the authorities why we're sending it to them. */
-static const char *desc_gen_reason = NULL;
+static const char *desc_gen_reason = "uninitialized reason";
/** Since when has our descriptor been "clean"? 0 if we need to regenerate it
* now. */
static time_t desc_clean_since = 0;
/** Why did we mark the descriptor dirty? */
-static const char *desc_dirty_reason = NULL;
+static const char *desc_dirty_reason = "Tor just started";
/** Boolean: do we need to regenerate the above? */
static int desc_needs_upload = 0;
@@ -1965,7 +1952,7 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port)
desc_routerinfo->ipv6_exit_policy &&
compare_tor_addr_to_short_policy(addr, port,
me->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED;
-#endif
+#endif /* 0 */
} else {
return -1;
}
@@ -2307,7 +2294,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
if (!strcasecmp(name, options->Nickname))
continue; /* Don't list ourself, that's redundant */
else
- member = node_get_by_nickname(name, 1);
+ member = node_get_by_nickname(name, 0);
if (!member) {
int is_legal = is_legal_nickname_or_hexdigest(name);
if (!smartlist_contains_string(warned_nonexistent_family, name) &&
@@ -2467,6 +2454,9 @@ router_rebuild_descriptor(int force)
desc_clean_since = time(NULL);
desc_needs_upload = 1;
desc_gen_reason = desc_dirty_reason;
+ if (BUG(desc_gen_reason == NULL)) {
+ desc_gen_reason = "descriptor was marked dirty earlier, for no reason.";
+ }
desc_dirty_reason = NULL;
control_event_my_descriptor_changed();
return 0;
@@ -2523,6 +2513,9 @@ void
mark_my_descriptor_dirty(const char *reason)
{
const or_options_t *options = get_options();
+ if (BUG(reason == NULL)) {
+ reason = "marked descriptor dirty for unspecified reason";
+ }
if (server_mode(options) && options->PublishServerDescriptor_)
log_info(LD_OR, "Decided to publish new relay descriptor: %s", reason);
desc_clean_since = 0;
@@ -2978,10 +2971,13 @@ router_dump_router_to_string(routerinfo_t *router,
if (options->BridgeRelay) {
const char *bd;
- if (options->PublishServerDescriptor_ & BRIDGE_DIRINFO)
+ if (options->BridgeDistribution && strlen(options->BridgeDistribution)) {
+ bd = options->BridgeDistribution;
+ } else {
bd = "any";
- else
- bd = "none";
+ }
+ if (strchr(bd, '\n') || strchr(bd, '\r'))
+ bd = escaped(bd);
smartlist_add_asprintf(chunks, "bridge-distribution-request %s\n", bd);
}
@@ -3046,7 +3042,6 @@ router_dump_router_to_string(routerinfo_t *router,
crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1);
- note_crypto_pk_op(SIGN_RTR);
{
char *sig;
if (!(sig = router_get_dirobj_signature(digest, DIGEST_LEN, ident_key))) {
@@ -3077,7 +3072,7 @@ router_dump_router_to_string(routerinfo_t *router,
tor_free(s_dup);
routerinfo_free(ri_tmp);
}
-#endif
+#endif /* defined(DEBUG_ROUTER_DUMP_ROUTER_TO_STRING) */
goto done;
@@ -3527,7 +3522,7 @@ router_get_description(char *buf, const routerinfo_t *ri)
return "<null>";
return format_node_description(buf,
ri->cache_info.identity_digest,
- router_is_named(ri),
+ 0,
ri->nickname,
NULL,
ri->addr);
@@ -3637,7 +3632,7 @@ routerstatus_describe(const routerstatus_t *rs)
return routerstatus_get_description(buf, rs);
}
-/** Return a human-readable description of the extend_info_t <b>ri</b>.
+/** Return a human-readable description of the extend_info_t <b>ei</b>.
*
* This function is not thread-safe. Each call to this function invalidates
* previous values returned by this function.
@@ -3653,21 +3648,16 @@ extend_info_describe(const extend_info_t *ei)
* verbose representation of the identity of <b>router</b>. The format is:
* A dollar sign.
* The upper-case hexadecimal encoding of the SHA1 hash of router's identity.
- * A "=" if the router is named; a "~" if it is not.
+ * A "=" if the router is named (no longer implemented); a "~" if it is not.
* The router's nickname.
**/
void
router_get_verbose_nickname(char *buf, const routerinfo_t *router)
{
- const char *good_digest = networkstatus_get_router_digest_by_nickname(
- router->nickname);
- int is_named = good_digest && tor_memeq(good_digest,
- router->cache_info.identity_digest,
- DIGEST_LEN);
buf[0] = '$';
base16_encode(buf+1, HEX_DIGEST_LEN+1, router->cache_info.identity_digest,
DIGEST_LEN);
- buf[1+HEX_DIGEST_LEN] = is_named ? '=' : '~';
+ buf[1+HEX_DIGEST_LEN] = '~';
strlcpy(buf+1+HEX_DIGEST_LEN+1, router->nickname, MAX_NICKNAME_LEN+1);
}
diff --git a/src/or/router.h b/src/or/router.h
index 9c5def5218..3351400911 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -54,8 +54,6 @@ int net_is_disabled(void);
int authdir_mode(const or_options_t *options);
int authdir_mode_v3(const or_options_t *options);
-int authdir_mode_any_main(const or_options_t *options);
-int authdir_mode_any_nonhidserv(const or_options_t *options);
int authdir_mode_handles_descs(const or_options_t *options, int purpose);
int authdir_mode_publishes_statuses(const or_options_t *options);
int authdir_mode_tests_reachability(const or_options_t *options);
@@ -162,5 +160,5 @@ STATIC void get_platform_str(char *platform, size_t len);
STATIC int router_write_fingerprint(int hashed);
#endif
-#endif
+#endif /* !defined(TOR_ROUTER_H) */
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index fd4c6ce0dd..f0973044b5 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -536,7 +536,8 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
bad_cert = 1;
} else if (signing_key &&
tor_cert_checksig(cert, &signing_key->pubkey, now) < 0) {
- tor_log(severity, LD_OR, "Can't check certificate");
+ tor_log(severity, LD_OR, "Can't check certificate: %s",
+ tor_cert_describe_signature_status(cert));
bad_cert = 1;
} else if (cert->cert_expired) {
tor_log(severity, LD_OR, "Certificate is expired");
@@ -883,8 +884,12 @@ load_ed_keys(const or_options_t *options, time_t now)
if (! ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey))
FAIL("The signing cert we have was not signed with the master key "
"we loaded!");
- if (tor_cert_checksig(sign_cert, &id->pubkey, 0) < 0)
- FAIL("The signing cert we loaded was not signed correctly!");
+ if (tor_cert_checksig(sign_cert, &id->pubkey, 0) < 0) {
+ log_warn(LD_OR, "The signing cert we loaded was not signed "
+ "correctly: %s!",
+ tor_cert_describe_signature_status(sign_cert));
+ goto err;
+ }
}
if (want_new_signing_key && sign_signing_key_with_id) {
@@ -1134,7 +1139,109 @@ init_mock_ed_keys(const crypto_pk_t *rsa_identity_key)
}
#undef MAKEKEY
#undef MAKECERT
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/**
+ * Print the ISO8601-formated <b>expiration</b> for a certificate with
+ * some <b>description</b> to stdout.
+ *
+ * For example, for a signing certificate, this might print out:
+ * signing-cert-expiry: 2017-07-25 08:30:15 UTC
+ */
+static void
+print_cert_expiration(const char *expiration,
+ const char *description)
+{
+ fprintf(stderr, "%s-cert-expiry: %s\n", description, expiration);
+}
+
+/**
+ * Log when a certificate, <b>cert</b>, with some <b>description</b> and
+ * stored in a file named <b>fname</b>, is going to expire.
+ */
+static void
+log_ed_cert_expiration(const tor_cert_t *cert,
+ const char *description,
+ const char *fname) {
+ char expiration[ISO_TIME_LEN+1];
+
+ if (BUG(!cert)) { /* If the specified key hasn't been loaded */
+ log_warn(LD_OR, "No %s key loaded; can't get certificate expiration.",
+ description);
+ } else {
+ format_local_iso_time(expiration, cert->valid_until);
+ log_notice(LD_OR, "The %s certificate stored in %s is valid until %s.",
+ description, fname, expiration);
+ print_cert_expiration(expiration, description);
+ }
+}
+
+/**
+ * Log when our master signing key certificate expires. Used when tor is given
+ * the --key-expiration command-line option.
+ *
+ * Returns 0 on success and 1 on failure.
+ */
+static int
+log_master_signing_key_cert_expiration(const or_options_t *options)
+{
+ const tor_cert_t *signing_key;
+ char *fn = NULL;
+ int failed = 0;
+ time_t now = approx_time();
+
+ fn = options_get_datadir_fname2(options, "keys", "ed25519_signing_cert");
+
+ /* Try to grab our cached copy of the key. */
+ signing_key = get_master_signing_key_cert();
+
+ tor_assert(server_identity_key_is_set());
+
+ /* Load our keys from disk, if necessary. */
+ if (!signing_key) {
+ failed = load_ed_keys(options, now) < 0;
+ signing_key = get_master_signing_key_cert();
+ }
+
+ /* If we do have a signing key, log the expiration time. */
+ if (signing_key) {
+ log_ed_cert_expiration(signing_key, "signing", fn);
+ } else {
+ log_warn(LD_OR, "Could not load signing key certificate from %s, so " \
+ "we couldn't learn anything about certificate expiration.", fn);
+ }
+
+ tor_free(fn);
+
+ return failed;
+}
+
+/**
+ * Log when a key certificate expires. Used when tor is given the
+ * --key-expiration command-line option.
+ *
+ * If an command argument is given, which should specify the type of
+ * key to get expiry information about (currently supported arguments
+ * are "sign"), get info about that type of certificate. Otherwise,
+ * print info about the supported arguments.
+ *
+ * Returns 0 on success and -1 on failure.
+ */
+int
+log_cert_expiration(void)
+{
+ const or_options_t *options = get_options();
+ const char *arg = options->command_arg;
+
+ if (!strcmp(arg, "sign")) {
+ return log_master_signing_key_cert_expiration(options);
+ } else {
+ fprintf(stderr, "No valid argument to --key-expiration found!\n");
+ fprintf(stderr, "Currently recognised arguments are: 'sign'\n");
+
+ return -1;
+ }
+}
const ed25519_public_key_t *
get_master_identity_key(void)
@@ -1160,7 +1267,7 @@ get_master_identity_keypair(void)
{
return master_identity_key;
}
-#endif
+#endif /* defined(TOR_UNIT_TESTS) */
const ed25519_keypair_t *
get_master_signing_keypair(void)
diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h
index c10cf32a71..3e67952ea0 100644
--- a/src/or/routerkeys.h
+++ b/src/or/routerkeys.h
@@ -63,6 +63,7 @@ MOCK_DECL(int, check_tap_onion_key_crosscert,(const uint8_t *crosscert,
const ed25519_public_key_t *master_id_pkey,
const uint8_t *rsa_id_digest));
+int log_cert_expiration(void);
int load_ed_keys(const or_options_t *options, time_t now);
int should_make_new_ed_keys(const or_options_t *options, const time_t now);
@@ -80,5 +81,5 @@ const ed25519_keypair_t *get_master_identity_keypair(void);
void init_mock_ed_keys(const crypto_pk_t *rsa_identity_key);
#endif
-#endif
+#endif /* !defined(TOR_ROUTERKEYS_H) */
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index c7beec77b7..af4f67dc12 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1873,7 +1873,7 @@ router_picked_poor_directory_log(const routerstatus_t *rs)
|| !router_have_minimum_dir_info()) {
return;
}
-#endif
+#endif /* !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP */
/* We couldn't find a node, or the one we have doesn't fit our preferences.
* Sometimes this is normal, sometimes it can be a reachability issue. */
@@ -2307,7 +2307,7 @@ routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router)
{
/* XXXX MOVE ? */
node_t fake_node;
- const node_t *node = node_get_by_id(router->cache_info.identity_digest);;
+ const node_t *node = node_get_by_id(router->cache_info.identity_digest);
if (node == NULL) {
memset(&fake_node, 0, sizeof(fake_node));
fake_node.ri = (routerinfo_t *)router;
@@ -2813,6 +2813,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
const int need_desc = (flags & CRN_NEED_DESC) != 0;
const int pref_addr = (flags & CRN_PREF_ADDR) != 0;
const int direct_conn = (flags & CRN_DIRECT_CONN) != 0;
+ const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0;
smartlist_t *sl=smartlist_new(),
*excludednodes=smartlist_new();
@@ -2824,12 +2825,19 @@ router_choose_random_node(smartlist_t *excludedsmartlist,
rule = weight_for_exit ? WEIGHT_FOR_EXIT :
(need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID);
- /* Exclude relays that allow single hop exit circuits. This is an obsolete
- * option since 0.2.9.2-alpha and done by default in 0.3.1.0-alpha. */
- SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) {
if (node_allows_single_hop_exits(node)) {
+ /* Exclude relays that allow single hop exit circuits. This is an
+ * obsolete option since 0.2.9.2-alpha and done by default in
+ * 0.3.1.0-alpha. */
smartlist_add(excludednodes, node);
- });
+ } else if (rendezvous_v3 &&
+ !node_supports_v3_rendezvous_point(node)) {
+ /* Exclude relays that do not support to rendezvous for a hidden service
+ * version 3. */
+ smartlist_add(excludednodes, node);
+ }
+ } SMARTLIST_FOREACH_END(node);
/* If the node_t is not found we won't be to exclude ourself but we
* won't be able to pick ourself in router_choose_random_node() so
@@ -2943,7 +2951,7 @@ hex_digest_nickname_decode(const char *hexdigest,
* <b>hexdigest</b> is malformed, or it doesn't match. */
int
hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
- const char *nickname, int is_named)
+ const char *nickname)
{
char digest[DIGEST_LEN];
char nn_char='\0';
@@ -2952,30 +2960,20 @@ hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest,
if (hex_digest_nickname_decode(hexdigest, digest, &nn_char, nn_buf) == -1)
return 0;
- if (nn_char == '=' || nn_char == '~') {
- if (!nickname)
+ if (nn_char == '=') {
+ return 0;
+ }
+
+ if (nn_char == '~') {
+ if (!nickname) // XXX This seems wrong. -NM
return 0;
if (strcasecmp(nn_buf, nickname))
return 0;
- if (nn_char == '=' && !is_named)
- return 0;
}
return tor_memeq(digest, identity_digest, DIGEST_LEN);
}
-/** Return true iff <b>router</b> is listed as named in the current
- * consensus. */
-int
-router_is_named(const routerinfo_t *router)
-{
- const char *digest =
- networkstatus_get_router_digest_by_nickname(router->nickname);
-
- return (digest &&
- tor_memeq(digest, router->cache_info.identity_digest, DIGEST_LEN));
-}
-
/** Return true iff <b>digest</b> is the digest of the identity key of a
* trusted directory matching at least one bit of <b>type</b>. If <b>type</b>
* is zero (NO_DIRINFO), or ALL_DIRINFO, any authority is okay. */
@@ -4071,7 +4069,7 @@ routerlist_remove_old_cached_routers_with_id(time_t now,
signed_descriptor_t *r = smartlist_get(lst, i);
tor_assert(tor_memeq(ident, r->identity_digest, DIGEST_LEN));
}
-#endif
+#endif /* 1 */
/* Check whether we need to do anything at all. */
{
int mdpr = directory_caches_dir_info(get_options()) ? 2 : 1;
@@ -4988,8 +4986,9 @@ max_dl_per_request(const or_options_t *options, int purpose)
}
/** Don't split our requests so finely that we are requesting fewer than
- * this number per server. */
-#define MIN_DL_PER_REQUEST 4
+ * this number per server. (Grouping more than this at once leads to
+ * diminishing returns.) */
+#define MIN_DL_PER_REQUEST 32
/** To prevent a single screwy cache from confusing us by selective reply,
* try to split our requests into at least this many requests. */
#define MIN_REQUESTS 3
@@ -5055,7 +5054,7 @@ launch_descriptor_downloads(int purpose,
}
}
- if (!authdir_mode_any_nonhidserv(options)) {
+ if (!authdir_mode(options)) {
/* If we wind up going to the authorities, we want to only open one
* connection to each authority at a time, so that we don't overload
* them. We do this by setting PDS_NO_EXISTING_SERVERDESC_FETCH
@@ -5077,8 +5076,9 @@ launch_descriptor_downloads(int purpose,
if (n_per_request > max_dl_per_req)
n_per_request = max_dl_per_req;
- if (n_per_request < MIN_DL_PER_REQUEST)
- n_per_request = MIN_DL_PER_REQUEST;
+ if (n_per_request < MIN_DL_PER_REQUEST) {
+ n_per_request = MIN(MIN_DL_PER_REQUEST, n_downloadable);
+ }
if (n_downloadable > n_per_request)
req_plural = rtr_plural = "s";
@@ -5186,7 +5186,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
smartlist_add(downloadable, rs->descriptor_digest);
} SMARTLIST_FOREACH_END(rsp);
- if (!authdir_mode_handles_descs(options, ROUTER_PURPOSE_GENERAL)
+ if (!authdir_mode_v3(options)
&& smartlist_len(no_longer_old)) {
routerlist_t *rl = router_get_routerlist();
log_info(LD_DIR, "%d router descriptors listed in consensus are "
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index e0ed4e623a..8384c7eb8c 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -80,7 +80,6 @@ const node_t *router_choose_random_node(smartlist_t *excludedsmartlist,
struct routerset_t *excludedset,
router_crn_flags_t flags);
-int router_is_named(const routerinfo_t *router);
int router_digest_is_trusted_dir_type(const char *digest,
dirinfo_type_t type);
#define router_digest_is_trusted_dir(d) \
@@ -228,7 +227,7 @@ int hex_digest_nickname_decode(const char *hexdigest,
char *nickname_out);
int hex_digest_nickname_matches(const char *hexdigest,
const char *identity_digest,
- const char *nickname, int is_named);
+ const char *nickname);
#ifdef ROUTERLIST_PRIVATE
STATIC int choose_array_element_by_weight(const uint64_t *entries,
@@ -252,7 +251,7 @@ MOCK_DECL(STATIC void, initiate_descriptor_downloads,
STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap,
int serverdesc, int microdesc);
-#endif
+#endif /* defined(ROUTERLIST_PRIVATE) */
-#endif
+#endif /* !defined(TOR_ROUTERLIST_H) */
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 22521a3069..3eda024f0f 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -388,9 +388,9 @@ static int check_signature_token(const char *digest,
log_debug(LD_MM, "Area for %s has %lu allocated; using %lu.", \
name, (unsigned long)alloc, (unsigned long)used); \
STMT_END
-#else
+#else /* !(defined(DEBUG_AREA_ALLOC)) */
#define DUMP_AREA(a,name) STMT_NIL
-#endif
+#endif /* defined(DEBUG_AREA_ALLOC) */
/* Dump mechanism for unparseable descriptors */
@@ -701,7 +701,7 @@ dump_desc_populate_one_file, (const char *dirname, const char *f))
goto done;
/* LCOV_EXCL_STOP */
}
-#endif
+#endif /* SIZE_MAX > UINT64_MAX */
if (BUG(st.st_size < 0)) {
/* LCOV_EXCL_START
* Should be impossible, since the OS isn't supposed to be b0rken. */
@@ -1425,9 +1425,9 @@ dump_distinct_digest_count(int severity)
verified_digests = digestmap_new();
tor_log(severity, LD_GENERAL, "%d *distinct* router digests verified",
digestmap_size(verified_digests));
-#else
+#else /* !(defined(COUNT_DISTINCT_DIGESTS)) */
(void)severity; /* suppress "unused parameter" warning */
-#endif
+#endif /* defined(COUNT_DISTINCT_DIGESTS) */
}
/** Try to find an IPv6 OR port in <b>list</b> of directory_token_t's
@@ -1996,7 +1996,6 @@ router_parse_entry_from_string(const char *s, const char *end,
}
tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE);
- note_crypto_pk_op(VERIFY_RTR);
#ifdef COUNT_DISTINCT_DIGESTS
if (!verified_digests)
verified_digests = digestmap_new();
@@ -2231,7 +2230,6 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
}
if (key) {
- note_crypto_pk_op(VERIFY_RTR);
if (check_signature_token(digest, DIGEST_LEN, tok, key, 0,
"extra-info") < 0)
goto err;
@@ -2708,7 +2706,11 @@ routerstatus_parse_entry_from_string(memarea_t *area,
rs->supports_ed25519_hs_intro =
protocol_list_supports_protocol(tok->args[0], PRT_HSINTRO, 4);
rs->supports_v3_hsdir =
- protocol_list_supports_protocol(tok->args[0], PRT_HSDIR, 2);
+ protocol_list_supports_protocol(tok->args[0], PRT_HSDIR,
+ PROTOVER_HSDIR_V3);
+ rs->supports_v3_rendezvous_point =
+ protocol_list_supports_protocol(tok->args[0], PRT_HSREND,
+ PROTOVER_HS_RENDEZVOUS_POINT_V3);
}
if ((tok = find_opt_by_keyword(tokens, K_V))) {
tor_assert(tok->n_args == 1);
@@ -2720,6 +2722,12 @@ routerstatus_parse_entry_from_string(memarea_t *area,
tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha");
rs->protocols_known = 1;
}
+ if (!strcmpstart(tok->args[0], "Tor ") && found_protocol_list) {
+ /* Bug #22447 forces us to filter on this version. */
+ if (!tor_version_as_new_as(tok->args[0], "0.3.0.8")) {
+ rs->supports_v3_hsdir = 0;
+ }
+ }
if (vote_rs) {
vote_rs->version = tor_strdup(tok->args[0]);
}
@@ -2856,7 +2864,6 @@ compare_vote_routerstatus_entries(const void **_a, const void **_b)
int
networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
{
- int64_t weight_scale;
int64_t G=0, M=0, E=0, D=0, T=0;
double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
double Gtotal=0, Mtotal=0, Etotal=0;
@@ -2864,7 +2871,8 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
int valid = 1;
(void) consensus_method;
- weight_scale = networkstatus_get_weight_scale_param(ns);
+ const int64_t weight_scale = networkstatus_get_weight_scale_param(ns);
+ tor_assert(weight_scale >= 1);
Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
@@ -2926,7 +2934,7 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
}
Wgg /= weight_scale;
- Wgm /= weight_scale;
+ Wgm /= weight_scale; (void) Wgm; // unused from here on.
Wgd /= weight_scale;
Wmg /= weight_scale;
@@ -2934,8 +2942,8 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
Wme /= weight_scale;
Wmd /= weight_scale;
- Weg /= weight_scale;
- Wem /= weight_scale;
+ Weg /= weight_scale; (void) Weg; // unused from here on.
+ Wem /= weight_scale; (void) Wem; // unused from here on.
Wee /= weight_scale;
Wed /= weight_scale;
@@ -3360,8 +3368,8 @@ extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
voter_identity = "consensus";
}
- /* We extract both and on error, everything is stopped because it means
- * the votes is malformed for the shared random value(s). */
+ /* We extract both, and on error everything is stopped because it means
+ * the vote is malformed for the shared random value(s). */
if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
log_warn(LD_DIR, "SR: Unable to parse previous SRV from %s",
voter_identity);
@@ -5288,7 +5296,6 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
}
/* Parse and verify signature. */
tok = find_by_keyword(tokens, R_SIGNATURE);
- note_crypto_pk_op(VERIFY_RTR);
if (check_signature_token(desc_hash, DIGEST_LEN, tok, result->pk, 0,
"v2 rendezvous service descriptor") < 0)
goto err;
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index 088f773c5e..c4c8635f4b 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -133,9 +133,9 @@ MOCK_DECL(STATIC int, router_compute_hash_final,(char *digest,
digest_algorithm_t alg));
MOCK_DECL(STATIC int, signed_digest_equals,
(const uint8_t *d1, const uint8_t *d2, size_t len));
-#endif
+#endif /* defined(ROUTERPARSE_PRIVATE) */
#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1"
-#endif
+#endif /* !defined(TOR_ROUTERPARSE_H) */
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 4906c6a51d..54e26ef943 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -362,7 +362,7 @@ routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset,
/* No routers are specified by type; all are given by name or digest.
* we can do a lookup in O(len(routerset)). */
SMARTLIST_FOREACH(routerset->list, const char *, name, {
- const node_t *node = node_get_by_nickname(name, 1);
+ const node_t *node = node_get_by_nickname(name, 0);
if (node) {
if (!running_only || node->is_running)
if (!routerset_contains_node(excludeset, node))
diff --git a/src/or/routerset.h b/src/or/routerset.h
index a63677b471..d8819ef3fd 100644
--- a/src/or/routerset.h
+++ b/src/or/routerset.h
@@ -82,6 +82,6 @@ struct routerset_t {
* reloaded. */
bitarray_t *countries;
};
-#endif
-#endif
+#endif /* defined(ROUTERSET_PRIVATE) */
+#endif /* !defined(TOR_ROUTERSET_H) */
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index fac545fba7..1984084feb 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -1,46 +1,55 @@
-/* * Copyright (c) 2013-2017, The Tor Project, Inc. */
+/* Copyright (c) 2013-2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#include "or.h"
-
-#define TOR_CHANNEL_INTERNAL_ /* For channel_flush_some_cells() */
-#include "channel.h"
+#include "config.h"
#include "compat_libevent.h"
#define SCHEDULER_PRIVATE_
+#define SCHEDULER_KIST_PRIVATE
#include "scheduler.h"
+#include "main.h"
+#include "buffers.h"
+#define TOR_CHANNEL_INTERNAL_
+#include "channeltls.h"
#include <event2/event.h>
-/*
- * Scheduler high/low watermarks
- */
-
-static uint32_t sched_q_low_water = 16384;
-static uint32_t sched_q_high_water = 32768;
-
-/*
- * Maximum cells to flush in a single call to channel_flush_some_cells();
- * setting this low means more calls, but too high and we could overshoot
- * sched_q_high_water.
- */
-
-static uint32_t sched_max_flush_cells = 16;
-
/**
* \file scheduler.c
* \brief Channel scheduling system: decides which channels should send and
* receive when.
*
- * This module implements a scheduler algorithm, to decide
- * which channels should send/receive when.
+ * This module is the global/common parts of the scheduling system. This system
+ * is what decides what channels get to send cells on their circuits and when.
+ *
+ * Terms:
+ * - "Scheduling system": the collection of scheduler*.{h,c} files and their
+ * aggregate behavior.
+ * - "Scheduler implementation": a scheduler_t. The scheduling system has one
+ * active scheduling implementation at a time.
+ *
+ * In this file you will find state that any scheduler implementation can have
+ * access to as well as the functions the rest of Tor uses to interact with the
+ * scheduling system.
*
* The earliest versions of Tor approximated a kind of round-robin system
- * among active connections, but only approximated it.
+ * among active connections, but only approximated it. It would only consider
+ * one connection (roughly equal to a channel in today's terms) at a time, and
+ * thus could only prioritize circuits against others on the same connection.
+ *
+ * Then in response to the KIST paper[0], Tor implemented a global
+ * circuit scheduler. It was supposed to prioritize circuits across many
+ * channels, but wasn't effective. It is preserved in scheduler_vanilla.c.
*
- * Now, write scheduling works by keeping track of which channels can
- * accept cells, and have cells to write. From the scheduler's perspective,
- * a channel can be in four possible states:
+ * [0]: http://www.robgjansen.com/publications/kist-sec2014.pdf
+ *
+ * Then we actually got around to implementing KIST for real. We decided to
+ * modularize the scheduler so new ones can be implemented. You can find KIST
+ * in scheduler_kist.c.
+ *
+ * Channels have one of four scheduling states based on whether or not they
+ * have cells to send and whether or not they are able to send.
*
* <ol>
* <li>
@@ -90,7 +99,7 @@ static uint32_t sched_max_flush_cells = 16;
* <ul>
* <li>Not open for writes/no cells by arrival of cells on an attached
* circuit
- * <li> Open for writes/has cells by filling an output buffer without
+ * <li>Open for writes/has cells by filling an output buffer without
* draining all cells from attached circuits
* </ul>
* <li> Transitions to:
@@ -107,9 +116,9 @@ static uint32_t sched_max_flush_cells = 16;
* SCHED_CHAN_PENDING.
* <li>Transitions from:
* <ul>
- * <li> Not open for writes/has cells by the connection_or_flushed_some()
+ * <li>Not open for writes/has cells by the connection_or_flushed_some()
* path
- * <li> Open for writes/no cells by the append_cell_to_circuit_queue()
+ * <li>Open for writes/no cells by the append_cell_to_circuit_queue()
* path
* </ul>
* <li> Transitions to:
@@ -125,85 +134,246 @@ static uint32_t sched_max_flush_cells = 16;
* </ol>
*
* Other event-driven parts of the code move channels between these scheduling
- * states by calling scheduler functions; the scheduler only runs on open-for-
- * writes/has-cells channels and is the only path for those to transition to
- * other states. The scheduler_run() function gives us the opportunity to do
- * scheduling work, and is called from other scheduler functions whenever a
- * state transition occurs, and periodically from the main event loop.
+ * states by calling scheduler functions. The scheduling system builds up a
+ * list of channels in the SCHED_CHAN_PENDING state that the scheduler
+ * implementation should then use when it runs. Scheduling implementations need
+ * to properly update channel states during their scheduler_t->run() function
+ * as that is the only opportunity for channels to move from SCHED_CHAN_PENDING
+ * to any other state.
+ *
+ * The remainder of this file is a small amount of state that any scheduler
+ * implementation should have access to, and the functions the rest of Tor uses
+ * to interact with the scheduling system.
*/
-/* Scheduler global data structures */
+/*****************************************************************************
+ * Scheduling system state
+ *
+ * State that can be accessed from any scheduler implementation (but not
+ * outside the scheduling system)
+ *****************************************************************************/
-/*
+/** DOCDOC */
+STATIC const scheduler_t *the_scheduler;
+
+/**
* We keep a list of channels that are pending - i.e, have cells to write
- * and can accept them to send. The enum scheduler_state in channel_t
+ * and can accept them to send. The enum scheduler_state in channel_t
* is reserved for our use.
+ *
+ * Priority queue of channels that can write and have cells (pending work)
*/
-
-/* Pqueue of channels that can write and have cells (pending work) */
STATIC smartlist_t *channels_pending = NULL;
-/*
+/**
* This event runs the scheduler from its callback, and is manually
* activated whenever a channel enters open for writes/cells to send.
*/
-
STATIC struct event *run_sched_ev = NULL;
-/*
- * Queue heuristic; this is not the queue size, but an 'effective queuesize'
- * that ages out contributions from stalled channels.
- */
+static int have_logged_kist_suddenly_disabled = 0;
-STATIC uint64_t queue_heuristic = 0;
+/*****************************************************************************
+ * Scheduling system static function definitions
+ *
+ * Functions that can only be accessed from this file.
+ *****************************************************************************/
-/*
- * Timestamp for last queue heuristic update
+/** Return a human readable string for the given scheduler type. */
+static const char *
+get_scheduler_type_string(scheduler_types_t type)
+{
+ switch (type) {
+ case SCHEDULER_VANILLA:
+ return "Vanilla";
+ case SCHEDULER_KIST:
+ return "KIST";
+ case SCHEDULER_KIST_LITE:
+ return "KISTLite";
+ case SCHEDULER_NONE:
+ /* fallthrough */
+ default:
+ tor_assert_unreached();
+ return "(N/A)";
+ }
+}
+
+/**
+ * Scheduler event callback; this should get triggered once per event loop
+ * if any scheduling work was created during the event loop.
*/
+static void
+scheduler_evt_callback(evutil_socket_t fd, short events, void *arg)
+{
+ (void) fd;
+ (void) events;
+ (void) arg;
-STATIC time_t queue_heuristic_timestamp = 0;
+ log_debug(LD_SCHED, "Scheduler event callback called");
-/* Scheduler static function declarations */
+ /* Run the scheduler. This is a mandatory function. */
-static void scheduler_evt_callback(evutil_socket_t fd,
- short events, void *arg);
-static int scheduler_more_work(void);
-static void scheduler_retrigger(void);
-#if 0
-static void scheduler_trigger(void);
-#endif
+ /* We might as well assert on this. If this function doesn't exist, no cells
+ * are getting scheduled. Things are very broken. scheduler_t says the run()
+ * function is mandatory. */
+ tor_assert(the_scheduler->run);
+ the_scheduler->run();
-/* Scheduler function implementations */
+ /* Schedule itself back in if it has more work. */
-/** Free everything and shut down the scheduling system */
+ /* Again, might as well assert on this mandatory scheduler_t function. If it
+ * doesn't exist, there's no way to tell libevent to run the scheduler again
+ * in the future. */
+ tor_assert(the_scheduler->schedule);
+ the_scheduler->schedule();
+}
-void
-scheduler_free_all(void)
+/** Using the global options, select the scheduler we should be using. */
+static void
+select_scheduler(void)
{
- log_debug(LD_SCHED, "Shutting down scheduler");
-
- if (run_sched_ev) {
- if (event_del(run_sched_ev) < 0) {
- log_warn(LD_BUG, "Problem deleting run_sched_ev");
+ scheduler_t *new_scheduler = NULL;
+
+#ifdef TOR_UNIT_TESTS
+ /* This is hella annoying to set in the options for every test that passes
+ * through the scheduler and there are many so if we don't explicitly have
+ * a list of types set, just put the vanilla one. */
+ if (get_options()->SchedulerTypes_ == NULL) {
+ the_scheduler = get_vanilla_scheduler();
+ return;
+ }
+#endif /* defined(TOR_UNIT_TESTS) */
+
+ /* This list is ordered that is first entry has the first priority. Thus, as
+ * soon as we find a scheduler type that we can use, we use it and stop. */
+ SMARTLIST_FOREACH_BEGIN(get_options()->SchedulerTypes_, int *, type) {
+ switch (*type) {
+ case SCHEDULER_VANILLA:
+ new_scheduler = get_vanilla_scheduler();
+ goto end;
+ case SCHEDULER_KIST:
+ if (!scheduler_can_use_kist()) {
+#ifdef HAVE_KIST_SUPPORT
+ if (!have_logged_kist_suddenly_disabled) {
+ /* We should only log this once in most cases. If it was the kernel
+ * losing support for kist that caused scheduler_can_use_kist() to
+ * return false, then this flag makes sure we only log this message
+ * once. If it was the consensus that switched from "yes use kist"
+ * to "no don't use kist", then we still set the flag so we log
+ * once, but we unset the flag elsewhere if we ever can_use_kist()
+ * again.
+ */
+ have_logged_kist_suddenly_disabled = 1;
+ log_notice(LD_SCHED, "Scheduler type KIST has been disabled by "
+ "the consensus or no kernel support.");
+ }
+#else /* !(defined(HAVE_KIST_SUPPORT)) */
+ log_info(LD_SCHED, "Scheduler type KIST not built in");
+#endif /* defined(HAVE_KIST_SUPPORT) */
+ continue;
+ }
+ /* This flag will only get set in one of two cases:
+ * 1 - the kernel lost support for kist. In that case, we don't expect to
+ * ever end up here
+ * 2 - the consensus went from "yes use kist" to "no don't use kist".
+ * We might end up here if the consensus changes back to "yes", in which
+ * case we might want to warn the user again if it goes back to "no"
+ * yet again. Thus we unset the flag */
+ have_logged_kist_suddenly_disabled = 0;
+ new_scheduler = get_kist_scheduler();
+ scheduler_kist_set_full_mode();
+ goto end;
+ case SCHEDULER_KIST_LITE:
+ new_scheduler = get_kist_scheduler();
+ scheduler_kist_set_lite_mode();
+ goto end;
+ case SCHEDULER_NONE:
+ /* fallthrough */
+ default:
+ /* Our option validation should have caught this. */
+ tor_assert_unreached();
}
- tor_event_free(run_sched_ev);
- run_sched_ev = NULL;
+ } SMARTLIST_FOREACH_END(type);
+
+ end:
+ if (new_scheduler == NULL) {
+ log_err(LD_SCHED, "Tor was unable to select a scheduler type. Please "
+ "make sure Schedulers is correctly configured with "
+ "what Tor does support.");
+ /* We weren't able to choose a scheduler which means that none of the ones
+ * set in Schedulers are supported or usable. We will respect the user
+ * wishes of using what it has been configured and don't do a sneaky
+ * fallback. Because this can be changed at runtime, we have to stop tor
+ * right now. */
+ exit(1);
}
- if (channels_pending) {
- smartlist_free(channels_pending);
- channels_pending = NULL;
- }
+ /* Set the chosen scheduler. */
+ the_scheduler = new_scheduler;
}
/**
- * Comparison function to use when sorting pending channels
+ * Helper function called from a few different places. It changes the
+ * scheduler implementation, if necessary. And if it did, it then tells the
+ * old one to free its state and the new one to initialize.
*/
+static void
+set_scheduler(void)
+{
+ const scheduler_t *old_scheduler = the_scheduler;
+ scheduler_types_t old_scheduler_type = SCHEDULER_NONE;
+
+ /* We keep track of the type in order to log only if the type switched. We
+ * can't just use the scheduler pointers because KIST and KISTLite share the
+ * same object. */
+ if (the_scheduler) {
+ old_scheduler_type = the_scheduler->type;
+ }
+
+ /* From the options, select the scheduler type to set. */
+ select_scheduler();
+ tor_assert(the_scheduler);
-MOCK_IMPL(STATIC int,
+ /* We look at the pointer difference in case the old sched and new sched
+ * share the same scheduler object, as is the case with KIST and KISTLite. */
+ if (old_scheduler != the_scheduler) {
+ /* Allow the old scheduler to clean up, if needed. */
+ if (old_scheduler && old_scheduler->free_all) {
+ old_scheduler->free_all();
+ }
+
+ /* Initialize the new scheduler. */
+ if (the_scheduler->init) {
+ the_scheduler->init();
+ }
+ }
+
+ /* Finally we notice log if we switched schedulers. We use the type in case
+ * two schedulers share a scheduler object. */
+ if (old_scheduler_type != the_scheduler->type) {
+ log_notice(LD_CONFIG, "Scheduler type %s has been enabled.",
+ get_scheduler_type_string(the_scheduler->type));
+ }
+}
+
+/*****************************************************************************
+ * Scheduling system private function definitions
+ *
+ * Functions that can only be accessed from scheduler*.c
+ *****************************************************************************/
+
+/** Return the pending channel list. */
+smartlist_t *
+get_channels_pending(void)
+{
+ return channels_pending;
+}
+
+/** Comparison function to use when sorting pending channels. */
+MOCK_IMPL(int,
scheduler_compare_channels, (const void *c1_v, const void *c2_v))
{
- channel_t *c1 = NULL, *c2 = NULL;
+ const channel_t *c1 = NULL, *c2 = NULL;
/* These are a workaround for -Wbad-function-cast throwing a fit */
const circuitmux_policy_t *p1, *p2;
uintptr_t p1_i, p2_i;
@@ -211,11 +381,8 @@ scheduler_compare_channels, (const void *c1_v, const void *c2_v))
tor_assert(c1_v);
tor_assert(c2_v);
- c1 = (channel_t *)(c1_v);
- c2 = (channel_t *)(c2_v);
-
- tor_assert(c1);
- tor_assert(c2);
+ c1 = (const channel_t *)(c1_v);
+ c2 = (const channel_t *)(c2_v);
if (c1 != c2) {
if (circuitmux_get_policy(c1->cmux) ==
@@ -242,36 +409,83 @@ scheduler_compare_channels, (const void *c1_v, const void *c2_v))
}
}
-/*
- * Scheduler event callback; this should get triggered once per event loop
- * if any scheduling work was created during the event loop.
- */
+/*****************************************************************************
+ * Scheduling system global functions
+ *
+ * Functions that can be accessed from anywhere in Tor.
+ *****************************************************************************/
-static void
-scheduler_evt_callback(evutil_socket_t fd, short events, void *arg)
+/**
+ * This is how the scheduling system is notified of Tor's configuration
+ * changing. For example: a SIGHUP was issued.
+ */
+void
+scheduler_conf_changed(void)
{
- (void)fd;
- (void)events;
- (void)arg;
- log_debug(LD_SCHED, "Scheduler event callback called");
+ /* Let the scheduler decide what it should do. */
+ set_scheduler();
- tor_assert(run_sched_ev);
+ /* Then tell the (possibly new) scheduler that we have new options. */
+ if (the_scheduler->on_new_options) {
+ the_scheduler->on_new_options();
+ }
+}
- /* Run the scheduler */
- scheduler_run();
+/**
+ * Whenever we get a new consensus, this function is called.
+ */
+void
+scheduler_notify_networkstatus_changed(void)
+{
+ /* Maybe the consensus param made us change the scheduler. */
+ set_scheduler();
- /* Do we have more work to do? */
- if (scheduler_more_work()) scheduler_retrigger();
+ /* Then tell the (possibly new) scheduler that we have a new consensus */
+ if (the_scheduler->on_new_consensus) {
+ the_scheduler->on_new_consensus();
+ }
}
-/** Mark a channel as no longer ready to accept writes */
+/**
+ * Free everything scheduling-related from main.c. Note this is only called
+ * when Tor is shutting down, while scheduler_t->free_all() is called both when
+ * Tor is shutting down and when we are switching schedulers.
+ */
+void
+scheduler_free_all(void)
+{
+ log_debug(LD_SCHED, "Shutting down scheduler");
+
+ if (run_sched_ev) {
+ if (event_del(run_sched_ev) < 0) {
+ log_warn(LD_BUG, "Problem deleting run_sched_ev");
+ }
+ tor_event_free(run_sched_ev);
+ run_sched_ev = NULL;
+ }
+
+ if (channels_pending) {
+ /* We don't have ownership of the objects in this list. */
+ smartlist_free(channels_pending);
+ channels_pending = NULL;
+ }
+ if (the_scheduler && the_scheduler->free_all) {
+ the_scheduler->free_all();
+ }
+ the_scheduler = NULL;
+}
+
+/** Mark a channel as no longer ready to accept writes. */
MOCK_IMPL(void,
scheduler_channel_doesnt_want_writes,(channel_t *chan))
{
- tor_assert(chan);
-
- tor_assert(channels_pending);
+ IF_BUG_ONCE(!chan) {
+ return;
+ }
+ IF_BUG_ONCE(!channels_pending) {
+ return;
+ }
/* If it's already in pending, we can put it in waiting_to_write */
if (chan->scheduler_state == SCHED_CHAN_PENDING) {
@@ -282,7 +496,7 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan))
*/
smartlist_pqueue_remove(channels_pending,
scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx),
+ offsetof(channel_t, sched_heap_idx),
chan);
chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
log_debug(LD_SCHED,
@@ -304,17 +518,18 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan))
}
}
-/** Mark a channel as having waiting cells */
-
+/** Mark a channel as having waiting cells. */
MOCK_IMPL(void,
scheduler_channel_has_waiting_cells,(channel_t *chan))
{
- int became_pending = 0;
-
- tor_assert(chan);
- tor_assert(channels_pending);
+ IF_BUG_ONCE(!chan) {
+ return;
+ }
+ IF_BUG_ONCE(!channels_pending) {
+ return;
+ }
- /* First, check if this one also writeable */
+ /* First, check if it's also writeable */
if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) {
/*
* It's in channels_waiting_for_cells, so it shouldn't be in any of
@@ -322,15 +537,19 @@ scheduler_channel_has_waiting_cells,(channel_t *chan))
* channels_pending.
*/
chan->scheduler_state = SCHED_CHAN_PENDING;
- smartlist_pqueue_add(channels_pending,
- scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx),
- chan);
+ if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
+ smartlist_pqueue_add(channels_pending,
+ scheduler_compare_channels,
+ offsetof(channel_t, sched_heap_idx),
+ chan);
+ }
log_debug(LD_SCHED,
"Channel " U64_FORMAT " at %p went from waiting_for_cells "
"to pending",
U64_PRINTF_ARG(chan->global_identifier), chan);
- became_pending = 1;
+ /* If we made a channel pending, we potentially have scheduling work to
+ * do. */
+ the_scheduler->schedule();
} else {
/*
* It's not in waiting_for_cells, so it can't become pending; it's
@@ -345,250 +564,125 @@ scheduler_channel_has_waiting_cells,(channel_t *chan))
U64_PRINTF_ARG(chan->global_identifier), chan);
}
}
+}
- /*
- * If we made a channel pending, we potentially have scheduling work
- * to do.
- */
- if (became_pending) scheduler_retrigger();
+/** Add the scheduler event to the set of pending events with next_run being
+ * the longest time libevent should wait before triggering the event. */
+void
+scheduler_ev_add(const struct timeval *next_run)
+{
+ tor_assert(run_sched_ev);
+ tor_assert(next_run);
+ if (BUG(event_add(run_sched_ev, next_run) < 0)) {
+ log_warn(LD_SCHED, "Adding to libevent failed. Next run time was set to: "
+ "%ld.%06ld", next_run->tv_sec, (long)next_run->tv_usec);
+ return;
+ }
}
-/** Set up the scheduling system */
+/** Make the scheduler event active with the given flags. */
+void
+scheduler_ev_active(int flags)
+{
+ tor_assert(run_sched_ev);
+ event_active(run_sched_ev, flags, 1);
+}
+/*
+ * Initialize everything scheduling-related from config.c. Note this is only
+ * called when Tor is starting up, while scheduler_t->init() is called both
+ * when Tor is starting up and when we are switching schedulers.
+ */
void
scheduler_init(void)
{
log_debug(LD_SCHED, "Initting scheduler");
- tor_assert(!run_sched_ev);
+ // Two '!' because we really do want to check if the pointer is non-NULL
+ IF_BUG_ONCE(!!run_sched_ev) {
+ log_warn(LD_SCHED, "We should not already have a libevent scheduler event."
+ "I'll clean the old one up, but this is odd.");
+ tor_event_free(run_sched_ev);
+ run_sched_ev = NULL;
+ }
run_sched_ev = tor_event_new(tor_libevent_get_base(), -1,
0, scheduler_evt_callback, NULL);
-
channels_pending = smartlist_new();
- queue_heuristic = 0;
- queue_heuristic_timestamp = approx_time();
-}
-
-/** Check if there's more scheduling work */
-static int
-scheduler_more_work(void)
-{
- tor_assert(channels_pending);
-
- return ((scheduler_get_queue_heuristic() < sched_q_low_water) &&
- ((smartlist_len(channels_pending) > 0))) ? 1 : 0;
-}
-
-/** Retrigger the scheduler in a way safe to use from the callback */
-
-static void
-scheduler_retrigger(void)
-{
- tor_assert(run_sched_ev);
- event_active(run_sched_ev, EV_TIMEOUT, 1);
+ set_scheduler();
}
-/** Notify the scheduler of a channel being closed */
-
+/*
+ * If a channel is going away, this is how the scheduling system is informed
+ * so it can do any freeing necessary. This ultimately calls
+ * scheduler_t->on_channel_free() so the current scheduler can release any
+ * state specific to this channel.
+ */
MOCK_IMPL(void,
scheduler_release_channel,(channel_t *chan))
{
- tor_assert(chan);
- tor_assert(channels_pending);
+ IF_BUG_ONCE(!chan) {
+ return;
+ }
+ IF_BUG_ONCE(!channels_pending) {
+ return;
+ }
- if (chan->scheduler_state == SCHED_CHAN_PENDING) {
+ /* Try to remove the channel from the pending list regardless of its
+ * scheduler state. We can release a channel in many places in the tor code
+ * so we can't rely on the channel state (PENDING) to remove it from the
+ * list.
+ *
+ * For instance, the channel can change state from OPEN to CLOSING while
+ * being handled in the scheduler loop leading to the channel being in
+ * PENDING state but not in the pending list. Furthermore, we release the
+ * channel when it changes state to close and a second time when we free it.
+ * Not ideal at all but for now that is the way it is. */
+ if (chan->sched_heap_idx != -1) {
smartlist_pqueue_remove(channels_pending,
scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx),
+ offsetof(channel_t, sched_heap_idx),
chan);
}
- chan->scheduler_state = SCHED_CHAN_IDLE;
-}
-
-/** Run the scheduling algorithm if necessary */
-
-MOCK_IMPL(void,
-scheduler_run, (void))
-{
- int n_cells, n_chans_before, n_chans_after;
- uint64_t q_len_before, q_heur_before, q_len_after, q_heur_after;
- ssize_t flushed, flushed_this_time;
- smartlist_t *to_readd = NULL;
- channel_t *chan = NULL;
-
- log_debug(LD_SCHED, "We have a chance to run the scheduler");
-
- if (scheduler_get_queue_heuristic() < sched_q_low_water) {
- n_chans_before = smartlist_len(channels_pending);
- q_len_before = channel_get_global_queue_estimate();
- q_heur_before = scheduler_get_queue_heuristic();
-
- while (scheduler_get_queue_heuristic() <= sched_q_high_water &&
- smartlist_len(channels_pending) > 0) {
- /* Pop off a channel */
- chan = smartlist_pqueue_pop(channels_pending,
- scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx));
- tor_assert(chan);
-
- /* Figure out how many cells we can write */
- n_cells = channel_num_cells_writeable(chan);
- if (n_cells > 0) {
- log_debug(LD_SCHED,
- "Scheduler saw pending channel " U64_FORMAT " at %p with "
- "%d cells writeable",
- U64_PRINTF_ARG(chan->global_identifier), chan, n_cells);
-
- flushed = 0;
- while (flushed < n_cells &&
- scheduler_get_queue_heuristic() <= sched_q_high_water) {
- flushed_this_time =
- channel_flush_some_cells(chan,
- MIN(sched_max_flush_cells,
- (size_t) n_cells - flushed));
- if (flushed_this_time <= 0) break;
- flushed += flushed_this_time;
- }
-
- if (flushed < n_cells) {
- /* We ran out of cells to flush */
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "entered waiting_for_cells from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
- } else {
- /* The channel may still have some cells */
- if (channel_more_to_flush(chan)) {
- /* The channel goes to either pending or waiting_to_write */
- if (channel_num_cells_writeable(chan) > 0) {
- /* Add it back to pending later */
- if (!to_readd) to_readd = smartlist_new();
- smartlist_add(to_readd, chan);
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "is still pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
- } else {
- /* It's waiting to be able to write more */
- chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "entered waiting_to_write from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
- }
- } else {
- /* No cells left; it can go to idle or waiting_for_cells */
- if (channel_num_cells_writeable(chan) > 0) {
- /*
- * It can still accept writes, so it goes to
- * waiting_for_cells
- */
- chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "entered waiting_for_cells from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
- } else {
- /*
- * We exactly filled up the output queue with all available
- * cells; go to idle.
- */
- chan->scheduler_state = SCHED_CHAN_IDLE;
- log_debug(LD_SCHED,
- "Channel " U64_FORMAT " at %p "
- "become idle from pending",
- U64_PRINTF_ARG(chan->global_identifier),
- chan);
- }
- }
- }
-
- log_debug(LD_SCHED,
- "Scheduler flushed %d cells onto pending channel "
- U64_FORMAT " at %p",
- (int)flushed, U64_PRINTF_ARG(chan->global_identifier),
- chan);
- } else {
- log_info(LD_SCHED,
- "Scheduler saw pending channel " U64_FORMAT " at %p with "
- "no cells writeable",
- U64_PRINTF_ARG(chan->global_identifier), chan);
- /* Put it back to WAITING_TO_WRITE */
- chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
- }
- }
-
- /* Readd any channels we need to */
- if (to_readd) {
- SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
- readd_chan->scheduler_state = SCHED_CHAN_PENDING;
- smartlist_pqueue_add(channels_pending,
- scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx),
- readd_chan);
- } SMARTLIST_FOREACH_END(readd_chan);
- smartlist_free(to_readd);
- }
-
- n_chans_after = smartlist_len(channels_pending);
- q_len_after = channel_get_global_queue_estimate();
- q_heur_after = scheduler_get_queue_heuristic();
- log_debug(LD_SCHED,
- "Scheduler handled %d of %d pending channels, queue size from "
- U64_FORMAT " to " U64_FORMAT ", queue heuristic from "
- U64_FORMAT " to " U64_FORMAT,
- n_chans_before - n_chans_after, n_chans_before,
- U64_PRINTF_ARG(q_len_before), U64_PRINTF_ARG(q_len_after),
- U64_PRINTF_ARG(q_heur_before), U64_PRINTF_ARG(q_heur_after));
+ if (the_scheduler->on_channel_free) {
+ the_scheduler->on_channel_free(chan);
}
+ chan->scheduler_state = SCHED_CHAN_IDLE;
}
-/** Trigger the scheduling event so we run the scheduler later */
-
-#if 0
-static void
-scheduler_trigger(void)
-{
- log_debug(LD_SCHED, "Triggering scheduler event");
-
- tor_assert(run_sched_ev);
-
- event_add(run_sched_ev, EV_TIMEOUT, 1);
-}
-#endif
-
/** Mark a channel as ready to accept writes */
void
scheduler_channel_wants_writes(channel_t *chan)
{
- int became_pending = 0;
-
- tor_assert(chan);
- tor_assert(channels_pending);
+ IF_BUG_ONCE(!chan) {
+ return;
+ }
+ IF_BUG_ONCE(!channels_pending) {
+ return;
+ }
/* If it's already in waiting_to_write, we can put it in pending */
if (chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE) {
/*
* It can write now, so it goes to channels_pending.
*/
- smartlist_pqueue_add(channels_pending,
- scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx),
- chan);
+ log_debug(LD_SCHED, "chan=%" PRIu64 " became pending",
+ chan->global_identifier);
+ if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
+ smartlist_pqueue_add(channels_pending,
+ scheduler_compare_channels,
+ offsetof(channel_t, sched_heap_idx),
+ chan);
+ }
chan->scheduler_state = SCHED_CHAN_PENDING;
log_debug(LD_SCHED,
"Channel " U64_FORMAT " at %p went from waiting_to_write "
"to pending",
U64_PRINTF_ARG(chan->global_identifier), chan);
- became_pending = 1;
+ /* We just made a channel pending, we have scheduling work to do. */
+ the_scheduler->schedule();
} else {
/*
* It's not in SCHED_CHAN_WAITING_TO_WRITE, so it can't become pending;
@@ -602,137 +696,70 @@ scheduler_channel_wants_writes(channel_t *chan)
U64_PRINTF_ARG(chan->global_identifier), chan);
}
}
+}
- /*
- * If we made a channel pending, we potentially have scheduling work
- * to do.
- */
- if (became_pending) scheduler_retrigger();
+/* Log warn the given channel and extra scheduler context as well. This is
+ * used by SCHED_BUG() in order to be able to extract as much information as
+ * we can when we hit a bug. Channel chan can be NULL. */
+void
+scheduler_bug_occurred(const channel_t *chan)
+{
+ char buf[128];
+
+ if (chan != NULL) {
+ const size_t outbuf_len =
+ buf_datalen(TO_CONN(BASE_CHAN_TO_TLS((channel_t *) chan)->conn)->outbuf);
+ tor_snprintf(buf, sizeof(buf),
+ "Channel %" PRIu64 " in state %s and scheduler state %d."
+ " Num cells on cmux: %d. Connection outbuf len: %lu.",
+ chan->global_identifier,
+ channel_state_to_string(chan->state),
+ chan->scheduler_state, circuitmux_num_cells(chan->cmux),
+ (unsigned long)outbuf_len);
+ }
+
+ {
+ char *msg;
+ /* Rate limit every 60 seconds. If we start seeing this every 60 sec, we
+ * know something is stuck/wrong. It *should* be loud but not too much. */
+ static ratelim_t rlimit = RATELIM_INIT(60);
+ if ((msg = rate_limit_log(&rlimit, approx_time()))) {
+ log_warn(LD_BUG, "%s Num pending channels: %d. "
+ "Channel in pending list: %s.%s",
+ (chan != NULL) ? buf : "No channel in bug context.",
+ smartlist_len(channels_pending),
+ (smartlist_pos(channels_pending, chan) == -1) ? "no" : "yes",
+ msg);
+ tor_free(msg);
+ }
+ }
}
-/**
- * Notify the scheduler that a channel's position in the pqueue may have
- * changed
- */
+#ifdef TOR_UNIT_TESTS
+/*
+ * Notify scheduler that a channel's queue position may have changed.
+ */
void
scheduler_touch_channel(channel_t *chan)
{
- tor_assert(chan);
+ IF_BUG_ONCE(!chan) {
+ return;
+ }
if (chan->scheduler_state == SCHED_CHAN_PENDING) {
/* Remove and re-add it */
smartlist_pqueue_remove(channels_pending,
scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx),
+ offsetof(channel_t, sched_heap_idx),
chan);
smartlist_pqueue_add(channels_pending,
scheduler_compare_channels,
- STRUCT_OFFSET(channel_t, sched_heap_idx),
+ offsetof(channel_t, sched_heap_idx),
chan);
}
/* else no-op, since it isn't in the queue */
}
-/**
- * Notify the scheduler of a queue size adjustment, to recalculate the
- * queue heuristic.
- */
-
-void
-scheduler_adjust_queue_size(channel_t *chan, int dir, uint64_t adj)
-{
- time_t now = approx_time();
-
- log_debug(LD_SCHED,
- "Queue size adjustment by %s" U64_FORMAT " for channel "
- U64_FORMAT,
- (dir >= 0) ? "+" : "-",
- U64_PRINTF_ARG(adj),
- U64_PRINTF_ARG(chan->global_identifier));
-
- /* Get the queue heuristic up to date */
- scheduler_update_queue_heuristic(now);
-
- /* Adjust as appropriate */
- if (dir >= 0) {
- /* Increasing it */
- queue_heuristic += adj;
- } else {
- /* Decreasing it */
- if (queue_heuristic > adj) queue_heuristic -= adj;
- else queue_heuristic = 0;
- }
-
- log_debug(LD_SCHED,
- "Queue heuristic is now " U64_FORMAT,
- U64_PRINTF_ARG(queue_heuristic));
-}
-
-/**
- * Query the current value of the queue heuristic
- */
-
-STATIC uint64_t
-scheduler_get_queue_heuristic(void)
-{
- time_t now = approx_time();
-
- scheduler_update_queue_heuristic(now);
-
- return queue_heuristic;
-}
-
-/**
- * Adjust the queue heuristic value to the present time
- */
-
-STATIC void
-scheduler_update_queue_heuristic(time_t now)
-{
- time_t diff;
-
- if (queue_heuristic_timestamp == 0) {
- /*
- * Nothing we can sensibly do; must not have been initted properly.
- * Oh well.
- */
- queue_heuristic_timestamp = now;
- } else if (queue_heuristic_timestamp < now) {
- diff = now - queue_heuristic_timestamp;
- /*
- * This is a simple exponential age-out; the other proposed alternative
- * was a linear age-out using the bandwidth history in rephist.c; I'm
- * going with this out of concern that if an adversary can jam the
- * scheduler long enough, it would cause the bandwidth to drop to
- * zero and render the aging mechanism ineffective thereafter.
- */
- if (0 <= diff && diff < 64) queue_heuristic >>= diff;
- else queue_heuristic = 0;
-
- queue_heuristic_timestamp = now;
-
- log_debug(LD_SCHED,
- "Queue heuristic is now " U64_FORMAT,
- U64_PRINTF_ARG(queue_heuristic));
- }
- /* else no update needed, or time went backward */
-}
-
-/**
- * Set scheduler watermarks and flush size
- */
-
-void
-scheduler_set_watermarks(uint32_t lo, uint32_t hi, uint32_t max_flush)
-{
- /* Sanity assertions - caller should ensure these are true */
- tor_assert(lo > 0);
- tor_assert(hi > lo);
- tor_assert(max_flush > 0);
-
- sched_q_low_water = lo;
- sched_q_high_water = hi;
- sched_max_flush_cells = max_flush;
-}
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/scheduler.h b/src/or/scheduler.h
index e29c13de7e..559f1c8afc 100644
--- a/src/or/scheduler.h
+++ b/src/or/scheduler.h
@@ -1,9 +1,9 @@
-/* * Copyright (c) 2013-2017, The Tor Project, Inc. */
+/* * Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file scheduler.h
- * \brief Header file for scheduler.c
+ * \brief Header file for scheduler*.c
**/
#ifndef TOR_SCHEDULER_H
@@ -13,45 +13,203 @@
#include "channel.h"
#include "testsupport.h"
-/* Global-visibility scheduler functions */
+/** Scheduler type, we build an ordered list with those values from the
+ * parsed strings in Schedulers. The reason to do such a thing is so we can
+ * quickly and without parsing strings select the scheduler at anytime. */
+typedef enum {
+ SCHEDULER_NONE = -1,
+ SCHEDULER_VANILLA = 1,
+ SCHEDULER_KIST = 2,
+ SCHEDULER_KIST_LITE = 3,
+} scheduler_types_t;
-/* Set up and shut down the scheduler from main.c */
-void scheduler_free_all(void);
-void scheduler_init(void);
-MOCK_DECL(void, scheduler_run, (void));
+/**
+ * A scheduler implementation is a collection of function pointers. If you
+ * would like to add a new scheduler called foo, create scheduler_foo.c,
+ * implement at least the mandatory ones, and implement get_foo_scheduler()
+ * that returns a complete scheduler_t for your foo scheduler. See
+ * scheduler_kist.c for an example.
+ *
+ * These function pointers SHOULD NOT be used anywhere outside of the
+ * scheduling source files. The rest of Tor should communicate with the
+ * scheduling system through the functions near the bottom of this file, and
+ * those functions will call into the current scheduler implementation as
+ * necessary.
+ *
+ * If your scheduler doesn't need to implement something (for example: it
+ * doesn't create any state for itself, thus it has nothing to free when Tor
+ * is shutting down), then set that function pointer to NULL.
+ */
+typedef struct scheduler_s {
+ /* Scheduler type. This is used for logging when the scheduler is switched
+ * during runtime. */
+ scheduler_types_t type;
-/* Mark channels as having cells or wanting/not wanting writes */
-MOCK_DECL(void,scheduler_channel_doesnt_want_writes,(channel_t *chan));
-MOCK_DECL(void,scheduler_channel_has_waiting_cells,(channel_t *chan));
-void scheduler_channel_wants_writes(channel_t *chan);
+ /* (Optional) To be called when we want to prepare a scheduler for use.
+ * Perhaps Tor just started and we are the lucky chosen scheduler, or
+ * perhaps Tor is switching to this scheduler. No matter the case, this is
+ * where we would prepare any state and initialize parameters. You might
+ * think of this as the opposite of free_all(). */
+ void (*init)(void);
-/* Notify the scheduler of a channel being closed */
-MOCK_DECL(void,scheduler_release_channel,(channel_t *chan));
+ /* (Optional) To be called when we want to tell the scheduler to delete all
+ * of its state (if any). Perhaps Tor is shutting down or perhaps we are
+ * switching schedulers. */
+ void (*free_all)(void);
-/* Notify scheduler of queue size adjustments */
-void scheduler_adjust_queue_size(channel_t *chan, int dir, uint64_t adj);
+ /* (Mandatory) Libevent controls the main event loop in Tor, and this is
+ * where we register with libevent the next execution of run_sched_ev [which
+ * ultimately calls run()]. */
+ void (*schedule)(void);
-/* Notify scheduler that a channel's queue position may have changed */
-void scheduler_touch_channel(channel_t *chan);
+ /* (Mandatory) This is the heart of a scheduler! This is where the
+ * excitement happens! Here libevent has given us the chance to execute, and
+ * we should do whatever we need to do in order to move some cells from
+ * their circuit queues to output buffers in an intelligent manner. We
+ * should do this quickly. When we are done, we'll try to schedule() ourself
+ * if more work needs to be done to setup the next scheduling run. */
+ void (*run)(void);
+
+ /*
+ * External event not related to the scheduler but that can influence it.
+ */
+
+ /* (Optional) To be called whenever Tor finds out about a new consensus.
+ * First the scheduling system as a whole will react to the new consensus
+ * and change the scheduler if needed. After that, the current scheduler
+ * (which might be new) will call this so it has the chance to react to the
+ * new consensus too. If there's a consensus parameter that your scheduler
+ * wants to keep an eye on, this is where you should check for it. */
+ void (*on_new_consensus)(void);
+
+ /* (Optional) To be called when a channel is being freed. Sometimes channels
+ * go away (for example: the relay on the other end is shutting down). If
+ * the scheduler keeps any channel-specific state and has memory to free
+ * when channels go away, implement this and free it here. */
+ void (*on_channel_free)(const channel_t *);
-/* Adjust the watermarks from config file*/
-void scheduler_set_watermarks(uint32_t lo, uint32_t hi, uint32_t max_flush);
+ /* (Optional) To be called whenever Tor is reloading configuration options.
+ * For example: SIGHUP was issued and Tor is rereading its torrc. A
+ * scheduler should use this as an opportunity to parse and cache torrc
+ * options so that it doesn't have to call get_options() all the time. */
+ void (*on_new_options)(void);
+} scheduler_t;
-/* Things only scheduler.c and its test suite should see */
+/*****************************************************************************
+ * Globally visible scheduler variables/values
+ *
+ * These are variables/constants that all of Tor should be able to see.
+ *****************************************************************************/
+/* Default interval that KIST runs (in ms). */
+#define KIST_SCHED_RUN_INTERVAL_DEFAULT 10
+/* Minimum interval that KIST runs. This value disables KIST. */
+#define KIST_SCHED_RUN_INTERVAL_MIN 0
+/* Maximum interval that KIST runs (in ms). */
+#define KIST_SCHED_RUN_INTERVAL_MAX 100
+
+/*****************************************************************************
+ * Globally visible scheduler functions
+ *
+ * These functions are how the rest of Tor communicates with the scheduling
+ * system.
+ *****************************************************************************/
+
+void scheduler_init(void);
+void scheduler_free_all(void);
+void scheduler_conf_changed(void);
+void scheduler_notify_networkstatus_changed(void);
+MOCK_DECL(void, scheduler_release_channel, (channel_t *chan));
+
+/*
+ * Ways for a channel to interact with the scheduling system. A channel only
+ * really knows (i) whether or not it has cells it wants to send, and
+ * (ii) whether or not it would like to write.
+ */
+void scheduler_channel_wants_writes(channel_t *chan);
+MOCK_DECL(void, scheduler_channel_doesnt_want_writes, (channel_t *chan));
+MOCK_DECL(void, scheduler_channel_has_waiting_cells, (channel_t *chan));
+
+/*****************************************************************************
+ * Private scheduler functions
+ *
+ * These functions are only visible to the scheduling system, the current
+ * scheduler implementation, and tests.
+ *****************************************************************************/
#ifdef SCHEDULER_PRIVATE_
-MOCK_DECL(STATIC int, scheduler_compare_channels,
+
+/*********************************
+ * Defined in scheduler.c
+ *********************************/
+
+/* Triggers a BUG() and extra information with chan if available. */
+#define SCHED_BUG(cond, chan) \
+ (PREDICT_UNLIKELY(cond) ? \
+ ((BUG(cond)) ? (scheduler_bug_occurred(chan), 1) : 0) : 0)
+
+void scheduler_bug_occurred(const channel_t *chan);
+
+smartlist_t *get_channels_pending(void);
+MOCK_DECL(int, scheduler_compare_channels,
(const void *c1_v, const void *c2_v));
-STATIC uint64_t scheduler_get_queue_heuristic(void);
-STATIC void scheduler_update_queue_heuristic(time_t now);
+void scheduler_ev_active(int flags);
+void scheduler_ev_add(const struct timeval *next_run);
#ifdef TOR_UNIT_TESTS
extern smartlist_t *channels_pending;
extern struct event *run_sched_ev;
-extern uint64_t queue_heuristic;
-extern time_t queue_heuristic_timestamp;
-#endif
-#endif
+extern const scheduler_t *the_scheduler;
+void scheduler_touch_channel(channel_t *chan);
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/*********************************
+ * Defined in scheduler_kist.c
+ *********************************/
+
+#ifdef SCHEDULER_KIST_PRIVATE
+
+/* Socket table entry which holds information of a channel's socket and kernel
+ * TCP information. Only used by KIST. */
+typedef struct socket_table_ent_s {
+ HT_ENTRY(socket_table_ent_s) node;
+ const channel_t *chan;
+ /* Amount written this scheduling run */
+ uint64_t written;
+ /* Amount that can be written this scheduling run */
+ uint64_t limit;
+ /* TCP info from the kernel */
+ uint32_t cwnd;
+ uint32_t unacked;
+ uint32_t mss;
+ uint32_t notsent;
+} socket_table_ent_t;
+
+typedef HT_HEAD(outbuf_table_s, outbuf_table_ent_s) outbuf_table_t;
+
+MOCK_DECL(int, channel_should_write_to_kernel,
+ (outbuf_table_t *table, channel_t *chan));
+MOCK_DECL(void, channel_write_to_kernel, (channel_t *chan));
+MOCK_DECL(void, update_socket_info_impl, (socket_table_ent_t *ent));
+
+int scheduler_can_use_kist(void);
+void scheduler_kist_set_full_mode(void);
+void scheduler_kist_set_lite_mode(void);
+scheduler_t *get_kist_scheduler(void);
+int kist_scheduler_run_interval(void);
+
+#ifdef TOR_UNIT_TESTS
+extern int32_t sched_run_interval;
+#endif /* TOR_UNIT_TESTS */
+
+#endif /* defined(SCHEDULER_KIST_PRIVATE) */
+
+/*********************************
+ * Defined in scheduler_vanilla.c
+ *********************************/
+
+scheduler_t *get_vanilla_scheduler(void);
+
+#endif /* defined(SCHEDULER_PRIVATE_) */
#endif /* !defined(TOR_SCHEDULER_H) */
diff --git a/src/or/scheduler_kist.c b/src/or/scheduler_kist.c
new file mode 100644
index 0000000000..c79b413b88
--- /dev/null
+++ b/src/or/scheduler_kist.c
@@ -0,0 +1,844 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define SCHEDULER_KIST_PRIVATE
+
+#include <event2/event.h>
+
+#include "or.h"
+#include "buffers.h"
+#include "config.h"
+#include "connection.h"
+#include "networkstatus.h"
+#define TOR_CHANNEL_INTERNAL_
+#include "channel.h"
+#include "channeltls.h"
+#define SCHEDULER_PRIVATE_
+#include "scheduler.h"
+
+#define TLS_PER_CELL_OVERHEAD 29
+
+#ifdef HAVE_KIST_SUPPORT
+/* Kernel interface needed for KIST. */
+#include <netinet/tcp.h>
+#include <linux/sockios.h>
+#endif /* HAVE_KIST_SUPPORT */
+
+/*****************************************************************************
+ * Data structures and supporting functions
+ *****************************************************************************/
+
+/* Socket_table hash table stuff. The socket_table keeps track of per-socket
+ * limit information imposed by kist and used by kist. */
+
+static uint32_t
+socket_table_ent_hash(const socket_table_ent_t *ent)
+{
+ return (uint32_t)ent->chan->global_identifier;
+}
+
+static unsigned
+socket_table_ent_eq(const socket_table_ent_t *a, const socket_table_ent_t *b)
+{
+ return a->chan == b->chan;
+}
+
+typedef HT_HEAD(socket_table_s, socket_table_ent_s) socket_table_t;
+
+static socket_table_t socket_table = HT_INITIALIZER();
+
+HT_PROTOTYPE(socket_table_s, socket_table_ent_s, node, socket_table_ent_hash,
+ socket_table_ent_eq)
+HT_GENERATE2(socket_table_s, socket_table_ent_s, node, socket_table_ent_hash,
+ socket_table_ent_eq, 0.6, tor_reallocarray, tor_free_)
+
+/* outbuf_table hash table stuff. The outbuf_table keeps track of which
+ * channels have data sitting in their outbuf so the kist scheduler can force
+ * a write from outbuf to kernel periodically during a run and at the end of a
+ * run. */
+
+typedef struct outbuf_table_ent_s {
+ HT_ENTRY(outbuf_table_ent_s) node;
+ channel_t *chan;
+} outbuf_table_ent_t;
+
+static uint32_t
+outbuf_table_ent_hash(const outbuf_table_ent_t *ent)
+{
+ return (uint32_t)ent->chan->global_identifier;
+}
+
+static unsigned
+outbuf_table_ent_eq(const outbuf_table_ent_t *a, const outbuf_table_ent_t *b)
+{
+ return a->chan->global_identifier == b->chan->global_identifier;
+}
+
+HT_PROTOTYPE(outbuf_table_s, outbuf_table_ent_s, node, outbuf_table_ent_hash,
+ outbuf_table_ent_eq)
+HT_GENERATE2(outbuf_table_s, outbuf_table_ent_s, node, outbuf_table_ent_hash,
+ outbuf_table_ent_eq, 0.6, tor_reallocarray, tor_free_)
+
+/*****************************************************************************
+ * Other internal data
+ *****************************************************************************/
+
+/* Store the last time the scheduler was run so we can decide when to next run
+ * the scheduler based on it. */
+static monotime_t scheduler_last_run;
+/* This is a factor for the extra_space calculation in kist per-socket limits.
+ * It is the number of extra congestion windows we want to write to the kernel.
+ */
+static double sock_buf_size_factor = 1.0;
+/* How often the scheduler runs. */
+STATIC int sched_run_interval = KIST_SCHED_RUN_INTERVAL_DEFAULT;
+
+#ifdef HAVE_KIST_SUPPORT
+/* Indicate if KIST lite mode is on or off. We can disable it at runtime.
+ * Important to have because of the KISTLite -> KIST possible transition. */
+static unsigned int kist_lite_mode = 0;
+/* Indicate if we don't have the kernel support. This can happen if the kernel
+ * changed and it doesn't recognized the values passed to the syscalls needed
+ * by KIST. In that case, fallback to the naive approach. */
+static unsigned int kist_no_kernel_support = 0;
+#else /* !(defined(HAVE_KIST_SUPPORT)) */
+static unsigned int kist_lite_mode = 1;
+#endif /* defined(HAVE_KIST_SUPPORT) */
+
+/*****************************************************************************
+ * Internally called function implementations
+ *****************************************************************************/
+
+/* Little helper function to get the length of a channel's output buffer */
+static inline size_t
+channel_outbuf_length(channel_t *chan)
+{
+ /* In theory, this can not happen because we can not scheduler a channel
+ * without a connection that has its outbuf initialized. Just in case, bug
+ * on this so we can understand a bit more why it happened. */
+ if (SCHED_BUG(BASE_CHAN_TO_TLS(chan)->conn == NULL, chan)) {
+ return 0;
+ }
+ return buf_datalen(TO_CONN(BASE_CHAN_TO_TLS(chan)->conn)->outbuf);
+}
+
+/* Little helper function for HT_FOREACH_FN. */
+static int
+each_channel_write_to_kernel(outbuf_table_ent_t *ent, void *data)
+{
+ (void) data; /* Make compiler happy. */
+ channel_write_to_kernel(ent->chan);
+ return 0; /* Returning non-zero removes the element from the table. */
+}
+
+/* Free the given outbuf table entry ent. */
+static int
+free_outbuf_info_by_ent(outbuf_table_ent_t *ent, void *data)
+{
+ (void) data; /* Make compiler happy. */
+ log_debug(LD_SCHED, "Freeing outbuf table entry from chan=%" PRIu64,
+ ent->chan->global_identifier);
+ tor_free(ent);
+ return 1; /* So HT_FOREACH_FN will remove the element */
+}
+
+/* Free the given socket table entry ent. */
+static int
+free_socket_info_by_ent(socket_table_ent_t *ent, void *data)
+{
+ (void) data; /* Make compiler happy. */
+ log_debug(LD_SCHED, "Freeing socket table entry from chan=%" PRIu64,
+ ent->chan->global_identifier);
+ tor_free(ent);
+ return 1; /* So HT_FOREACH_FN will remove the element */
+}
+
+/* Clean up socket_table. Probably because the KIST sched impl is going away */
+static void
+free_all_socket_info(void)
+{
+ HT_FOREACH_FN(socket_table_s, &socket_table, free_socket_info_by_ent, NULL);
+ HT_CLEAR(socket_table_s, &socket_table);
+}
+
+static socket_table_ent_t *
+socket_table_search(socket_table_t *table, const channel_t *chan)
+{
+ socket_table_ent_t search, *ent = NULL;
+ search.chan = chan;
+ ent = HT_FIND(socket_table_s, table, &search);
+ return ent;
+}
+
+/* Free a socket entry in table for the given chan. */
+static void
+free_socket_info_by_chan(socket_table_t *table, const channel_t *chan)
+{
+ socket_table_ent_t *ent = NULL;
+ ent = socket_table_search(table, chan);
+ if (!ent)
+ return;
+ log_debug(LD_SCHED, "scheduler free socket info for chan=%" PRIu64,
+ chan->global_identifier);
+ HT_REMOVE(socket_table_s, table, ent);
+ free_socket_info_by_ent(ent, NULL);
+}
+
+/* Perform system calls for the given socket in order to calculate kist's
+ * per-socket limit as documented in the function body. */
+MOCK_IMPL(void,
+update_socket_info_impl, (socket_table_ent_t *ent))
+{
+#ifdef HAVE_KIST_SUPPORT
+ int64_t tcp_space, extra_space;
+ const tor_socket_t sock =
+ TO_CONN(BASE_CHAN_TO_TLS((channel_t *) ent->chan)->conn)->s;
+ struct tcp_info tcp;
+ socklen_t tcp_info_len = sizeof(tcp);
+
+ if (kist_no_kernel_support || kist_lite_mode) {
+ goto fallback;
+ }
+
+ /* Gather information */
+ if (getsockopt(sock, SOL_TCP, TCP_INFO, (void *)&(tcp), &tcp_info_len) < 0) {
+ if (errno == EINVAL) {
+ /* Oops, this option is not provided by the kernel, we'll have to
+ * disable KIST entirely. This can happen if tor was built on a machine
+ * with the support previously or if the kernel was updated and lost the
+ * support. */
+ log_notice(LD_SCHED, "Looks like our kernel doesn't have the support "
+ "for KIST anymore. We will fallback to the naive "
+ "approach. Remove KIST from the Schedulers list "
+ "to disable.");
+ kist_no_kernel_support = 1;
+ }
+ goto fallback;
+ }
+ if (ioctl(sock, SIOCOUTQNSD, &(ent->notsent)) < 0) {
+ if (errno == EINVAL) {
+ log_notice(LD_SCHED, "Looks like our kernel doesn't have the support "
+ "for KIST anymore. We will fallback to the naive "
+ "approach. Remove KIST from the Schedulers list "
+ "to disable.");
+ /* Same reason as the above. */
+ kist_no_kernel_support = 1;
+ }
+ goto fallback;
+ }
+ ent->cwnd = tcp.tcpi_snd_cwnd;
+ ent->unacked = tcp.tcpi_unacked;
+ ent->mss = tcp.tcpi_snd_mss;
+
+ /* In order to reduce outbound kernel queuing delays and thus improve Tor's
+ * ability to prioritize circuits, KIST wants to set a socket write limit
+ * that is near the amount that the socket would be able to immediately send
+ * into the Internet.
+ *
+ * We first calculate how much the socket could send immediately (assuming
+ * completely full packets) according to the congestion window and the number
+ * of unacked packets.
+ *
+ * Then we add a little extra space in a controlled way. We do this so any
+ * when the kernel gets ACKs back for data currently sitting in the "TCP
+ * space", it will already have some more data to send immediately. It will
+ * not have to wait for the scheduler to run again. The amount of extra space
+ * is a factor of the current congestion window. With the suggested
+ * sock_buf_size_factor value of 1.0, we allow at most 2*cwnd bytes to sit in
+ * the kernel: 1 cwnd on the wire waiting for ACKs and 1 cwnd ready and
+ * waiting to be sent when those ACKs finally come.
+ *
+ * In the below diagram, we see some bytes in the TCP-space (denoted by '*')
+ * that have be sent onto the wire and are waiting for ACKs. We have a little
+ * more room in "TCP space" that we can fill with data that will be
+ * immediately sent. We also see the "extra space" KIST calculates. The sum
+ * of the empty "TCP space" and the "extra space" is the kist-imposed write
+ * limit for this socket.
+ *
+ * <----------------kernel-outbound-socket-queue----------------|
+ * <*********---------------------------------------------------|
+ * |----TCP-space-----|----extra-space-----|
+ * |------------------|
+ * ^ ((cwnd - unacked) * mss) bytes
+ * |--------------------|
+ * ^ ((cwnd * mss) * factor) bytes
+ */
+
+ /* These values from the kernel are uint32_t, they will always fit into a
+ * int64_t tcp_space variable but if the congestion window cwnd is smaller
+ * than the unacked packets, the remaining TCP space is set to 0. */
+ if (ent->cwnd >= ent->unacked) {
+ tcp_space = (ent->cwnd - ent->unacked) * (int64_t)(ent->mss);
+ } else {
+ tcp_space = 0;
+ }
+
+ /* The clamp_double_to_int64 makes sure the first part fits into an int64_t.
+ * In fact, if sock_buf_size_factor is still forced to be >= 0 in config.c,
+ * then it will be positive for sure. Then we subtract a uint32_t. Getting a
+ * negative value is OK, see after how it is being handled. */
+ extra_space =
+ clamp_double_to_int64(
+ (ent->cwnd * (int64_t)ent->mss) * sock_buf_size_factor) -
+ ent->notsent;
+ if ((tcp_space + extra_space) < 0) {
+ /* This means that the "notsent" queue is just too big so we shouldn't put
+ * more in the kernel for now. */
+ ent->limit = 0;
+ } else {
+ /* The positive sum of two int64_t will always fit into an uint64_t.
+ * And we know this will always be positive, since we checked above. */
+ ent->limit = (uint64_t)tcp_space + (uint64_t)extra_space;
+ }
+ return;
+
+#else /* !(defined(HAVE_KIST_SUPPORT)) */
+ goto fallback;
+#endif /* defined(HAVE_KIST_SUPPORT) */
+
+ fallback:
+ /* If all of a sudden we don't have kist support, we just zero out all the
+ * variables for this socket since we don't know what they should be. We
+ * also allow the socket to write as much as it can from the estimated
+ * number of cells the lower layer can accept, effectively returning it to
+ * Vanilla scheduler behavior. */
+ ent->cwnd = ent->unacked = ent->mss = ent->notsent = 0;
+ /* This function calls the specialized channel object (currently channeltls)
+ * and ask how many cells it can write on the outbuf which we then multiply
+ * by the size of the cells for this channel. The cast is because this
+ * function requires a non-const channel object, meh. */
+ ent->limit = channel_num_cells_writeable((channel_t *) ent->chan) *
+ (get_cell_network_size(ent->chan->wide_circ_ids) +
+ TLS_PER_CELL_OVERHEAD);
+}
+
+/* Given a socket that isn't in the table, add it.
+ * Given a socket that is in the table, re-init values that need init-ing
+ * every scheduling run
+ */
+static void
+init_socket_info(socket_table_t *table, const channel_t *chan)
+{
+ socket_table_ent_t *ent = NULL;
+ ent = socket_table_search(table, chan);
+ if (!ent) {
+ log_debug(LD_SCHED, "scheduler init socket info for chan=%" PRIu64,
+ chan->global_identifier);
+ ent = tor_malloc_zero(sizeof(*ent));
+ ent->chan = chan;
+ HT_INSERT(socket_table_s, table, ent);
+ }
+ ent->written = 0;
+}
+
+/* Add chan to the outbuf table if it isn't already in it. If it is, then don't
+ * do anything */
+static void
+outbuf_table_add(outbuf_table_t *table, channel_t *chan)
+{
+ outbuf_table_ent_t search, *ent;
+ search.chan = chan;
+ ent = HT_FIND(outbuf_table_s, table, &search);
+ if (!ent) {
+ log_debug(LD_SCHED, "scheduler init outbuf info for chan=%" PRIu64,
+ chan->global_identifier);
+ ent = tor_malloc_zero(sizeof(*ent));
+ ent->chan = chan;
+ HT_INSERT(outbuf_table_s, table, ent);
+ }
+}
+
+static void
+outbuf_table_remove(outbuf_table_t *table, channel_t *chan)
+{
+ outbuf_table_ent_t search, *ent;
+ search.chan = chan;
+ ent = HT_FIND(outbuf_table_s, table, &search);
+ if (ent) {
+ HT_REMOVE(outbuf_table_s, table, ent);
+ free_outbuf_info_by_ent(ent, NULL);
+ }
+}
+
+/* Set the scheduler running interval. */
+static void
+set_scheduler_run_interval(void)
+{
+ int old_sched_run_interval = sched_run_interval;
+ sched_run_interval = kist_scheduler_run_interval();
+ if (old_sched_run_interval != sched_run_interval) {
+ log_info(LD_SCHED, "Scheduler KIST changing its running interval "
+ "from %" PRId32 " to %" PRId32,
+ old_sched_run_interval, sched_run_interval);
+ }
+}
+
+/* Return true iff the channel hasn’t hit its kist-imposed write limit yet */
+static int
+socket_can_write(socket_table_t *table, const channel_t *chan)
+{
+ socket_table_ent_t *ent = NULL;
+ ent = socket_table_search(table, chan);
+ if (SCHED_BUG(!ent, chan)) {
+ return 1; // Just return true, saying that kist wouldn't limit the socket
+ }
+
+ /* We previously calculated a write limit for this socket. In the below
+ * calculation, first determine how much room is left in bytes. Then divide
+ * that by the amount of space a cell takes. If there's room for at least 1
+ * cell, then KIST will allow the socket to write. */
+ int64_t kist_limit_space =
+ (int64_t) (ent->limit - ent->written) /
+ (CELL_MAX_NETWORK_SIZE + TLS_PER_CELL_OVERHEAD);
+ return kist_limit_space > 0;
+}
+
+/* Update the channel's socket kernel information. */
+static void
+update_socket_info(socket_table_t *table, const channel_t *chan)
+{
+ socket_table_ent_t *ent = NULL;
+ ent = socket_table_search(table, chan);
+ if (SCHED_BUG(!ent, chan)) {
+ return; // Whelp. Entry didn't exist for some reason so nothing to do.
+ }
+ update_socket_info_impl(ent);
+ log_debug(LD_SCHED, "chan=%" PRIu64 " updated socket info, limit: %" PRIu64
+ ", cwnd: %" PRIu32 ", unacked: %" PRIu32
+ ", notsent: %" PRIu32 ", mss: %" PRIu32,
+ ent->chan->global_identifier, ent->limit, ent->cwnd, ent->unacked,
+ ent->notsent, ent->mss);
+}
+
+/* Increment the channel's socket written value by the number of bytes. */
+static void
+update_socket_written(socket_table_t *table, channel_t *chan, size_t bytes)
+{
+ socket_table_ent_t *ent = NULL;
+ ent = socket_table_search(table, chan);
+ if (SCHED_BUG(!ent, chan)) {
+ return; // Whelp. Entry didn't exist so nothing to do.
+ }
+
+ log_debug(LD_SCHED, "chan=%" PRIu64 " wrote %lu bytes, old was %" PRIi64,
+ chan->global_identifier, (unsigned long) bytes, ent->written);
+
+ ent->written += bytes;
+}
+
+/*
+ * A naive KIST impl would write every single cell all the way to the kernel.
+ * That would take a lot of system calls. A less bad KIST impl would write a
+ * channel's outbuf to the kernel only when we are switching to a different
+ * channel. But if we have two channels with equal priority, we end up writing
+ * one cell for each and bouncing back and forth. This KIST impl avoids that
+ * by only writing a channel's outbuf to the kernel if it has 8 cells or more
+ * in it.
+ */
+MOCK_IMPL(int, channel_should_write_to_kernel,
+ (outbuf_table_t *table, channel_t *chan))
+{
+ outbuf_table_add(table, chan);
+ /* CELL_MAX_NETWORK_SIZE * 8 because we only want to write the outbuf to the
+ * kernel if there's 8 or more cells waiting */
+ return channel_outbuf_length(chan) > (CELL_MAX_NETWORK_SIZE * 8);
+}
+
+/* Little helper function to write a channel's outbuf all the way to the
+ * kernel */
+MOCK_IMPL(void, channel_write_to_kernel, (channel_t *chan))
+{
+ log_debug(LD_SCHED, "Writing %lu bytes to kernel for chan %" PRIu64,
+ (unsigned long)channel_outbuf_length(chan),
+ chan->global_identifier);
+ connection_handle_write(TO_CONN(BASE_CHAN_TO_TLS(chan)->conn), 0);
+}
+
+/* Return true iff the scheduler has work to perform. */
+static int
+have_work(void)
+{
+ smartlist_t *cp = get_channels_pending();
+ IF_BUG_ONCE(!cp) {
+ return 0; // channels_pending doesn't exist so... no work?
+ }
+ return smartlist_len(cp) > 0;
+}
+
+/* Function of the scheduler interface: free_all() */
+static void
+kist_free_all(void)
+{
+ free_all_socket_info();
+}
+
+/* Function of the scheduler interface: on_channel_free() */
+static void
+kist_on_channel_free(const channel_t *chan)
+{
+ free_socket_info_by_chan(&socket_table, chan);
+}
+
+/* Function of the scheduler interface: on_new_consensus() */
+static void
+kist_scheduler_on_new_consensus(void)
+{
+ set_scheduler_run_interval();
+}
+
+/* Function of the scheduler interface: on_new_options() */
+static void
+kist_scheduler_on_new_options(void)
+{
+ sock_buf_size_factor = get_options()->KISTSockBufSizeFactor;
+
+ /* Calls kist_scheduler_run_interval which calls get_options(). */
+ set_scheduler_run_interval();
+}
+
+/* Function of the scheduler interface: init() */
+static void
+kist_scheduler_init(void)
+{
+ /* When initializing the scheduler, the last run could be 0 because it is
+ * declared static or a value in the past that was set when it was last
+ * used. In both cases, we want to initialize it to now so we don't risk
+ * using the value 0 which doesn't play well with our monotonic time
+ * interface.
+ *
+ * One side effect is that the first scheduler run will be at the next tick
+ * that is in now + 10 msec (KIST_SCHED_RUN_INTERVAL_DEFAULT) by default. */
+ monotime_get(&scheduler_last_run);
+
+ kist_scheduler_on_new_options();
+ IF_BUG_ONCE(sched_run_interval == 0) {
+ log_warn(LD_SCHED, "We are initing the KIST scheduler and noticed the "
+ "KISTSchedRunInterval is telling us to not use KIST. That's "
+ "weird! We'll continue using KIST, but at %" PRId32 "ms.",
+ KIST_SCHED_RUN_INTERVAL_DEFAULT);
+ sched_run_interval = KIST_SCHED_RUN_INTERVAL_DEFAULT;
+ }
+}
+
+/* Function of the scheduler interface: schedule() */
+static void
+kist_scheduler_schedule(void)
+{
+ struct monotime_t now;
+ struct timeval next_run;
+ int64_t diff;
+
+ if (!have_work()) {
+ return;
+ }
+ monotime_get(&now);
+
+ /* If time is really monotonic, we can never have now being smaller than the
+ * last scheduler run. The scheduler_last_run at first is set to 0.
+ * Unfortunately, not all platforms guarantee monotonic time so we log at
+ * info level but don't make it more noisy. */
+ diff = monotime_diff_msec(&scheduler_last_run, &now);
+ if (diff < 0) {
+ log_info(LD_SCHED, "Monotonic time between now and last run of scheduler "
+ "is negative: %" PRId64 ". Setting diff to 0.", diff);
+ diff = 0;
+ }
+ if (diff < sched_run_interval) {
+ next_run.tv_sec = 0;
+ /* Takes 1000 ms -> us. This will always be valid because diff can NOT be
+ * negative and can NOT be bigger than sched_run_interval so values can
+ * only go from 1000 usec (diff set to interval - 1) to 100000 usec (diff
+ * set to 0) for the maximum allowed run interval (100ms). */
+ next_run.tv_usec = (int) ((sched_run_interval - diff) * 1000);
+ /* Re-adding an event reschedules it. It does not duplicate it. */
+ scheduler_ev_add(&next_run);
+ } else {
+ scheduler_ev_active(EV_TIMEOUT);
+ }
+}
+
+/* Function of the scheduler interface: run() */
+static void
+kist_scheduler_run(void)
+{
+ /* Define variables */
+ channel_t *chan = NULL; // current working channel
+ /* The last distinct chan served in a sched loop. */
+ channel_t *prev_chan = NULL;
+ int flush_result; // temporarily store results from flush calls
+ /* Channels to be re-adding to pending at the end */
+ smartlist_t *to_readd = NULL;
+ smartlist_t *cp = get_channels_pending();
+
+ outbuf_table_t outbuf_table = HT_INITIALIZER();
+
+ /* For each pending channel, collect new kernel information */
+ SMARTLIST_FOREACH_BEGIN(cp, const channel_t *, pchan) {
+ init_socket_info(&socket_table, pchan);
+ update_socket_info(&socket_table, pchan);
+ } SMARTLIST_FOREACH_END(pchan);
+
+ log_debug(LD_SCHED, "Running the scheduler. %d channels pending",
+ smartlist_len(cp));
+
+ /* The main scheduling loop. Loop until there are no more pending channels */
+ while (smartlist_len(cp) > 0) {
+ /* get best channel */
+ chan = smartlist_pqueue_pop(cp, scheduler_compare_channels,
+ offsetof(channel_t, sched_heap_idx));
+ if (SCHED_BUG(!chan, NULL)) {
+ /* Some-freaking-how a NULL got into the channels_pending. That should
+ * never happen, but it should be harmless to ignore it and keep looping.
+ */
+ continue;
+ }
+ outbuf_table_add(&outbuf_table, chan);
+
+ /* if we have switched to a new channel, consider writing the previous
+ * channel's outbuf to the kernel. */
+ if (!prev_chan) {
+ prev_chan = chan;
+ }
+ if (prev_chan != chan) {
+ if (channel_should_write_to_kernel(&outbuf_table, prev_chan)) {
+ channel_write_to_kernel(prev_chan);
+ outbuf_table_remove(&outbuf_table, prev_chan);
+ }
+ prev_chan = chan;
+ }
+
+ /* Only flush and write if the per-socket limit hasn't been hit */
+ if (socket_can_write(&socket_table, chan)) {
+ /* flush to channel queue/outbuf */
+ flush_result = (int)channel_flush_some_cells(chan, 1); // 1 for num cells
+ /* XXX: While flushing cells, it is possible that the connection write
+ * fails leading to the channel to be closed which triggers a release
+ * and free its entry in the socket table. And because of a engineering
+ * design issue, the error is not propagated back so we don't get an
+ * error at this point. So before we continue, make sure the channel is
+ * open and if not just ignore it. See #23751. */
+ if (!CHANNEL_IS_OPEN(chan)) {
+ /* Channel isn't open so we put it back in IDLE mode. It is either
+ * renegotiating its TLS session or about to be released. */
+ chan->scheduler_state = SCHED_CHAN_IDLE;
+ continue;
+ }
+ /* flush_result has the # cells flushed */
+ if (flush_result > 0) {
+ update_socket_written(&socket_table, chan, flush_result *
+ (CELL_MAX_NETWORK_SIZE + TLS_PER_CELL_OVERHEAD));
+ } else {
+ /* XXX: This can happen because tor sometimes does flush in an
+ * opportunistic way cells from the circuit to the outbuf so the
+ * channel can end up here without having anything to flush nor needed
+ * to write to the kernel. Hopefully we'll fix that soon but for now
+ * we have to handle this case which happens kind of often. */
+ log_debug(LD_SCHED,
+ "We didn't flush anything on a chan that we think "
+ "can write and wants to write. The channel's state is '%s' "
+ "and in scheduler state %d. We're going to mark it as "
+ "waiting_for_cells (as that's most likely the issue) and "
+ "stop scheduling it this round.",
+ channel_state_to_string(chan->state),
+ chan->scheduler_state);
+ chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
+ continue;
+ }
+ }
+
+ /* Decide what to do with the channel now */
+
+ if (!channel_more_to_flush(chan) &&
+ !socket_can_write(&socket_table, chan)) {
+
+ /* Case 1: no more cells to send, and cannot write */
+
+ /*
+ * You might think we should put the channel in SCHED_CHAN_IDLE. And
+ * you're probably correct. While implementing KIST, we found that the
+ * scheduling system would sometimes lose track of channels when we did
+ * that. We suspect it has to do with the difference between "can't
+ * write because socket/outbuf is full" and KIST's "can't write because
+ * we've arbitrarily decided that that's enough for now." Sometimes
+ * channels run out of cells at the same time they hit their
+ * kist-imposed write limit and maybe the rest of Tor doesn't put the
+ * channel back in pending when it is supposed to.
+ *
+ * This should be investigated again. It is as simple as changing
+ * SCHED_CHAN_WAITING_FOR_CELLS to SCHED_CHAN_IDLE and seeing if Tor
+ * starts having serious throughput issues. Best done in shadow/chutney.
+ */
+ chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
+ log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells",
+ chan->global_identifier);
+ } else if (!channel_more_to_flush(chan)) {
+
+ /* Case 2: no more cells to send, but still open for writes */
+
+ chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
+ log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_for_cells",
+ chan->global_identifier);
+ } else if (!socket_can_write(&socket_table, chan)) {
+
+ /* Case 3: cells to send, but cannot write */
+
+ /*
+ * We want to write, but can't. If we left the channel in
+ * channels_pending, we would never exit the scheduling loop. We need to
+ * add it to a temporary list of channels to be added to channels_pending
+ * after the scheduling loop is over. They can hopefully be taken care of
+ * in the next scheduling round.
+ */
+ if (!to_readd) {
+ to_readd = smartlist_new();
+ }
+ smartlist_add(to_readd, chan);
+ log_debug(LD_SCHED, "chan=%" PRIu64 " now waiting_to_write",
+ chan->global_identifier);
+ } else {
+
+ /* Case 4: cells to send, and still open for writes */
+
+ chan->scheduler_state = SCHED_CHAN_PENDING;
+ if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
+ smartlist_pqueue_add(cp, scheduler_compare_channels,
+ offsetof(channel_t, sched_heap_idx), chan);
+ }
+ }
+ } /* End of main scheduling loop */
+
+ /* Write the outbuf of any channels that still have data */
+ HT_FOREACH_FN(outbuf_table_s, &outbuf_table, each_channel_write_to_kernel,
+ NULL);
+ /* We are done with it. */
+ HT_FOREACH_FN(outbuf_table_s, &outbuf_table, free_outbuf_info_by_ent, NULL);
+ HT_CLEAR(outbuf_table_s, &outbuf_table);
+
+ log_debug(LD_SCHED, "len pending=%d, len to_readd=%d",
+ smartlist_len(cp),
+ (to_readd ? smartlist_len(to_readd) : -1));
+
+ /* Re-add any channels we need to */
+ if (to_readd) {
+ SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
+ readd_chan->scheduler_state = SCHED_CHAN_PENDING;
+ if (!smartlist_contains(cp, readd_chan)) {
+ if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
+ /* XXXX Note that the check above is in theory redundant with
+ * the smartlist_contains check. But let's make sure we're
+ * not messing anything up, and leave them both for now. */
+ smartlist_pqueue_add(cp, scheduler_compare_channels,
+ offsetof(channel_t, sched_heap_idx), readd_chan);
+ }
+ }
+ } SMARTLIST_FOREACH_END(readd_chan);
+ smartlist_free(to_readd);
+ }
+
+ monotime_get(&scheduler_last_run);
+}
+
+/*****************************************************************************
+ * Externally called function implementations not called through scheduler_t
+ *****************************************************************************/
+
+/* Stores the kist scheduler function pointers. */
+static scheduler_t kist_scheduler = {
+ .type = SCHEDULER_KIST,
+ .free_all = kist_free_all,
+ .on_channel_free = kist_on_channel_free,
+ .init = kist_scheduler_init,
+ .on_new_consensus = kist_scheduler_on_new_consensus,
+ .schedule = kist_scheduler_schedule,
+ .run = kist_scheduler_run,
+ .on_new_options = kist_scheduler_on_new_options,
+};
+
+/* Return the KIST scheduler object. If it didn't exists, return a newly
+ * allocated one but init() is not called. */
+scheduler_t *
+get_kist_scheduler(void)
+{
+ return &kist_scheduler;
+}
+
+/* Check the torrc (and maybe consensus) for the configured KIST scheduler run
+ * interval.
+ * - If torrc > 0, then return the positive torrc value (should use KIST, and
+ * should use the set value)
+ * - If torrc == 0, then look in the consensus for what the value should be.
+ * - If == 0, then return 0 (don't use KIST)
+ * - If > 0, then return the positive consensus value
+ * - If consensus doesn't say anything, return 10 milliseconds, default.
+ */
+int
+kist_scheduler_run_interval(void)
+{
+ int run_interval = get_options()->KISTSchedRunInterval;
+
+ if (run_interval != 0) {
+ log_debug(LD_SCHED, "Found KISTSchedRunInterval=%" PRId32 " in torrc. "
+ "Using that.", run_interval);
+ return run_interval;
+ }
+
+ log_debug(LD_SCHED, "KISTSchedRunInterval=0, turning to the consensus.");
+
+ /* Will either be the consensus value or the default. Note that 0 can be
+ * returned which means the consensus wants us to NOT use KIST. */
+ return networkstatus_get_param(NULL, "KISTSchedRunInterval",
+ KIST_SCHED_RUN_INTERVAL_DEFAULT,
+ KIST_SCHED_RUN_INTERVAL_MIN,
+ KIST_SCHED_RUN_INTERVAL_MAX);
+}
+
+/* Set KISTLite mode that is KIST without kernel support. */
+void
+scheduler_kist_set_lite_mode(void)
+{
+ kist_lite_mode = 1;
+ kist_scheduler.type = SCHEDULER_KIST_LITE;
+ log_info(LD_SCHED,
+ "Setting KIST scheduler without kernel support (KISTLite mode)");
+}
+
+/* Set KIST mode that is KIST with kernel support. */
+void
+scheduler_kist_set_full_mode(void)
+{
+ kist_lite_mode = 0;
+ kist_scheduler.type = SCHEDULER_KIST;
+ log_info(LD_SCHED,
+ "Setting KIST scheduler with kernel support (KIST mode)");
+}
+
+#ifdef HAVE_KIST_SUPPORT
+
+/* Return true iff the scheduler subsystem should use KIST. */
+int
+scheduler_can_use_kist(void)
+{
+ if (kist_no_kernel_support) {
+ /* We have no kernel support so we can't use KIST. */
+ return 0;
+ }
+
+ /* We do have the support, time to check if we can get the interval that the
+ * consensus can be disabling. */
+ int run_interval = kist_scheduler_run_interval();
+ log_debug(LD_SCHED, "Determined KIST sched_run_interval should be "
+ "%" PRId32 ". Can%s use KIST.",
+ run_interval, (run_interval > 0 ? "" : " not"));
+ return run_interval > 0;
+}
+
+#else /* !(defined(HAVE_KIST_SUPPORT)) */
+
+int
+scheduler_can_use_kist(void)
+{
+ return 0;
+}
+
+#endif /* defined(HAVE_KIST_SUPPORT) */
+
diff --git a/src/or/scheduler_vanilla.c b/src/or/scheduler_vanilla.c
new file mode 100644
index 0000000000..303b3dbba8
--- /dev/null
+++ b/src/or/scheduler_vanilla.c
@@ -0,0 +1,197 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include <event2/event.h>
+
+#include "or.h"
+#include "config.h"
+#define TOR_CHANNEL_INTERNAL_
+#include "channel.h"
+#define SCHEDULER_PRIVATE_
+#include "scheduler.h"
+
+/*****************************************************************************
+ * Other internal data
+ *****************************************************************************/
+
+/* Maximum cells to flush in a single call to channel_flush_some_cells(); */
+#define MAX_FLUSH_CELLS 1000
+
+/*****************************************************************************
+ * Externally called function implementations
+ *****************************************************************************/
+
+/* Return true iff the scheduler has work to perform. */
+static int
+have_work(void)
+{
+ smartlist_t *cp = get_channels_pending();
+ IF_BUG_ONCE(!cp) {
+ return 0; // channels_pending doesn't exist so... no work?
+ }
+ return smartlist_len(cp) > 0;
+}
+
+/** Re-trigger the scheduler in a way safe to use from the callback */
+
+static void
+vanilla_scheduler_schedule(void)
+{
+ if (!have_work()) {
+ return;
+ }
+
+ /* Activate our event so it can process channels. */
+ scheduler_ev_active(EV_TIMEOUT);
+}
+
+static void
+vanilla_scheduler_run(void)
+{
+ int n_cells, n_chans_before, n_chans_after;
+ ssize_t flushed, flushed_this_time;
+ smartlist_t *cp = get_channels_pending();
+ smartlist_t *to_readd = NULL;
+ channel_t *chan = NULL;
+
+ log_debug(LD_SCHED, "We have a chance to run the scheduler");
+
+ n_chans_before = smartlist_len(cp);
+
+ while (smartlist_len(cp) > 0) {
+ /* Pop off a channel */
+ chan = smartlist_pqueue_pop(cp,
+ scheduler_compare_channels,
+ offsetof(channel_t, sched_heap_idx));
+ IF_BUG_ONCE(!chan) {
+ /* Some-freaking-how a NULL got into the channels_pending. That should
+ * never happen, but it should be harmless to ignore it and keep looping.
+ */
+ continue;
+ }
+
+ /* Figure out how many cells we can write */
+ n_cells = channel_num_cells_writeable(chan);
+ if (n_cells > 0) {
+ log_debug(LD_SCHED,
+ "Scheduler saw pending channel " U64_FORMAT " at %p with "
+ "%d cells writeable",
+ U64_PRINTF_ARG(chan->global_identifier), chan, n_cells);
+
+ flushed = 0;
+ while (flushed < n_cells) {
+ flushed_this_time =
+ channel_flush_some_cells(chan,
+ MIN(MAX_FLUSH_CELLS, (size_t) n_cells - flushed));
+ if (flushed_this_time <= 0) break;
+ flushed += flushed_this_time;
+ }
+
+ if (flushed < n_cells) {
+ /* We ran out of cells to flush */
+ chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
+ log_debug(LD_SCHED,
+ "Channel " U64_FORMAT " at %p "
+ "entered waiting_for_cells from pending",
+ U64_PRINTF_ARG(chan->global_identifier),
+ chan);
+ } else {
+ /* The channel may still have some cells */
+ if (channel_more_to_flush(chan)) {
+ /* The channel goes to either pending or waiting_to_write */
+ if (channel_num_cells_writeable(chan) > 0) {
+ /* Add it back to pending later */
+ if (!to_readd) to_readd = smartlist_new();
+ smartlist_add(to_readd, chan);
+ log_debug(LD_SCHED,
+ "Channel " U64_FORMAT " at %p "
+ "is still pending",
+ U64_PRINTF_ARG(chan->global_identifier),
+ chan);
+ } else {
+ /* It's waiting to be able to write more */
+ chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
+ log_debug(LD_SCHED,
+ "Channel " U64_FORMAT " at %p "
+ "entered waiting_to_write from pending",
+ U64_PRINTF_ARG(chan->global_identifier),
+ chan);
+ }
+ } else {
+ /* No cells left; it can go to idle or waiting_for_cells */
+ if (channel_num_cells_writeable(chan) > 0) {
+ /*
+ * It can still accept writes, so it goes to
+ * waiting_for_cells
+ */
+ chan->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
+ log_debug(LD_SCHED,
+ "Channel " U64_FORMAT " at %p "
+ "entered waiting_for_cells from pending",
+ U64_PRINTF_ARG(chan->global_identifier),
+ chan);
+ } else {
+ /*
+ * We exactly filled up the output queue with all available
+ * cells; go to idle.
+ */
+ chan->scheduler_state = SCHED_CHAN_IDLE;
+ log_debug(LD_SCHED,
+ "Channel " U64_FORMAT " at %p "
+ "become idle from pending",
+ U64_PRINTF_ARG(chan->global_identifier),
+ chan);
+ }
+ }
+ }
+
+ log_debug(LD_SCHED,
+ "Scheduler flushed %d cells onto pending channel "
+ U64_FORMAT " at %p",
+ (int)flushed, U64_PRINTF_ARG(chan->global_identifier),
+ chan);
+ } else {
+ log_info(LD_SCHED,
+ "Scheduler saw pending channel " U64_FORMAT " at %p with "
+ "no cells writeable",
+ U64_PRINTF_ARG(chan->global_identifier), chan);
+ /* Put it back to WAITING_TO_WRITE */
+ chan->scheduler_state = SCHED_CHAN_WAITING_TO_WRITE;
+ }
+ }
+
+ /* Readd any channels we need to */
+ if (to_readd) {
+ SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, readd_chan) {
+ readd_chan->scheduler_state = SCHED_CHAN_PENDING;
+ smartlist_pqueue_add(cp,
+ scheduler_compare_channels,
+ offsetof(channel_t, sched_heap_idx),
+ readd_chan);
+ } SMARTLIST_FOREACH_END(readd_chan);
+ smartlist_free(to_readd);
+ }
+
+ n_chans_after = smartlist_len(cp);
+ log_debug(LD_SCHED, "Scheduler handled %d of %d pending channels",
+ n_chans_before - n_chans_after, n_chans_before);
+}
+
+/* Stores the vanilla scheduler function pointers. */
+static scheduler_t vanilla_scheduler = {
+ .type = SCHEDULER_VANILLA,
+ .free_all = NULL,
+ .on_channel_free = NULL,
+ .init = NULL,
+ .on_new_consensus = NULL,
+ .schedule = vanilla_scheduler_schedule,
+ .run = vanilla_scheduler_run,
+ .on_new_options = NULL,
+};
+
+scheduler_t *
+get_vanilla_scheduler(void)
+{
+ return &vanilla_scheduler;
+}
+
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 25ca0611cd..b3f62a8fd8 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -1333,13 +1333,7 @@ sr_act_post_consensus(const networkstatus_t *consensus)
}
/* Prepare our state so that it's ready for the next voting period. */
- {
- voting_schedule_t *voting_schedule =
- get_voting_schedule(options,time(NULL), LOG_NOTICE);
- time_t interval_starts = voting_schedule->interval_starts;
- sr_state_update(interval_starts);
- voting_schedule_free(voting_schedule);
- }
+ sr_state_update(dirvote_get_next_valid_after_time());
}
/* Initialize shared random subsystem. This MUST be called early in the boot
@@ -1390,6 +1384,52 @@ sr_get_previous_for_control(void)
return srv_str;
}
+/* Return current shared random value from the latest consensus. Caller can
+ * NOT keep a reference to the returned pointer. Return NULL if none. */
+const sr_srv_t *
+sr_get_current(const networkstatus_t *ns)
+{
+ const networkstatus_t *consensus;
+
+ /* Use provided ns else get a live one */
+ if (ns) {
+ consensus = ns;
+ } else {
+ consensus = networkstatus_get_live_consensus(approx_time());
+ }
+ /* Ideally we would never be asked for an SRV without a live consensus. Make
+ * sure this assumption is correct. */
+ tor_assert_nonfatal(consensus);
+
+ if (consensus) {
+ return consensus->sr_info.current_srv;
+ }
+ return NULL;
+}
+
+/* Return previous shared random value from the latest consensus. Caller can
+ * NOT keep a reference to the returned pointer. Return NULL if none. */
+const sr_srv_t *
+sr_get_previous(const networkstatus_t *ns)
+{
+ const networkstatus_t *consensus;
+
+ /* Use provided ns else get a live one */
+ if (ns) {
+ consensus = ns;
+ } else {
+ consensus = networkstatus_get_live_consensus(approx_time());
+ }
+ /* Ideally we would never be asked for an SRV without a live consensus. Make
+ * sure this assumption is correct. */
+ tor_assert_nonfatal(consensus);
+
+ if (consensus) {
+ return consensus->sr_info.previous_srv;
+ }
+ return NULL;
+}
+
#ifdef TOR_UNIT_TESTS
/* Set the global value of number of SRV agreements so the test can play
@@ -1401,5 +1441,5 @@ set_num_srv_agreements(int32_t value)
num_srv_agreements_from_vote = value;
}
-#endif /* TOR_UNIT_TESTS */
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index 1f027c70e0..c0992489cb 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -130,6 +130,9 @@ sr_commit_t *sr_generate_our_commit(time_t timestamp,
char *sr_get_current_for_control(void);
char *sr_get_previous_for_control(void);
+const sr_srv_t *sr_get_current(const networkstatus_t *ns);
+const sr_srv_t *sr_get_previous(const networkstatus_t *ns);
+
#ifdef SHARED_RANDOM_PRIVATE
/* Encode */
@@ -157,7 +160,7 @@ STATIC int should_keep_commit(const sr_commit_t *commit,
sr_phase_t phase);
STATIC void save_commit_during_reveal_phase(const sr_commit_t *commit);
-#endif /* SHARED_RANDOM_PRIVATE */
+#endif /* defined(SHARED_RANDOM_PRIVATE) */
#ifdef TOR_UNIT_TESTS
@@ -165,5 +168,5 @@ void set_num_srv_agreements(int32_t value);
#endif /* TOR_UNIT_TESTS */
-#endif /* TOR_SHARED_RANDOM_H */
+#endif /* !defined(TOR_SHARED_RANDOM_H) */
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 89d2e8d7f6..ae904cfda3 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -40,10 +40,14 @@ static const char dstate_commit_key[] = "Commit";
static const char dstate_prev_srv_key[] = "SharedRandPreviousValue";
static const char dstate_cur_srv_key[] = "SharedRandCurrentValue";
+/** dummy instance of sr_disk_state_t, used for type-checking its
+ * members with CONF_CHECK_VAR_TYPE. */
+DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t);
+
/* These next two are duplicates or near-duplicates from config.c */
#define VAR(name, conftype, member, initvalue) \
- { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(sr_disk_state_t, member), \
- initvalue }
+ { name, CONFIG_TYPE_ ## conftype, offsetof(sr_disk_state_t, member), \
+ initvalue CONF_TEST_MEMBERS(sr_disk_state_t, conftype, member) }
/* As VAR, but the option name and member name are the same. */
#define V(member, conftype, initvalue) \
VAR(#member, conftype, member, initvalue)
@@ -70,21 +74,22 @@ static config_var_t state_vars[] = {
V(SharedRandValues, LINELIST_V, NULL),
VAR("SharedRandPreviousValue",LINELIST_S, SharedRandValues, NULL),
VAR("SharedRandCurrentValue", LINELIST_S, SharedRandValues, NULL),
- { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+ END_OF_CONFIG_VARS
};
/* "Extra" variable in the state that receives lines we can't parse. This
* lets us preserve options from versions of Tor newer than us. */
static config_var_t state_extra_var = {
"__extra", CONFIG_TYPE_LINELIST,
- STRUCT_OFFSET(sr_disk_state_t, ExtraLines), NULL
+ offsetof(sr_disk_state_t, ExtraLines), NULL
+ CONF_TEST_MEMBERS(sr_disk_state_t, LINELIST, ExtraLines)
};
/* Configuration format of sr_disk_state_t. */
static const config_format_t state_format = {
sizeof(sr_disk_state_t),
SR_DISK_STATE_MAGIC,
- STRUCT_OFFSET(sr_disk_state_t, magic_),
+ offsetof(sr_disk_state_t, magic_),
NULL,
NULL,
state_vars,
@@ -133,27 +138,56 @@ get_voting_interval(void)
/* Given the time <b>now</b>, return the start time of the current round of
* the SR protocol. For example, if it's 23:47:08, the current round thus
* started at 23:47:00 for a voting interval of 10 seconds. */
-static time_t
-get_start_time_of_current_round(time_t now)
+STATIC time_t
+get_start_time_of_current_round(void)
{
const or_options_t *options = get_options();
int voting_interval = get_voting_interval();
- voting_schedule_t *new_voting_schedule =
- get_voting_schedule(options, now, LOG_INFO);
- tor_assert(new_voting_schedule);
-
/* First, get the start time of the next round */
- time_t next_start = new_voting_schedule->interval_starts;
+ time_t next_start = dirvote_get_next_valid_after_time();
/* Now roll back next_start by a voting interval to find the start time of
the current round. */
time_t curr_start = dirvote_get_start_of_next_interval(
next_start - voting_interval - 1,
voting_interval,
options->TestingV3AuthVotingStartOffset);
+ return curr_start;
+}
- voting_schedule_free(new_voting_schedule);
+/** Return the start time of the current SR protocol run. For example, if the
+ * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this
+ * function should return 23/06/2017 00:00:00. */
+time_t
+sr_state_get_start_time_of_current_protocol_run(time_t now)
+{
+ int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ int voting_interval = get_voting_interval();
+ /* Find the time the current round started. */
+ time_t beginning_of_current_round = get_start_time_of_current_round();
- return curr_start;
+ /* Get current SR protocol round */
+ int current_round = (now / voting_interval) % total_rounds;
+
+ /* Get start time by subtracting the time elapsed from the beginning of the
+ protocol run */
+ time_t time_elapsed_since_start_of_run = current_round * voting_interval;
+ return beginning_of_current_round - time_elapsed_since_start_of_run;
+}
+
+/** Return the time (in seconds) it takes to complete a full SR protocol phase
+ * (e.g. the commit phase). */
+unsigned int
+sr_state_get_phase_duration(void)
+{
+ return SHARED_RANDOM_N_ROUNDS * get_voting_interval();
+}
+
+/** Return the time (in seconds) it takes to complete a full SR protocol run */
+unsigned int
+sr_state_get_protocol_run_duration(void)
+{
+ int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ return total_protocol_rounds * get_voting_interval();
}
/* Return the time we should expire the state file created at <b>now</b>.
@@ -167,7 +201,7 @@ get_state_valid_until_time(time_t now)
voting_interval = get_voting_interval();
/* Find the time the current round started. */
- beginning_of_current_round = get_start_time_of_current_round(now);
+ beginning_of_current_round = get_start_time_of_current_round();
/* Find how many rounds are left till the end of the protocol run */
current_round = (now / voting_interval) % total_rounds;
@@ -1329,7 +1363,7 @@ sr_state_init(int save_to_disk, int read_from_disk)
/* We have a state in memory, let's make sure it's updated for the current
* and next voting round. */
{
- time_t valid_after = get_next_valid_after_time(now);
+ time_t valid_after = dirvote_get_next_valid_after_time();
sr_state_update(valid_after);
}
return 0;
@@ -1355,5 +1389,5 @@ get_sr_state(void)
return sr_state;
}
-#endif /* TOR_UNIT_TESTS */
+#endif /* defined(TOR_UNIT_TESTS) */
diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h
index 3526ad47d3..866725c435 100644
--- a/src/or/shared_random_state.h
+++ b/src/or/shared_random_state.h
@@ -77,7 +77,7 @@ typedef struct sr_state_t {
typedef struct sr_disk_state_t {
uint32_t magic_;
/* Version of the protocol. */
- uint32_t Version;
+ int Version;
/* Version of our running tor. */
char *TorVersion;
/* Creation time of this state */
@@ -121,11 +121,16 @@ int sr_state_is_initialized(void);
void sr_state_save(void);
void sr_state_free(void);
+time_t sr_state_get_start_time_of_current_protocol_run(time_t now);
+unsigned int sr_state_get_phase_duration(void);
+unsigned int sr_state_get_protocol_run_duration(void);
+
#ifdef SHARED_RANDOM_STATE_PRIVATE
STATIC int disk_state_load_from_disk_impl(const char *fname);
STATIC sr_phase_t get_sr_protocol_phase(time_t valid_after);
+STATIC time_t get_start_time_of_current_round(void);
STATIC time_t get_state_valid_until_time(time_t now);
STATIC const char *get_phase_str(sr_phase_t phase);
@@ -134,14 +139,14 @@ STATIC void new_protocol_run(time_t valid_after);
STATIC void state_rotate_srv(void);
STATIC int is_phase_transition(sr_phase_t next_phase);
-#endif /* SHARED_RANDOM_STATE_PRIVATE */
+#endif /* defined(SHARED_RANDOM_STATE_PRIVATE) */
#ifdef TOR_UNIT_TESTS
STATIC void set_sr_phase(sr_phase_t phase);
STATIC sr_state_t *get_sr_state(void);
-#endif /* TOR_UNIT_TESTS */
+#endif /* defined(TOR_UNIT_TESTS) */
-#endif /* TOR_SHARED_RANDOM_STATE_H */
+#endif /* !defined(TOR_SHARED_RANDOM_STATE_H) */
diff --git a/src/or/statefile.c b/src/or/statefile.c
index d0606b3012..97bd9cac36 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -34,6 +34,7 @@
#include "config.h"
#include "confparse.h"
#include "connection.h"
+#include "control.h"
#include "entrynodes.h"
#include "hibernate.h"
#include "rephist.h"
@@ -53,10 +54,14 @@ static config_abbrev_t state_abbrevs_[] = {
{ NULL, NULL, 0, 0},
};
+/** dummy instance of or_state_t, used for type-checking its
+ * members with CONF_CHECK_VAR_TYPE. */
+DUMMY_TYPECHECK_INSTANCE(or_state_t);
+
/*XXXX these next two are duplicates or near-duplicates from config.c */
#define VAR(name,conftype,member,initvalue) \
- { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(or_state_t, member), \
- initvalue }
+ { name, CONFIG_TYPE_ ## conftype, offsetof(or_state_t, member), \
+ initvalue CONF_TEST_MEMBERS(or_state_t, conftype, member) }
/** As VAR, but the option name and member name are the same. */
#define V(member,conftype,initvalue) \
VAR(#member, conftype, member, initvalue)
@@ -85,6 +90,8 @@ static config_var_t state_vars_[] = {
VAR("TransportProxy", LINELIST_S, TransportProxies, NULL),
V(TransportProxies, LINELIST_V, NULL),
+ V(HidServRevCounter, LINELIST, NULL),
+
V(BWHistoryReadEnds, ISOTIME, NULL),
V(BWHistoryReadInterval, UINT, "900"),
V(BWHistoryReadValues, CSV, ""),
@@ -113,7 +120,8 @@ static config_var_t state_vars_[] = {
V(CircuitBuildAbandonedCount, UINT, "0"),
VAR("CircuitBuildTimeBin", LINELIST_S, BuildtimeHistogram, NULL),
VAR("BuildtimeHistogram", LINELIST_V, BuildtimeHistogram, NULL),
- { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+
+ END_OF_CONFIG_VARS
};
#undef VAR
@@ -131,14 +139,15 @@ static int or_state_validate_cb(void *old_options, void *options,
/** "Extra" variable in the state that receives lines we can't parse. This
* lets us preserve options from versions of Tor newer than us. */
static config_var_t state_extra_var = {
- "__extra", CONFIG_TYPE_LINELIST, STRUCT_OFFSET(or_state_t, ExtraLines), NULL
+ "__extra", CONFIG_TYPE_LINELIST, offsetof(or_state_t, ExtraLines), NULL
+ CONF_TEST_MEMBERS(or_state_t, LINELIST, ExtraLines)
};
/** Configuration format for or_state_t. */
static const config_format_t state_format = {
sizeof(or_state_t),
OR_STATE_MAGIC,
- STRUCT_OFFSET(or_state_t, magic_),
+ offsetof(or_state_t, magic_),
state_abbrevs_,
NULL,
state_vars_,
@@ -402,10 +411,15 @@ or_state_load(void)
log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
/* Warn the user if their clock has been set backwards,
* they could be tricked into using old consensuses */
- time_t apparent_skew = new_state->LastWritten - time(NULL);
- if (apparent_skew > 0)
+ time_t apparent_skew = time(NULL) - new_state->LastWritten;
+ if (apparent_skew < 0) {
+ /* Initialize bootstrap event reporting because we might call
+ * clock_skew_warning() before the bootstrap state is
+ * initialized, causing an assertion failure. */
+ control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
clock_skew_warning(NULL, (long)apparent_skew, 1, LD_GENERAL,
"local state file", fname);
+ }
} else {
log_info(LD_GENERAL, "Initialized state");
}
@@ -657,8 +671,6 @@ save_transport_to_state(const char *transport,
*next = line = tor_malloc_zero(sizeof(config_line_t));
line->key = tor_strdup("TransportProxy");
tor_asprintf(&line->value, "%s %s", transport, fmt_addrport(addr, port));
-
- next = &(line->next);
}
if (!get_options()->AvoidDiskWrites)
diff --git a/src/or/statefile.h b/src/or/statefile.h
index 10c09324bc..574afb3622 100644
--- a/src/or/statefile.h
+++ b/src/or/statefile.h
@@ -24,5 +24,5 @@ STATIC void or_state_free(or_state_t *state);
STATIC or_state_t *or_state_new(void);
#endif
-#endif
+#endif /* !defined(TOR_STATEFILE_H) */
diff --git a/src/or/status.h b/src/or/status.h
index c1a0033ce0..49da6abc0f 100644
--- a/src/or/status.h
+++ b/src/or/status.h
@@ -14,5 +14,5 @@ STATIC char *secs_to_uptime(long secs);
STATIC char *bytes_to_usage(uint64_t bytes);
#endif
-#endif
+#endif /* !defined(TOR_STATUS_H) */
diff --git a/src/or/torcert.c b/src/or/torcert.c
index 658e620ca5..212534d311 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -76,29 +76,39 @@ tor_cert_sign_impl(const ed25519_keypair_t *signing_key,
ed25519_signature_t signature;
if (ed25519_sign(&signature, encoded,
real_len-ED25519_SIG_LEN, signing_key)<0) {
+ /* LCOV_EXCL_START */
log_warn(LD_BUG, "Can't sign certificate");
goto err;
+ /* LCOV_EXCL_STOP */
}
memcpy(sig, signature.sig, ED25519_SIG_LEN);
torcert = tor_cert_parse(encoded, real_len);
if (! torcert) {
+ /* LCOV_EXCL_START */
log_warn(LD_BUG, "Generated a certificate we cannot parse");
goto err;
+ /* LCOV_EXCL_STOP */
}
if (tor_cert_checksig(torcert, &signing_key->pubkey, now) < 0) {
- log_warn(LD_BUG, "Generated a certificate whose signature we can't check");
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Generated a certificate whose signature we can't "
+ "check: %s", tor_cert_describe_signature_status(torcert));
goto err;
+ /* LCOV_EXCL_STOP */
}
tor_free(encoded);
goto done;
+ /* LCOV_EXCL_START */
err:
tor_cert_free(torcert);
torcert = NULL;
+ /* LCOV_EXCL_STOP */
+
done:
ed25519_cert_free(cert);
tor_free(encoded);
@@ -258,6 +268,24 @@ tor_cert_checksig(tor_cert_t *cert,
}
}
+/** Return a string describing the status of the signature on <b>cert</b>
+ *
+ * Will always be "unchecked" unless tor_cert_checksig has been called.
+ */
+const char *
+tor_cert_describe_signature_status(const tor_cert_t *cert)
+{
+ if (cert->cert_expired) {
+ return "expired";
+ } else if (cert->sig_bad) {
+ return "mis-signed";
+ } else if (cert->sig_ok) {
+ return "okay";
+ } else {
+ return "unchecked";
+ }
+}
+
/** Return a new copy of <b>cert</b> */
tor_cert_t *
tor_cert_dup(const tor_cert_t *cert)
@@ -356,12 +384,12 @@ tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key,
*
* Return 0 on success, negative on failure.
*/
-int
-rsa_ed25519_crosscert_check(const uint8_t *crosscert,
- const size_t crosscert_len,
- const crypto_pk_t *rsa_id_key,
- const ed25519_public_key_t *master_key,
- const time_t reject_if_expired_before)
+MOCK_IMPL(int,
+rsa_ed25519_crosscert_check, (const uint8_t *crosscert,
+ const size_t crosscert_len,
+ const crypto_pk_t *rsa_id_key,
+ const ed25519_public_key_t *master_key,
+ const time_t reject_if_expired_before))
{
rsa_ed_crosscert_t *cc = NULL;
int rv;
@@ -393,7 +421,7 @@ rsa_ed25519_crosscert_check(const uint8_t *crosscert,
}
const uint32_t expiration_date = rsa_ed_crosscert_get_expiration(cc);
- const uint64_t expiration_time = expiration_date * 3600;
+ const uint64_t expiration_time = ((uint64_t)expiration_date) * 3600;
if (reject_if_expired_before < 0 ||
expiration_time < (uint64_t)reject_if_expired_before) {
@@ -675,8 +703,10 @@ tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out)
if (base64_encode(ed_cert_b64, ed_cert_b64_len,
(const char *) cert->encoded, cert->encoded_len,
BASE64_ENCODE_MULTILINE) < 0) {
+ /* LCOV_EXCL_START */
log_err(LD_BUG, "Couldn't base64-encode ed22519 cert!");
goto err;
+ /* LCOV_EXCL_STOP */
}
/* Put everything together in a NUL terminated string. */
diff --git a/src/or/torcert.h b/src/or/torcert.h
index 51f7665f1e..ac227db209 100644
--- a/src/or/torcert.h
+++ b/src/or/torcert.h
@@ -66,6 +66,7 @@ int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out,
int tor_cert_checksig(tor_cert_t *cert,
const ed25519_public_key_t *pubkey, time_t now);
+const char *tor_cert_describe_signature_status(const tor_cert_t *cert);
tor_cert_t *tor_cert_dup(const tor_cert_t *cert);
int tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2);
@@ -75,11 +76,12 @@ ssize_t tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key,
const crypto_pk_t *rsa_key,
time_t expires,
uint8_t **cert);
-int rsa_ed25519_crosscert_check(const uint8_t *crosscert,
- const size_t crosscert_len,
- const crypto_pk_t *rsa_id_key,
- const ed25519_public_key_t *master_key,
- const time_t reject_if_expired_before);
+MOCK_DECL(int,
+rsa_ed25519_crosscert_check, (const uint8_t *crosscert,
+ const size_t crosscert_len,
+ const crypto_pk_t *rsa_id_key,
+ const ed25519_public_key_t *master_key,
+ const time_t reject_if_expired_before));
or_handshake_certs_t *or_handshake_certs_new(void);
void or_handshake_certs_free(or_handshake_certs_t *certs);
@@ -100,5 +102,5 @@ void or_handshake_certs_check_both(int severity,
int tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out);
-#endif
+#endif /* !defined(TORCERT_H_INCLUDED) */
diff --git a/src/or/transports.c b/src/or/transports.c
index 31849a8d15..5fb24e11a0 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -527,12 +527,12 @@ launch_managed_proxy(managed_proxy_t *mp)
(const char **)mp->argv,
env,
&mp->process_handle);
-#else
+#else /* !(defined(_WIN32)) */
retval = tor_spawn_background(mp->argv[0],
(const char **)mp->argv,
env,
&mp->process_handle);
-#endif
+#endif /* defined(_WIN32) */
process_environment_free(env);
@@ -1094,8 +1094,6 @@ parse_smethod_line(const char *line, managed_proxy_t *mp)
transport = transport_new(&tor_addr, port, method_name,
PROXY_NONE, args_string);
- if (!transport)
- goto err;
smartlist_add(mp->transports, transport);
@@ -1186,8 +1184,6 @@ parse_cmethod_line(const char *line, managed_proxy_t *mp)
}
transport = transport_new(&tor_addr, port, method_name, socks_ver, NULL);
- if (!transport)
- goto err;
smartlist_add(mp->transports, transport);
diff --git a/src/or/transports.h b/src/or/transports.h
index 44a9626e50..e368e447c3 100644
--- a/src/or/transports.h
+++ b/src/or/transports.h
@@ -133,7 +133,7 @@ STATIC char* get_pt_proxy_uri(void);
STATIC void free_execve_args(char **arg);
-#endif
+#endif /* defined(PT_PRIVATE) */
-#endif
+#endif /* !defined(TOR_TRANSPORTS_H) */
diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock
index 4ac9606ce8..e208e14320 100644
--- a/src/rust/Cargo.lock
+++ b/src/rust/Cargo.lock
@@ -1,14 +1,14 @@
-[root]
+[[package]]
+name = "libc"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "tor_util"
version = "0.0.1"
dependencies = [
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
]
-[[package]]
-name = "libc"
-version = "0.2.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
[metadata]
"checksum libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)" = "babb8281da88cba992fa1f4ddec7d63ed96280a1a53ec9b919fd37b53d71e502"
diff --git a/src/rust/tor_util/include.am b/src/rust/tor_util/include.am
index f0cd63920c..ec3898577b 100644
--- a/src/rust/tor_util/include.am
+++ b/src/rust/tor_util/include.am
@@ -4,7 +4,7 @@ EXTRA_DIST +=\
src/rust/tor_util/ffi.rs \
src/rust/tor_util/rust_string.rs
-src/rust/target/release/libtor_util.a: FORCE
+src/rust/target/release/@TOR_RUST_UTIL_STATIC_NAME@: FORCE
( cd "$(abs_top_srcdir)/src/rust/tor_util" ; \
CARGO_TARGET_DIR="$(abs_top_builddir)/src/rust/target" \
CARGO_HOME="$(abs_top_builddir)/src/rust" \
diff --git a/src/test/bench.c b/src/test/bench.c
index a44dc94a61..b7b123eee2 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -58,7 +58,7 @@ perftime(void)
return timespec_to_nsec(&ts) - nanostart;
}
-#else
+#else /* !(defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID)) */
static struct timeval tv_start = { 0, 0 };
static void
reset_perftime(void)
@@ -73,7 +73,7 @@ perftime(void)
timersub(&now, &tv_start, &out);
return ((uint64_t)out.tv_sec)*1000000000 + out.tv_usec*1000;
}
-#endif
+#endif /* defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_PROCESS_CPUTIME_ID) */
#define NANOCOUNT(start,end,iters) \
( ((double)((end)-(start))) / (iters) )
@@ -200,6 +200,7 @@ bench_onion_ntor_impl(void)
curve25519_public_key_generate(&keypair2.pubkey, &keypair2.seckey);
dimap_add_entry(&keymap, keypair1.pubkey.public_key, &keypair1);
dimap_add_entry(&keymap, keypair2.pubkey.public_key, &keypair2);
+ crypto_rand((char *)nodeid, sizeof(nodeid));
reset_perftime();
start = perftime();
diff --git a/src/test/ed25519_exts_ref.py b/src/test/ed25519_exts_ref.py
index af5010415e..f84d3002d3 100644
--- a/src/test/ed25519_exts_ref.py
+++ b/src/test/ed25519_exts_ref.py
@@ -32,8 +32,7 @@ def curve25519ToEd25519(c, sign):
return encodepoint([x,y])
def blindESK(esk, param):
- h = H("Derive temporary signing key" + param)
- mult = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
+ mult = 2**(b-2) + sum(2**i * bit(param,i) for i in range(3,b-2))
s = decodeint(esk[:32])
s_prime = (s * mult) % ell
k = esk[32:]
@@ -42,8 +41,7 @@ def blindESK(esk, param):
return encodeint(s_prime) + k_prime
def blindPK(pk, param):
- h = H("Derive temporary signing key" + param)
- mult = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
+ mult = 2**(b-2) + sum(2**i * bit(param,i) for i in range(3,b-2))
P = decodepoint(pk)
return encodepoint(scalarmult(P, mult))
@@ -69,6 +67,11 @@ def signatureWithESK(m,h,pk):
def newSK():
return os.urandom(32)
+def random_scalar(entropy_f): # 0..L-1 inclusive
+ # reduce the bias to a safe level by generating 256 extra bits
+ oversized = int(binascii.hexlify(entropy_f(32+32)), 16)
+ return oversized % ell
+
# ------------------------------------------------------------
MSG = "This is extremely silly. But it is also incredibly serious business!"
@@ -126,6 +129,31 @@ class SelfTest(unittest.TestCase):
self._testSignatures(besk, bpk)
+ def testIdentity(self):
+ # Base point:
+ # B is the unique point (x, 4/5) \in E for which x is positive
+ By = 4 * inv(5)
+ Bx = xrecover(By)
+ B = [Bx % q,By % q]
+
+ # Get identity E by doing: E = l*B, where l is the group order
+ identity = scalarmult(B, ell)
+
+ # Get identity E by doing: E = l*A, where A is a random point
+ sk = newSK()
+ pk = decodepoint(publickey(sk))
+ identity2 = scalarmult(pk, ell)
+
+ # Check that identities match
+ assert(identity == identity2)
+ # Check that identity is the point (0,1)
+ assert(identity == [0L,1L])
+
+ # Check identity element: a*E = E, where a is a random scalar
+ scalar = random_scalar(os.urandom)
+ result = scalarmult(identity, scalar)
+ assert(result == identity == identity2)
+
# ------------------------------------------------------------
# From pprint.pprint([ binascii.b2a_hex(os.urandom(32)) for _ in xrange(8) ])
diff --git a/src/test/ed25519_vectors.inc b/src/test/ed25519_vectors.inc
index 760bafb971..60c863beba 100644
--- a/src/test/ed25519_vectors.inc
+++ b/src/test/ed25519_vectors.inc
@@ -91,21 +91,21 @@ static const char *ED25519_BLINDING_PARAMS[] = {
* blinding parameter.
*/
static const char *ED25519_BLINDED_SECRET_KEYS[] = {
- "014e83abadb2ca9a27e0ffe23920333d817729f48700e97656ec2823d694050e171d43"
+ "293c3acff4e902f6f63ddc5d5caa2a57e771db4f24de65d4c28df3232f47fa01171d43"
"f24e3f53e70ec7ac280044ac77d4942dee5d6807118a59bdf3ee647e89",
- "fad8cca0b4335847795288b1452508752b253e64e6c7c78d4a02dbbd7d46aa0eb8ceff"
+ "38b88f9f9440358da544504ee152fb475528f7c51c285bd1c68b14ade8e29a07b8ceff"
"20dfcf53eb52b891fc078c934efbf0353af7242e7dc51bb32a093afa29",
- "116eb0ae0a4a91763365bdf86db427b00862db448487808788cc339ac10e5e089217f5"
+ "4d03ce16a3f3249846aac9de0a0075061495c3b027248eeee47da4ddbaf9e0049217f5"
"2e92797462bd890fc274672e05c98f2c82970d640084781334aae0f940",
- "bd1fbb0ee5acddc4adbcf5f33e95d9445f40326ce579fdd764a24483a9ccb20f509ece"
+ "51d7db01aaa0d937a9fd7c8c7381445a14d8fa61f43347af5460d7cd8fda9904509ece"
"e77082ce088f7c19d5a00e955eeef8df6fa41686abc1030c2d76807733",
- "237f5345cefe8573ce9fa7e216381a1172796c9e3f70668ab503b1352952530fb57b95"
+ "1f76cab834e222bd2546efa7e073425680ab88df186ff41327d3e40770129b00b57b95"
"a440570659a440a3e4771465022a8e67af86bdf2d0990c54e7bb87ff9a",
- "ba8ff23bc4ad2b739e1ccffc9fbc7837053ea81cdfdb15073f56411cfbae1d0ec492fc"
+ "c23588c23ee76093419d07b27c6df5922a03ac58f96c53671456a7d1bdbf560ec492fc"
"87d5ec2a1b185ca5a40541fdef0b1e128fd5c2380c888bfa924711bcab",
- "0fa68f969de038c7a90a4a74ee6167c77582006f2dedecc1956501ba6b6fb10391b476"
+ "3ed249c6932d076e1a2f6916975914b14e8c739da00992358b8f37d3e790650691b476"
"8f8e556d78f4bdcb9a13b6f6066fe81d3134ae965dc48cd0785b3af2b8",
- "deaa3456d1c21944d5dcd361a646858c6cf9336b0a6851d925717eb1ae186902053d9c"
+ "288cbfd923cb286d48c084555b5bdd06c05e92fb81acdb45271367f57515380e053d9c"
"00c81e1331c06ab50087be8cfc7dc11691b132614474f1aa9c2503cccd",
};
@@ -115,14 +115,14 @@ static const char *ED25519_BLINDED_SECRET_KEYS[] = {
* blinding parameter.
*/
static const char *ED25519_BLINDED_PUBLIC_KEYS[] = {
- "722d6da6348e618967ef782e71061e27163a8b35f21856475d9d2023f65b6495",
- "1dffa0586da6cbfcff2024eedf4fc6c818242d9a82dbbe635d6da1b975a1160d",
- "5ed81f98fed5a6acda4ea6da2c34fab0ab359d950c510c256473f1f33ff438b4",
- "6e6f92a54fb282120c46d9603df41135f025bc1f58f283809d04be96aeb04040",
- "cda236f28edc4c7e02d18007b8dab49d669265b0f7aefb1824d7cc8e73a2cd63",
- "367b03b17b67ca7329b89a520bdab91782402a41cd67264e34b5541a4b3f875b",
- "8d486b03ac4e3b486b7a1d563706c7fdac75aee789a7cf6f22789eedeff61a31",
- "9f297ff0aa2ceda91c5ab1b6446f12533d145940de6d850dc323417afde0cb78",
+ "1fc1fa4465bd9d4956fdbdc9d3acb3c7019bb8d5606b951c2e1dfe0b42eaeb41",
+ "1cbbd4a88ce8f165447f159d9f628ada18674158c4f7c5ead44ce8eb0fa6eb7e",
+ "c5419ad133ffde7e0ac882055d942f582054132b092de377d587435722deb028",
+ "3e08d0dc291066272e313014bfac4d39ad84aa93c038478a58011f431648105f",
+ "59381f06acb6bf1389ba305f70874eed3e0f2ab57cdb7bc69ed59a9b8899ff4d",
+ "2b946a484344eb1c17c89dd8b04196a84f3b7222c876a07a4cece85f676f87d9",
+ "c6b585129b135f8769df2eba987e76e089e80ba3a2a6729134d3b28008ac098e",
+ "0eefdc795b59cabbc194c6174e34ba9451e8355108520554ec285acabebb34ac",
};
/**
diff --git a/src/test/fuzz/dict/hsdescv3 b/src/test/fuzz/dict/hsdescv3
new file mode 100644
index 0000000000..84e8db578a
--- /dev/null
+++ b/src/test/fuzz/dict/hsdescv3
@@ -0,0 +1,6 @@
+"hs-descriptor"
+"descriptor-lifetime"
+"descriptor-signing-key-cert"
+"revision-counter"
+"superencrypted"
+"signature"
diff --git a/src/test/fuzz/fuzz_hsdescv3.c b/src/test/fuzz/fuzz_hsdescv3.c
new file mode 100644
index 0000000000..428774e330
--- /dev/null
+++ b/src/test/fuzz/fuzz_hsdescv3.c
@@ -0,0 +1,99 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define ROUTERPARSE_PRIVATE
+#define HS_DESCRIPTOR_PRIVATE
+
+#include "or.h"
+#include "ed25519_cert.h" /* Trunnel interface. */
+#include "crypto_ed25519.h"
+#include "hs_descriptor.h"
+#include "routerparse.h"
+#include "util.h"
+
+#include "fuzzing.h"
+
+static void
+mock_dump_desc__nodump(const char *desc, const char *type)
+{
+ (void)desc;
+ (void)type;
+}
+
+static int
+mock_rsa_ed25519_crosscert_check(const uint8_t *crosscert,
+ const size_t crosscert_len,
+ const crypto_pk_t *rsa_id_key,
+ const ed25519_public_key_t *master_key,
+ const time_t reject_if_expired_before)
+{
+ (void) crosscert;
+ (void) crosscert_len;
+ (void) rsa_id_key;
+ (void) master_key;
+ (void) reject_if_expired_before;
+ return 0;
+}
+
+static size_t
+mock_decrypt_desc_layer(const hs_descriptor_t *desc,
+ const uint8_t *encrypted_blob,
+ size_t encrypted_blob_size,
+ int is_superencrypted_layer,
+ char **decrypted_out)
+{
+ (void)is_superencrypted_layer;
+ (void)desc;
+ const size_t overhead = HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN;
+ if (encrypted_blob_size < overhead)
+ return 0;
+ *decrypted_out = tor_memdup_nulterm(
+ encrypted_blob + HS_DESC_ENCRYPTED_SALT_LEN,
+ encrypted_blob_size - overhead);
+ size_t result = strlen(*decrypted_out);
+ if (result) {
+ return result;
+ } else {
+ tor_free(*decrypted_out);
+ return 0;
+ }
+}
+
+int
+fuzz_init(void)
+{
+ disable_signature_checking();
+ MOCK(dump_desc, mock_dump_desc__nodump);
+ MOCK(rsa_ed25519_crosscert_check, mock_rsa_ed25519_crosscert_check);
+ MOCK(decrypt_desc_layer, mock_decrypt_desc_layer);
+ ed25519_init();
+ return 0;
+}
+
+int
+fuzz_cleanup(void)
+{
+ return 0;
+}
+
+int
+fuzz_main(const uint8_t *data, size_t sz)
+{
+ hs_descriptor_t *desc = NULL;
+ uint8_t subcredential[DIGEST256_LEN];
+
+ char *fuzzing_data = tor_memdup_nulterm(data, sz);
+ memset(subcredential, 'A', sizeof(subcredential));
+
+ hs_desc_decode_descriptor(fuzzing_data, subcredential, &desc);
+ if (desc) {
+ log_debug(LD_GENERAL, "Decoding okay");
+ hs_descriptor_free(desc);
+ } else {
+ log_debug(LD_GENERAL, "Decoding failed");
+ }
+
+ tor_free(fuzzing_data);
+ return 0;
+}
+
diff --git a/src/test/fuzz/fuzz_http_connect.c b/src/test/fuzz/fuzz_http_connect.c
new file mode 100644
index 0000000000..dc674070b2
--- /dev/null
+++ b/src/test/fuzz/fuzz_http_connect.c
@@ -0,0 +1,106 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#define BUFFERS_PRIVATE
+#define CONNECTION_EDGE_PRIVATE
+
+#include "or.h"
+#include "backtrace.h"
+#include "buffers.h"
+#include "config.h"
+#include "connection.h"
+#include "connection_edge.h"
+#include "proto_socks.h"
+#include "torlog.h"
+
+#include "fuzzing.h"
+
+static void
+mock_connection_write_to_buf_impl_(const char *string, size_t len,
+ connection_t *conn, int compressed)
+{
+ log_debug(LD_GENERAL, "%sResponse:\n%u\nConnection: %p\n%s\n",
+ compressed ? "Compressed " : "", (unsigned)len, conn, string);
+}
+
+static void
+mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
+ int line, const char *file)
+{
+ (void)conn;
+ (void)endreason;
+ (void)line;
+ (void)file;
+}
+
+static int
+mock_connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
+ origin_circuit_t *circ,
+ crypt_path_t *cpath)
+{
+ (void)conn;
+ (void)circ;
+ (void)cpath;
+ return 0;
+}
+
+int
+fuzz_init(void)
+{
+ /* Set up fake response handler */
+ MOCK(connection_write_to_buf_impl_, mock_connection_write_to_buf_impl_);
+ /* Set up the fake handler functions */
+ MOCK(connection_mark_unattached_ap_, mock_connection_mark_unattached_ap_);
+ MOCK(connection_ap_rewrite_and_attach_if_allowed,
+ mock_connection_ap_rewrite_and_attach_if_allowed);
+
+ return 0;
+}
+
+int
+fuzz_cleanup(void)
+{
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(connection_mark_unattached_ap_);
+ UNMOCK(connection_ap_rewrite_and_attach_if_allowed);
+ return 0;
+}
+
+int
+fuzz_main(const uint8_t *stdin_buf, size_t data_size)
+{
+ entry_connection_t conn;
+
+ /* Set up the fake connection */
+ memset(&conn, 0, sizeof(conn));
+ conn.edge_.base_.type = CONN_TYPE_AP;
+ conn.edge_.base_.state = AP_CONN_STATE_HTTP_CONNECT_WAIT;
+ conn.socks_request = tor_malloc_zero(sizeof(socks_request_t));
+ conn.socks_request->listener_type = CONN_TYPE_AP_HTTP_CONNECT_LISTENER;
+
+ conn.edge_.base_.inbuf = buf_new_with_data((char*)stdin_buf, data_size);
+ if (!conn.edge_.base_.inbuf) {
+ log_debug(LD_GENERAL, "Zero-Length-Input\n");
+ goto done;
+ }
+
+ /* Parse the headers */
+ int rv = connection_ap_process_http_connect(&conn);
+
+ /* TODO: check the output is correctly parsed based on the input */
+
+ log_debug(LD_GENERAL, "Result:\n%d\n", rv);
+
+ goto done;
+
+ done:
+ /* Reset. */
+ socks_request_free(conn.socks_request);
+ buf_free(conn.edge_.base_.inbuf);
+ conn.edge_.base_.inbuf = NULL;
+
+ return 0;
+}
+
diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c
index 7aee92df63..1e98eb6c85 100644
--- a/src/test/fuzz/fuzzing_common.c
+++ b/src/test/fuzz/fuzzing_common.c
@@ -28,8 +28,9 @@ mock_crypto_pk_public_checksig__nocheck(const crypto_pk_t *env, char *to,
(void)fromlen;
/* We could look at from[0..fromlen-1] ... */
tor_assert(tolen >= crypto_pk_keysize(env));
- memset(to, 0x01, 20);
- return 20;
+ size_t siglen = MIN(20, crypto_pk_keysize(env));
+ memset(to, 0x01, siglen);
+ return (int)siglen;
}
static int
@@ -107,7 +108,7 @@ global_init(void)
configure_backtrace_handler(get_version());
/* set up the options. */
- mock_options = tor_malloc(sizeof(or_options_t));
+ mock_options = tor_malloc_zero(sizeof(or_options_t));
MOCK(get_options, mock_get_options);
/* Make BUG() and nonfatal asserts crash */
diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am
index 2961dab56f..cd16dc05be 100644
--- a/src/test/fuzz/include.am
+++ b/src/test/fuzz/include.am
@@ -15,13 +15,13 @@ FUZZING_LIBS = \
src/common/libor-ctime-testing.a \
src/common/libor-event-testing.a \
src/trunnel/libor-trunnel-testing.a \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
- @TOR_LIBEVENT_LIBS@ \
- @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
+ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@ \
@TOR_LZMA_LIBS@ \
- @TOR_ZSTD_LIBS@ \
- $(rust_ldadd)
+ @TOR_ZSTD_LIBS@
oss-fuzz-prereqs: \
src/or/libtor-testing.a \
@@ -94,6 +94,14 @@ src_test_fuzz_fuzz_hsdescv2_CFLAGS = $(FUZZING_CFLAGS)
src_test_fuzz_fuzz_hsdescv2_LDFLAGS = $(FUZZING_LDFLAG)
src_test_fuzz_fuzz_hsdescv2_LDADD = $(FUZZING_LIBS)
+src_test_fuzz_fuzz_hsdescv3_SOURCES = \
+ src/test/fuzz/fuzzing_common.c \
+ src/test/fuzz/fuzz_hsdescv3.c
+src_test_fuzz_fuzz_hsdescv3_CPPFLAGS = $(FUZZING_CPPFLAGS)
+src_test_fuzz_fuzz_hsdescv3_CFLAGS = $(FUZZING_CFLAGS)
+src_test_fuzz_fuzz_hsdescv3_LDFLAGS = $(FUZZING_LDFLAG)
+src_test_fuzz_fuzz_hsdescv3_LDADD = $(FUZZING_LIBS)
+
src_test_fuzz_fuzz_http_SOURCES = \
src/test/fuzz/fuzzing_common.c \
src/test/fuzz/fuzz_http.c
@@ -102,6 +110,14 @@ src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS)
src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG)
src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS)
+src_test_fuzz_fuzz_http_connect_SOURCES = \
+ src/test/fuzz/fuzzing_common.c \
+ src/test/fuzz/fuzz_http_connect.c
+src_test_fuzz_fuzz_http_connect_CPPFLAGS = $(FUZZING_CPPFLAGS)
+src_test_fuzz_fuzz_http_connect_CFLAGS = $(FUZZING_CFLAGS)
+src_test_fuzz_fuzz_http_connect_LDFLAGS = $(FUZZING_LDFLAG)
+src_test_fuzz_fuzz_http_connect_LDADD = $(FUZZING_LIBS)
+
src_test_fuzz_fuzz_iptsv2_SOURCES = \
src/test/fuzz/fuzzing_common.c \
src/test/fuzz/fuzz_iptsv2.c
@@ -133,7 +149,9 @@ FUZZERS = \
src/test/fuzz/fuzz-diff-apply \
src/test/fuzz/fuzz-extrainfo \
src/test/fuzz/fuzz-hsdescv2 \
+ src/test/fuzz/fuzz-hsdescv3 \
src/test/fuzz/fuzz-http \
+ src/test/fuzz/fuzz-http-connect \
src/test/fuzz/fuzz-iptsv2 \
src/test/fuzz/fuzz-microdesc \
src/test/fuzz/fuzz-vrs
@@ -183,6 +201,13 @@ src_test_fuzz_lf_fuzz_hsdescv2_CFLAGS = $(LIBFUZZER_CFLAGS)
src_test_fuzz_lf_fuzz_hsdescv2_LDFLAGS = $(LIBFUZZER_LDFLAG)
src_test_fuzz_lf_fuzz_hsdescv2_LDADD = $(LIBFUZZER_LIBS)
+src_test_fuzz_lf_fuzz_hsdescv3_SOURCES = \
+ $(src_test_fuzz_fuzz_hsdescv3_SOURCES)
+src_test_fuzz_lf_fuzz_hsdescv3_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
+src_test_fuzz_lf_fuzz_hsdescv3_CFLAGS = $(LIBFUZZER_CFLAGS)
+src_test_fuzz_lf_fuzz_hsdescv3_LDFLAGS = $(LIBFUZZER_LDFLAG)
+src_test_fuzz_lf_fuzz_hsdescv3_LDADD = $(LIBFUZZER_LIBS)
+
src_test_fuzz_lf_fuzz_http_SOURCES = \
$(src_test_fuzz_fuzz_http_SOURCES)
src_test_fuzz_lf_fuzz_http_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
@@ -190,6 +215,13 @@ src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS)
src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG)
src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS)
+src_test_fuzz_lf_fuzz_http_connect_SOURCES = \
+ $(src_test_fuzz_fuzz_http_connect_SOURCES)
+src_test_fuzz_lf_fuzz_http_connect_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
+src_test_fuzz_lf_fuzz_http_connect_CFLAGS = $(LIBFUZZER_CFLAGS)
+src_test_fuzz_lf_fuzz_http_connect_LDFLAGS = $(LIBFUZZER_LDFLAG)
+src_test_fuzz_lf_fuzz_http_connect_LDADD = $(LIBFUZZER_LIBS)
+
src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \
$(src_test_fuzz_fuzz_iptsv2_SOURCES)
src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS)
@@ -218,7 +250,9 @@ LIBFUZZER_FUZZERS = \
src/test/fuzz/lf-fuzz-diff-apply \
src/test/fuzz/lf-fuzz-extrainfo \
src/test/fuzz/lf-fuzz-hsdescv2 \
+ src/test/fuzz/lf-fuzz-hsdescv3 \
src/test/fuzz/lf-fuzz-http \
+ src/test/fuzz/lf-fuzz-http-connect \
src/test/fuzz/lf-fuzz-iptsv2 \
src/test/fuzz/lf-fuzz-microdesc \
src/test/fuzz/lf-fuzz-vrs
@@ -260,11 +294,21 @@ src_test_fuzz_liboss_fuzz_hsdescv2_a_SOURCES = \
src_test_fuzz_liboss_fuzz_hsdescv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
src_test_fuzz_liboss_fuzz_hsdescv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
+src_test_fuzz_liboss_fuzz_hsdescv3_a_SOURCES = \
+ $(src_test_fuzz_fuzz_hsdescv3_SOURCES)
+src_test_fuzz_liboss_fuzz_hsdescv3_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
+src_test_fuzz_liboss_fuzz_hsdescv3_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
+
src_test_fuzz_liboss_fuzz_http_a_SOURCES = \
$(src_test_fuzz_fuzz_http_SOURCES)
src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
+src_test_fuzz_liboss_fuzz_http_connect_a_SOURCES = \
+ $(src_test_fuzz_fuzz_http_connect_SOURCES)
+src_test_fuzz_liboss_fuzz_http_connect_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
+src_test_fuzz_liboss_fuzz_http_connect_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS)
+
src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \
$(src_test_fuzz_fuzz_iptsv2_SOURCES)
src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS)
@@ -287,7 +331,9 @@ OSS_FUZZ_FUZZERS = \
src/test/fuzz/liboss-fuzz-diff-apply.a \
src/test/fuzz/liboss-fuzz-extrainfo.a \
src/test/fuzz/liboss-fuzz-hsdescv2.a \
+ src/test/fuzz/liboss-fuzz-hsdescv3.a \
src/test/fuzz/liboss-fuzz-http.a \
+ src/test/fuzz/liboss-fuzz-http-connect.a \
src/test/fuzz/liboss-fuzz-iptsv2.a \
src/test/fuzz/liboss-fuzz-microdesc.a \
src/test/fuzz/liboss-fuzz-vrs.a
diff --git a/src/test/hs_build_address.py b/src/test/hs_build_address.py
new file mode 100644
index 0000000000..7ff22c3a9a
--- /dev/null
+++ b/src/test/hs_build_address.py
@@ -0,0 +1,38 @@
+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)
+
+# Checksum is built like so:
+# CHECKSUM = SHA3(".onion checksum" || PUBKEY || VERSION)
+PREFIX = ".onion checksum".encode()
+# 32 bytes ed25519 pubkey from first test vector of
+# https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6
+PUBKEY = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".decode('hex')
+# Version 3 is proposal224
+VERSION = 3
+
+data = struct.pack('15s32sb', PREFIX, PUBKEY, VERSION)
+checksum = hashlib.sha3_256(data).digest()
+
+# Onion address is built like so:
+# onion_address = base32(PUBKEY || CHECKSUM || VERSION) + ".onion"
+address = struct.pack('!32s2sb', PUBKEY, checksum, VERSION)
+onion_addr = base64.b32encode(address).decode().lower()
+
+print("%s" % (onion_addr))
diff --git a/src/test/hs_indexes.py b/src/test/hs_indexes.py
new file mode 100644
index 0000000000..af0b81f8de
--- /dev/null
+++ b/src/test/hs_indexes.py
@@ -0,0 +1,70 @@
+#
+# The hidden service subsystem has two type of index. The first type is a
+# value that each node in the network gets assigned to using their identity
+# key which is their position in the hashring. (hs_build_hsdir_index()).
+#
+# The second type is a value that both the client and service computes to
+# store/fetch the descriptor on the hashring. (hs_build_hs_index()).
+#
+
+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)
+
+# The first index we'll build is the position index in the hashring that is
+# constructed by the hs_build_hsdir_index() function. Construction is:
+# SHA3-256("node-idx" | node_identity |
+# shared_random_value | INT_8(period_length) | INT_8(period_num) )
+
+PREFIX = "node-idx".encode()
+# 32 bytes ed25519 pubkey.
+IDENTITY = ("\x42" * 32).encode()
+# SRV is 32 bytes.
+SRV = ("\x43" * 32).encode()
+# Time period length is a 8 bytes value.
+PERIOD_LEN = 1440
+# Period number is a 8 bytes value.
+PERIOD_NUM = 42
+
+data = struct.pack('!8s32s32sQQ', PREFIX, IDENTITY, SRV, PERIOD_NUM,
+ PERIOD_LEN)
+hsdir_index = hashlib.sha3_256(data).hexdigest()
+
+print("[hs_build_hsdir_index] %s" % (hsdir_index))
+
+# The second index we'll build is where the HS stores and the client fetches
+# the descriptor on the hashring. It is constructed by the hs_build_hs_index()
+# function and the construction is:
+# SHA3-256("store-at-idx" | blinded_public_key |
+# INT_8(replicanum) | INT_8(period_num) | INT_8(period_length) )
+
+PREFIX = "store-at-idx".encode()
+# 32 bytes ed25519 pubkey.
+PUBKEY = ("\x42" * 32).encode()
+# Replica number is a 8 bytes value.
+REPLICA_NUM = 1
+# Time period length is a 8 bytes value.
+PERIOD_LEN = 1440
+# Period number is a 8 bytes value.
+PERIOD_NUM = 42
+
+data = struct.pack('!12s32sQQQ', PREFIX, PUBKEY, REPLICA_NUM, PERIOD_LEN,
+ PERIOD_NUM)
+hs_index = hashlib.sha3_256(data).hexdigest()
+
+print("[hs_build_hs_index] %s" % (hs_index))
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index 3f0d6a9413..9355971267 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -6,6 +6,7 @@
#include "test.h"
#include "torcert.h"
+#include "hs_common.h"
#include "hs_test_helpers.h"
hs_desc_intro_point_t *
@@ -15,31 +16,31 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
int ret;
ed25519_keypair_t auth_kp;
hs_desc_intro_point_t *intro_point = NULL;
- hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip));
- ip->link_specifiers = smartlist_new();
+ hs_desc_intro_point_t *ip = hs_desc_intro_point_new();
+ /* For a usable intro point we need at least two link specifiers: One legacy
+ * keyid and one ipv4 */
{
- hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls));
- if (legacy) {
- ls->type = LS_LEGACY_ID;
- memcpy(ls->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8",
- DIGEST_LEN);
- } else {
- ls->u.ap.port = 9001;
- int family = tor_addr_parse(&ls->u.ap.addr, addr);
- switch (family) {
- case AF_INET:
- ls->type = LS_IPV4;
+ hs_desc_link_specifier_t *ls_legacy = tor_malloc_zero(sizeof(*ls_legacy));
+ hs_desc_link_specifier_t *ls_v4 = tor_malloc_zero(sizeof(*ls_v4));
+ ls_legacy->type = LS_LEGACY_ID;
+ memcpy(ls_legacy->u.legacy_id, "0299F268FCA9D55CD157976D39AE92B4B455B3A8",
+ DIGEST_LEN);
+ ls_v4->u.ap.port = 9001;
+ int family = tor_addr_parse(&ls_v4->u.ap.addr, addr);
+ switch (family) {
+ case AF_INET:
+ ls_v4->type = LS_IPV4;
break;
case AF_INET6:
- ls->type = LS_IPV6;
+ ls_v4->type = LS_IPV6;
break;
default:
/* Stop the test, not suppose to have an error. */
tt_int_op(family, OP_EQ, AF_INET);
- }
}
- smartlist_add(ip->link_specifiers, ls);
+ smartlist_add(ip->link_specifiers, ls_legacy);
+ smartlist_add(ip->link_specifiers, ls_v4);
}
ret = ed25519_keypair_generate(&auth_kp, 0);
@@ -94,8 +95,7 @@ static hs_descriptor_t *
hs_helper_build_hs_desc_impl(unsigned int no_ip,
const ed25519_keypair_t *signing_kp)
{
- int ret;
- time_t now = time(NULL);
+ time_t now = approx_time();
ed25519_keypair_t blinded_kp;
hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc));
@@ -105,8 +105,9 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
memcpy(&desc->plaintext_data.signing_pubkey, &signing_kp->pubkey,
sizeof(ed25519_public_key_t));
- ret = ed25519_keypair_generate(&blinded_kp, 0);
- tt_int_op(ret, ==, 0);
+ uint64_t current_time_period = hs_get_time_period_num(0);
+ hs_build_blinded_keypair(signing_kp, NULL, 0,
+ current_time_period, &blinded_kp);
/* Copy only the public key into the descriptor. */
memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey,
sizeof(ed25519_public_key_t));
@@ -119,6 +120,9 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
desc->plaintext_data.revision_counter = 42;
desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
+ hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
+ desc->subcredential);
+
/* Setup encrypted data section. */
desc->encrypted_data.create2_ntor = 1;
desc->encrypted_data.intro_auth_types = smartlist_new();
@@ -134,7 +138,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
smartlist_add(desc->encrypted_data.intro_points,
hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1));
smartlist_add(desc->encrypted_data.intro_points,
- hs_helper_build_intro_point(signing_kp, now, "", 1));
+ hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1));
}
descp = desc;
@@ -142,6 +146,21 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
return descp;
}
+/** Helper function to get the HS subcredential using the identity keypair of
+ * an HS. Used to decrypt descriptors in unittests. */
+void
+hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
+ uint8_t *subcred_out)
+{
+ ed25519_keypair_t blinded_kp;
+ uint64_t current_time_period = hs_get_time_period_num(approx_time());
+ hs_build_blinded_keypair(signing_kp, NULL, 0,
+ current_time_period, &blinded_kp);
+
+ hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
+ subcred_out);
+}
+
/* Build a descriptor with introduction points. */
hs_descriptor_t *
hs_helper_build_hs_desc_with_ip(const ed25519_keypair_t *signing_kp)
diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h
index a7fedab136..b1b0490f05 100644
--- a/src/test/hs_test_helpers.h
+++ b/src/test/hs_test_helpers.h
@@ -17,6 +17,9 @@ hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
const ed25519_keypair_t *signing_kp);
void hs_helper_desc_equal(const hs_descriptor_t *desc1,
const hs_descriptor_t *desc2);
+void
+hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
+ uint8_t *subcred_out);
-#endif /* TOR_HS_TEST_HELPERS_H */
+#endif /* !defined(TOR_HS_TEST_HELPERS_H) */
diff --git a/src/test/include.am b/src/test/include.am
index 723b4964e1..230845a1ed 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -34,16 +34,20 @@ endif
TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
src/test/test_workqueue \
src/test/test_keygen.sh \
+ src/test/test_key_expiration.sh \
src/test/test-timers \
$(TESTSCRIPTS)
# These flavors are run using automake's test-driver and test-network.sh
-TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min single-onion
+TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v2-min hs-v3-min \
+ single-onion-v23
# only run if we can ping6 ::1 (localhost)
-TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-ipv6 \
- single-onion-ipv6
+# IPv6-only v3 single onion services don't work yet, so we don't test the
+# single-onion-v23-ipv6-md flavor
+TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-v23-ipv6-md \
+ single-onion-ipv6-md
# only run if we can find a stable (or simply another) version of tor
-TEST_CHUTNEY_FLAVORS_MIXED = mixed
+TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v23
### This is a lovely feature, but it requires automake >= 1.12, and Tor
### doesn't require that yet.
@@ -116,7 +120,12 @@ src_test_test_SOURCES = \
src/test/test_guardfraction.c \
src/test/test_extorport.c \
src/test/test_hs.c \
+ src/test/test_hs_common.c \
+ src/test/test_hs_config.c \
+ src/test/test_hs_cell.c \
+ src/test/test_hs_ntor.c \
src/test/test_hs_service.c \
+ src/test/test_hs_client.c \
src/test/test_hs_intropoint.c \
src/test/test_handles.c \
src/test/test_hs_cache.c \
@@ -132,6 +141,8 @@ src_test_test_SOURCES = \
src/test/test_options.c \
src/test/test_policy.c \
src/test/test_procmon.c \
+ src/test/test_proto_http.c \
+ src/test/test_proto_misc.c \
src/test/test_protover.c \
src/test/test_pt.c \
src/test/test_pubsub.c \
@@ -139,6 +150,7 @@ src_test_test_SOURCES = \
src/test/test_relaycell.c \
src/test/test_rendcache.c \
src/test/test_replay.c \
+ src/test/test_router.c \
src/test/test_routerkeys.c \
src/test/test_routerlist.c \
src/test/test_routerset.c \
@@ -193,9 +205,10 @@ src_test_test_switch_id_LDFLAGS = @TOR_LDFLAGS_zlib@
src_test_test_switch_id_LDADD = \
src/common/libor-testing.a \
src/common/libor-ctime-testing.a \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
- @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \
- $(rust_ldadd)
+ @TOR_LIB_WS32@ @TOR_LIB_USERENV@ \
+ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
@@ -208,10 +221,11 @@ src_test_test_LDADD = src/or/libtor-testing.a \
src/common/libor-event-testing.a \
src/trunnel/libor-trunnel-testing.a \
src/trace/libor-trace.a \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
- @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
- @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \
- $(rust_ldadd)
+ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+ @CURVE25519_LIBS@ \
+ @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS)
src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
@@ -233,10 +247,11 @@ src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \
src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event.a src/trunnel/libor-trunnel.a \
src/trace/libor-trace.a \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
- @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
- @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \
- $(rust_ldadd)
+ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+ @CURVE25519_LIBS@ \
+ @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
@@ -246,10 +261,11 @@ src_test_test_workqueue_LDADD = src/or/libtor-testing.a \
src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event-testing.a \
src/trace/libor-trace.a \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
- @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
- @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ \
- $(rust_ldadd)
+ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+ @CURVE25519_LIBS@ \
+ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@
src_test_test_timers_CPPFLAGS = $(src_test_test_CPPFLAGS)
src_test_test_timers_CFLAGS = $(src_test_test_CFLAGS)
@@ -258,10 +274,11 @@ src_test_test_timers_LDADD = \
src/common/libor-ctime-testing.a \
src/common/libor-event-testing.a \
src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
- @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
- @TOR_LZMA_LIBS@ \
- $(rust_ldadd)
+ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+ @CURVE25519_LIBS@ \
+ @TOR_LZMA_LIBS@
src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
noinst_HEADERS+= \
@@ -272,6 +289,7 @@ noinst_HEADERS+= \
src/test/test.h \
src/test/test_helpers.h \
src/test/test_dir_common.h \
+ src/test/test_connection.h \
src/test/test_descriptors.inc \
src/test/example_extrainfo.inc \
src/test/failing_routerdescs.inc \
@@ -288,10 +306,10 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
src/common/libor-ctime.a \
src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/trace/libor-trace.a \
+ $(rust_ldadd) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
- @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
- @TOR_LZMA_LIBS@ \
- $(rust_ldadd)
+ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \
+ @CURVE25519_LIBS@ @TOR_LZMA_LIBS@
src_test_test_ntor_cl_AM_CPPFLAGS = \
-I"$(top_srcdir)/src/or"
@@ -311,9 +329,9 @@ src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c
src_test_test_bt_cl_LDADD = src/common/libor-testing.a \
src/common/libor-ctime-testing.a \
src/trace/libor-trace.a \
+ $(rust_ldadd) \
@TOR_LIB_MATH@ \
- @TOR_LIB_WS32@ @TOR_LIB_GDI@ \
- $(rust_ldadd)
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@
src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS)
@@ -321,10 +339,13 @@ EXTRA_DIST += \
src/test/bt_test.py \
src/test/ntor_ref.py \
src/test/hs_ntor_ref.py \
+ src/test/hs_build_address.py \
+ src/test/hs_indexes.py \
src/test/fuzz_static_testcases.sh \
src/test/slownacl_curve25519.py \
src/test/zero_length_keys.sh \
src/test/test_keygen.sh \
+ src/test/test_key_expiration.sh \
src/test/test_zero_length_keys.sh \
src/test/test_ntor.sh src/test/test_hs_ntor.sh src/test/test_bt.sh \
src/test/test-network.sh \
diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h
index f7798c0249..70c584eb37 100644
--- a/src/test/log_test_helpers.h
+++ b/src/test/log_test_helpers.h
@@ -101,5 +101,5 @@ void mock_dump_saved_logs(void);
assert_log_predicate(!mock_saved_log_has_entry(), \
"expected log to not contain entries");
-#endif
+#endif /* !defined(TOR_LOG_TEST_HELPERS_H) */
diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c
index f7880046fb..095bfecf21 100644
--- a/src/test/rend_test_helpers.c
+++ b/src/test/rend_test_helpers.c
@@ -71,3 +71,19 @@ create_descriptor(rend_service_descriptor_t **generated, char **service_id,
crypto_pk_free(pk2);
}
+rend_data_t *
+mock_rend_data(const char *onion_address)
+{
+ rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data));
+ rend_data_t *rend_query = &v2_data->base_;
+ rend_query->version = 2;
+
+ strlcpy(v2_data->onion_address, onion_address,
+ sizeof(v2_data->onion_address));
+ v2_data->auth_type = REND_NO_AUTH;
+ rend_query->hsdirs_fp = smartlist_new();
+ smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
+ DIGEST_LEN));
+ return rend_query;
+}
+
diff --git a/src/test/rend_test_helpers.h b/src/test/rend_test_helpers.h
index 486adba436..abf4324988 100644
--- a/src/test/rend_test_helpers.h
+++ b/src/test/rend_test_helpers.h
@@ -10,6 +10,7 @@ void generate_desc(int time_diff, rend_encoded_v2_service_descriptor_t **desc,
char **service_id, int intro_points);
void create_descriptor(rend_service_descriptor_t **generated,
char **service_id, int intro_points);
+rend_data_t *mock_rend_data(const char *onion_address);
-#endif
+#endif /* !defined(TOR_REND_TEST_HELPERS_H) */
diff --git a/src/test/test-child.c b/src/test/test-child.c
index f0bdb3ea26..f78a829107 100644
--- a/src/test/test-child.c
+++ b/src/test/test-child.c
@@ -8,7 +8,7 @@
#include <windows.h>
#else
#include <unistd.h>
-#endif
+#endif /* defined(_WIN32) */
#include <string.h>
#ifdef _WIN32
diff --git a/src/test/test.c b/src/test/test.c
index 911ef0c24e..383bc00002 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -20,7 +20,7 @@
#include <direct.h>
#else
#include <dirent.h>
-#endif
+#endif /* defined(_WIN32) */
/* These macros pull in declarations for some functions and structures that
* are typically file-private. */
@@ -142,7 +142,8 @@ test_bad_onion_handshake(void *arg)
/* Server: Case 1: the encrypted data is degenerate. */
memset(junk_buf, 0, sizeof(junk_buf));
- crypto_pk_public_hybrid_encrypt(pk, junk_buf2, TAP_ONIONSKIN_CHALLENGE_LEN,
+ crypto_pk_obsolete_public_hybrid_encrypt(pk,
+ junk_buf2, TAP_ONIONSKIN_CHALLENGE_LEN,
junk_buf, DH_KEY_LEN, PK_PKCS1_OAEP_PADDING, 1);
tt_int_op(-1, OP_EQ,
onion_skin_TAP_server_handshake(junk_buf2, pk, NULL,
@@ -408,11 +409,11 @@ test_circuit_timeout(void *arg)
} while (fabs(circuit_build_times_cdf(&initial, timeout0) -
circuit_build_times_cdf(&initial, timeout1)) > 0.02);
- tt_assert(estimate.total_build_times <= CBT_NCIRCUITS_TO_OBSERVE);
+ tt_int_op(estimate.total_build_times, OP_LE, CBT_NCIRCUITS_TO_OBSERVE);
circuit_build_times_update_state(&estimate, state);
circuit_build_times_free_timeouts(&final);
- tt_assert(circuit_build_times_parse_state(&final, state) == 0);
+ tt_int_op(circuit_build_times_parse_state(&final, state), OP_EQ, 0);
circuit_build_times_update_alpha(&final);
timeout2 = circuit_build_times_calculate_timeout(&final,
@@ -490,7 +491,7 @@ test_circuit_timeout(void *arg)
}
}
- tt_assert(estimate.liveness.after_firsthop_idx == 0);
+ tt_int_op(estimate.liveness.after_firsthop_idx, OP_EQ, 0);
tt_assert(final.liveness.after_firsthop_idx ==
CBT_DEFAULT_MAX_RECENT_TIMEOUT_COUNT-1);
@@ -533,25 +534,8 @@ test_rend_fns(void *arg)
size_t intro_points_size;
size_t encoded_size;
int i;
- char address1[] = "fooaddress.onion";
- char address2[] = "aaaaaaaaaaaaaaaa.onion";
- char address3[] = "fooaddress.exit";
- char address4[] = "www.torproject.org";
- char address5[] = "foo.abcdefghijklmnop.onion";
- char address6[] = "foo.bar.abcdefghijklmnop.onion";
- char address7[] = ".abcdefghijklmnop.onion";
(void)arg;
- tt_assert(BAD_HOSTNAME == parse_extended_hostname(address1));
- tt_assert(ONION_HOSTNAME == parse_extended_hostname(address2));
- tt_str_op(address2,OP_EQ, "aaaaaaaaaaaaaaaa");
- tt_assert(EXIT_HOSTNAME == parse_extended_hostname(address3));
- tt_assert(NORMAL_HOSTNAME == parse_extended_hostname(address4));
- tt_assert(ONION_HOSTNAME == parse_extended_hostname(address5));
- tt_str_op(address5,OP_EQ, "abcdefghijklmnop");
- tt_assert(ONION_HOSTNAME == parse_extended_hostname(address6));
- tt_str_op(address6,OP_EQ, "abcdefghijklmnop");
- tt_assert(BAD_HOSTNAME == parse_extended_hostname(address7));
/* Initialize the service cache. */
rend_cache_init();
@@ -587,20 +571,21 @@ test_rend_fns(void *arg)
intro->intro_key = crypto_pk_dup_key(pk2);
smartlist_add(generated->intro_nodes, intro);
}
- tt_assert(rend_encode_v2_descriptors(descs, generated, now, 0,
- REND_NO_AUTH, NULL, NULL) > 0);
- tt_assert(rend_compute_v2_desc_id(computed_desc_id, service_id_base32,
- NULL, now, 0) == 0);
+ int rv = rend_encode_v2_descriptors(descs, generated, now, 0,
+ REND_NO_AUTH, NULL, NULL);
+ tt_int_op(rv, OP_GT, 0);
+ rv = rend_compute_v2_desc_id(computed_desc_id, service_id_base32, NULL,
+ now, 0);
+ tt_int_op(rv, OP_EQ, 0);
tt_mem_op(((rend_encoded_v2_service_descriptor_t *)
smartlist_get(descs, 0))->desc_id, OP_EQ,
computed_desc_id, DIGEST_LEN);
- tt_assert(rend_parse_v2_service_descriptor(&parsed, parsed_desc_id,
- &intro_points_encrypted,
- &intro_points_size,
- &encoded_size,
- &next_desc,
- ((rend_encoded_v2_service_descriptor_t *)
- smartlist_get(descs, 0))->desc_str, 1) == 0);
+ rv = rend_parse_v2_service_descriptor(&parsed, parsed_desc_id,
+ &intro_points_encrypted, &intro_points_size, &encoded_size,
+ &next_desc,
+ ((rend_encoded_v2_service_descriptor_t *)smartlist_get(descs, 0))
+ ->desc_str, 1);
+ tt_int_op(rv, OP_EQ, 0);
tt_assert(parsed);
tt_mem_op(((rend_encoded_v2_service_descriptor_t *)
smartlist_get(descs, 0))->desc_id,OP_EQ, parsed_desc_id, DIGEST_LEN);
@@ -802,7 +787,7 @@ test_geoip(void *arg)
/* Start testing bridge statistics by making sure that we don't output
* bridge stats without initializing them. */
s = geoip_format_bridge_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Initialize stats and generate the bridge-stats history string out of
* the connecting clients added above. */
@@ -816,7 +801,7 @@ test_geoip(void *arg)
* string anymore. */
geoip_bridge_stats_term();
s = geoip_format_bridge_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Stop being a bridge and start being a directory mirror that gathers
* directory request statistics. */
@@ -830,7 +815,7 @@ test_geoip(void *arg)
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Initialize stats, note one connecting client, and generate the
* dirreq-stats history string. */
@@ -847,7 +832,7 @@ test_geoip(void *arg)
SET_TEST_ADDRESS(101);
geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
s = geoip_format_dirreq_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Re-start stats, add a connecting client, reset stats, and make sure
* that we get an all empty history string. */
@@ -883,7 +868,7 @@ test_geoip(void *arg)
SET_TEST_ADDRESS(100);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Initialize stats, note one connecting client, and generate the
* entry-stats history string. */
@@ -900,7 +885,7 @@ test_geoip(void *arg)
SET_TEST_ADDRESS(101);
geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
s = geoip_format_entry_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Re-start stats, add a connecting client, reset stats, and make sure
* that we get an all empty history string. */
@@ -1029,7 +1014,7 @@ test_stats(void *arg)
rep_hist_note_exit_stream_opened(80);
rep_hist_note_exit_bytes(80, 100, 10000);
s = rep_hist_format_exit_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Initialize stats, note some streams and bytes, and generate history
* string. */
@@ -1067,7 +1052,7 @@ test_stats(void *arg)
rep_hist_exit_stats_term();
rep_hist_note_exit_bytes(80, 100, 10000);
s = rep_hist_format_exit_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Re-start stats, add some bytes, reset stats, and see what history we
* get when observing no streams or bytes at all. */
@@ -1086,7 +1071,7 @@ test_stats(void *arg)
* conn stats without initializing them. */
rep_hist_note_or_conn_bytes(1, 20, 400, now);
s = rep_hist_format_conn_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Initialize stats, note bytes, and generate history string. */
rep_hist_conn_stats_init(now);
@@ -1103,7 +1088,7 @@ test_stats(void *arg)
rep_hist_conn_stats_term();
rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15);
s = rep_hist_format_conn_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Re-start stats, add some bytes, reset stats, and see what history we
* get when observing no bytes at all. */
@@ -1121,7 +1106,7 @@ test_stats(void *arg)
* stats without initializing them. */
rep_hist_add_buffer_stats(2.0, 2.0, 20);
s = rep_hist_format_buffer_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Initialize stats, add statistics for a single circuit, and generate
* the history string. */
@@ -1156,7 +1141,7 @@ test_stats(void *arg)
rep_hist_buffer_stats_term();
rep_hist_add_buffer_stats(2.0, 2.0, 20);
s = rep_hist_format_buffer_stats(now + 86400);
- tt_assert(!s);
+ tt_ptr_op(s, OP_EQ, NULL);
/* Re-start stats, add statistics for one circuit, reset stats, and make
* sure that the history has all zeros. */
@@ -1233,8 +1218,13 @@ struct testgroup_t testgroups[] = {
{ "extorport/", extorport_tests },
{ "legacy_hs/", hs_tests },
{ "hs_cache/", hs_cache },
+ { "hs_cell/", hs_cell_tests },
+ { "hs_common/", hs_common_tests },
+ { "hs_config/", hs_config_tests },
{ "hs_descriptor/", hs_descriptor },
+ { "hs_ntor/", hs_ntor_tests },
{ "hs_service/", hs_service_tests },
+ { "hs_client/", hs_client_tests },
{ "hs_intropoint/", hs_intropoint_tests },
{ "introduce/", introduce_tests },
{ "keypin/", keypin_tests },
@@ -1245,12 +1235,15 @@ struct testgroup_t testgroups[] = {
{ "options/", options_tests },
{ "policy/" , policy_tests },
{ "procmon/", procmon_tests },
+ { "proto/http/", proto_http_tests },
+ { "proto/misc/", proto_misc_tests },
{ "protover/", protover_tests },
{ "pt/", pt_tests },
{ "relay/" , relay_tests },
{ "relaycell/", relaycell_tests },
{ "rend_cache/", rend_cache_tests },
{ "replaycache/", replaycache_tests },
+ { "router/", router_tests },
{ "routerkeys/", routerkeys_tests },
{ "routerlist/", routerlist_tests },
{ "routerset/" , routerset_tests },
diff --git a/src/test/test.h b/src/test/test.h
index ea1b16adee..3c12d2b673 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -45,8 +45,8 @@
* you're doing. */
#define tt_double_eq(a,b) \
STMT_BEGIN \
- tt_double_op((a), >=, (b)); \
- tt_double_op((a), <=, (b)); \
+ tt_double_op((a), OP_GE, (b)); \
+ tt_double_op((a), OP_LE, (b)); \
STMT_END
#ifdef _MSC_VER
@@ -55,7 +55,7 @@
#else
#define U64_PRINTF_TYPE unsigned long long
#define I64_PRINTF_TYPE long long
-#endif
+#endif /* defined(_MSC_VER) */
#define tt_size_op(a,op,b) \
tt_assert_test_fmt_type(a,b,#a" "#op" "#b,size_t,(val1_ op val2_), \
@@ -209,8 +209,13 @@ extern struct testcase_t guardfraction_tests[];
extern struct testcase_t extorport_tests[];
extern struct testcase_t hs_tests[];
extern struct testcase_t hs_cache[];
+extern struct testcase_t hs_cell_tests[];
+extern struct testcase_t hs_common_tests[];
+extern struct testcase_t hs_config_tests[];
extern struct testcase_t hs_descriptor[];
+extern struct testcase_t hs_ntor_tests[];
extern struct testcase_t hs_service_tests[];
+extern struct testcase_t hs_client_tests[];
extern struct testcase_t hs_intropoint_tests[];
extern struct testcase_t introduce_tests[];
extern struct testcase_t keypin_tests[];
@@ -223,6 +228,8 @@ extern struct testcase_t oos_tests[];
extern struct testcase_t options_tests[];
extern struct testcase_t policy_tests[];
extern struct testcase_t procmon_tests[];
+extern struct testcase_t proto_http_tests[];
+extern struct testcase_t proto_misc_tests[];
extern struct testcase_t protover_tests[];
extern struct testcase_t pubsub_tests[];
extern struct testcase_t pt_tests[];
@@ -266,5 +273,5 @@ extern const char AUTHORITY_SIGNKEY_3[];
extern const char AUTHORITY_SIGNKEY_C_DIGEST[];
extern const char AUTHORITY_SIGNKEY_C_DIGEST256[];
-#endif
+#endif /* !defined(TOR_TEST_H) */
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index 2f591bdfe7..e1a40b7e60 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -451,10 +451,10 @@ test_addr_ip6_helpers(void *arg)
"::ffff:6.0.0.0"); /* XXXX wrong. */
tor_addr_parse_mask_ports("[::ffff:2.3.4.5]", 0, &t1, NULL, NULL, NULL);
tor_addr_parse_mask_ports("2.3.4.5", 0, &t2, NULL, NULL, NULL);
- tt_assert(tor_addr_compare(&t1, &t2, CMP_SEMANTIC) == 0);
+ tt_int_op(tor_addr_compare(&t1, &t2, CMP_SEMANTIC), OP_EQ, 0);
tor_addr_parse_mask_ports("[::ffff:2.3.4.4]", 0, &t1, NULL, NULL, NULL);
tor_addr_parse_mask_ports("2.3.4.5", 0, &t2, NULL, NULL, NULL);
- tt_assert(tor_addr_compare(&t1, &t2, CMP_SEMANTIC) < 0);
+ tt_int_op(tor_addr_compare(&t1, &t2, CMP_SEMANTIC), OP_LT, 0);
/* test compare_masked */
test_addr_compare_masked("ffff::", OP_EQ, "ffff::0", 128);
@@ -637,7 +637,7 @@ test_addr_ip6_helpers(void *arg)
/* Try some long addresses. */
r=tor_addr_parse_mask_ports("[ffff:1111:1111:1111:1111:1111:1111:1111]",
0, &t1, NULL, NULL, NULL);
- tt_assert(r == AF_INET6);
+ tt_int_op(r, OP_EQ, AF_INET6);
r=tor_addr_parse_mask_ports("[ffff:1111:1111:1111:1111:1111:1111:11111]",
0, &t1, NULL, NULL, NULL);
tt_int_op(r, OP_EQ, -1);
@@ -686,38 +686,38 @@ test_addr_ip6_helpers(void *arg)
tt_int_op(r, OP_EQ, -1);
r=tor_addr_parse_mask_ports("*6",0,&t1, &mask, NULL, NULL);
tt_int_op(r, OP_EQ, -1);
- tt_assert(r == -1);
+ tt_int_op(r, OP_EQ, -1);
/* Try a mask with a wildcard. */
r=tor_addr_parse_mask_ports("*/16",0,&t1, &mask, NULL, NULL);
- tt_assert(r == -1);
+ tt_int_op(r, OP_EQ, -1);
r=tor_addr_parse_mask_ports("*4/16",TAPMP_EXTENDED_STAR,
&t1, &mask, NULL, NULL);
- tt_assert(r == -1);
+ tt_int_op(r, OP_EQ, -1);
r=tor_addr_parse_mask_ports("*6/30",TAPMP_EXTENDED_STAR,
&t1, &mask, NULL, NULL);
- tt_assert(r == -1);
+ tt_int_op(r, OP_EQ, -1);
/* Basic mask tests*/
r=tor_addr_parse_mask_ports("1.1.2.2/31",0,&t1, &mask, NULL, NULL);
- tt_assert(r == AF_INET);
+ tt_int_op(r, OP_EQ, AF_INET);
tt_int_op(mask,OP_EQ,31);
tt_int_op(tor_addr_family(&t1),OP_EQ,AF_INET);
tt_int_op(tor_addr_to_ipv4h(&t1),OP_EQ,0x01010202);
r=tor_addr_parse_mask_ports("3.4.16.032:1-2",0,&t1, &mask, &port1, &port2);
- tt_assert(r == AF_INET);
+ tt_int_op(r, OP_EQ, AF_INET);
tt_int_op(mask,OP_EQ,32);
tt_int_op(tor_addr_family(&t1),OP_EQ,AF_INET);
tt_int_op(tor_addr_to_ipv4h(&t1),OP_EQ,0x03041020);
- tt_assert(port1 == 1);
- tt_assert(port2 == 2);
+ tt_uint_op(port1, OP_EQ, 1);
+ tt_uint_op(port2, OP_EQ, 2);
r=tor_addr_parse_mask_ports("1.1.2.3/255.255.128.0",0,&t1, &mask,NULL,NULL);
- tt_assert(r == AF_INET);
+ tt_int_op(r, OP_EQ, AF_INET);
tt_int_op(mask,OP_EQ,17);
tt_int_op(tor_addr_family(&t1),OP_EQ,AF_INET);
tt_int_op(tor_addr_to_ipv4h(&t1),OP_EQ,0x01010203);
r=tor_addr_parse_mask_ports("[efef::]/112",0,&t1, &mask, &port1, &port2);
- tt_assert(r == AF_INET6);
- tt_assert(port1 == 1);
- tt_assert(port2 == 65535);
+ tt_int_op(r, OP_EQ, AF_INET6);
+ tt_uint_op(port1, OP_EQ, 1);
+ tt_uint_op(port2, OP_EQ, 65535);
/* Try regular wildcard behavior without TAPMP_EXTENDED_STAR */
r=tor_addr_parse_mask_ports("*:80-443",0,&t1,&mask,&port1,&port2);
tt_int_op(r,OP_EQ,AF_INET); /* Old users of this always get inet */
@@ -752,15 +752,17 @@ test_addr_ip6_helpers(void *arg)
tt_int_op(port2,OP_EQ,65535);
/* make sure inet address lengths >= max */
- tt_assert(INET_NTOA_BUF_LEN >= sizeof("255.255.255.255"));
- tt_assert(TOR_ADDR_BUF_LEN >=
- sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"));
+ tt_int_op(INET_NTOA_BUF_LEN, OP_GE, sizeof("255.255.255.255"));
+ tt_int_op(TOR_ADDR_BUF_LEN, OP_GE,
+ sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"));
tt_assert(sizeof(tor_addr_t) >= sizeof(struct in6_addr));
/* get interface addresses */
r = get_interface_address6(LOG_DEBUG, AF_INET, &t1);
+ tt_int_op(r, OP_LE, 0); // "it worked or it didn't"
i = get_interface_address6(LOG_DEBUG, AF_INET6, &t2);
+ tt_int_op(i, OP_LE, 0); // "it worked or it didn't"
TT_BLATHER(("v4 address: %s (family=%d)", fmt_addr(&t1),
tor_addr_family(&t1)));
@@ -1010,7 +1012,7 @@ test_addr_sockaddr_to_str(void *arg)
s_un.sun_family = AF_UNIX;
strlcpy(s_un.sun_path, "/here/is/a/path", sizeof(s_un.sun_path));
CHECK(s_un, "unix:/here/is/a/path");
-#endif
+#endif /* defined(HAVE_SYS_UN_H) */
memset(&sin6,0,sizeof(sin6));
sin6.sin6_family = AF_INET6;
diff --git a/src/test/test_address.c b/src/test/test_address.c
index 50a0574522..f36ff6998b 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -21,7 +21,7 @@
#include <sys/ioctl.h>
#endif
#include <net/if.h>
-#endif
+#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
#include "or.h"
#include "address.h"
@@ -224,7 +224,7 @@ test_address_ifaddrs_to_smartlist(void *arg)
smartlist = ifaddrs_to_smartlist(ifa, AF_UNSPEC);
tt_assert(smartlist);
- tt_assert(smartlist_len(smartlist) == 3);
+ tt_int_op(smartlist_len(smartlist), OP_EQ, 3);
sockaddr_to_check = tor_malloc(sizeof(struct sockaddr_in6));
@@ -233,7 +233,7 @@ test_address_ifaddrs_to_smartlist(void *arg)
tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
sizeof(struct sockaddr_in));
- tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+ tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
ipv4_sockaddr_local));
@@ -242,7 +242,7 @@ test_address_ifaddrs_to_smartlist(void *arg)
tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
sizeof(struct sockaddr_in));
- tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+ tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal((struct sockaddr_in *)sockaddr_to_check,
ipv4_sockaddr_remote));
@@ -251,7 +251,7 @@ test_address_ifaddrs_to_smartlist(void *arg)
tor_addr_to_sockaddr(tor_addr,0,sockaddr_to_check,
sizeof(struct sockaddr_in6));
- tt_int_op(addr_len,==,sizeof(struct sockaddr_in6));
+ tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in6));
tt_assert(sockaddr_in6_are_equal((struct sockaddr_in6*)sockaddr_to_check,
ipv6_sockaddr));
@@ -305,7 +305,7 @@ test_address_get_if_addrs_ifaddrs(void *arg)
return;
}
-#endif
+#endif /* defined(HAVE_IFADDRS_TO_SMARTLIST) */
#ifdef HAVE_IP_ADAPTER_TO_SMARTLIST
@@ -319,7 +319,7 @@ test_address_get_if_addrs_win32(void *arg)
results = get_interface_addresses_win32(LOG_ERR, AF_UNSPEC);
- tt_int_op(smartlist_len(results),>=,1);
+ tt_int_op(smartlist_len(results),OP_GE,1);
tt_assert(smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_null_tor_addr(results));
@@ -384,7 +384,7 @@ test_address_ip_adapter_addresses_to_smartlist(void *arg)
result = ip_adapter_addresses_to_smartlist(addrs1);
tt_assert(result);
- tt_assert(smartlist_len(result) == 3);
+ tt_int_op(smartlist_len(result), OP_EQ, 3);
tor_addr = smartlist_get(result,0);
@@ -421,7 +421,7 @@ test_address_ip_adapter_addresses_to_smartlist(void *arg)
tor_free(sockaddr_to_check);
return;
}
-#endif
+#endif /* defined(HAVE_IP_ADAPTER_TO_SMARTLIST) */
#ifdef HAVE_IFCONF_TO_SMARTLIST
@@ -456,14 +456,14 @@ test_address_ifreq_to_smartlist(void *arg)
ifc->ifc_ifcu.ifcu_req = ifr;
results = ifreq_to_smartlist(ifc->ifc_buf,ifc->ifc_len);
- tt_int_op(smartlist_len(results),==,1);
+ tt_int_op(smartlist_len(results),OP_EQ,1);
tor_addr = smartlist_get(results, 0);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
- tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+ tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr,sockaddr_to_check));
ifr = tor_realloc(ifr,2*sizeof(struct ifreq));
@@ -479,14 +479,14 @@ test_address_ifreq_to_smartlist(void *arg)
smartlist_free(results);
results = ifreq_to_smartlist(ifc->ifc_buf,ifc->ifc_len);
- tt_int_op(smartlist_len(results),==,2);
+ tt_int_op(smartlist_len(results),OP_EQ,2);
tor_addr = smartlist_get(results, 0);
addr_len =
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
- tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+ tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr,sockaddr_to_check));
tor_addr = smartlist_get(results, 1);
@@ -494,7 +494,7 @@ test_address_ifreq_to_smartlist(void *arg)
tor_addr_to_sockaddr(tor_addr,0,(struct sockaddr *)sockaddr_to_check,
sizeof(struct sockaddr_in));
- tt_int_op(addr_len,==,sizeof(struct sockaddr_in));
+ tt_int_op(addr_len,OP_EQ,sizeof(struct sockaddr_in));
tt_assert(sockaddr_in_are_equal(sockaddr_eth1,sockaddr_to_check));
done:
@@ -543,7 +543,7 @@ test_address_get_if_addrs_ioctl(void *arg)
return;
}
-#endif
+#endif /* defined(HAVE_IFCONF_TO_SMARTLIST) */
#define FAKE_SOCKET_FD (42)
@@ -633,7 +633,7 @@ test_address_udp_socket_trick_whitebox(void *arg)
get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET, addr_from_hack);
- tt_int_op(hack_retval,==,0);
+ tt_int_op(hack_retval,OP_EQ,0);
tt_assert(tor_addr_eq_ipv4h(addr_from_hack, 0x1720f676));
/* Now, lets do an IPv6 case. */
@@ -648,7 +648,7 @@ test_address_udp_socket_trick_whitebox(void *arg)
get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET6, addr_from_hack);
- tt_int_op(hack_retval,==,0);
+ tt_int_op(hack_retval,OP_EQ,0);
tor_addr_to_sockaddr(addr_from_hack,0,(struct sockaddr *)ipv6_to_check,
sizeof(struct sockaddr_in6));
@@ -693,7 +693,7 @@ test_address_udp_socket_trick_blackbox(void *arg)
AF_INET,
&addr4_to_check);
- tt_int_op(retval,==,retval_reference);
+ tt_int_op(retval,OP_EQ,retval_reference);
tt_assert( (retval == -1 && retval_reference == -1) ||
(tor_addr_compare(&addr4,&addr4_to_check,CMP_EXACT) == 0) );
@@ -702,11 +702,11 @@ test_address_udp_socket_trick_blackbox(void *arg)
AF_INET6,
&addr6_to_check);
- tt_int_op(retval,==,retval_reference);
+ tt_int_op(retval,OP_EQ,retval_reference);
tt_assert( (retval == -1 && retval_reference == -1) ||
(tor_addr_compare(&addr6,&addr6_to_check,CMP_EXACT) == 0) );
-#else
+#else /* !(0) */
/* Both of the blackbox test cases fail horribly if:
* * The host has no external addreses.
* * There are multiple interfaces with either AF_INET or AF_INET6.
@@ -721,7 +721,7 @@ test_address_udp_socket_trick_blackbox(void *arg)
(void)addr6_to_check;
(void)addr6;
(void) retval_reference;
-#endif
+#endif /* 0 */
/* When family is neither AF_INET nor AF_INET6, we want _hack to
* fail and return -1.
@@ -730,7 +730,7 @@ test_address_udp_socket_trick_blackbox(void *arg)
retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
AF_INET+AF_INET6,&addr4);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
return;
@@ -745,11 +745,11 @@ test_address_get_if_addrs_list_internal(void *arg)
results = get_interface_address_list(LOG_ERR, 1);
- tt_assert(results != NULL);
+ tt_ptr_op(results, OP_NE, NULL);
/* When the network is down, a system might not have any non-local
* non-multicast addresseses, not even internal ones.
* Unit tests shouldn't fail because of this. */
- tt_int_op(smartlist_len(results),>=,0);
+ tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
@@ -776,9 +776,9 @@ test_address_get_if_addrs_list_no_internal(void *arg)
results = get_interface_address_list(LOG_ERR, 0);
- tt_assert(results != NULL);
+ tt_ptr_op(results, OP_NE, NULL);
/* Work even on systems with only internal IPv4 addresses */
- tt_int_op(smartlist_len(results),>=,0);
+ tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
@@ -818,9 +818,9 @@ test_address_get_if_addrs6_list_internal(void *arg)
}
teardown_capture_of_logs();
- tt_assert(results != NULL);
+ tt_ptr_op(results, OP_NE, NULL);
/* Work even on systems without IPv6 interfaces */
- tt_int_op(smartlist_len(results),>=,0);
+ tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
@@ -861,9 +861,9 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
}
teardown_capture_of_logs();
- tt_assert(results != NULL);
+ tt_ptr_op(results, OP_NE, NULL);
/* Work even on systems without IPv6 interfaces */
- tt_int_op(smartlist_len(results),>=,0);
+ tt_int_op(smartlist_len(results),OP_GE,0);
tt_assert(!smartlist_contains_localhost_tor_addr(results));
tt_assert(!smartlist_contains_multicast_tor_addr(results));
@@ -927,18 +927,18 @@ test_address_get_if_addrs_internal_fail(void *arg)
mock_get_interface_address6_via_udp_socket_hack_fail);
results1 = get_interface_address6_list(LOG_ERR, AF_INET6, 1);
- tt_assert(results1 != NULL);
- tt_int_op(smartlist_len(results1),==,0);
+ tt_ptr_op(results1, OP_NE, NULL);
+ tt_int_op(smartlist_len(results1),OP_EQ,0);
results2 = get_interface_address_list(LOG_ERR, 1);
- tt_assert(results2 != NULL);
- tt_int_op(smartlist_len(results2),==,0);
+ tt_ptr_op(results2, OP_NE, NULL);
+ tt_int_op(smartlist_len(results2),OP_EQ,0);
rv = get_interface_address6(LOG_ERR, AF_INET6, &ipv6_addr);
- tt_assert(rv == -1);
+ tt_int_op(rv, OP_EQ, -1);
rv = get_interface_address(LOG_ERR, &ipv4h_addr);
- tt_assert(rv == -1);
+ tt_int_op(rv, OP_EQ, -1);
done:
UNMOCK(get_interface_addresses_raw);
@@ -961,12 +961,12 @@ test_address_get_if_addrs_no_internal_fail(void *arg)
mock_get_interface_address6_via_udp_socket_hack_fail);
results1 = get_interface_address6_list(LOG_ERR, AF_INET6, 0);
- tt_assert(results1 != NULL);
- tt_int_op(smartlist_len(results1),==,0);
+ tt_ptr_op(results1, OP_NE, NULL);
+ tt_int_op(smartlist_len(results1),OP_EQ,0);
results2 = get_interface_address_list(LOG_ERR, 0);
- tt_assert(results2 != NULL);
- tt_int_op(smartlist_len(results2),==,0);
+ tt_ptr_op(results2, OP_NE, NULL);
+ tt_int_op(smartlist_len(results2),OP_EQ,0);
done:
UNMOCK(get_interface_addresses_raw);
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
index ed588ecc5b..b5c8d7cf9e 100644
--- a/src/test/test_bt_cl.c
+++ b/src/test/test_bt_cl.c
@@ -38,7 +38,7 @@ crash(int x)
* don't need to see us dereference NULL. */
#else
*(volatile int *)0 = 0;
-#endif
+#endif /* defined(__clang_analyzer__) || defined(__COVERITY__) */
} else if (crashtype == 1) {
tor_assert(1 == 0);
} else if (crashtype == -1) {
diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c
index 07114a8571..9f4e19bc5f 100644
--- a/src/test/test_buffers.c
+++ b/src/test/test_buffers.c
@@ -4,9 +4,12 @@
/* See LICENSE for licensing information */
#define BUFFERS_PRIVATE
+#define PROTO_HTTP_PRIVATE
#include "or.h"
#include "buffers.h"
-#include "ext_orport.h"
+#include "buffers_tls.h"
+#include "proto_http.h"
+#include "proto_socks.h"
#include "test.h"
/** Run unit tests for buffers.c */
@@ -38,15 +41,15 @@ test_buffers_basic(void *arg)
for (j=0;j<256;++j) {
str[j] = (char)j;
}
- write_to_buf(str, 256, buf);
- write_to_buf(str, 256, buf);
+ buf_add(buf, str, 256);
+ buf_add(buf, str, 256);
tt_int_op(buf_datalen(buf),OP_EQ, 512);
- fetch_from_buf(str2, 200, buf);
+ buf_get_bytes(buf, str2, 200);
tt_mem_op(str,OP_EQ, str2, 200);
tt_int_op(buf_datalen(buf),OP_EQ, 312);
memset(str2, 0, sizeof(str2));
- fetch_from_buf(str2, 256, buf);
+ buf_get_bytes(buf, str2, 256);
tt_mem_op(str+200,OP_EQ, str2, 56);
tt_mem_op(str,OP_EQ, str2+56, 200);
tt_int_op(buf_datalen(buf),OP_EQ, 56);
@@ -54,16 +57,16 @@ test_buffers_basic(void *arg)
/* Okay, now we should be 512 bytes into the 4096-byte buffer. If we add
* another 3584 bytes, we hit the end. */
for (j=0;j<15;++j) {
- write_to_buf(str, 256, buf);
+ buf_add(buf, str, 256);
}
- assert_buf_ok(buf);
+ buf_assert_ok(buf);
tt_int_op(buf_datalen(buf),OP_EQ, 3896);
- fetch_from_buf(str2, 56, buf);
+ buf_get_bytes(buf, str2, 56);
tt_int_op(buf_datalen(buf),OP_EQ, 3840);
tt_mem_op(str+200,OP_EQ, str2, 56);
for (j=0;j<15;++j) {
memset(str2, 0, sizeof(str2));
- fetch_from_buf(str2, 256, buf);
+ buf_get_bytes(buf, str2, 256);
tt_mem_op(str,OP_EQ, str2, 256);
}
tt_int_op(buf_datalen(buf),OP_EQ, 0);
@@ -73,38 +76,38 @@ test_buffers_basic(void *arg)
/* Okay, now make sure growing can work. */
buf = buf_new_with_capacity(16);
//test_eq(buf_capacity(buf), 16);
- write_to_buf(str+1, 255, buf);
+ buf_add(buf, str+1, 255);
//test_eq(buf_capacity(buf), 256);
- fetch_from_buf(str2, 254, buf);
+ buf_get_bytes(buf, str2, 254);
tt_mem_op(str+1,OP_EQ, str2, 254);
//test_eq(buf_capacity(buf), 256);
- assert_buf_ok(buf);
- write_to_buf(str, 32, buf);
+ buf_assert_ok(buf);
+ buf_add(buf, str, 32);
//test_eq(buf_capacity(buf), 256);
- assert_buf_ok(buf);
- write_to_buf(str, 256, buf);
- assert_buf_ok(buf);
+ buf_assert_ok(buf);
+ buf_add(buf, str, 256);
+ buf_assert_ok(buf);
//test_eq(buf_capacity(buf), 512);
tt_int_op(buf_datalen(buf),OP_EQ, 33+256);
- fetch_from_buf(str2, 33, buf);
+ buf_get_bytes(buf, str2, 33);
tt_int_op(*str2,OP_EQ, str[255]);
tt_mem_op(str2+1,OP_EQ, str, 32);
//test_eq(buf_capacity(buf), 512);
tt_int_op(buf_datalen(buf),OP_EQ, 256);
- fetch_from_buf(str2, 256, buf);
+ buf_get_bytes(buf, str2, 256);
tt_mem_op(str,OP_EQ, str2, 256);
/* now try shrinking: case 1. */
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
- write_to_buf(str,255, buf);
+ buf_add(buf, str,255);
}
//test_eq(buf_capacity(buf), 33668);
tt_int_op(buf_datalen(buf),OP_EQ, 17085);
for (j=0; j < 40; ++j) {
- fetch_from_buf(str2, 255,buf);
+ buf_get_bytes(buf, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
@@ -112,18 +115,18 @@ test_buffers_basic(void *arg)
buf_free(buf);
buf = buf_new_with_capacity(33668);
for (j=0;j<67;++j) {
- write_to_buf(str,255, buf);
+ buf_add(buf, str, 255);
}
for (j=0; j < 20; ++j) {
- fetch_from_buf(str2, 255,buf);
+ buf_get_bytes(buf, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
for (j=0;j<80;++j) {
- write_to_buf(str,255, buf);
+ buf_add(buf, str, 255);
}
//test_eq(buf_capacity(buf),33668);
for (j=0; j < 120; ++j) {
- fetch_from_buf(str2, 255,buf);
+ buf_get_bytes(buf, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
@@ -132,27 +135,27 @@ test_buffers_basic(void *arg)
buf = buf_new_with_capacity(4096);
buf2 = buf_new_with_capacity(4096);
for (j=0;j<100;++j)
- write_to_buf(str, 255, buf);
+ buf_add(buf, str, 255);
tt_int_op(buf_datalen(buf),OP_EQ, 25500);
for (j=0;j<100;++j) {
r = 10;
- move_buf_to_buf(buf2, buf, &r);
+ buf_move_to_buf(buf2, buf, &r);
tt_int_op(r,OP_EQ, 0);
}
tt_int_op(buf_datalen(buf),OP_EQ, 24500);
tt_int_op(buf_datalen(buf2),OP_EQ, 1000);
for (j=0;j<3;++j) {
- fetch_from_buf(str2, 255, buf2);
+ buf_get_bytes(buf2, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
r = 8192; /*big move*/
- move_buf_to_buf(buf2, buf, &r);
+ buf_move_to_buf(buf2, buf, &r);
tt_int_op(r,OP_EQ, 0);
r = 30000; /* incomplete move */
- move_buf_to_buf(buf2, buf, &r);
+ buf_move_to_buf(buf2, buf, &r);
tt_int_op(r,OP_EQ, 13692);
for (j=0;j<97;++j) {
- fetch_from_buf(str2, 255, buf2);
+ buf_get_bytes(buf2, str2, 255);
tt_mem_op(str2,OP_EQ, str, 255);
}
buf_free(buf);
@@ -162,7 +165,7 @@ test_buffers_basic(void *arg)
buf = buf_new_with_capacity(5);
cp = "Testing. This is a moderately long Testing string.";
for (j = 0; cp[j]; j++)
- write_to_buf(cp+j, 1, buf);
+ buf_add(buf, cp+j, 1);
tt_int_op(0,OP_EQ, buf_find_string_offset(buf, "Testing", 7));
tt_int_op(1,OP_EQ, buf_find_string_offset(buf, "esting", 6));
tt_int_op(1,OP_EQ, buf_find_string_offset(buf, "est", 3));
@@ -180,7 +183,7 @@ test_buffers_basic(void *arg)
{
char *mem = tor_malloc_zero(65536);
buf = buf_new();
- write_to_buf(mem, 65536, buf);
+ buf_add(buf, mem, 65536);
tor_free(mem);
tt_int_op(buf_datalen(buf), OP_EQ, 65536);
@@ -215,8 +218,7 @@ test_buffer_pullup(void *arg)
/* There are a bunch of cases for pullup. One is the trivial case. Let's
mess around with an empty buffer. */
- buf_pullup(buf, 16);
- buf_get_first_chunk_data(buf, &cp, &sz);
+ buf_pullup(buf, 16, &cp, &sz);
tt_ptr_op(cp, OP_EQ, NULL);
tt_uint_op(sz, OP_EQ, 0);
@@ -227,65 +229,62 @@ test_buffer_pullup(void *arg)
/* Let's add some data. */
crypto_rand(stuff, 16384);
- write_to_buf(stuff, 3000, buf);
- write_to_buf(stuff+3000, 3000, buf);
- buf_get_first_chunk_data(buf, &cp, &sz);
+ buf_add(buf, stuff, 3000);
+ buf_add(buf, stuff+3000, 3000);
+ buf_pullup(buf, 0, &cp, &sz);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_LE, 4096);
/* Make room for 3000 bytes in the first chunk, so that the pullup-move code
* can get tested. */
- tt_int_op(fetch_from_buf(tmp, 3000, buf), OP_EQ, 3000);
+ tt_int_op(buf_get_bytes(buf, tmp, 3000), OP_EQ, 3000);
tt_mem_op(tmp,OP_EQ, stuff, 3000);
- buf_pullup(buf, 2048);
- assert_buf_ok(buf);
- buf_get_first_chunk_data(buf, &cp, &sz);
+ buf_pullup(buf, 2048, &cp, &sz);
+ buf_assert_ok(buf);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_GE, 2048);
tt_mem_op(cp,OP_EQ, stuff+3000, 2048);
tt_int_op(3000, OP_EQ, buf_datalen(buf));
- tt_int_op(fetch_from_buf(tmp, 3000, buf), OP_EQ, 0);
+ tt_int_op(buf_get_bytes(buf, tmp, 3000), OP_EQ, 0);
tt_mem_op(tmp,OP_EQ, stuff+3000, 2048);
buf_free(buf);
/* Now try the large-chunk case. */
buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
- write_to_buf(stuff, 4000, buf);
- write_to_buf(stuff+4000, 4000, buf);
- write_to_buf(stuff+8000, 4000, buf);
- write_to_buf(stuff+12000, 4000, buf);
+ buf_add(buf, stuff, 4000);
+ buf_add(buf, stuff+4000, 4000);
+ buf_add(buf, stuff+8000, 4000);
+ buf_add(buf, stuff+12000, 4000);
tt_int_op(buf_datalen(buf), OP_EQ, 16000);
- buf_get_first_chunk_data(buf, &cp, &sz);
+ buf_pullup(buf, 0, &cp, &sz);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_LE, 4096);
- buf_pullup(buf, 12500);
- assert_buf_ok(buf);
- buf_get_first_chunk_data(buf, &cp, &sz);
+ buf_pullup(buf, 12500, &cp, &sz);
+ buf_assert_ok(buf);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_GE, 12500);
tt_mem_op(cp,OP_EQ, stuff, 12500);
tt_int_op(buf_datalen(buf), OP_EQ, 16000);
- fetch_from_buf(tmp, 12400, buf);
+ buf_get_bytes(buf, tmp, 12400);
tt_mem_op(tmp,OP_EQ, stuff, 12400);
tt_int_op(buf_datalen(buf), OP_EQ, 3600);
- fetch_from_buf(tmp, 3500, buf);
+ buf_get_bytes(buf, tmp, 3500);
tt_mem_op(tmp,OP_EQ, stuff+12400, 3500);
- fetch_from_buf(tmp, 100, buf);
+ buf_get_bytes(buf, tmp, 100);
tt_mem_op(tmp,OP_EQ, stuff+15900, 10);
buf_free(buf);
/* Make sure that the pull-up-whole-buffer case works */
buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
- write_to_buf(stuff, 4000, buf);
- write_to_buf(stuff+4000, 4000, buf);
- fetch_from_buf(tmp, 100, buf); /* dump 100 bytes from first chunk */
- buf_pullup(buf, 16000); /* Way too much. */
- assert_buf_ok(buf);
- buf_get_first_chunk_data(buf, &cp, &sz);
+ buf_add(buf, stuff, 4000);
+ buf_add(buf, stuff+4000, 4000);
+ buf_get_bytes(buf, tmp, 100); /* dump 100 bytes from first chunk */
+ buf_pullup(buf, 16000, &cp, &sz);
+ buf_assert_ok(buf);
tt_ptr_op(cp, OP_NE, NULL);
tt_int_op(sz, OP_EQ, 7900);
tt_mem_op(cp,OP_EQ, stuff+100, 7900);
@@ -321,23 +320,23 @@ test_buffer_copy(void *arg)
/* Now try with a short buffer. */
s = "And now comes an act of enormous enormance!";
len = strlen(s);
- write_to_buf(s, len, buf);
+ buf_add(buf, s, len);
tt_int_op(len, OP_EQ, buf_datalen(buf));
/* Add junk to buf2 so we can test replacing.*/
- write_to_buf("BLARG", 5, buf2);
+ buf_add(buf2, "BLARG", 5);
tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_int_op(len, OP_EQ, buf_datalen(buf2));
- fetch_from_buf(b, len, buf2);
+ buf_get_bytes(buf2, b, len);
tt_mem_op(b, OP_EQ, s, len);
/* Now free buf2 and retry so we can test allocating */
buf_free(buf2);
buf2 = NULL;
tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_int_op(len, OP_EQ, buf_datalen(buf2));
- fetch_from_buf(b, len, buf2);
+ buf_get_bytes(buf2, b, len);
tt_mem_op(b, OP_EQ, s, len);
/* Clear buf for next test */
- fetch_from_buf(b, len, buf);
+ buf_get_bytes(buf, b, len);
tt_int_op(buf_datalen(buf),OP_EQ,0);
/* Okay, now let's try a bigger buffer. */
@@ -347,13 +346,13 @@ test_buffer_copy(void *arg)
len = strlen(s);
for (i = 0; i < 256; ++i) {
b[0]=i;
- write_to_buf(b, 1, buf);
- write_to_buf(s, len, buf);
+ buf_add(buf, b, 1);
+ buf_add(buf, s, len);
}
tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_int_op(buf_datalen(buf2), OP_EQ, buf_datalen(buf));
for (i = 0; i < 256; ++i) {
- fetch_from_buf(b, len+1, buf2);
+ buf_get_bytes(buf2, b, len+1);
tt_int_op((unsigned char)b[0],OP_EQ,i);
tt_mem_op(b+1, OP_EQ, s, len);
}
@@ -366,79 +365,6 @@ test_buffer_copy(void *arg)
}
static void
-test_buffer_ext_or_cmd(void *arg)
-{
- ext_or_cmd_t *cmd = NULL;
- buf_t *buf = buf_new();
- char *tmp = NULL;
- (void) arg;
-
- /* Empty -- should give "not there. */
- tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tt_ptr_op(NULL, OP_EQ, cmd);
-
- /* Three bytes: shouldn't work. */
- write_to_buf("\x00\x20\x00", 3, buf);
- tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tt_ptr_op(NULL, OP_EQ, cmd);
- tt_int_op(3, OP_EQ, buf_datalen(buf));
-
- /* 0020 0000: That's a nil command. It should work. */
- write_to_buf("\x00", 1, buf);
- tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tt_ptr_op(NULL, OP_NE, cmd);
- tt_int_op(0x20, OP_EQ, cmd->cmd);
- tt_int_op(0, OP_EQ, cmd->len);
- tt_int_op(0, OP_EQ, buf_datalen(buf));
- ext_or_cmd_free(cmd);
- cmd = NULL;
-
- /* Now try a length-6 command with one byte missing. */
- write_to_buf("\x10\x21\x00\x06""abcde", 9, buf);
- tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tt_ptr_op(NULL, OP_EQ, cmd);
- write_to_buf("f", 1, buf);
- tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tt_ptr_op(NULL, OP_NE, cmd);
- tt_int_op(0x1021, OP_EQ, cmd->cmd);
- tt_int_op(6, OP_EQ, cmd->len);
- tt_mem_op("abcdef", OP_EQ, cmd->body, 6);
- tt_int_op(0, OP_EQ, buf_datalen(buf));
- ext_or_cmd_free(cmd);
- cmd = NULL;
-
- /* Now try a length-10 command with 4 extra bytes. */
- write_to_buf("\xff\xff\x00\x0aloremipsum\x10\x00\xff\xff", 18, buf);
- tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tt_ptr_op(NULL, OP_NE, cmd);
- tt_int_op(0xffff, OP_EQ, cmd->cmd);
- tt_int_op(10, OP_EQ, cmd->len);
- tt_mem_op("loremipsum", OP_EQ, cmd->body, 10);
- tt_int_op(4, OP_EQ, buf_datalen(buf));
- ext_or_cmd_free(cmd);
- cmd = NULL;
-
- /* Finally, let's try a maximum-length command. We already have the header
- * waiting. */
- tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tmp = tor_malloc_zero(65535);
- write_to_buf(tmp, 65535, buf);
- tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
- tt_ptr_op(NULL, OP_NE, cmd);
- tt_int_op(0x1000, OP_EQ, cmd->cmd);
- tt_int_op(0xffff, OP_EQ, cmd->len);
- tt_mem_op(tmp, OP_EQ, cmd->body, 65535);
- tt_int_op(0, OP_EQ, buf_datalen(buf));
- ext_or_cmd_free(cmd);
- cmd = NULL;
-
- done:
- ext_or_cmd_free(cmd);
- buf_free(buf);
- tor_free(tmp);
-}
-
-static void
test_buffer_allocation_tracking(void *arg)
{
char *junk = tor_malloc(16384);
@@ -458,36 +384,36 @@ test_buffer_allocation_tracking(void *arg)
tt_int_op(buf_allocation(buf1), OP_EQ, 0);
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
- write_to_buf(junk, 4000, buf1);
- write_to_buf(junk, 4000, buf1);
- write_to_buf(junk, 4000, buf1);
- write_to_buf(junk, 4000, buf1);
+ buf_add(buf1, junk, 4000);
+ buf_add(buf1, junk, 4000);
+ buf_add(buf1, junk, 4000);
+ buf_add(buf1, junk, 4000);
tt_int_op(buf_allocation(buf1), OP_EQ, 16384);
- fetch_from_buf(junk, 100, buf1);
+ buf_get_bytes(buf1, junk, 100);
tt_int_op(buf_allocation(buf1), OP_EQ, 16384); /* still 4 4k chunks */
tt_int_op(buf_get_total_allocation(), OP_EQ, 16384);
- fetch_from_buf(junk, 4096, buf1); /* drop a 1k chunk... */
+ buf_get_bytes(buf1, junk, 4096); /* drop a 1k chunk... */
tt_int_op(buf_allocation(buf1), OP_EQ, 3*4096); /* now 3 4k chunks */
tt_int_op(buf_get_total_allocation(), OP_EQ, 12288); /* that chunk was really
freed. */
- write_to_buf(junk, 4000, buf2);
+ buf_add(buf2, junk, 4000);
tt_int_op(buf_allocation(buf2), OP_EQ, 4096); /* another 4k chunk. */
/*
* We bounce back up to 16384 by allocating a new chunk.
*/
tt_int_op(buf_get_total_allocation(), OP_EQ, 16384);
- write_to_buf(junk, 4000, buf2);
+ buf_add(buf2, junk, 4000);
tt_int_op(buf_allocation(buf2), OP_EQ, 8192); /* another 4k chunk. */
tt_int_op(buf_get_total_allocation(),
OP_EQ, 5*4096); /* that chunk was new. */
/* Make a really huge buffer */
for (i = 0; i < 1000; ++i) {
- write_to_buf(junk, 4000, buf2);
+ buf_add(buf2, junk, 4000);
}
tt_int_op(buf_allocation(buf2), OP_GE, 4008000);
tt_int_op(buf_get_total_allocation(), OP_GE, 4008000);
@@ -530,7 +456,7 @@ test_buffer_time_tracking(void *arg)
tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC));
tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000));
- write_to_buf("ABCDEFG", 7, buf);
+ buf_add(buf, "ABCDEFG", 7);
tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000));
buf2 = buf_copy(buf);
@@ -541,7 +467,7 @@ test_buffer_time_tracking(void *arg)
/* Now add more bytes; enough to overflow the first chunk. */
monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000);
for (i = 0; i < 600; ++i)
- write_to_buf("ABCDEFG", 7, buf);
+ buf_add(buf, "ABCDEFG", 7);
tt_int_op(4207, OP_EQ, buf_datalen(buf));
/* The oldest bytes are still in the front. */
@@ -549,12 +475,12 @@ test_buffer_time_tracking(void *arg)
/* Once those bytes are dropped, the chunk is still on the first
* timestamp. */
- fetch_from_buf(tmp, 100, buf);
+ buf_get_bytes(buf, tmp, 100);
tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000));
/* But once we discard the whole first chunk, we get the data in the second
* chunk. */
- fetch_from_buf(tmp, 4000, buf);
+ buf_get_bytes(buf, tmp, 4000);
tt_int_op(107, OP_EQ, buf_datalen(buf));
tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123));
@@ -562,12 +488,12 @@ test_buffer_time_tracking(void *arg)
its time gets updated */
monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000);
for (i = 0; i < 600; ++i)
- write_to_buf("ABCDEFG", 7, buf);
+ buf_add(buf, "ABCDEFG", 7);
tt_int_op(4307, OP_EQ, buf_datalen(buf));
tt_int_op(2000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123));
- fetch_from_buf(tmp, 4000, buf);
- fetch_from_buf(tmp, 306, buf);
+ buf_get_bytes(buf, tmp, 4000);
+ buf_get_bytes(buf, tmp, 306);
tt_int_op(0, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+5617));
tt_int_op(383, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+6000));
@@ -593,23 +519,23 @@ test_buffers_compress_fin_at_chunk_end_impl(compress_method_t method,
sz = buf_get_default_chunk_size(buf);
msg = tor_malloc_zero(sz);
- write_to_buf(msg, 1, buf);
+ buf_add(buf, msg, 1);
tt_assert(buf->head);
/* Fill up the chunk so the compression stuff won't fit in one chunk. */
tt_uint_op(buf->head->memlen, OP_LT, sz);
headerjunk = buf->head->memlen - 7;
- write_to_buf(msg, headerjunk-1, buf);
+ buf_add(buf, msg, headerjunk-1);
tt_uint_op(buf->head->datalen, OP_EQ, headerjunk);
tt_uint_op(buf_datalen(buf), OP_EQ, headerjunk);
/* Write an empty string, with finalization on. */
compress_state = tor_compress_new(1, method, level);
- tt_int_op(write_to_buf_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
+ tt_int_op(buf_add_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
in_len = buf_datalen(buf);
contents = tor_malloc(in_len);
- tt_int_op(fetch_from_buf(contents, in_len, buf), OP_EQ, 0);
+ tt_int_op(buf_get_bytes(buf, contents, in_len), OP_EQ, 0);
if (method == NO_METHOD) {
tt_uint_op(in_len, OP_EQ, headerjunk);
@@ -652,23 +578,23 @@ test_buffers_compress_impl(compress_method_t method,
msg = tor_malloc(512);
crypto_rand(msg, 512);
- tt_int_op(write_to_buf_compress(buf, compress_state,
+ tt_int_op(buf_add_compress(buf, compress_state,
msg, 128, 0), OP_EQ, 0);
- tt_int_op(write_to_buf_compress(buf, compress_state,
+ tt_int_op(buf_add_compress(buf, compress_state,
msg+128, 128, 0), OP_EQ, 0);
- tt_int_op(write_to_buf_compress(buf, compress_state,
+ tt_int_op(buf_add_compress(buf, compress_state,
msg+256, 256, 0), OP_EQ, 0);
done = !finalize_with_nil;
- tt_int_op(write_to_buf_compress(buf, compress_state,
+ tt_int_op(buf_add_compress(buf, compress_state,
"all done", 9, done), OP_EQ, 0);
if (finalize_with_nil) {
- tt_int_op(write_to_buf_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
+ tt_int_op(buf_add_compress(buf, compress_state, "", 0, 1), OP_EQ, 0);
}
in_len = buf_datalen(buf);
contents = tor_malloc(in_len);
- tt_int_op(fetch_from_buf(contents, in_len, buf), OP_EQ, 0);
+ tt_int_op(buf_get_bytes(buf, contents, in_len), OP_EQ, 0);
tt_int_op(0, OP_EQ, tor_uncompress(&expanded, &out_len,
contents, in_len,
@@ -762,11 +688,11 @@ test_buffers_tls_read_mocked(void *arg)
buf = buf_new();
next_reply_val[0] = 1024;
- tt_int_op(128, ==, read_to_buf_tls(NULL, 128, buf));
+ tt_int_op(128, OP_EQ, buf_read_from_tls(buf, NULL, 128));
next_reply_val[0] = 5000;
next_reply_val[1] = 5000;
- tt_int_op(6000, ==, read_to_buf_tls(NULL, 6000, buf));
+ tt_int_op(6000, OP_EQ, buf_read_from_tls(buf, NULL, 6000));
done:
UNMOCK(tor_tls_read);
@@ -780,17 +706,17 @@ test_buffers_chunk_size(void *arg)
(void)arg;
const int min = 256;
const int max = 65536;
- tt_uint_op(preferred_chunk_size(3), OP_EQ, min);
- tt_uint_op(preferred_chunk_size(25), OP_EQ, min);
- tt_uint_op(preferred_chunk_size(0), OP_EQ, min);
- tt_uint_op(preferred_chunk_size(256), OP_EQ, 512);
- tt_uint_op(preferred_chunk_size(65400), OP_EQ, max);
+ tt_uint_op(buf_preferred_chunk_size(3), OP_EQ, min);
+ tt_uint_op(buf_preferred_chunk_size(25), OP_EQ, min);
+ tt_uint_op(buf_preferred_chunk_size(0), OP_EQ, min);
+ tt_uint_op(buf_preferred_chunk_size(256), OP_EQ, 512);
+ tt_uint_op(buf_preferred_chunk_size(65400), OP_EQ, max);
/* Here, we're implicitly saying that the chunk header overhead is
* between 1 and 100 bytes. 24..48 would probably be more accurate. */
- tt_uint_op(preferred_chunk_size(65536), OP_GT, 65536);
- tt_uint_op(preferred_chunk_size(65536), OP_LT, 65536+100);
- tt_uint_op(preferred_chunk_size(165536), OP_GT, 165536);
- tt_uint_op(preferred_chunk_size(165536), OP_LT, 165536+100);
+ tt_uint_op(buf_preferred_chunk_size(65536), OP_GT, 65536);
+ tt_uint_op(buf_preferred_chunk_size(65536), OP_LT, 65536+100);
+ tt_uint_op(buf_preferred_chunk_size(165536), OP_GT, 165536);
+ tt_uint_op(buf_preferred_chunk_size(165536), OP_LT, 165536+100);
done:
;
}
@@ -831,18 +757,44 @@ test_buffers_find_contentlen(void *arg)
r = buf_http_find_content_length(tmp, headerlen, &sz);
tor_free(tmp);
log_debug(LD_DIR, "%d: %s", i, escaped(results[i].headers));
- tt_int_op(r, ==, results[i].r);
- tt_int_op(sz, ==, results[i].contentlen);
+ tt_int_op(r, OP_EQ, results[i].r);
+ tt_int_op(sz, OP_EQ, results[i].contentlen);
}
done:
;
}
+static void
+test_buffer_peek_startswith(void *arg)
+{
+ (void)arg;
+ buf_t *buf;
+ buf = buf_new();
+ tt_ptr_op(buf, OP_NE, NULL);
+
+ tt_assert(buf_peek_startswith(buf, ""));
+ tt_assert(! buf_peek_startswith(buf, "X"));
+
+ buf_add(buf, "Tor", 3);
+
+ tt_assert(buf_peek_startswith(buf, ""));
+ tt_assert(buf_peek_startswith(buf, "T"));
+ tt_assert(buf_peek_startswith(buf, "To"));
+ tt_assert(buf_peek_startswith(buf, "Tor"));
+ tt_assert(! buf_peek_startswith(buf, "Top"));
+ tt_assert(! buf_peek_startswith(buf, "For"));
+ tt_assert(! buf_peek_startswith(buf, "Tork"));
+ tt_assert(! buf_peek_startswith(buf, "Torpor"));
+
+ done:
+ buf_free(buf);
+}
+
struct testcase_t buffer_tests[] = {
{ "basic", test_buffers_basic, TT_FORK, NULL, NULL },
{ "copy", test_buffer_copy, TT_FORK, NULL, NULL },
{ "pullup", test_buffer_pullup, TT_FORK, NULL, NULL },
- { "ext_or_cmd", test_buffer_ext_or_cmd, TT_FORK, NULL, NULL },
+ { "startswith", test_buffer_peek_startswith, 0, NULL, NULL },
{ "allocation_tracking", test_buffer_allocation_tracking, TT_FORK,
NULL, NULL },
{ "time_tracking", test_buffer_time_tracking, TT_FORK, NULL, NULL },
diff --git a/src/test/test_channel.c b/src/test/test_channel.c
index f5999b8e67..023c2950c9 100644
--- a/src/test/test_channel.c
+++ b/src/test/test_channel.c
@@ -86,7 +86,7 @@ channel_note_destroy_not_pending_mock(channel_t *ch,
static const char *
chan_test_describe_transport(channel_t *ch)
{
- tt_assert(ch != NULL);
+ tt_ptr_op(ch, OP_NE, NULL);
done:
return "Fake channel for unit tests";
@@ -100,7 +100,7 @@ chan_test_describe_transport(channel_t *ch)
static void
chan_test_channel_dump_statistics_mock(channel_t *chan, int severity)
{
- tt_assert(chan != NULL);
+ tt_ptr_op(chan, OP_NE, NULL);
(void)severity;
@@ -125,7 +125,7 @@ chan_test_channel_flush_from_first_active_circuit_mock(channel_t *chan,
int result = 0, c = 0;
packed_cell_t *cell = NULL;
- tt_assert(chan != NULL);
+ tt_ptr_op(chan, OP_NE, NULL);
if (test_target_cmux != NULL &&
test_target_cmux == chan->cmux) {
while (c <= max && test_cmux_cells > 0) {
@@ -154,7 +154,7 @@ chan_test_circuitmux_num_cells_mock(circuitmux_t *cmux)
{
unsigned int result = 0;
- tt_assert(cmux != NULL);
+ tt_ptr_op(cmux, OP_NE, NULL);
if (cmux != NULL) {
if (cmux == test_target_cmux) {
result = test_cmux_cells;
@@ -193,7 +193,7 @@ chan_test_cell_handler(channel_t *ch,
static void
chan_test_dumpstats(channel_t *ch, int severity)
{
- tt_assert(ch != NULL);
+ tt_ptr_op(ch, OP_NE, NULL);
(void)severity;
@@ -268,7 +268,7 @@ static const char *
chan_test_get_remote_descr(channel_t *ch, int flags)
{
tt_assert(ch);
- tt_int_op(flags & ~(GRD_FLAG_ORIGINAL | GRD_FLAG_ADDR_ONLY), ==, 0);
+ tt_int_op(flags & ~(GRD_FLAG_ORIGINAL | GRD_FLAG_ADDR_ONLY), OP_EQ, 0);
done:
return "Fake channel for unit tests; no real endpoint";
@@ -286,7 +286,7 @@ chan_test_get_overhead_estimate(channel_t *ch)
static int
chan_test_is_canonical(channel_t *ch, int req)
{
- tt_assert(ch != NULL);
+ tt_ptr_op(ch, OP_NE, NULL);
tt_assert(req == 0 || req == 1);
done:
@@ -380,7 +380,7 @@ chan_test_write_var_cell(channel_t *ch, var_cell_t *var_cell)
void
make_fake_cell(cell_t *c)
{
- tt_assert(c != NULL);
+ tt_ptr_op(c, OP_NE, NULL);
c->circ_id = 1;
c->command = CELL_RELAY;
@@ -397,7 +397,7 @@ make_fake_cell(cell_t *c)
void
make_fake_var_cell(var_cell_t *c)
{
- tt_assert(c != NULL);
+ tt_ptr_op(c, OP_NE, NULL);
c->circ_id = 1;
c->command = CELL_VERSIONS;
@@ -552,24 +552,24 @@ test_channel_dumpstats(void *arg)
channel_dumpstats(LOG_DEBUG);
/* Assert that we hit the mock */
- tt_int_op(dump_statistics_mock_matches, ==, 1);
+ tt_int_op(dump_statistics_mock_matches, OP_EQ, 1);
/* Close the channel */
channel_mark_for_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
/* Try again and hit the finished channel */
channel_dumpstats(LOG_DEBUG);
- tt_int_op(dump_statistics_mock_matches, ==, 2);
+ tt_int_op(dump_statistics_mock_matches, OP_EQ, 2);
channel_run_cleanup();
ch = NULL;
/* Now we should hit nothing */
channel_dumpstats(LOG_DEBUG);
- tt_int_op(dump_statistics_mock_matches, ==, 2);
+ tt_int_op(dump_statistics_mock_matches, OP_EQ, 2);
/* Unmock */
UNMOCK(channel_dump_statistics);
@@ -594,7 +594,7 @@ test_channel_dumpstats(void *arg)
old_count = test_cells_written;
channel_write_cell(ch, cell);
cell = NULL;
- tt_int_op(test_cells_written, ==, old_count + 1);
+ tt_int_op(test_cells_written, OP_EQ, old_count + 1);
tt_assert(ch->n_bytes_xmitted > 0);
tt_assert(ch->n_cells_xmitted > 0);
@@ -602,14 +602,15 @@ test_channel_dumpstats(void *arg)
channel_set_cell_handlers(ch,
chan_test_cell_handler,
chan_test_var_cell_handler);
- tt_ptr_op(channel_get_cell_handler(ch), ==, chan_test_cell_handler);
- tt_ptr_op(channel_get_var_cell_handler(ch), ==, chan_test_var_cell_handler);
+ tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler);
+ tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ,
+ chan_test_var_cell_handler);
cell = tor_malloc_zero(sizeof(cell_t));
make_fake_cell(cell);
old_count = test_chan_fixed_cells_recved;
channel_queue_cell(ch, cell);
tor_free(cell);
- tt_int_op(test_chan_fixed_cells_recved, ==, old_count + 1);
+ tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1);
tt_assert(ch->n_bytes_recved > 0);
tt_assert(ch->n_cells_recved > 0);
@@ -619,13 +620,13 @@ test_channel_dumpstats(void *arg)
ch->is_canonical = chan_test_is_canonical;
old_count = test_dumpstats_calls;
channel_dump_statistics(ch, LOG_DEBUG);
- tt_int_op(test_dumpstats_calls, ==, old_count + 1);
+ tt_int_op(test_dumpstats_calls, OP_EQ, old_count + 1);
/* Close the channel */
channel_mark_for_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
channel_run_cleanup();
ch = NULL;
@@ -664,21 +665,21 @@ test_channel_flush(void *arg)
make_fake_cell(cell);
channel_write_cell(ch, cell);
/* It should be queued, so assert that we didn't write it */
- tt_int_op(test_cells_written, ==, init_count);
+ tt_int_op(test_cells_written, OP_EQ, init_count);
/* Queue a var cell */
v_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
make_fake_var_cell(v_cell);
channel_write_var_cell(ch, v_cell);
/* It should be queued, so assert that we didn't write it */
- tt_int_op(test_cells_written, ==, init_count);
+ tt_int_op(test_cells_written, OP_EQ, init_count);
/* Try a packed cell now */
p_cell = packed_cell_new();
tt_assert(p_cell);
channel_write_packed_cell(ch, p_cell);
/* It should be queued, so assert that we didn't write it */
- tt_int_op(test_cells_written, ==, init_count);
+ tt_int_op(test_cells_written, OP_EQ, init_count);
/* Now allow writes through again */
test_chan_accept_cells = 1;
@@ -687,7 +688,7 @@ test_channel_flush(void *arg)
channel_flush_cells(ch);
/* All three should have gone through */
- tt_int_op(test_cells_written, ==, init_count + 3);
+ tt_int_op(test_cells_written, OP_EQ, init_count + 3);
done:
tor_free(ch);
@@ -728,9 +729,9 @@ test_channel_flushmux(void *arg)
result = channel_flush_some_cells(ch, 1);
- tt_int_op(result, ==, 1);
- tt_int_op(test_cells_written, ==, old_count + 1);
- tt_int_op(test_cmux_cells, ==, 0);
+ tt_int_op(result, OP_EQ, 1);
+ tt_int_op(test_cells_written, OP_EQ, old_count + 1);
+ tt_int_op(test_cmux_cells, OP_EQ, 0);
/* Now try it without accepting to force them into the queue */
test_chan_accept_cells = 0;
@@ -740,19 +741,19 @@ test_channel_flushmux(void *arg)
result = channel_flush_some_cells(ch, 1);
/* We should not have actually flushed any */
- tt_int_op(result, ==, 0);
- tt_int_op(test_cells_written, ==, old_count + 1);
+ tt_int_op(result, OP_EQ, 0);
+ tt_int_op(test_cells_written, OP_EQ, old_count + 1);
/* But we should have gotten to the fake cellgen loop */
- tt_int_op(test_cmux_cells, ==, 0);
+ tt_int_op(test_cmux_cells, OP_EQ, 0);
/* ...and we should have a queued cell */
q_len_after = chan_cell_queue_len(&(ch->outgoing_queue));
- tt_int_op(q_len_after, ==, q_len_before + 1);
+ tt_int_op(q_len_after, OP_EQ, q_len_before + 1);
/* Now accept cells again and drain the queue */
test_chan_accept_cells = 1;
channel_flush_cells(ch);
- tt_int_op(test_cells_written, ==, old_count + 2);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0);
+ tt_int_op(test_cells_written, OP_EQ, old_count + 2);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
test_target_cmux = NULL;
test_cmux_cells = 0;
@@ -803,16 +804,17 @@ test_channel_incoming(void *arg)
chan_test_cell_handler,
chan_test_var_cell_handler);
/* Test cell handler getters */
- tt_ptr_op(channel_get_cell_handler(ch), ==, chan_test_cell_handler);
- tt_ptr_op(channel_get_var_cell_handler(ch), ==, chan_test_var_cell_handler);
+ tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler);
+ tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ,
+ chan_test_var_cell_handler);
/* Try to register it */
channel_register(ch);
tt_assert(ch->registered);
/* Open it */
- channel_change_state(ch, CHANNEL_STATE_OPEN);
- tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN);
/* Receive a fixed cell */
cell = tor_malloc_zero(sizeof(cell_t));
@@ -820,7 +822,7 @@ test_channel_incoming(void *arg)
old_count = test_chan_fixed_cells_recved;
channel_queue_cell(ch, cell);
tor_free(cell);
- tt_int_op(test_chan_fixed_cells_recved, ==, old_count + 1);
+ tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_count + 1);
/* Receive a variable-size cell */
var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
@@ -828,13 +830,13 @@ test_channel_incoming(void *arg)
old_count = test_chan_var_cells_recved;
channel_queue_var_cell(ch, var_cell);
tor_free(cell);
- tt_int_op(test_chan_var_cells_recved, ==, old_count + 1);
+ tt_int_op(test_chan_var_cells_recved, OP_EQ, old_count + 1);
/* Close it */
channel_mark_for_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
channel_run_cleanup();
ch = NULL;
@@ -896,13 +898,13 @@ test_channel_lifecycle(void *arg)
make_fake_cell(cell);
old_count = test_cells_written;
channel_write_cell(ch1, cell);
- tt_int_op(old_count, ==, test_cells_written);
+ tt_int_op(old_count, OP_EQ, test_cells_written);
/* Move it to OPEN and flush */
- channel_change_state(ch1, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch1);
/* Queue should drain */
- tt_int_op(old_count + 1, ==, test_cells_written);
+ tt_int_op(old_count + 1, OP_EQ, test_cells_written);
/* Get another one */
ch2 = new_fake_channel();
@@ -915,41 +917,42 @@ test_channel_lifecycle(void *arg)
tt_assert(ch2->registered);
/* Check counters */
- tt_int_op(test_doesnt_want_writes_count, ==, init_doesnt_want_writes_count);
- tt_int_op(test_releases_count, ==, init_releases_count);
+ tt_int_op(test_doesnt_want_writes_count, OP_EQ,
+ init_doesnt_want_writes_count);
+ tt_int_op(test_releases_count, OP_EQ, init_releases_count);
/* Move ch1 to MAINT */
channel_change_state(ch1, CHANNEL_STATE_MAINT);
- tt_int_op(test_doesnt_want_writes_count, ==,
+ tt_int_op(test_doesnt_want_writes_count, OP_EQ,
init_doesnt_want_writes_count + 1);
- tt_int_op(test_releases_count, ==, init_releases_count);
+ tt_int_op(test_releases_count, OP_EQ, init_releases_count);
/* Move ch2 to OPEN */
- channel_change_state(ch2, CHANNEL_STATE_OPEN);
- tt_int_op(test_doesnt_want_writes_count, ==,
+ channel_change_state_open(ch2);
+ tt_int_op(test_doesnt_want_writes_count, OP_EQ,
init_doesnt_want_writes_count + 1);
- tt_int_op(test_releases_count, ==, init_releases_count);
+ tt_int_op(test_releases_count, OP_EQ, init_releases_count);
/* Move ch1 back to OPEN */
- channel_change_state(ch1, CHANNEL_STATE_OPEN);
- tt_int_op(test_doesnt_want_writes_count, ==,
+ channel_change_state_open(ch1);
+ tt_int_op(test_doesnt_want_writes_count, OP_EQ,
init_doesnt_want_writes_count + 1);
- tt_int_op(test_releases_count, ==, init_releases_count);
+ tt_int_op(test_releases_count, OP_EQ, init_releases_count);
/* Mark ch2 for close */
channel_mark_for_close(ch2);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSING);
- tt_int_op(test_doesnt_want_writes_count, ==,
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSING);
+ tt_int_op(test_doesnt_want_writes_count, OP_EQ,
init_doesnt_want_writes_count + 1);
- tt_int_op(test_releases_count, ==, init_releases_count + 1);
+ tt_int_op(test_releases_count, OP_EQ, init_releases_count + 1);
/* Shut down channels */
channel_free_all();
ch1 = ch2 = NULL;
- tt_int_op(test_doesnt_want_writes_count, ==,
+ tt_int_op(test_doesnt_want_writes_count, OP_EQ,
init_doesnt_want_writes_count + 1);
/* channel_free() calls scheduler_release_channel() */
- tt_int_op(test_releases_count, ==, init_releases_count + 4);
+ tt_int_op(test_releases_count, OP_EQ, init_releases_count + 4);
done:
free_fake_channel(ch1);
@@ -1001,11 +1004,11 @@ test_channel_lifecycle_2(void *arg)
/* Try to close it */
channel_mark_for_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
/* Finish closing it */
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
channel_run_cleanup();
ch = NULL;
@@ -1018,13 +1021,13 @@ test_channel_lifecycle_2(void *arg)
tt_assert(ch->registered);
/* Finish opening it */
- channel_change_state(ch, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
/* Error exit from lower layer */
chan_test_error(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_ERROR);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_ERROR);
channel_run_cleanup();
ch = NULL;
@@ -1037,20 +1040,20 @@ test_channel_lifecycle_2(void *arg)
tt_assert(ch->registered);
/* Finish opening it */
- channel_change_state(ch, CHANNEL_STATE_OPEN);
- tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN);
/* Go to maintenance state */
channel_change_state(ch, CHANNEL_STATE_MAINT);
- tt_int_op(ch->state, ==, CHANNEL_STATE_MAINT);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_MAINT);
/* Lower layer close */
channel_mark_for_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
/* Finish */
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
channel_run_cleanup();
ch = NULL;
@@ -1066,20 +1069,20 @@ test_channel_lifecycle_2(void *arg)
tt_assert(ch->registered);
/* Finish opening it */
- channel_change_state(ch, CHANNEL_STATE_OPEN);
- tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN);
/* Go to maintenance state */
channel_change_state(ch, CHANNEL_STATE_MAINT);
- tt_int_op(ch->state, ==, CHANNEL_STATE_MAINT);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_MAINT);
/* Lower layer close */
channel_close_from_lower_layer(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
/* Finish */
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
channel_run_cleanup();
ch = NULL;
@@ -1092,20 +1095,20 @@ test_channel_lifecycle_2(void *arg)
tt_assert(ch->registered);
/* Finish opening it */
- channel_change_state(ch, CHANNEL_STATE_OPEN);
- tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN);
/* Go to maintenance state */
channel_change_state(ch, CHANNEL_STATE_MAINT);
- tt_int_op(ch->state, ==, CHANNEL_STATE_MAINT);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_MAINT);
/* Lower layer close */
chan_test_error(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
/* Finish */
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_ERROR);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_ERROR);
channel_run_cleanup();
ch = NULL;
@@ -1142,11 +1145,11 @@ test_channel_multi(void *arg)
/* Initial queue size update */
channel_update_xmit_queue_size(ch1);
- tt_u64_op(ch1->bytes_queued_for_xmit, ==, 0);
+ tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0);
channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0);
+ tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 0);
+ tt_u64_op(global_queue_estimate, OP_EQ, 0);
/* Queue some cells, check queue estimates */
cell = tor_malloc_zero(sizeof(cell_t));
@@ -1159,10 +1162,10 @@ test_channel_multi(void *arg)
channel_update_xmit_queue_size(ch1);
channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, ==, 0);
- tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0);
+ tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0);
+ tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 0);
+ tt_u64_op(global_queue_estimate, OP_EQ, 0);
/* Stop accepting cells at lower layer */
test_chan_accept_cells = 0;
@@ -1173,18 +1176,18 @@ test_channel_multi(void *arg)
channel_write_cell(ch1, cell);
channel_update_xmit_queue_size(ch1);
- tt_u64_op(ch1->bytes_queued_for_xmit, ==, 512);
+ tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 512);
+ tt_u64_op(global_queue_estimate, OP_EQ, 512);
cell = tor_malloc_zero(sizeof(cell_t));
make_fake_cell(cell);
channel_write_cell(ch2, cell);
channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch2->bytes_queued_for_xmit, ==, 512);
+ tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 1024);
+ tt_u64_op(global_queue_estimate, OP_EQ, 1024);
/* Allow cells through again */
test_chan_accept_cells = 1;
@@ -1195,10 +1198,10 @@ test_channel_multi(void *arg)
/* Update and check queue sizes */
channel_update_xmit_queue_size(ch1);
channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, ==, 512);
- tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0);
+ tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512);
+ tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 512);
+ tt_u64_op(global_queue_estimate, OP_EQ, 512);
/* Flush chan 1 */
channel_flush_cells(ch1);
@@ -1206,10 +1209,10 @@ test_channel_multi(void *arg)
/* Update and check queue sizes */
channel_update_xmit_queue_size(ch1);
channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, ==, 0);
- tt_u64_op(ch2->bytes_queued_for_xmit, ==, 0);
+ tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 0);
+ tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 0);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 0);
+ tt_u64_op(global_queue_estimate, OP_EQ, 0);
/* Now block again */
test_chan_accept_cells = 0;
@@ -1227,10 +1230,10 @@ test_channel_multi(void *arg)
/* Check the estimates */
channel_update_xmit_queue_size(ch1);
channel_update_xmit_queue_size(ch2);
- tt_u64_op(ch1->bytes_queued_for_xmit, ==, 512);
- tt_u64_op(ch2->bytes_queued_for_xmit, ==, 512);
+ tt_u64_op(ch1->bytes_queued_for_xmit, OP_EQ, 512);
+ tt_u64_op(ch2->bytes_queued_for_xmit, OP_EQ, 512);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 1024);
+ tt_u64_op(global_queue_estimate, OP_EQ, 1024);
/* Now close channel 2; it should be subtracted from the global queue */
MOCK(scheduler_release_channel, scheduler_release_channel_mock);
@@ -1238,7 +1241,7 @@ test_channel_multi(void *arg)
UNMOCK(scheduler_release_channel);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 512);
+ tt_u64_op(global_queue_estimate, OP_EQ, 512);
/*
* Since the fake channels aren't registered, channel_free_all() can't
@@ -1249,7 +1252,7 @@ test_channel_multi(void *arg)
UNMOCK(scheduler_release_channel);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 0);
+ tt_u64_op(global_queue_estimate, OP_EQ, 0);
/* Now free everything */
MOCK(scheduler_release_channel, scheduler_release_channel_mock);
@@ -1297,7 +1300,7 @@ test_channel_queue_impossible(void *arg)
old_count = test_cells_written;
/* Assert that the queue is initially empty */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
/* Get a fresh cell and write it to the channel*/
cell = tor_malloc_zero(sizeof(cell_t));
@@ -1306,11 +1309,11 @@ test_channel_queue_impossible(void *arg)
channel_write_cell(ch, cell);
/* Now it should be queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
tt_assert(q);
if (q) {
- tt_int_op(q->type, ==, CELL_QUEUE_FIXED);
+ tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED);
tt_assert((uintptr_t)q->u.fixed.cell == cellintptr);
}
/* Do perverse things to it */
@@ -1322,9 +1325,9 @@ test_channel_queue_impossible(void *arg)
* gets thrown away properly.
*/
test_chan_accept_cells = 1;
- channel_change_state(ch, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
/* Same thing but for a var_cell */
@@ -1336,11 +1339,11 @@ test_channel_queue_impossible(void *arg)
channel_write_var_cell(ch, var_cell);
/* Check that it's queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
tt_assert(q);
if (q) {
- tt_int_op(q->type, ==, CELL_QUEUE_VAR);
+ tt_int_op(q->type, OP_EQ, CELL_QUEUE_VAR);
tt_assert((uintptr_t)q->u.var.var_cell == cellintptr);
}
@@ -1350,9 +1353,9 @@ test_channel_queue_impossible(void *arg)
/* Let it drain and check that the bad entry is discarded */
test_chan_accept_cells = 1;
- channel_change_state(ch, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
/* Same thing with a packed_cell */
@@ -1364,11 +1367,11 @@ test_channel_queue_impossible(void *arg)
channel_write_packed_cell(ch, packed_cell);
/* Check that it's queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
tt_assert(q);
if (q) {
- tt_int_op(q->type, ==, CELL_QUEUE_PACKED);
+ tt_int_op(q->type, OP_EQ, CELL_QUEUE_PACKED);
tt_assert((uintptr_t)q->u.packed.packed_cell == cellintptr);
}
@@ -1378,9 +1381,9 @@ test_channel_queue_impossible(void *arg)
/* Let it drain and check that the bad entry is discarded */
test_chan_accept_cells = 1;
- channel_change_state(ch, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
/* Unknown cell type case */
test_chan_accept_cells = 0;
@@ -1391,11 +1394,11 @@ test_channel_queue_impossible(void *arg)
channel_write_cell(ch, cell);
/* Check that it's queued */
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 1);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 1);
q = TOR_SIMPLEQ_FIRST(&(ch->outgoing_queue));
tt_assert(q);
if (q) {
- tt_int_op(q->type, ==, CELL_QUEUE_FIXED);
+ tt_int_op(q->type, OP_EQ, CELL_QUEUE_FIXED);
tt_assert((uintptr_t)q->u.fixed.cell == cellintptr);
}
/* Clobber it, including the queue entry type */
@@ -1406,11 +1409,11 @@ test_channel_queue_impossible(void *arg)
/* Let it drain and check that the bad entry is discarded */
test_chan_accept_cells = 1;
tor_capture_bugs_(1);
- channel_change_state(ch, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
tt_assert(test_cells_written == old_count);
- tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0);
+ tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), OP_EQ, 0);
- tt_int_op(smartlist_len(tor_get_captured_bug_log_()), ==, 1);
+ tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1);
tor_end_capture_bugs_();
done:
@@ -1455,16 +1458,16 @@ test_channel_queue_incoming(void *arg)
ch->cmux = circuitmux_alloc();
/* Test cell handler getters */
- tt_ptr_op(channel_get_cell_handler(ch), ==, NULL);
- tt_ptr_op(channel_get_var_cell_handler(ch), ==, NULL);
+ tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, NULL);
+ tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ, NULL);
/* Try to register it */
channel_register(ch);
tt_assert(ch->registered);
/* Open it */
- channel_change_state(ch, CHANNEL_STATE_OPEN);
- tt_int_op(ch->state, ==, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_OPEN);
/* Assert that the incoming queue is empty */
tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue)));
@@ -1475,7 +1478,7 @@ test_channel_queue_incoming(void *arg)
channel_queue_cell(ch, cell);
/* Assert that the incoming queue has one entry */
- tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), ==, 1);
+ tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 1);
/* Queue an incoming var cell */
var_cell = tor_malloc_zero(sizeof(var_cell_t) + CELL_PAYLOAD_SIZE);
@@ -1483,7 +1486,7 @@ test_channel_queue_incoming(void *arg)
channel_queue_var_cell(ch, var_cell);
/* Assert that the incoming queue has two entries */
- tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), ==, 2);
+ tt_int_op(chan_cell_queue_len(&(ch->incoming_queue)), OP_EQ, 2);
/*
* Install cell handlers; this will drain the queue, so save the old
@@ -1494,12 +1497,13 @@ test_channel_queue_incoming(void *arg)
channel_set_cell_handlers(ch,
chan_test_cell_handler,
chan_test_var_cell_handler);
- tt_ptr_op(channel_get_cell_handler(ch), ==, chan_test_cell_handler);
- tt_ptr_op(channel_get_var_cell_handler(ch), ==, chan_test_var_cell_handler);
+ tt_ptr_op(channel_get_cell_handler(ch), OP_EQ, chan_test_cell_handler);
+ tt_ptr_op(channel_get_var_cell_handler(ch), OP_EQ,
+ chan_test_var_cell_handler);
/* Assert cells were received */
- tt_int_op(test_chan_fixed_cells_recved, ==, old_fixed_count + 1);
- tt_int_op(test_chan_var_cells_recved, ==, old_var_count + 1);
+ tt_int_op(test_chan_fixed_cells_recved, OP_EQ, old_fixed_count + 1);
+ tt_int_op(test_chan_var_cells_recved, OP_EQ, old_var_count + 1);
/*
* Assert that the pointers are different from the cells we allocated;
@@ -1508,17 +1512,17 @@ test_channel_queue_incoming(void *arg)
* delivery. These pointers will have already been freed by the time
* we get here, so don't dereference them.
*/
- tt_ptr_op(test_chan_last_seen_fixed_cell_ptr, !=, cell);
- tt_ptr_op(test_chan_last_seen_var_cell_ptr, !=, var_cell);
+ tt_ptr_op(test_chan_last_seen_fixed_cell_ptr, OP_NE, cell);
+ tt_ptr_op(test_chan_last_seen_var_cell_ptr, OP_NE, var_cell);
/* Assert queue is now empty */
tt_assert(TOR_SIMPLEQ_EMPTY(&(ch->incoming_queue)));
/* Close it; this contains an assertion that the incoming queue is empty */
channel_mark_for_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSING);
chan_test_finish_close(ch);
- tt_int_op(ch->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch->state, OP_EQ, CHANNEL_STATE_CLOSED);
channel_run_cleanup();
ch = NULL;
@@ -1548,14 +1552,14 @@ test_channel_queue_size(void *arg)
/* Initial queue size update */
channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, ==, 0);
+ tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 0);
+ tt_u64_op(global_queue_estimate, OP_EQ, 0);
/* Test the call-through to our fake lower layer */
n = channel_num_cells_writeable(ch);
/* chan_test_num_cells_writeable() always returns 32 */
- tt_int_op(n, ==, 32);
+ tt_int_op(n, OP_EQ, 32);
/*
* Now we queue some cells and check that channel_num_cells_writeable()
@@ -1574,72 +1578,73 @@ test_channel_queue_size(void *arg)
old_count = test_cells_written;
channel_write_cell(ch, cell);
/* Assert that it got queued, not written through, correctly */
- tt_int_op(test_cells_written, ==, old_count);
+ tt_int_op(test_cells_written, OP_EQ, old_count);
/* Now check chan_test_num_cells_writeable() again */
n = channel_num_cells_writeable(ch);
- tt_int_op(n, ==, 0); /* Should return 0 since we're in CHANNEL_STATE_MAINT */
+ /* Should return 0 since we're in CHANNEL_STATE_MAINT */
+ tt_int_op(n, OP_EQ, 0);
/* Update queue size estimates */
channel_update_xmit_queue_size(ch);
/* One cell, times an overhead factor of 1.0 */
- tt_u64_op(ch->bytes_queued_for_xmit, ==, 512);
+ tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
/* Try a different overhead factor */
test_overhead_estimate = 0.5;
/* This one should be ignored since it's below 1.0 */
channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, ==, 512);
+ tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
/* Now try a larger one */
test_overhead_estimate = 2.0;
channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, ==, 1024);
+ tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 1024);
/* Go back to 1.0 */
test_overhead_estimate = 1.0;
channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, ==, 512);
+ tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
/* Check the global estimate too */
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 512);
+ tt_u64_op(global_queue_estimate, OP_EQ, 512);
/* Go to open */
old_count = test_cells_written;
- channel_change_state(ch, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
/*
* It should try to write, but we aren't accepting cells right now, so
* it'll requeue
*/
- tt_int_op(test_cells_written, ==, old_count);
+ tt_int_op(test_cells_written, OP_EQ, old_count);
/* Check the queue size again */
channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, ==, 512);
+ tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 512);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 512);
+ tt_u64_op(global_queue_estimate, OP_EQ, 512);
/*
* Now the cell is in the queue, and we're open, so we should get 31
* writeable cells.
*/
n = channel_num_cells_writeable(ch);
- tt_int_op(n, ==, 31);
+ tt_int_op(n, OP_EQ, 31);
/* Accept cells again */
test_chan_accept_cells = 1;
/* ...and re-process the queue */
old_count = test_cells_written;
channel_flush_cells(ch);
- tt_int_op(test_cells_written, ==, old_count + 1);
+ tt_int_op(test_cells_written, OP_EQ, old_count + 1);
/* Should have 32 writeable now */
n = channel_num_cells_writeable(ch);
- tt_int_op(n, ==, 32);
+ tt_int_op(n, OP_EQ, 32);
/* Should have queue size estimate of zero */
channel_update_xmit_queue_size(ch);
- tt_u64_op(ch->bytes_queued_for_xmit, ==, 0);
+ tt_u64_op(ch->bytes_queued_for_xmit, OP_EQ, 0);
global_queue_estimate = channel_get_global_queue_estimate();
- tt_u64_op(global_queue_estimate, ==, 0);
+ tt_u64_op(global_queue_estimate, OP_EQ, 0);
/* Okay, now we're done with this one */
MOCK(scheduler_release_channel, scheduler_release_channel_mock);
@@ -1706,7 +1711,7 @@ test_channel_write(void *arg)
* gets drained from the queue.
*/
test_chan_accept_cells = 1;
- channel_change_state(ch, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch);
tt_assert(test_cells_written == old_count + 1);
/*
@@ -1780,7 +1785,7 @@ test_channel_id_map(void *arg)
ed25519_public_key_t ed_zero;
memset(&ed_zero, 0, sizeof(ed_zero));
- tt_assert(sizeof(rsa_id[0]) == DIGEST_LEN); // Do I remember C?
+ tt_int_op(DIGEST_LEN, OP_EQ, sizeof(rsa_id[0])); // Do I remember C?
for (i = 0; i < N_CHAN; ++i) {
crypto_rand(rsa_id[i], DIGEST_LEN);
@@ -1822,7 +1827,7 @@ test_channel_id_map(void *arg)
ch = channel_next_with_rsa_identity(ch);
tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]);
ch = channel_next_with_rsa_identity(ch);
- tt_assert(ch == NULL);
+ tt_ptr_op(ch, OP_EQ, NULL);
/* As above, but with zero Ed25519 ID (meaning "any ID") */
tt_ptr_op(chan[0], OP_EQ,
@@ -1838,7 +1843,7 @@ test_channel_id_map(void *arg)
ch = channel_next_with_rsa_identity(ch);
tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]);
ch = channel_next_with_rsa_identity(ch);
- tt_assert(ch == NULL);
+ tt_ptr_op(ch, OP_EQ, NULL);
/* Lookup nonexistent RSA identity */
tt_ptr_op(NULL, OP_EQ,
diff --git a/src/test/test_channelpadding.c b/src/test/test_channelpadding.c
index d54c9cc52c..d5713688a0 100644
--- a/src/test/test_channelpadding.c
+++ b/src/test/test_channelpadding.c
@@ -193,7 +193,8 @@ static void
setup_mock_network(void)
{
routerstatus_t *relay;
- connection_array = smartlist_new();
+ if (!connection_array)
+ connection_array = smartlist_new();
relay1_relay2 = (channel_t*)new_fake_channeltls(2);
relay1_relay2->write_cell = mock_channel_write_cell_relay1;
@@ -280,7 +281,8 @@ test_channelpadding_timers(void *arg)
tor_libevent_postfork();
- connection_array = smartlist_new();
+ if (!connection_array)
+ connection_array = smartlist_new();
monotime_init();
monotime_enable_test_mocking();
@@ -570,7 +572,8 @@ test_channelpadding_consensus(void *arg)
monotime_coarse_set_mock_time_nsec(1);
timers_initialize();
- connection_array = smartlist_new();
+ if (!connection_array)
+ connection_array = smartlist_new();
chan = (channel_t*)new_fake_channeltls(0);
channel_timestamp_active(chan);
@@ -928,7 +931,8 @@ test_channelpadding_decide_to_pad_channel(void *arg)
*/
channel_t *chan;
int64_t new_time;
- connection_array = smartlist_new();
+ if (!connection_array)
+ connection_array = smartlist_new();
(void)arg;
tor_libevent_postfork();
diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c
index 96c5eba9a5..94f1893cae 100644
--- a/src/test/test_channeltls.c
+++ b/src/test/test_channeltls.c
@@ -72,7 +72,7 @@ test_channeltls_create(void *arg)
/* Try connecting */
ch = channel_tls_connect(&test_addr, 567, test_digest, NULL);
- tt_assert(ch != NULL);
+ tt_ptr_op(ch, OP_NE, NULL);
done:
if (ch) {
@@ -121,7 +121,7 @@ test_channeltls_num_bytes_queued(void *arg)
/* Try connecting */
ch = channel_tls_connect(&test_addr, 567, test_digest, NULL);
- tt_assert(ch != NULL);
+ tt_ptr_op(ch, OP_NE, NULL);
/*
* Next, we have to test ch->num_bytes_queued, which is
@@ -132,7 +132,7 @@ test_channeltls_num_bytes_queued(void *arg)
tt_assert(ch->num_bytes_queued != NULL);
tlschan = BASE_CHAN_TO_TLS(ch);
- tt_assert(tlschan != NULL);
+ tt_ptr_op(tlschan, OP_NE, NULL);
if (TO_CONN(tlschan->conn)->outbuf == NULL) {
/* We need an outbuf to make sure buf_datalen() gets called */
fake_outbuf = 1;
@@ -142,7 +142,7 @@ test_channeltls_num_bytes_queued(void *arg)
tlschan_buf_datalen_mock_size = 1024;
MOCK(buf_datalen, tlschan_buf_datalen_mock);
len = ch->num_bytes_queued(ch);
- tt_int_op(len, ==, tlschan_buf_datalen_mock_size);
+ tt_int_op(len, OP_EQ, tlschan_buf_datalen_mock_size);
/*
* We also cover num_cells_writeable here; since wide_circ_ids = 0 on
* the fake tlschans, cell_network_size returns 512, and so with
@@ -151,7 +151,7 @@ test_channeltls_num_bytes_queued(void *arg)
* - 2 cells.
*/
n = ch->num_cells_writeable(ch);
- tt_int_op(n, ==, CEIL_DIV(OR_CONN_HIGHWATER, 512) - 2);
+ tt_int_op(n, OP_EQ, CEIL_DIV(OR_CONN_HIGHWATER, 512) - 2);
UNMOCK(buf_datalen);
tlschan_buf_datalen_mock_target = NULL;
tlschan_buf_datalen_mock_size = 0;
@@ -206,11 +206,11 @@ test_channeltls_overhead_estimate(void *arg)
/* Try connecting */
ch = channel_tls_connect(&test_addr, 567, test_digest, NULL);
- tt_assert(ch != NULL);
+ tt_ptr_op(ch, OP_NE, NULL);
/* First case: silly low ratios should get clamped to 1.0 */
tlschan = BASE_CHAN_TO_TLS(ch);
- tt_assert(tlschan != NULL);
+ tt_ptr_op(tlschan, OP_NE, NULL);
tlschan->conn->bytes_xmitted = 128;
tlschan->conn->bytes_xmitted_by_tls = 64;
r = ch->get_overhead_estimate(ch);
@@ -273,10 +273,10 @@ tlschan_connection_or_connect_mock(const tor_addr_t *addr,
or_connection_t *result = NULL;
(void) ed_id; // XXXX Not yet used.
- tt_assert(addr != NULL);
- tt_assert(port != 0);
- tt_assert(digest != NULL);
- tt_assert(tlschan != NULL);
+ tt_ptr_op(addr, OP_NE, NULL);
+ tt_uint_op(port, OP_NE, 0);
+ tt_ptr_op(digest, OP_NE, NULL);
+ tt_ptr_op(tlschan, OP_NE, NULL);
/* Make a fake orconn */
result = tor_malloc_zero(sizeof(*result));
@@ -301,11 +301,11 @@ tlschan_fake_close_method(channel_t *chan)
{
channel_tls_t *tlschan = NULL;
- tt_assert(chan != NULL);
- tt_int_op(chan->magic, ==, TLS_CHAN_MAGIC);
+ tt_ptr_op(chan, OP_NE, NULL);
+ tt_int_op(chan->magic, OP_EQ, TLS_CHAN_MAGIC);
tlschan = BASE_CHAN_TO_TLS(chan);
- tt_assert(tlschan != NULL);
+ tt_ptr_op(tlschan, OP_NE, NULL);
/* Just free the fake orconn */
tor_free(tlschan->conn->base_.address);
@@ -320,7 +320,7 @@ tlschan_fake_close_method(channel_t *chan)
static int
tlschan_is_local_addr_mock(const tor_addr_t *addr)
{
- tt_assert(addr != NULL);
+ tt_ptr_op(addr, OP_NE, NULL);
done:
return tlschan_local;
diff --git a/src/test/test_checkdir.c b/src/test/test_checkdir.c
index 38f3360b61..bf6a8376b3 100644
--- a/src/test/test_checkdir.c
+++ b/src/test/test_checkdir.c
@@ -20,7 +20,7 @@
#define umask(mask) ((void)0)
#else
#define tt_int_op_nowin(a,op,b) tt_int_op((a),op,(b))
-#endif
+#endif /* defined(_WIN32) */
/** Run unit tests for private dir permission enforcement logic. */
static void
diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c
index 344ab27921..f622704ec5 100644
--- a/src/test/test_circuitlist.c
+++ b/src/test/test_circuitlist.c
@@ -180,6 +180,7 @@ static void
test_rend_token_maps(void *arg)
{
or_circuit_t *c1, *c2, *c3, *c4;
+ origin_circuit_t *c5;
const uint8_t tok1[REND_TOKEN_LEN] = "The cat can't tell y";
const uint8_t tok2[REND_TOKEN_LEN] = "ou its name, and it ";
const uint8_t tok3[REND_TOKEN_LEN] = "doesn't really care.";
@@ -194,6 +195,7 @@ test_rend_token_maps(void *arg)
c2 = or_circuit_new(0, NULL);
c3 = or_circuit_new(0, NULL);
c4 = or_circuit_new(0, NULL);
+ c5 = origin_circuit_new();
/* Make sure we really filled up the tok* variables */
tt_int_op(tok1[REND_TOKEN_LEN-1], OP_EQ, 'y');
@@ -264,6 +266,13 @@ test_rend_token_maps(void *arg)
tt_ptr_op(TO_CIRCUIT(c4)->hs_token, OP_EQ, NULL);
tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2_relay_side(tok3));
+ /* Now let's do a check for the client-side rend circuitmap */
+ c5->base_.purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
+ hs_circuitmap_register_rend_circ_client_side(c5, tok1);
+
+ tt_ptr_op(c5, OP_EQ, hs_circuitmap_get_rend_circ_client_side(tok1));
+ tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ_client_side(tok2));
+
done:
if (c1)
circuit_free(TO_CIRCUIT(c1));
@@ -273,6 +282,8 @@ test_rend_token_maps(void *arg)
circuit_free(TO_CIRCUIT(c3));
if (c4)
circuit_free(TO_CIRCUIT(c4));
+ if (c5)
+ circuit_free(TO_CIRCUIT(c5));
}
static void
diff --git a/src/test/test_circuitmux.c b/src/test/test_circuitmux.c
index 99abdbc685..854f725054 100644
--- a/src/test/test_circuitmux.c
+++ b/src/test/test_circuitmux.c
@@ -49,8 +49,8 @@ test_cmux_destroy_cell_queue(void *arg)
ch->wide_circ_ids = 1;
circ = circuitmux_get_first_active_circuit(cmux, &cq);
- tt_assert(!circ);
- tt_assert(!cq);
+ tt_ptr_op(circ, OP_EQ, NULL);
+ tt_ptr_op(cq, OP_EQ, NULL);
circuitmux_append_destroy_cell(ch, cmux, 100, 10);
circuitmux_append_destroy_cell(ch, cmux, 190, 6);
@@ -59,7 +59,7 @@ test_cmux_destroy_cell_queue(void *arg)
tt_int_op(circuitmux_num_cells(cmux), OP_EQ, 3);
circ = circuitmux_get_first_active_circuit(cmux, &cq);
- tt_assert(!circ);
+ tt_ptr_op(circ, OP_EQ, NULL);
tt_assert(cq);
tt_int_op(cq->n, OP_EQ, 3);
diff --git a/src/test/test_circuituse.c b/src/test/test_circuituse.c
index 5cc9fe571e..df1b43807f 100644
--- a/src/test/test_circuituse.c
+++ b/src/test/test_circuituse.c
@@ -22,7 +22,7 @@ test_circuit_is_available_for_use_ret_false_when_marked_for_close(void *arg)
circuit_t *circ = tor_malloc(sizeof(circuit_t));
circ->marked_for_close = 1;
- tt_int_op(0, ==, circuit_is_available_for_use(circ));
+ tt_int_op(0, OP_EQ, circuit_is_available_for_use(circ));
done:
tor_free(circ);
@@ -36,7 +36,7 @@ test_circuit_is_available_for_use_ret_false_when_timestamp_dirty(void *arg)
circuit_t *circ = tor_malloc(sizeof(circuit_t));
circ->timestamp_dirty = 1;
- tt_int_op(0, ==, circuit_is_available_for_use(circ));
+ tt_int_op(0, OP_EQ, circuit_is_available_for_use(circ));
done:
tor_free(circ);
@@ -50,7 +50,7 @@ test_circuit_is_available_for_use_ret_false_for_non_general_purpose(void *arg)
circuit_t *circ = tor_malloc(sizeof(circuit_t));
circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING;
- tt_int_op(0, ==, circuit_is_available_for_use(circ));
+ tt_int_op(0, OP_EQ, circuit_is_available_for_use(circ));
done:
tor_free(circ);
@@ -64,7 +64,7 @@ test_circuit_is_available_for_use_ret_false_for_non_general_origin(void *arg)
circuit_t *circ = tor_malloc(sizeof(circuit_t));
circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT;
- tt_int_op(0, ==, circuit_is_available_for_use(circ));
+ tt_int_op(0, OP_EQ, circuit_is_available_for_use(circ));
done:
tor_free(circ);
@@ -78,7 +78,7 @@ test_circuit_is_available_for_use_ret_false_for_non_origin_purpose(void *arg)
circuit_t *circ = tor_malloc(sizeof(circuit_t));
circ->purpose = CIRCUIT_PURPOSE_OR;
- tt_int_op(0, ==, circuit_is_available_for_use(circ));
+ tt_int_op(0, OP_EQ, circuit_is_available_for_use(circ));
done:
tor_free(circ);
@@ -92,7 +92,7 @@ test_circuit_is_available_for_use_ret_false_unusable_for_new_conns(void *arg)
circuit_t *circ = dummy_origin_circuit_new(30);
mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ));
- tt_int_op(0, ==, circuit_is_available_for_use(circ));
+ tt_int_op(0, OP_EQ, circuit_is_available_for_use(circ));
done:
circuit_free(circ);
@@ -108,7 +108,7 @@ test_circuit_is_available_for_use_returns_false_for_onehop_tunnel(void *arg)
oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
oc->build_state->onehop_tunnel = 1;
- tt_int_op(0, ==, circuit_is_available_for_use(circ));
+ tt_int_op(0, OP_EQ, circuit_is_available_for_use(circ));
done:
circuit_free(circ);
@@ -124,7 +124,7 @@ test_circuit_is_available_for_use_returns_true_for_clean_circuit(void *arg)
oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
oc->build_state->onehop_tunnel = 0;
- tt_int_op(1, ==, circuit_is_available_for_use(circ));
+ tt_int_op(1, OP_EQ, circuit_is_available_for_use(circ));
done:
circuit_free(circ);
@@ -165,7 +165,8 @@ test_needs_exit_circuits_ret_false_for_predicted_ports_and_path(void *arg)
int needs_capacity = 0;
time_t now = time(NULL);
- tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity));
+ tt_int_op(0, OP_EQ,
+ needs_exit_circuits(now, &needs_uptime, &needs_capacity));
done:
UNMOCK(circuit_all_predicted_ports_handled);
@@ -183,7 +184,8 @@ test_needs_exit_circuits_ret_false_for_non_exit_consensus_path(void *arg)
MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path);
time_t now = time(NULL);
- tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity));
+ tt_int_op(0, OP_EQ,
+ needs_exit_circuits(now, &needs_uptime, &needs_capacity));
done:
UNMOCK(circuit_all_predicted_ports_handled);
@@ -202,7 +204,8 @@ test_needs_exit_circuits_ret_true_for_predicted_ports_and_path(void *arg)
MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path);
time_t now = time(NULL);
- tt_int_op(1, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity));
+ tt_int_op(1, OP_EQ,
+ needs_exit_circuits(now, &needs_uptime, &needs_capacity));
done:
UNMOCK(circuit_all_predicted_ports_handled);
@@ -214,7 +217,7 @@ test_needs_circuits_for_build_ret_false_consensus_path_unknown(void *arg)
{
(void)arg;
MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path);
- tt_int_op(0, ==, needs_circuits_for_build(0));
+ tt_int_op(0, OP_EQ, needs_circuits_for_build(0));
done: ;
}
@@ -223,7 +226,7 @@ test_needs_circuits_for_build_ret_false_if_num_less_than_max(void *arg)
{
(void)arg;
MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path);
- tt_int_op(0, ==, needs_circuits_for_build(13));
+ tt_int_op(0, OP_EQ, needs_circuits_for_build(13));
done:
UNMOCK(router_have_consensus_path);
}
@@ -233,7 +236,7 @@ test_needs_circuits_for_build_returns_true_when_more_are_needed(void *arg)
{
(void)arg;
MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path);
- tt_int_op(1, ==, needs_circuits_for_build(0));
+ tt_int_op(1, OP_EQ, needs_circuits_for_build(0));
done:
UNMOCK(router_have_consensus_path);
}
diff --git a/src/test/test_config.c b/src/test/test_config.c
index beaab00f73..e7380c1d14 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -279,7 +279,7 @@ test_config_check_or_create_data_subdir(void *arg)
tt_assert(!is_private_dir(subpath));
tt_assert(!check_or_create_data_subdir(subdir));
tt_assert(is_private_dir(subpath));
-#endif
+#endif /* !defined (_WIN32) */
done:
rmdir(subpath);
@@ -368,12 +368,12 @@ good_bridge_line_test(const char *string, const char *test_addrport,
/* If we were asked to validate a digest, but we did not get a
digest after parsing, we failed. */
if (test_digest && tor_digest_is_zero(bridge_line->digest))
- tt_assert(0);
+ tt_abort();
/* If we were not asked to validate a digest, and we got a digest
after parsing, we failed again. */
if (!test_digest && !tor_digest_is_zero(bridge_line->digest))
- tt_assert(0);
+ tt_abort();
/* If we were asked to validate a digest, and we got a digest after
parsing, make sure it's correct. */
@@ -387,17 +387,17 @@ good_bridge_line_test(const char *string, const char *test_addrport,
/* If we were asked to validate a transport name, make sure tha it
matches with the transport name that was parsed. */
if (test_transport && !bridge_line->transport_name)
- tt_assert(0);
+ tt_abort();
if (!test_transport && bridge_line->transport_name)
- tt_assert(0);
+ tt_abort();
if (test_transport)
tt_str_op(test_transport,OP_EQ, bridge_line->transport_name);
/* Validate the SOCKS argument smartlist. */
if (test_socks_args && !bridge_line->socks_args)
- tt_assert(0);
+ tt_abort();
if (!test_socks_args && bridge_line->socks_args)
- tt_assert(0);
+ tt_abort();
if (test_socks_args)
tt_assert(smartlist_strings_eq(test_socks_args,
bridge_line->socks_args));
@@ -415,7 +415,7 @@ bad_bridge_line_test(const char *string)
bridge_line_t *bridge_line = parse_bridge_line(string);
if (bridge_line)
TT_FAIL(("%s was supposed to fail, but it didn't.", string));
- tt_assert(!bridge_line);
+ tt_ptr_op(bridge_line, OP_EQ, NULL);
done:
bridge_line_free(bridge_line);
@@ -523,18 +523,18 @@ test_config_parse_transport_options_line(void *arg)
{ /* too small line */
options_sl = get_options_from_transport_options_line("valley", NULL);
- tt_assert(!options_sl);
+ tt_ptr_op(options_sl, OP_EQ, NULL);
}
{ /* no k=v values */
options_sl = get_options_from_transport_options_line("hit it!", NULL);
- tt_assert(!options_sl);
+ tt_ptr_op(options_sl, OP_EQ, NULL);
}
{ /* correct line, but wrong transport specified */
options_sl =
get_options_from_transport_options_line("trebuchet k=v", "rook");
- tt_assert(!options_sl);
+ tt_ptr_op(options_sl, OP_EQ, NULL);
}
{ /* correct -- no transport specified */
@@ -658,84 +658,85 @@ test_config_parse_transport_plugin_line(void *arg)
/* Bad transport lines - too short */
r = parse_transport_line(options, "bad", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options, "bad", 1, 1);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options, "bad bad", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options, "bad bad", 1, 1);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
/* Test transport list parsing */
r = parse_transport_line(options,
"transport_1 exec /usr/bin/fake-transport", 1, 0);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
r = parse_transport_line(options,
"transport_1 exec /usr/bin/fake-transport", 1, 1);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
r = parse_transport_line(options,
"transport_1,transport_2 exec /usr/bin/fake-transport", 1, 0);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
r = parse_transport_line(options,
"transport_1,transport_2 exec /usr/bin/fake-transport", 1, 1);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
/* Bad transport identifiers */
r = parse_transport_line(options,
"transport_* exec /usr/bin/fake-transport", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options,
"transport_* exec /usr/bin/fake-transport", 1, 1);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
/* Check SOCKS cases for client transport */
r = parse_transport_line(options,
"transport_1 socks4 1.2.3.4:567", 1, 0);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
r = parse_transport_line(options,
"transport_1 socks5 1.2.3.4:567", 1, 0);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
/* Proxy case for server transport */
r = parse_transport_line(options,
"transport_1 proxy 1.2.3.4:567", 1, 1);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
/* Multiple-transport error exit */
r = parse_transport_line(options,
"transport_1,transport_2 socks5 1.2.3.4:567", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options,
"transport_1,transport_2 proxy 1.2.3.4:567", 1, 1);
+ tt_int_op(r, OP_LT, 0);
/* No port error exit */
r = parse_transport_line(options,
"transport_1 socks5 1.2.3.4", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options,
"transport_1 proxy 1.2.3.4", 1, 1);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
/* Unparsable address error exit */
r = parse_transport_line(options,
"transport_1 socks5 1.2.3:6x7", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options,
"transport_1 proxy 1.2.3:6x7", 1, 1);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
/* "Strange {Client|Server}TransportPlugin field" error exit */
r = parse_transport_line(options,
"transport_1 foo bar", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options,
"transport_1 foo bar", 1, 1);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
/* No sandbox mode error exit */
tmp = options->Sandbox;
options->Sandbox = 1;
r = parse_transport_line(options,
"transport_1 exec /usr/bin/fake-transport", 1, 0);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
r = parse_transport_line(options,
"transport_1 exec /usr/bin/fake-transport", 1, 1);
- tt_assert(r < 0);
+ tt_int_op(r, OP_LT, 0);
options->Sandbox = tmp;
/*
@@ -747,7 +748,7 @@ test_config_parse_transport_plugin_line(void *arg)
pt_kickstart_proxy_mock_call_count;
r = parse_transport_line(options,
"transport_1 exec /usr/bin/fake-transport", 0, 1);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_assert(pt_kickstart_proxy_mock_call_count ==
old_pt_kickstart_proxy_mock_call_count + 1);
UNMOCK(pt_kickstart_proxy);
@@ -755,7 +756,7 @@ test_config_parse_transport_plugin_line(void *arg)
/* This one hits a log line in the !validate_only case only */
r = parse_transport_line(options,
"transport_1 proxy 1.2.3.4:567", 0, 1);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
/* Check mocked client transport cases */
MOCK(pt_kickstart_proxy, pt_kickstart_proxy_mock);
@@ -773,7 +774,7 @@ test_config_parse_transport_plugin_line(void *arg)
r = parse_transport_line(options,
"transport_1 exec /usr/bin/fake-transport", 0, 0);
/* Should have succeeded */
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
/* transport_is_needed() should have been called */
tt_assert(transport_is_needed_mock_call_count ==
old_transport_is_needed_mock_call_count + 1);
@@ -797,7 +798,7 @@ test_config_parse_transport_plugin_line(void *arg)
r = parse_transport_line(options,
"transport_1 exec /usr/bin/fake-transport", 0, 0);
/* Should have succeeded */
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
/*
* transport_is_needed() and pt_kickstart_proxy() should have been
* called.
@@ -821,7 +822,7 @@ test_config_parse_transport_plugin_line(void *arg)
r = parse_transport_line(options,
"transport_1 socks5 1.2.3.4:567", 0, 0);
/* Should have succeeded */
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
/*
* transport_is_needed() and transport_add_from_config() should have
* been called.
@@ -1139,7 +1140,7 @@ test_config_resolve_my_address(void *arg)
&method_used,&hostname_out);
tt_want(retval == 0);
- tt_want_str_op(method_used,==,"CONFIGURED");
+ tt_want_str_op(method_used,OP_EQ,"CONFIGURED");
tt_want(hostname_out == NULL);
tt_assert(resolved_addr == 0x80348069);
@@ -1164,8 +1165,8 @@ test_config_resolve_my_address(void *arg)
tt_want(retval == 0);
tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1);
- tt_want_str_op(method_used,==,"RESOLVED");
- tt_want_str_op(hostname_out,==,"www.torproject.org");
+ tt_want_str_op(method_used,OP_EQ,"RESOLVED");
+ tt_want_str_op(hostname_out,OP_EQ,"www.torproject.org");
tt_assert(resolved_addr == 0x01010101);
UNMOCK(tor_lookup_hostname);
@@ -1196,8 +1197,8 @@ test_config_resolve_my_address(void *arg)
tt_want(retval == 0);
tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1);
- tt_want_str_op(method_used,==,"GETHOSTNAME");
- tt_want_str_op(hostname_out,==,"onionrouter!");
+ tt_want_str_op(method_used,OP_EQ,"GETHOSTNAME");
+ tt_want_str_op(hostname_out,OP_EQ,"onionrouter!");
tt_assert(resolved_addr == 0x01010101);
UNMOCK(tor_gethostname);
@@ -1219,7 +1220,7 @@ test_config_resolve_my_address(void *arg)
&method_used,&hostname_out);
tt_want(resolved_addr == 0);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
tor_free(options->Address);
tor_free(hostname_out);
@@ -1241,7 +1242,7 @@ test_config_resolve_my_address(void *arg)
&method_used,&hostname_out);
tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
UNMOCK(tor_lookup_hostname);
@@ -1262,7 +1263,7 @@ test_config_resolve_my_address(void *arg)
&method_used,&hostname_out);
tt_want(n_gethostname_failure == prev_n_gethostname_failure + 1);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
UNMOCK(tor_gethostname);
tor_free(hostname_out);
@@ -1285,11 +1286,11 @@ test_config_resolve_my_address(void *arg)
&method_used,&hostname_out);
tt_want(retval == 0);
- tt_want_int_op(n_gethostname_replacement, ==,
+ tt_want_int_op(n_gethostname_replacement, OP_EQ,
prev_n_gethostname_replacement + 1);
- tt_want_int_op(n_get_interface_address, ==,
+ tt_want_int_op(n_get_interface_address, OP_EQ,
prev_n_get_interface_address + 1);
- tt_want_str_op(method_used,==,"INTERFACE");
+ tt_want_str_op(method_used,OP_EQ,"INTERFACE");
tt_want(hostname_out == NULL);
tt_assert(resolved_addr == 0x08080808);
@@ -1315,7 +1316,7 @@ test_config_resolve_my_address(void *arg)
prev_n_get_interface_address_failure + 1);
tt_want(n_gethostname_replacement ==
prev_n_gethostname_replacement + 1);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
UNMOCK(get_interface_address);
tor_free(hostname_out);
@@ -1345,7 +1346,7 @@ test_config_resolve_my_address(void *arg)
tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
tt_want(retval == 0);
- tt_want_str_op(method_used,==,"INTERFACE");
+ tt_want_str_op(method_used,OP_EQ,"INTERFACE");
tt_assert(resolved_addr == 0x09090909);
UNMOCK(tor_lookup_hostname);
@@ -1375,7 +1376,7 @@ test_config_resolve_my_address(void *arg)
&method_used,&hostname_out);
tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
UNMOCK(tor_gethostname);
UNMOCK(tor_lookup_hostname);
@@ -1419,9 +1420,9 @@ test_config_resolve_my_address(void *arg)
tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1);
tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1);
- tt_str_op(method_used,==,"INTERFACE");
- tt_assert(!hostname_out);
- tt_assert(retval == 0);
+ tt_str_op(method_used,OP_EQ,"INTERFACE");
+ tt_ptr_op(hostname_out, OP_EQ, NULL);
+ tt_int_op(retval, OP_EQ, 0);
/*
* CASE 11b:
@@ -1446,7 +1447,7 @@ test_config_resolve_my_address(void *arg)
tt_want(n_get_interface_address6_failure ==
prev_n_get_interface_address6_failure + 1);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
UNMOCK(tor_gethostname);
UNMOCK(tor_lookup_hostname);
@@ -1475,7 +1476,7 @@ test_config_resolve_my_address(void *arg)
&method_used,&hostname_out);
tt_want(n_gethostname_localhost == prev_n_gethostname_localhost + 1);
- tt_assert(retval == -1);
+ tt_int_op(retval, OP_EQ, -1);
UNMOCK(tor_gethostname);
@@ -1510,18 +1511,18 @@ test_config_adding_trusted_dir_server(void *arg)
NULL, V3_DIRINFO, 1.0);
tt_assert(ds);
dir_server_add(ds);
- tt_assert(get_n_authorities(V3_DIRINFO) == 1);
- tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+ tt_int_op(get_n_authorities(V3_DIRINFO), OP_EQ, 1);
+ tt_int_op(smartlist_len(router_get_fallback_dir_servers()), OP_EQ, 1);
/* create a trusted ds with an IPv6 address and port */
rv = tor_addr_port_parse(LOG_WARN, "[::1]:9061", &ipv6.addr, &ipv6.port, -1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, &ipv6, digest,
NULL, V3_DIRINFO, 1.0);
tt_assert(ds);
dir_server_add(ds);
- tt_assert(get_n_authorities(V3_DIRINFO) == 2);
- tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 2);
+ tt_int_op(get_n_authorities(V3_DIRINFO), OP_EQ, 2);
+ tt_int_op(smartlist_len(router_get_fallback_dir_servers()), OP_EQ, 2);
done:
clear_dir_servers();
@@ -1543,21 +1544,21 @@ test_config_adding_fallback_dir_server(void *arg)
routerlist_free_all();
rv = tor_addr_parse(&ipv4, "127.0.0.1");
- tt_assert(rv == AF_INET);
+ tt_int_op(rv, OP_EQ, AF_INET);
/* create a trusted ds without an IPv6 address and port */
ds = fallback_dir_server_new(&ipv4, 9059, 9060, NULL, digest, 1.0);
tt_assert(ds);
dir_server_add(ds);
- tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+ tt_int_op(smartlist_len(router_get_fallback_dir_servers()), OP_EQ, 1);
/* create a trusted ds with an IPv6 address and port */
rv = tor_addr_port_parse(LOG_WARN, "[::1]:9061", &ipv6.addr, &ipv6.port, -1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
ds = fallback_dir_server_new(&ipv4, 9059, 9060, &ipv6, digest, 1.0);
tt_assert(ds);
dir_server_add(ds);
- tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 2);
+ tt_int_op(smartlist_len(router_get_fallback_dir_servers()), OP_EQ, 2);
done:
clear_dir_servers();
@@ -1588,14 +1589,14 @@ test_config_parsing_trusted_dir_server(void *arg)
rv = parse_dir_authority_line(TEST_DIR_AUTH_LINE_START
TEST_DIR_AUTH_LINE_END,
V3_DIRINFO, 1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
/* parse a trusted dir server with an IPv6 address and port */
rv = parse_dir_authority_line(TEST_DIR_AUTH_LINE_START
TEST_DIR_AUTH_IPV6_FLAG
TEST_DIR_AUTH_LINE_END,
V3_DIRINFO, 1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
/* Since we are only validating, there is no cleanup. */
done:
@@ -1623,13 +1624,13 @@ test_config_parsing_fallback_dir_server(void *arg)
/* parse a trusted dir server without an IPv6 address and port */
rv = parse_dir_fallback_line(TEST_DIR_FALLBACK_LINE, 1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
/* parse a trusted dir server with an IPv6 address and port */
rv = parse_dir_fallback_line(TEST_DIR_FALLBACK_LINE
TEST_DIR_FALLBACK_IPV6_FLAG,
1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
/* Since we are only validating, there is no cleanup. */
done:
@@ -1649,8 +1650,8 @@ test_config_adding_default_trusted_dir_servers(void *arg)
/* Assume we only have one bridge authority */
add_default_trusted_dir_authorities(BRIDGE_DIRINFO);
- tt_assert(get_n_authorities(BRIDGE_DIRINFO) == 1);
- tt_assert(smartlist_len(router_get_fallback_dir_servers()) == 1);
+ tt_int_op(get_n_authorities(BRIDGE_DIRINFO), OP_EQ, 1);
+ tt_int_op(smartlist_len(router_get_fallback_dir_servers()), OP_EQ, 1);
/* Assume we have eight V3 authorities */
add_default_trusted_dir_authorities(V3_DIRINFO);
@@ -1838,7 +1839,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 1);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -1857,7 +1858,7 @@ test_config_adding_dir_servers(void *arg)
1 : 0)
);
/* If we have no default bridge authority, something has gone wrong */
- tt_assert(n_default_alt_bridge_authority >= 1);
+ tt_int_op(n_default_alt_bridge_authority, OP_GE, 1);
/* Count v3 Authorities */
SMARTLIST_FOREACH(fallback_servers,
@@ -1869,7 +1870,7 @@ test_config_adding_dir_servers(void *arg)
1 : 0)
);
/* If we have no default authorities, something has gone really wrong */
- tt_assert(n_default_alt_dir_authority >= 1);
+ tt_int_op(n_default_alt_dir_authority, OP_GE, 1);
/* Calculate Fallback Directory Count */
n_default_fallback_dir = (smartlist_len(fallback_servers) -
@@ -1879,7 +1880,7 @@ test_config_adding_dir_servers(void *arg)
* or some authorities aren't being added as fallback directories.
* (networkstatus_consensus_can_use_extra_fallbacks depends on all
* authorities being fallback directories.) */
- tt_assert(n_default_fallback_dir >= 0);
+ tt_int_op(n_default_fallback_dir, OP_GE, 0);
}
}
@@ -1920,7 +1921,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -1929,7 +1930,7 @@ test_config_adding_dir_servers(void *arg)
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
/* D0, (No B1), (No A2) */
- tt_assert(smartlist_len(dir_servers) == 1);
+ tt_int_op(smartlist_len(dir_servers), OP_EQ, 1);
/* DirAuthority - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -1941,7 +1942,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 1);
+ tt_int_op(found_D0, OP_EQ, 1);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -1953,7 +1954,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -1965,14 +1966,14 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
}
{
/* fallback_dir_servers */
const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
/* D0, (No B1), (No A2), Custom Fallback */
- tt_assert(smartlist_len(fallback_servers) == 2);
+ tt_int_op(smartlist_len(fallback_servers), OP_EQ, 2);
/* DirAuthority - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -1984,7 +1985,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 1);
+ tt_int_op(found_D0, OP_EQ, 1);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -1996,7 +1997,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2008,7 +2009,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* Custom FallbackDir - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -2020,7 +2021,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 1);
+ tt_int_op(found_non_default_fallback, OP_EQ, 1);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -2032,7 +2033,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
}
}
@@ -2061,7 +2062,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we just have the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
@@ -2070,7 +2071,7 @@ test_config_adding_dir_servers(void *arg)
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
/* D0, (No B1), (No A2) */
- tt_assert(smartlist_len(dir_servers) == 1);
+ tt_int_op(smartlist_len(dir_servers), OP_EQ, 1);
/* DirAuthority - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -2082,7 +2083,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 1);
+ tt_int_op(found_D0, OP_EQ, 1);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2094,7 +2095,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2106,14 +2107,14 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
}
{
/* fallback_dir_servers */
const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
/* D0, (No B1), (No A2), (No Fallback) */
- tt_assert(smartlist_len(fallback_servers) == 1);
+ tt_int_op(smartlist_len(fallback_servers), OP_EQ, 1);
/* DirAuthority - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -2125,7 +2126,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 1);
+ tt_int_op(found_D0, OP_EQ, 1);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2137,7 +2138,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2149,7 +2150,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -2161,7 +2162,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 0);
+ tt_int_op(found_non_default_fallback, OP_EQ, 0);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -2173,7 +2174,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
}
}
@@ -2202,7 +2203,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -2211,7 +2212,7 @@ test_config_adding_dir_servers(void *arg)
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
/* (No D0), B1, A2 */
- tt_assert(smartlist_len(dir_servers) == 2);
+ tt_int_op(smartlist_len(dir_servers), OP_EQ, 2);
/* (No DirAuthority) - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -2223,7 +2224,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2235,7 +2236,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2247,14 +2248,14 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
}
{
/* fallback_dir_servers */
const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
/* (No D0), B1, A2, Custom Fallback */
- tt_assert(smartlist_len(fallback_servers) == 3);
+ tt_int_op(smartlist_len(fallback_servers), OP_EQ, 3);
/* (No DirAuthority) - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -2266,7 +2267,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2278,7 +2279,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2290,7 +2291,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
/* Custom FallbackDir - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -2302,7 +2303,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 1);
+ tt_int_op(found_non_default_fallback, OP_EQ, 1);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -2314,7 +2315,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
}
}
@@ -2344,7 +2345,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
@@ -2353,7 +2354,7 @@ test_config_adding_dir_servers(void *arg)
/* trusted_dir_servers */
const smartlist_t *dir_servers = router_get_trusted_dir_servers();
/* (No D0), B1, A2 */
- tt_assert(smartlist_len(dir_servers) == 2);
+ tt_int_op(smartlist_len(dir_servers), OP_EQ, 2);
/* (No DirAuthority) - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -2365,7 +2366,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2377,7 +2378,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2389,14 +2390,14 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
}
{
/* fallback_dir_servers */
const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
/* (No D0), B1, A2, (No Fallback) */
- tt_assert(smartlist_len(fallback_servers) == 2);
+ tt_int_op(smartlist_len(fallback_servers), OP_EQ, 2);
/* (No DirAuthority) - D0 - dir_port: 60090 */
int found_D0 = 0;
@@ -2408,7 +2409,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2420,7 +2421,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2432,7 +2433,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
/* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -2444,7 +2445,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 0);
+ tt_int_op(found_non_default_fallback, OP_EQ, 0);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -2456,7 +2457,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
}
}
@@ -2496,7 +2497,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -2517,7 +2518,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2529,7 +2530,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2541,7 +2542,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default v3 non-Bridge directory authorities, so let's assume that
@@ -2567,7 +2568,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2579,7 +2580,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2591,7 +2592,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* Custom FallbackDir - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -2603,7 +2604,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 1);
+ tt_int_op(found_non_default_fallback, OP_EQ, 1);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -2615,7 +2616,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default v3 non-Bridge directory authorities, so let's assume that
@@ -2650,7 +2651,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 1);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -2671,7 +2672,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2683,7 +2684,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2695,7 +2696,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default v3 non-Bridge directory authorities, so let's assume that
@@ -2721,7 +2722,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* AlternateBridgeAuthority - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2733,7 +2734,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 1);
+ tt_int_op(found_B1, OP_EQ, 1);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2745,7 +2746,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -2757,7 +2758,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 0);
+ tt_int_op(found_non_default_fallback, OP_EQ, 0);
/* Default FallbackDir - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -2769,7 +2770,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 1);
+ tt_int_op(found_default_fallback, OP_EQ, 1);
/* There's no easy way of checking that we have included all the
* default v3 non-Bridge directory authorities, so let's assume that
@@ -2813,7 +2814,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -2835,7 +2836,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2847,7 +2848,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2859,7 +2860,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
/* There's no easy way of checking that we have included all the
* default Bridge authorities (except for hard-coding tonga's details),
@@ -2886,7 +2887,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -2898,7 +2899,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -2910,7 +2911,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
/* Custom FallbackDir - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -2922,7 +2923,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 1);
+ tt_int_op(found_non_default_fallback, OP_EQ, 1);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -2934,7 +2935,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default Bridge authorities (except for hard-coding tonga's details),
@@ -2970,7 +2971,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we just have the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 0);
@@ -2993,7 +2994,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -3005,7 +3006,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -3017,7 +3018,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
/* There's no easy way of checking that we have included all the
* default Bridge authorities (except for hard-coding tonga's details),
@@ -3044,7 +3045,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -3056,7 +3057,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* AlternateDirAuthority - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -3068,7 +3069,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 1);
+ tt_int_op(found_A2, OP_EQ, 1);
/* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -3080,7 +3081,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 0);
+ tt_int_op(found_non_default_fallback, OP_EQ, 0);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -3092,7 +3093,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default Bridge authorities (except for hard-coding tonga's details),
@@ -3136,7 +3137,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must not have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 0);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -3158,7 +3159,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -3170,7 +3171,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -3182,7 +3183,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default Bridge & V3 Directory authorities, so let's assume that
@@ -3209,7 +3210,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -3221,7 +3222,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -3233,7 +3234,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* Custom FallbackDir - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -3245,7 +3246,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 1);
+ tt_int_op(found_non_default_fallback, OP_EQ, 1);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -3257,7 +3258,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 0);
+ tt_int_op(found_default_fallback, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default Bridge & V3 Directory authorities, so let's assume that
@@ -3299,7 +3300,7 @@ test_config_adding_dir_servers(void *arg)
/* check outcome */
/* we must have added the default fallback dirs */
- tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+ tt_int_op(n_add_default_fallback_dir_servers_known_default, OP_EQ, 1);
/* we have more fallbacks than just the authorities */
tt_assert(networkstatus_consensus_can_use_extra_fallbacks(options) == 1);
@@ -3321,7 +3322,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -3333,7 +3334,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -3345,7 +3346,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* There's no easy way of checking that we have included all the
* default Bridge & V3 Directory authorities, so let's assume that
@@ -3372,7 +3373,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60090 ?
1 : 0)
);
- tt_assert(found_D0 == 0);
+ tt_int_op(found_D0, OP_EQ, 0);
/* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
int found_B1 = 0;
@@ -3384,7 +3385,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60091 ?
1 : 0)
);
- tt_assert(found_B1 == 0);
+ tt_int_op(found_B1, OP_EQ, 0);
/* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
int found_A2 = 0;
@@ -3396,7 +3397,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60092 ?
1 : 0)
);
- tt_assert(found_A2 == 0);
+ tt_int_op(found_A2, OP_EQ, 0);
/* Custom FallbackDir - No Nickname - dir_port: 60093 */
int found_non_default_fallback = 0;
@@ -3408,7 +3409,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60093 ?
1 : 0)
);
- tt_assert(found_non_default_fallback == 0);
+ tt_int_op(found_non_default_fallback, OP_EQ, 0);
/* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
int found_default_fallback = 0;
@@ -3420,7 +3421,7 @@ test_config_adding_dir_servers(void *arg)
(ds->dir_port == 60099 ?
1 : 0)
);
- tt_assert(found_default_fallback == 1);
+ tt_int_op(found_default_fallback, OP_EQ, 1);
/* There's no easy way of checking that we have included all the
* default Bridge & V3 Directory authorities, and the default
@@ -3477,7 +3478,7 @@ test_config_default_dir_servers(void *arg)
opts = NULL;
/* assume a release will never go out with less than 7 authorities */
- tt_assert(trusted_count >= 7);
+ tt_int_op(trusted_count, OP_GE, 7);
/* if we disable the default fallbacks, there must not be any extra */
tt_assert(fallback_count == trusted_count);
@@ -3490,7 +3491,7 @@ test_config_default_dir_servers(void *arg)
opts = NULL;
/* assume a release will never go out with less than 7 authorities */
- tt_assert(trusted_count >= 7);
+ tt_int_op(trusted_count, OP_GE, 7);
/* XX/teor - allow for default fallbacks to be added without breaking
* the unit tests. Set a minimum fallback count once the list is stable. */
tt_assert(fallback_count >= trusted_count);
@@ -3559,18 +3560,18 @@ test_config_directory_fetch(void *arg)
options->ClientOnly = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 1);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 1);
/* Bridge Clients can use multiple directory mirrors for bootstrap */
memset(options, 0, sizeof(or_options_t));
options->UseBridges = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 1);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 1);
/* Bridge Relays (Bridges) must act like clients, and use multiple
* directory mirrors for bootstrap */
@@ -3579,9 +3580,9 @@ test_config_directory_fetch(void *arg)
options->ORPort_set = 1;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 0);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 1);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 1);
/* Clients set to FetchDirInfoEarly must fetch it from the authorities,
* but can use multiple authorities for bootstrap */
@@ -3589,9 +3590,9 @@ test_config_directory_fetch(void *arg)
options->FetchDirInfoEarly = 1;
tt_assert(server_mode(options) == 0);
tt_assert(public_server_mode(options) == 0);
- tt_assert(directory_fetches_from_authorities(options) == 1);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 1);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 1);
/* OR servers only fetch the consensus from the authorities when they don't
* know their own address, but never use multiple directories for bootstrap
@@ -3602,16 +3603,16 @@ test_config_directory_fetch(void *arg)
mock_router_pick_published_address_result = -1;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 1);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
mock_router_pick_published_address_result = 0;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
/* Exit OR servers only fetch the consensus from the authorities when they
* refuse unknown exits, but never use multiple directories for bootstrap
@@ -3629,17 +3630,17 @@ test_config_directory_fetch(void *arg)
options->RefuseUnknownExits = 1;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 1);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
options->RefuseUnknownExits = 0;
mock_router_pick_published_address_result = 0;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
/* Dir servers fetch the consensus from the authorities, unless they are not
* advertising themselves (hibernating) or have no routerinfo or are not
@@ -3658,26 +3659,26 @@ test_config_directory_fetch(void *arg)
mock_router_get_my_routerinfo_result = &routerinfo;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 1);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
mock_advertised_server_mode_result = 0;
routerinfo.dir_port = 1;
mock_router_get_my_routerinfo_result = &routerinfo;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
mock_advertised_server_mode_result = 1;
mock_router_get_my_routerinfo_result = NULL;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
mock_advertised_server_mode_result = 1;
routerinfo.dir_port = 0;
@@ -3685,9 +3686,9 @@ test_config_directory_fetch(void *arg)
mock_router_get_my_routerinfo_result = &routerinfo;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 0);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
mock_advertised_server_mode_result = 1;
routerinfo.dir_port = 1;
@@ -3695,9 +3696,9 @@ test_config_directory_fetch(void *arg)
mock_router_get_my_routerinfo_result = &routerinfo;
tt_assert(server_mode(options) == 1);
tt_assert(public_server_mode(options) == 1);
- tt_assert(directory_fetches_from_authorities(options) == 1);
- tt_assert(networkstatus_consensus_can_use_multiple_directories(options)
- == 0);
+ tt_int_op(directory_fetches_from_authorities(options), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_can_use_multiple_directories(options),
+ OP_EQ, 0);
done:
tor_free(options);
@@ -3744,119 +3745,119 @@ test_config_port_cfg_line_extract_addrport(void *arg)
tt_int_op(port_cfg_line_extract_addrport("", &a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, "");;
+ tt_str_op(a, OP_EQ, "");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("hello", &a, &unixy, &rest),
OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, "hello");;
+ tt_str_op(a, OP_EQ, "hello");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport(" flipperwalt gersplut",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(a, OP_EQ, "flipperwalt");
tt_str_op(rest, OP_EQ, "gersplut");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport(" flipperwalt \t gersplut",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(a, OP_EQ, "flipperwalt");
tt_str_op(rest, OP_EQ, "gersplut");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("flipperwalt \t gersplut",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(a, OP_EQ, "flipperwalt");
tt_str_op(rest, OP_EQ, "gersplut");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:flipperwalt \t gersplut",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "flipperwalt");;
+ tt_str_op(a, OP_EQ, "flipperwalt");
tt_str_op(rest, OP_EQ, "gersplut");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("lolol",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(a, OP_EQ, "lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:lolol",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(a, OP_EQ, "lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:lolol ",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(a, OP_EQ, "lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport(" unix:lolol",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(a, OP_EQ, "lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("foobar:lolol",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, "foobar:lolol");;
+ tt_str_op(a, OP_EQ, "foobar:lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport(":lolol",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 0);
- tt_str_op(a, OP_EQ, ":lolol");;
+ tt_str_op(a, OP_EQ, ":lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\"",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(a, OP_EQ, "lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\" ",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(a, OP_EQ, "lolol");
tt_str_op(rest, OP_EQ, "");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:\"lolol\" foo ",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lolol");;
+ tt_str_op(a, OP_EQ, "lolol");
tt_str_op(rest, OP_EQ, "foo ");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:\"lol ol\" foo ",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lol ol");;
+ tt_str_op(a, OP_EQ, "lol ol");
tt_str_op(rest, OP_EQ, "foo ");
tor_free(a);
tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\\" ol\" foo ",
&a, &unixy, &rest), OP_EQ, 0);
tt_int_op(unixy, OP_EQ, 1);
- tt_str_op(a, OP_EQ, "lol\" ol");;
+ tt_str_op(a, OP_EQ, "lol\" ol");
tt_str_op(rest, OP_EQ, "foo ");
tor_free(a);
@@ -4002,9 +4003,9 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->entry_cfg.dns_request, OP_EQ, 1);
tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1);
tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1);
- tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.prefer_ipv6_virtaddr, OP_EQ, 1);
-#endif
+#endif /* defined(_WIN32) */
// Test failure if we have no ipv4 and no ipv6 and no onion (DNS only)
config_free_lines(config_port_invalid); config_port_invalid = NULL;
@@ -4076,7 +4077,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1);
-#endif
+#endif /* defined(_WIN32) */
// Test success with quoted unix: address.
config_free_lines(config_port_valid); config_port_valid = NULL;
@@ -4098,7 +4099,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1);
-#endif
+#endif /* defined(_WIN32) */
// Test failure with broken quoted unix: address.
config_free_lines(config_port_valid); config_port_valid = NULL;
@@ -4143,7 +4144,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1);
-#endif
+#endif /* defined(_WIN32) */
// Test success with no ipv4 but take ipv6
config_free_lines(config_port_valid); config_port_valid = NULL;
@@ -4162,7 +4163,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1);
-#endif
+#endif /* defined(_WIN32) */
// Test success with both ipv4 and ipv6
config_free_lines(config_port_valid); config_port_valid = NULL;
@@ -4181,7 +4182,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1);
tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1);
-#endif
+#endif /* defined(_WIN32) */
// Test failure if we specify world writable for an IP Port
config_free_lines(config_port_invalid); config_port_invalid = NULL;
@@ -4345,7 +4346,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(ret, OP_EQ, 0);
tt_int_op(smartlist_len(slout), OP_EQ, 1);
port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
- tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 1);
+ tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 0);
tt_int_op(port_cfg->entry_cfg.cache_ipv6_answers, OP_EQ, 1);
// Test success with no cache ipv4 DNS
@@ -4645,7 +4646,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(smartlist_len(slout), OP_EQ, 1);
port_cfg = (port_cfg_t *)smartlist_get(slout, 0);
tt_int_op(port_cfg->is_group_writable, OP_EQ, 1);
-#endif
+#endif /* defined(_WIN32) */
done:
if (slout)
@@ -4816,6 +4817,7 @@ test_config_include_limit(void *data)
(void)data;
config_line_t *result = NULL;
+ char *torrc_path = NULL;
char *dir = tor_strdup(get_fname("test_include_limit"));
tt_ptr_op(dir, OP_NE, NULL);
@@ -4825,8 +4827,7 @@ test_config_include_limit(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char torrc_path[PATH_MAX+1];
- tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir);
+ tor_asprintf(&torrc_path, "%s"PATH_SEPARATOR"torrc", dir);
char torrc_contents[1000];
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
torrc_path);
@@ -4837,6 +4838,7 @@ test_config_include_limit(void *data)
done:
config_free_lines(result);
+ tor_free(torrc_path);
tor_free(dir);
}
@@ -4847,6 +4849,7 @@ test_config_include_does_not_exist(void *data)
config_line_t *result = NULL;
char *dir = tor_strdup(get_fname("test_include_does_not_exist"));
+ char *missing_path = NULL;
tt_ptr_op(dir, OP_NE, NULL);
#ifdef _WIN32
@@ -4855,9 +4858,7 @@ test_config_include_does_not_exist(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char missing_path[PATH_MAX+1];
- tor_snprintf(missing_path, sizeof(missing_path), "%s"PATH_SEPARATOR"missing",
- dir);
+ tor_asprintf(&missing_path, "%s"PATH_SEPARATOR"missing", dir);
char torrc_contents[1000];
tor_snprintf(torrc_contents, sizeof(torrc_contents), "%%include %s",
missing_path);
@@ -4868,6 +4869,7 @@ test_config_include_does_not_exist(void *data)
done:
config_free_lines(result);
tor_free(dir);
+ tor_free(missing_path);
}
static void
@@ -4877,6 +4879,7 @@ test_config_include_error_in_included_file(void *data)
config_line_t *result = NULL;
char *dir = tor_strdup(get_fname("test_error_in_included_file"));
+ char *invalid_path = NULL;
tt_ptr_op(dir, OP_NE, NULL);
#ifdef _WIN32
@@ -4885,9 +4888,7 @@ test_config_include_error_in_included_file(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char invalid_path[PATH_MAX+1];
- tor_snprintf(invalid_path, sizeof(invalid_path), "%s"PATH_SEPARATOR"invalid",
- dir);
+ tor_asprintf(&invalid_path, "%s"PATH_SEPARATOR"invalid", dir);
tt_int_op(write_str_to_file(invalid_path, "unclosed \"", 0), OP_EQ, 0);
char torrc_contents[1000];
@@ -4900,6 +4901,7 @@ test_config_include_error_in_included_file(void *data)
done:
config_free_lines(result);
tor_free(dir);
+ tor_free(invalid_path);
}
static void
@@ -4908,6 +4910,8 @@ test_config_include_empty_file_folder(void *data)
(void)data;
config_line_t *result = NULL;
+ char *folder_path = NULL;
+ char *file_path = NULL;
char *dir = tor_strdup(get_fname("test_include_empty_file_folder"));
tt_ptr_op(dir, OP_NE, NULL);
@@ -4917,17 +4921,13 @@ test_config_include_empty_file_folder(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char folder_path[PATH_MAX+1];
- tor_snprintf(folder_path, sizeof(folder_path), "%s"PATH_SEPARATOR"empty_dir",
- dir);
+ tor_asprintf(&folder_path, "%s"PATH_SEPARATOR"empty_dir", dir);
#ifdef _WIN32
tt_int_op(mkdir(folder_path), OP_EQ, 0);
#else
tt_int_op(mkdir(folder_path, 0700), OP_EQ, 0);
#endif
- char file_path[PATH_MAX+1];
- tor_snprintf(file_path, sizeof(file_path), "%s"PATH_SEPARATOR"empty_file",
- dir);
+ tor_asprintf(&file_path, "%s"PATH_SEPARATOR"empty_file", dir);
tt_int_op(write_str_to_file(file_path, "", 0), OP_EQ, 0);
char torrc_contents[1000];
@@ -4944,15 +4944,57 @@ test_config_include_empty_file_folder(void *data)
done:
config_free_lines(result);
+ tor_free(folder_path);
+ tor_free(file_path);
tor_free(dir);
}
+#ifndef _WIN32
+static void
+test_config_include_no_permission(void *data)
+{
+ (void)data;
+ config_line_t *result = NULL;
+
+ char *folder_path = NULL;
+ char *dir = NULL;
+ if (geteuid() == 0)
+ tt_skip();
+
+ dir = tor_strdup(get_fname("test_include_forbidden_folder"));
+ tt_ptr_op(dir, OP_NE, NULL);
+
+ tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
+
+ tor_asprintf(&folder_path, "%s"PATH_SEPARATOR"forbidden_dir", dir);
+ tt_int_op(mkdir(folder_path, 0100), OP_EQ, 0);
+
+ char torrc_contents[1000];
+ tor_snprintf(torrc_contents, sizeof(torrc_contents),
+ "%%include %s\n",
+ folder_path);
+
+ int include_used;
+ tt_int_op(config_get_lines_include(torrc_contents, &result, 0,&include_used),
+ OP_EQ, -1);
+ tt_ptr_op(result, OP_EQ, NULL);
+
+ done:
+ config_free_lines(result);
+ tor_free(folder_path);
+ if (dir)
+ chmod(dir, 0700);
+ tor_free(dir);
+}
+#endif
+
static void
test_config_include_recursion_before_after(void *data)
{
(void)data;
config_line_t *result = NULL;
+ char *torrc_path = NULL;
char *dir = tor_strdup(get_fname("test_include_recursion_before_after"));
tt_ptr_op(dir, OP_NE, NULL);
@@ -4962,8 +5004,7 @@ test_config_include_recursion_before_after(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char torrc_path[PATH_MAX+1];
- tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir);
+ tor_asprintf(&torrc_path, "%s"PATH_SEPARATOR"torrc", dir);
char file_contents[1000];
const int limit = MAX_INCLUDE_RECURSION_LEVEL;
@@ -4982,9 +5023,10 @@ test_config_include_recursion_before_after(void *data)
}
if (i > 1) {
- char file_path[PATH_MAX+1];
- tor_snprintf(file_path, sizeof(file_path), "%s%d", torrc_path, i);
+ char *file_path = NULL;
+ tor_asprintf(&file_path, "%s%d", torrc_path, i);
tt_int_op(write_str_to_file(file_path, file_contents, 0), OP_EQ, 0);
+ tor_free(file_path);
}
}
@@ -5008,6 +5050,7 @@ test_config_include_recursion_before_after(void *data)
done:
config_free_lines(result);
tor_free(dir);
+ tor_free(torrc_path);
}
static void
@@ -5016,6 +5059,7 @@ test_config_include_recursion_after_only(void *data)
(void)data;
config_line_t *result = NULL;
+ char *torrc_path = NULL;
char *dir = tor_strdup(get_fname("test_include_recursion_after_only"));
tt_ptr_op(dir, OP_NE, NULL);
@@ -5025,8 +5069,7 @@ test_config_include_recursion_after_only(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char torrc_path[PATH_MAX+1];
- tor_snprintf(torrc_path, sizeof(torrc_path), "%s"PATH_SEPARATOR"torrc", dir);
+ tor_asprintf(&torrc_path, "%s"PATH_SEPARATOR"torrc", dir);
char file_contents[1000];
const int limit = MAX_INCLUDE_RECURSION_LEVEL;
@@ -5045,9 +5088,10 @@ test_config_include_recursion_after_only(void *data)
}
if (i > 1) {
- char file_path[PATH_MAX+1];
- tor_snprintf(file_path, sizeof(file_path), "%s%d", torrc_path, i);
+ char *file_path = NULL;
+ tor_asprintf(&file_path, "%s%d", torrc_path, i);
tt_int_op(write_str_to_file(file_path, file_contents, 0), OP_EQ, 0);
+ tor_free(file_path);
}
}
@@ -5071,6 +5115,7 @@ test_config_include_recursion_after_only(void *data)
done:
config_free_lines(result);
tor_free(dir);
+ tor_free(torrc_path);
}
static void
@@ -5079,6 +5124,9 @@ test_config_include_folder_order(void *data)
(void)data;
config_line_t *result = NULL;
+ char *torrcd = NULL;
+ char *path = NULL;
+ char *path2 = NULL;
char *dir = tor_strdup(get_fname("test_include_folder_order"));
tt_ptr_op(dir, OP_NE, NULL);
@@ -5088,8 +5136,7 @@ test_config_include_folder_order(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char torrcd[PATH_MAX+1];
- tor_snprintf(torrcd, sizeof(torrcd), "%s"PATH_SEPARATOR"%s", dir, "torrc.d");
+ tor_asprintf(&torrcd, "%s"PATH_SEPARATOR"%s", dir, "torrc.d");
#ifdef _WIN32
tt_int_op(mkdir(torrcd), OP_EQ, 0);
@@ -5098,9 +5145,7 @@ test_config_include_folder_order(void *data)
#endif
// test that files in subfolders are ignored
- char path[PATH_MAX+1];
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd,
- "subfolder");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", torrcd, "subfolder");
#ifdef _WIN32
tt_int_op(mkdir(path), OP_EQ, 0);
@@ -5108,27 +5153,31 @@ test_config_include_folder_order(void *data)
tt_int_op(mkdir(path, 0700), OP_EQ, 0);
#endif
- char path2[PATH_MAX+1];
- tor_snprintf(path2, sizeof(path2), "%s"PATH_SEPARATOR"%s", path,
- "01_ignore");
+ tor_asprintf(&path2, "%s"PATH_SEPARATOR"%s", path, "01_ignore");
tt_int_op(write_str_to_file(path2, "ShouldNotSee 1\n", 0), OP_EQ, 0);
+ tor_free(path);
// test that files starting with . are ignored
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, ".dot");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", torrcd, ".dot");
tt_int_op(write_str_to_file(path, "ShouldNotSee 2\n", 0), OP_EQ, 0);
+ tor_free(path);
// test file order
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "01_1st");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", torrcd, "01_1st");
tt_int_op(write_str_to_file(path, "Test 1\n", 0), OP_EQ, 0);
+ tor_free(path);
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "02_2nd");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", torrcd, "02_2nd");
tt_int_op(write_str_to_file(path, "Test 2\n", 0), OP_EQ, 0);
+ tor_free(path);
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "aa_3rd");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", torrcd, "aa_3rd");
tt_int_op(write_str_to_file(path, "Test 3\n", 0), OP_EQ, 0);
+ tor_free(path);
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", torrcd, "ab_4th");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", torrcd, "ab_4th");
tt_int_op(write_str_to_file(path, "Test 4\n", 0), OP_EQ, 0);
+ tor_free(path);
char torrc_contents[1000];
tor_snprintf(torrc_contents, sizeof(torrc_contents),
@@ -5154,6 +5203,9 @@ test_config_include_folder_order(void *data)
done:
config_free_lines(result);
+ tor_free(torrcd);
+ tor_free(path);
+ tor_free(path2);
tor_free(dir);
}
@@ -5285,6 +5337,7 @@ test_config_include_flag_torrc_only(void *data)
(void)data;
char *errmsg = NULL;
+ char *path = NULL;
char *dir = tor_strdup(get_fname("test_include_flag_torrc_only"));
tt_ptr_op(dir, OP_NE, NULL);
@@ -5294,8 +5347,7 @@ test_config_include_flag_torrc_only(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char path[PATH_MAX+1];
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", dir, "dummy");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", dir, "dummy");
tt_int_op(write_str_to_file(path, "\n", 0), OP_EQ, 0);
char conf_empty[1000];
@@ -5315,6 +5367,7 @@ test_config_include_flag_torrc_only(void *data)
done:
tor_free(errmsg);
+ tor_free(path);
tor_free(dir);
}
@@ -5324,6 +5377,7 @@ test_config_include_flag_defaults_only(void *data)
(void)data;
char *errmsg = NULL;
+ char *path = NULL;
char *dir = tor_strdup(get_fname("test_include_flag_defaults_only"));
tt_ptr_op(dir, OP_NE, NULL);
@@ -5333,8 +5387,7 @@ test_config_include_flag_defaults_only(void *data)
tt_int_op(mkdir(dir, 0700), OP_EQ, 0);
#endif
- char path[PATH_MAX+1];
- tor_snprintf(path, sizeof(path), "%s"PATH_SEPARATOR"%s", dir, "dummy");
+ tor_asprintf(&path, "%s"PATH_SEPARATOR"%s", dir, "dummy");
tt_int_op(write_str_to_file(path, "\n", 0), OP_EQ, 0);
char conf_empty[1000];
@@ -5354,9 +5407,115 @@ test_config_include_flag_defaults_only(void *data)
done:
tor_free(errmsg);
+ tor_free(path);
tor_free(dir);
}
+static void
+test_config_dup_and_filter(void *arg)
+{
+ (void)arg;
+ /* Test normal input. */
+ config_line_t *line = NULL;
+ config_line_append(&line, "abc", "def");
+ config_line_append(&line, "ghi", "jkl");
+ config_line_append(&line, "ABCD", "mno");
+
+ config_line_t *line_dup = config_lines_dup_and_filter(line, "aBc");
+ tt_ptr_op(line_dup, OP_NE, NULL);
+ tt_ptr_op(line_dup->next, OP_NE, NULL);
+ tt_ptr_op(line_dup->next->next, OP_EQ, NULL);
+
+ tt_str_op(line_dup->key, OP_EQ, "abc");
+ tt_str_op(line_dup->value, OP_EQ, "def");
+ tt_str_op(line_dup->next->key, OP_EQ, "ABCD");
+ tt_str_op(line_dup->next->value, OP_EQ, "mno");
+
+ /* empty output */
+ config_free_lines(line_dup);
+ line_dup = config_lines_dup_and_filter(line, "skdjfsdkljf");
+ tt_ptr_op(line_dup, OP_EQ, NULL);
+
+ /* empty input */
+ config_free_lines(line_dup);
+ line_dup = config_lines_dup_and_filter(NULL, "abc");
+ tt_ptr_op(line_dup, OP_EQ, NULL);
+
+ done:
+ config_free_lines(line);
+ config_free_lines(line_dup);
+}
+
+/* If we're not configured to be a bridge, but we set
+ * BridgeDistribution, then options_validate () should return -1. */
+static void
+test_config_check_bridge_distribution_setting_not_a_bridge(void *arg)
+{
+ or_options_t* options = get_options_mutable();
+ or_options_t* old_options = options;
+ or_options_t* default_options = options;
+ char* message = NULL;
+ int ret;
+
+ (void)arg;
+
+ options->BridgeRelay = 0;
+ options->BridgeDistribution = (char*)("https");
+
+ ret = options_validate(old_options, options, default_options, 0, &message);
+
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(message, OP_EQ, "You set BridgeDistribution, but you "
+ "didn't set BridgeRelay!");
+ done:
+ tor_free(message);
+ options->BridgeDistribution = NULL;
+}
+
+/* If the BridgeDistribution setting was valid, 0 should be returned. */
+static void
+test_config_check_bridge_distribution_setting_valid(void *arg)
+{
+ int ret = check_bridge_distribution_setting("https");
+
+ (void)arg;
+
+ tt_int_op(ret, OP_EQ, 0);
+ done:
+ return;
+}
+
+/* If the BridgeDistribution setting was invalid, -1 should be returned. */
+static void
+test_config_check_bridge_distribution_setting_invalid(void *arg)
+{
+ int ret = check_bridge_distribution_setting("hyphens-are-allowed");
+
+ (void)arg;
+
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = check_bridge_distribution_setting("asterisks*are*forbidden");
+
+ tt_int_op(ret, OP_EQ, -1);
+ done:
+ return;
+}
+
+/* If the BridgeDistribution setting was unrecognised, a warning should be
+ * logged and 0 should be returned. */
+static void
+test_config_check_bridge_distribution_setting_unrecognised(void *arg)
+{
+ int ret = check_bridge_distribution_setting("unicorn");
+
+ (void)arg;
+
+ tt_int_op(ret, OP_EQ, 0);
+ done:
+ return;
+}
+
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
@@ -5387,6 +5546,9 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(include_does_not_exist, 0),
CONFIG_TEST(include_error_in_included_file, 0),
CONFIG_TEST(include_empty_file_folder, 0),
+#ifndef _WIN32
+ CONFIG_TEST(include_no_permission, 0),
+#endif
CONFIG_TEST(include_recursion_before_after, 0),
CONFIG_TEST(include_recursion_after_only, 0),
CONFIG_TEST(include_folder_order, 0),
@@ -5396,6 +5558,11 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(include_flag_both_without, TT_FORK),
CONFIG_TEST(include_flag_torrc_only, TT_FORK),
CONFIG_TEST(include_flag_defaults_only, TT_FORK),
+ CONFIG_TEST(dup_and_filter, 0),
+ CONFIG_TEST(check_bridge_distribution_setting_not_a_bridge, TT_FORK),
+ CONFIG_TEST(check_bridge_distribution_setting_valid, 0),
+ CONFIG_TEST(check_bridge_distribution_setting_invalid, 0),
+ CONFIG_TEST(check_bridge_distribution_setting_unrecognised, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
index 7e5193b203..314b90cfda 100644
--- a/src/test/test_connection.c
+++ b/src/test/test_connection.c
@@ -17,9 +17,8 @@
#include "rendcache.h"
#include "directory.h"
-static void test_conn_lookup_addr_helper(const char *address,
- int family,
- tor_addr_t *addr);
+#include "test_connection.h"
+#include "test_helpers.h"
static void * test_conn_get_basic_setup(const struct testcase_t *tc);
static int test_conn_get_basic_teardown(const struct testcase_t *tc,
@@ -62,48 +61,7 @@ static int test_conn_get_rsrc_teardown(const struct testcase_t *tc,
#define TEST_CONN_UNATTACHED_STATE (AP_CONN_STATE_CIRCUIT_WAIT)
#define TEST_CONN_ATTACHED_STATE (AP_CONN_STATE_CONNECT_WAIT)
-#define TEST_CONN_FD_INIT 50
-static int mock_connection_connect_sockaddr_called = 0;
-static int fake_socket_number = TEST_CONN_FD_INIT;
-
-static int
-mock_connection_connect_sockaddr(connection_t *conn,
- const struct sockaddr *sa,
- socklen_t sa_len,
- const struct sockaddr *bindaddr,
- socklen_t bindaddr_len,
- int *socket_error)
-{
- (void)sa_len;
- (void)bindaddr;
- (void)bindaddr_len;
-
- tor_assert(conn);
- tor_assert(sa);
- tor_assert(socket_error);
-
- mock_connection_connect_sockaddr_called++;
-
- conn->s = fake_socket_number++;
- tt_assert(SOCKET_OK(conn->s));
- /* We really should call tor_libevent_initialize() here. Because we don't,
- * we are relying on other parts of the code not checking if the_event_base
- * (and therefore event->ev_base) is NULL. */
- tt_assert(connection_add_connecting(conn) == 0);
-
- done:
- /* Fake "connected" status */
- return 1;
-}
-
-static int
-fake_close_socket(evutil_socket_t sock)
-{
- (void)sock;
- return 0;
-}
-
-static void
+void
test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
{
int rv = 0;
@@ -112,7 +70,7 @@ test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
rv = tor_addr_lookup(address, family, addr);
/* XXXX - should we retry on transient failure? */
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
tt_assert(tor_addr_is_loopback(addr));
tt_assert(tor_addr_is_v4(addr));
@@ -122,51 +80,6 @@ test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
tor_addr_make_null(addr, TEST_CONN_FAMILY);
}
-static connection_t *
-test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
-{
- connection_t *conn = NULL;
- tor_addr_t addr;
- int socket_err = 0;
- int in_progress = 0;
-
- MOCK(connection_connect_sockaddr,
- mock_connection_connect_sockaddr);
- MOCK(tor_close_socket, fake_close_socket);
-
- init_connection_lists();
-
- conn = connection_new(type, TEST_CONN_FAMILY);
- tt_assert(conn);
-
- test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
- tt_assert(!tor_addr_is_null(&addr));
-
- tor_addr_copy_tight(&conn->addr, &addr);
- conn->port = TEST_CONN_PORT;
- mock_connection_connect_sockaddr_called = 0;
- in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr,
- TEST_CONN_PORT, &socket_err);
- tt_assert(mock_connection_connect_sockaddr_called == 1);
- tt_assert(!socket_err);
- tt_assert(in_progress == 0 || in_progress == 1);
-
- /* fake some of the attributes so the connection looks OK */
- conn->state = state;
- conn->purpose = purpose;
- assert_connection_ok(conn, time(NULL));
-
- UNMOCK(connection_connect_sockaddr);
- UNMOCK(tor_close_socket);
- return conn;
-
- /* On failure */
- done:
- UNMOCK(connection_connect_sockaddr);
- UNMOCK(tor_close_socket);
- return NULL;
-}
-
static void *
test_conn_get_basic_setup(const struct testcase_t *tc)
{
@@ -379,7 +292,7 @@ test_conn_download_status_teardown(const struct testcase_t *tc, void *arg)
/* connection_free_() cleans up requested_resource */
rv = test_conn_get_rsrc_teardown(tc, conn);
- tt_assert(rv == 1);
+ tt_int_op(rv, OP_EQ, 1);
}
} SMARTLIST_FOREACH_END(conn);
@@ -453,12 +366,13 @@ test_conn_get_basic(void *arg)
* its attributes, but get NULL when we supply a different value. */
tt_assert(connection_get_by_global_id(conn->global_identifier) == conn);
- tt_assert(connection_get_by_global_id(!conn->global_identifier) == NULL);
+ tt_ptr_op(connection_get_by_global_id(!conn->global_identifier), OP_EQ,
+ NULL);
tt_assert(connection_get_by_type(conn->type) == conn);
tt_assert(connection_get_by_type(TEST_CONN_TYPE) == conn);
- tt_assert(connection_get_by_type(!conn->type) == NULL);
- tt_assert(connection_get_by_type(!TEST_CONN_TYPE) == NULL);
+ tt_ptr_op(connection_get_by_type(!conn->type), OP_EQ, NULL);
+ tt_ptr_op(connection_get_by_type(!TEST_CONN_TYPE), OP_EQ, NULL);
tt_assert(connection_get_by_type_state(conn->type, conn->state)
== conn);
@@ -572,7 +486,7 @@ test_conn_get_rend(void *arg)
#define sl_is_conn_assert(sl_input, conn) \
do { \
the_sl = (sl_input); \
- tt_assert(smartlist_len((the_sl)) == 1); \
+ tt_int_op(smartlist_len((the_sl)), OP_EQ, 1); \
tt_assert(smartlist_get((the_sl), 0) == (conn)); \
smartlist_free(the_sl); the_sl = NULL; \
} while (0)
@@ -580,7 +494,7 @@ test_conn_get_rend(void *arg)
#define sl_no_conn_assert(sl_input) \
do { \
the_sl = (sl_input); \
- tt_assert(smartlist_len((the_sl)) == 0); \
+ tt_int_op(smartlist_len((the_sl)), OP_EQ, 0); \
smartlist_free(the_sl); the_sl = NULL; \
} while (0)
@@ -626,43 +540,32 @@ test_conn_get_rsrc(void *arg)
TEST_CONN_RSRC_2,
!TEST_CONN_STATE));
- tt_assert(connection_dir_count_by_purpose_and_resource(
- conn->base_.purpose,
- conn->requested_resource)
- == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- TEST_CONN_RSRC)
- == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- !conn->base_.purpose,
- "")
- == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- !TEST_CONN_RSRC_PURPOSE,
- TEST_CONN_RSRC_2)
- == 0);
-
- tt_assert(connection_dir_count_by_purpose_resource_and_state(
- conn->base_.purpose,
- conn->requested_resource,
- conn->base_.state)
- == 1);
- tt_assert(connection_dir_count_by_purpose_resource_and_state(
- TEST_CONN_RSRC_PURPOSE,
- TEST_CONN_RSRC,
- TEST_CONN_STATE)
- == 1);
- tt_assert(connection_dir_count_by_purpose_resource_and_state(
- !conn->base_.purpose,
- "",
- !conn->base_.state)
- == 0);
- tt_assert(connection_dir_count_by_purpose_resource_and_state(
- !TEST_CONN_RSRC_PURPOSE,
- TEST_CONN_RSRC_2,
- !TEST_CONN_STATE)
- == 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ conn->base_.purpose, conn->requested_resource),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ !conn->base_.purpose, ""),
+ OP_EQ, 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ !TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC_2),
+ OP_EQ, 0);
+
+ tt_int_op(connection_dir_count_by_purpose_resource_and_state(
+ conn->base_.purpose, conn->requested_resource,
+ conn->base_.state),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_resource_and_state(
+ TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC, TEST_CONN_STATE),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_resource_and_state(
+ !conn->base_.purpose, "", !conn->base_.state),
+ OP_EQ, 0);
+ tt_int_op(connection_dir_count_by_purpose_resource_and_state(
+ !TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC_2, !TEST_CONN_STATE),
+ OP_EQ, 0);
done:
smartlist_free(the_sl);
@@ -688,117 +591,127 @@ test_conn_download_status(void *arg)
const char *other_res = networkstatus_get_flavor_name(other_flavor);
/* no connections */
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* one connection, not downloading */
conn = test_conn_download_status_add_a_connection(res);
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* one connection, downloading but not linked (not possible on a client,
* but possible on a relay) */
conn->base_.state = TEST_CONN_DL_STATE;
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* one connection, downloading and linked, but not yet attached */
ap_conn = test_conn_get_linked_connection(TO_CONN(conn),
TEST_CONN_UNATTACHED_STATE);
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* one connection, downloading and linked and attached */
ap_conn->state = TEST_CONN_ATTACHED_STATE;
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* one connection, linked and attached but not downloading */
conn->base_.state = TEST_CONN_STATE;
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* two connections, both not downloading */
conn2 = test_conn_download_status_add_a_connection(res);
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 2);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 2);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* two connections, one downloading */
conn->base_.state = TEST_CONN_DL_STATE;
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 2);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 2);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
conn->base_.state = TEST_CONN_STATE;
/* more connections, all not downloading */
/* ignore the return value, it's free'd using the connection list */
(void)test_conn_download_status_add_a_connection(res);
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 3);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 3);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* more connections, one downloading */
conn->base_.state = TEST_CONN_DL_STATE;
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 3);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 3);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* more connections, two downloading (should never happen, but needs
* to be tested for completeness) */
@@ -806,38 +719,41 @@ test_conn_download_status(void *arg)
/* ignore the return value, it's free'd using the connection list */
(void)test_conn_get_linked_connection(TO_CONN(conn2),
TEST_CONN_ATTACHED_STATE);
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 3);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 3);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
conn->base_.state = TEST_CONN_STATE;
/* more connections, a different one downloading */
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 3);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 0);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 3);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 0);
/* a connection for the other flavor (could happen if a client is set to
* cache directory documents), one preferred flavor downloading
*/
conn4 = test_conn_download_status_add_a_connection(other_res);
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 3);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 0);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 3);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 1);
/* a connection for the other flavor (could happen if a client is set to
* cache directory documents), both flavors downloading
@@ -846,14 +762,15 @@ test_conn_download_status(void *arg)
/* ignore the return value, it's free'd using the connection list */
(void)test_conn_get_linked_connection(TO_CONN(conn4),
TEST_CONN_ATTACHED_STATE);
- tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
- tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 1);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- res) == 3);
- tt_assert(connection_dir_count_by_purpose_and_resource(
- TEST_CONN_RSRC_PURPOSE,
- other_res) == 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(res), OP_EQ, 1);
+ tt_int_op(networkstatus_consensus_is_already_downloading(other_res), OP_EQ,
+ 1);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, res),
+ OP_EQ, 3);
+ tt_int_op(connection_dir_count_by_purpose_and_resource(
+ TEST_CONN_RSRC_PURPOSE, other_res),
+ OP_EQ, 1);
done:
/* the teardown function removes all the connections in the global list*/;
diff --git a/src/test/test_connection.h b/src/test/test_connection.h
new file mode 100644
index 0000000000..392783b53b
--- /dev/null
+++ b/src/test/test_connection.h
@@ -0,0 +1,13 @@
+/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/** Some constants used by test_connection and helpers */
+#define TEST_CONN_FAMILY (AF_INET)
+#define TEST_CONN_ADDRESS "127.0.0.1"
+#define TEST_CONN_PORT (12345)
+#define TEST_CONN_ADDRESS_PORT "127.0.0.1:12345"
+#define TEST_CONN_FD_INIT 50
+
+void test_conn_lookup_addr_helper(const char *address,
+ int family, tor_addr_t *addr);
+
diff --git a/src/test/test_conscache.c b/src/test/test_conscache.c
index aee1ba8a06..ddb1bc53c1 100644
--- a/src/test/test_conscache.c
+++ b/src/test/test_conscache.c
@@ -200,7 +200,7 @@ test_conscache_cleanup(void *arg)
tt_assert(e_tmp);
tt_assert(consensus_cache_entry_is_mapped(e_tmp));
e_tmp = consensus_cache_find_first(cache, "index", "7");
- tt_assert(e_tmp == NULL); // not found because pending deletion.
+ tt_ptr_op(e_tmp, OP_EQ, NULL); // not found because pending deletion.
/* Delete the pending-deletion items. */
consensus_cache_delete_pending(cache, 0);
@@ -212,9 +212,9 @@ test_conscache_cleanup(void *arg)
tt_int_op(n, OP_EQ, 20 - 2); /* 1 entry was deleted; 1 is not-found. */
}
e_tmp = consensus_cache_find_first(cache, "index", "7"); // refcnt == 1...
- tt_assert(e_tmp == NULL); // so deleted.
+ tt_ptr_op(e_tmp, OP_EQ, NULL); // so deleted.
e_tmp = consensus_cache_find_first(cache, "index", "14"); // refcnt == 2
- tt_assert(e_tmp == NULL); // not deleted; but not found.
+ tt_ptr_op(e_tmp, OP_EQ, NULL); // not deleted; but not found.
/* Now do lazy unmapping. */
// should do nothing.
diff --git a/src/test/test_consdiff.c b/src/test/test_consdiff.c
index 7cf8d6ba2b..fda3a7f186 100644
--- a/src/test/test_consdiff.c
+++ b/src/test/test_consdiff.c
@@ -19,28 +19,29 @@ test_consdiff_smartlist_slice(void *arg)
{
smartlist_t *sl = smartlist_new();
smartlist_slice_t *sls;
+ int items[6] = {0,0,0,0,0,0};
/* Create a regular smartlist. */
(void)arg;
- smartlist_add(sl, (void*)1);
- smartlist_add(sl, (void*)2);
- smartlist_add(sl, (void*)3);
- smartlist_add(sl, (void*)4);
- smartlist_add(sl, (void*)5);
+ smartlist_add(sl, &items[1]);
+ smartlist_add(sl, &items[2]);
+ smartlist_add(sl, &items[3]);
+ smartlist_add(sl, &items[4]);
+ smartlist_add(sl, &items[5]);
/* See if the slice was done correctly. */
sls = smartlist_slice(sl, 2, 5);
tt_ptr_op(sl, OP_EQ, sls->list);
- tt_ptr_op((void*)3, OP_EQ, smartlist_get(sls->list, sls->offset));
- tt_ptr_op((void*)5, OP_EQ,
+ tt_ptr_op(&items[3], OP_EQ, smartlist_get(sls->list, sls->offset));
+ tt_ptr_op(&items[5], OP_EQ,
smartlist_get(sls->list, sls->offset + (sls->len-1)));
tor_free(sls);
/* See that using -1 as the end does get to the last element. */
sls = smartlist_slice(sl, 2, -1);
tt_ptr_op(sl, OP_EQ, sls->list);
- tt_ptr_op((void*)3, OP_EQ, smartlist_get(sls->list, sls->offset));
- tt_ptr_op((void*)5, OP_EQ,
+ tt_ptr_op(&items[3], OP_EQ, smartlist_get(sls->list, sls->offset));
+ tt_ptr_op(&items[5], OP_EQ,
smartlist_get(sls->list, sls->offset + (sls->len-1)));
done:
@@ -1107,7 +1108,7 @@ test_consdiff_apply_diff(void *arg)
tt_ptr_op(NULL, OP_EQ, cons2);
expect_log_msg_containing("Could not compute digests of the consensus "
"resulting from applying a consensus diff.");
-#endif
+#endif /* 0 */
/* Very simple test, only to see that nothing errors. */
smartlist_clear(diff);
diff --git a/src/test/test_consdiffmgr.c b/src/test/test_consdiffmgr.c
index 963a6e427a..80d3f943ab 100644
--- a/src/test/test_consdiffmgr.c
+++ b/src/test/test_consdiffmgr.c
@@ -230,7 +230,7 @@ test_consdiffmgr_init_failure(void *arg)
done:
tor_end_capture_bugs_();
}
-#endif
+#endif /* 0 */
static void
test_consdiffmgr_sha3_helper(void *arg)
@@ -325,8 +325,8 @@ test_consdiffmgr_add(void *arg)
tt_mem_op(body, OP_EQ, "quux", 4);
/* Try looking up another entry, but fail */
- tt_assert(NULL == cdm_cache_lookup_consensus(FLAV_MICRODESC, now-60));
- tt_assert(NULL == cdm_cache_lookup_consensus(FLAV_NS, now-61));
+ tt_ptr_op(cdm_cache_lookup_consensus(FLAV_MICRODESC, now - 60), OP_EQ, NULL);
+ tt_ptr_op(cdm_cache_lookup_consensus(FLAV_NS, now - 61), OP_EQ, NULL);
done:
networkstatus_vote_free(ns_tmp);
diff --git a/src/test/test_containers.c b/src/test/test_containers.c
index 54484a2a91..c4dba73750 100644
--- a/src/test/test_containers.c
+++ b/src/test/test_containers.c
@@ -510,22 +510,22 @@ test_container_smartlist_pos(void *arg)
smartlist_add_strdup(sl, "function");
/* Test string_pos */
- tt_int_op(smartlist_string_pos(NULL, "Fred"), ==, -1);
- tt_int_op(smartlist_string_pos(sl, "Fred"), ==, -1);
- tt_int_op(smartlist_string_pos(sl, "This"), ==, 0);
- tt_int_op(smartlist_string_pos(sl, "a"), ==, 2);
- tt_int_op(smartlist_string_pos(sl, "function"), ==, 6);
+ tt_int_op(smartlist_string_pos(NULL, "Fred"), OP_EQ, -1);
+ tt_int_op(smartlist_string_pos(sl, "Fred"), OP_EQ, -1);
+ tt_int_op(smartlist_string_pos(sl, "This"), OP_EQ, 0);
+ tt_int_op(smartlist_string_pos(sl, "a"), OP_EQ, 2);
+ tt_int_op(smartlist_string_pos(sl, "function"), OP_EQ, 6);
/* Test pos */
- tt_int_op(smartlist_pos(NULL, "Fred"), ==, -1);
- tt_int_op(smartlist_pos(sl, "Fred"), ==, -1);
- tt_int_op(smartlist_pos(sl, "This"), ==, -1);
- tt_int_op(smartlist_pos(sl, "a"), ==, -1);
- tt_int_op(smartlist_pos(sl, "function"), ==, -1);
- tt_int_op(smartlist_pos(sl, smartlist_get(sl,0)), ==, 0);
- tt_int_op(smartlist_pos(sl, smartlist_get(sl,2)), ==, 2);
- tt_int_op(smartlist_pos(sl, smartlist_get(sl,5)), ==, 5);
- tt_int_op(smartlist_pos(sl, smartlist_get(sl,6)), ==, 6);
+ tt_int_op(smartlist_pos(NULL, "Fred"), OP_EQ, -1);
+ tt_int_op(smartlist_pos(sl, "Fred"), OP_EQ, -1);
+ tt_int_op(smartlist_pos(sl, "This"), OP_EQ, -1);
+ tt_int_op(smartlist_pos(sl, "a"), OP_EQ, -1);
+ tt_int_op(smartlist_pos(sl, "function"), OP_EQ, -1);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,0)), OP_EQ, 0);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,2)), OP_EQ, 2);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,5)), OP_EQ, 5);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,6)), OP_EQ, 6);
done:
SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
@@ -681,7 +681,7 @@ test_container_pqueue(void *arg)
{
smartlist_t *sl = smartlist_new();
int (*cmp)(const void *, const void*);
- const int offset = STRUCT_OFFSET(pq_entry_t, idx);
+ const int offset = offsetof(pq_entry_t, idx);
#define ENTRY(s) pq_entry_t s = { #s, -1 }
ENTRY(cows);
ENTRY(zebras);
@@ -990,13 +990,13 @@ test_container_order_functions(void *arg)
tt_assert(15 == median_time(times, 5));
int32_t int32s[] = { -5, -10, -50, 100 };
- tt_int_op(-5, ==, median_int32(int32s, 1));
- tt_int_op(-10, ==, median_int32(int32s, 2));
- tt_int_op(-10, ==, median_int32(int32s, 3));
- tt_int_op(-10, ==, median_int32(int32s, 4));
+ tt_int_op(-5, OP_EQ, median_int32(int32s, 1));
+ tt_int_op(-10, OP_EQ, median_int32(int32s, 2));
+ tt_int_op(-10, OP_EQ, median_int32(int32s, 3));
+ tt_int_op(-10, OP_EQ, median_int32(int32s, 4));
long longs[] = { -30, 30, 100, -100, 7 };
- tt_int_op(7, ==, find_nth_long(longs, 5, 2));
+ tt_int_op(7, OP_EQ, find_nth_long(longs, 5, 2));
done:
;
@@ -1106,7 +1106,7 @@ test_container_fp_pair_map(void *arg)
tt_int_op(fp_pair_map_size(map),OP_EQ, 4);
fp_pair_map_assert_ok(map);
fp_pair_map_set(map, &fp5, v104);
- fp_pair_map_set(map, &fp6, v105);
+ fp_pair_map_set_by_digests(map, fp6.first, fp6.second, v105);
fp_pair_map_assert_ok(map);
/* Test iterator. */
@@ -1124,7 +1124,8 @@ test_container_fp_pair_map(void *arg)
/* Make sure we removed fp2, but not the others. */
tt_ptr_op(fp_pair_map_get(map, &fp2),OP_EQ, NULL);
- tt_ptr_op(fp_pair_map_get(map, &fp5),OP_EQ, v104);
+ tt_ptr_op(fp_pair_map_get_by_digests(map, fp5.first, fp5.second),
+ OP_EQ, v104);
fp_pair_map_assert_ok(map);
/* Clean up after ourselves. */
@@ -1153,31 +1154,31 @@ test_container_smartlist_most_frequent(void *arg)
const char *cp;
cp = smartlist_get_most_frequent_string_(sl, &count);
- tt_int_op(count, ==, 0);
- tt_ptr_op(cp, ==, NULL);
+ tt_int_op(count, OP_EQ, 0);
+ tt_ptr_op(cp, OP_EQ, NULL);
/* String must be sorted before we call get_most_frequent */
smartlist_split_string(sl, "abc:def:ghi", ":", 0, 0);
cp = smartlist_get_most_frequent_string_(sl, &count);
- tt_int_op(count, ==, 1);
- tt_str_op(cp, ==, "ghi"); /* Ties broken in favor of later element */
+ tt_int_op(count, OP_EQ, 1);
+ tt_str_op(cp, OP_EQ, "ghi"); /* Ties broken in favor of later element */
smartlist_split_string(sl, "def:ghi", ":", 0, 0);
smartlist_sort_strings(sl);
cp = smartlist_get_most_frequent_string_(sl, &count);
- tt_int_op(count, ==, 2);
- tt_ptr_op(cp, !=, NULL);
- tt_str_op(cp, ==, "ghi"); /* Ties broken in favor of later element */
+ tt_int_op(count, OP_EQ, 2);
+ tt_ptr_op(cp, OP_NE, NULL);
+ tt_str_op(cp, OP_EQ, "ghi"); /* Ties broken in favor of later element */
smartlist_split_string(sl, "def:abc:qwop", ":", 0, 0);
smartlist_sort_strings(sl);
cp = smartlist_get_most_frequent_string_(sl, &count);
- tt_int_op(count, ==, 3);
- tt_ptr_op(cp, !=, NULL);
- tt_str_op(cp, ==, "def"); /* No tie */
+ tt_int_op(count, OP_EQ, 3);
+ tt_ptr_op(cp, OP_NE, NULL);
+ tt_str_op(cp, OP_EQ, "def"); /* No tie */
done:
SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
@@ -1206,7 +1207,7 @@ test_container_smartlist_sort_ptrs(void *arg)
smartlist_shuffle(sl);
smartlist_sort_pointers(sl);
for (j = 0; j < ARRAY_LENGTH(arrayptrs); ++j) {
- tt_ptr_op(smartlist_get(sl, j), ==, arrayptrs[j]);
+ tt_ptr_op(smartlist_get(sl, j), OP_EQ, arrayptrs[j]);
}
}
@@ -1232,11 +1233,11 @@ test_container_smartlist_strings_eq(void *arg)
} while (0)
/* Both NULL, so equal */
- tt_int_op(1, ==, smartlist_strings_eq(NULL, NULL));
+ tt_int_op(1, OP_EQ, smartlist_strings_eq(NULL, NULL));
/* One NULL, not equal. */
- tt_int_op(0, ==, smartlist_strings_eq(NULL, sl1));
- tt_int_op(0, ==, smartlist_strings_eq(sl1, NULL));
+ tt_int_op(0, OP_EQ, smartlist_strings_eq(NULL, sl1));
+ tt_int_op(0, OP_EQ, smartlist_strings_eq(sl1, NULL));
/* Both empty, both equal. */
EQ_SHOULD_SAY("", "", 1);
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 592f91a988..472fcb8c53 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -31,7 +31,7 @@ test_add_onion_helper_keyarg(void *arg)
tt_assert(pk);
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
tt_assert(key_new_blob);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "BEST" key generation (Assumes BEST = RSA1024). */
crypto_pk_free(pk);
@@ -41,7 +41,7 @@ test_add_onion_helper_keyarg(void *arg)
tt_assert(pk);
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
tt_assert(key_new_blob);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test discarding the private key. */
crypto_pk_free(pk);
@@ -49,17 +49,17 @@ test_add_onion_helper_keyarg(void *arg)
pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
&err_msg);
tt_assert(pk);
- tt_assert(!key_new_alg);
- tt_assert(!key_new_blob);
- tt_assert(!err_msg);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test generating a invalid key type. */
crypto_pk_free(pk);
pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
&err_msg);
- tt_assert(!pk);
- tt_assert(!key_new_alg);
- tt_assert(!key_new_blob);
+ tt_ptr_op(pk, OP_EQ, NULL);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
/* Test loading a RSA1024 key. */
@@ -70,10 +70,10 @@ test_add_onion_helper_keyarg(void *arg)
pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
&err_msg);
tt_assert(pk2);
- tt_assert(!key_new_alg);
- tt_assert(!key_new_blob);
- tt_assert(!err_msg);
- tt_assert(crypto_pk_cmp_keys(pk, pk2) == 0);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
+ tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0);
/* Test loading a invalid key type. */
tor_free(arg_str);
@@ -81,9 +81,9 @@ test_add_onion_helper_keyarg(void *arg)
tor_asprintf(&arg_str, "RSA512:%s", encoded);
pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
&err_msg);
- tt_assert(!pk);
- tt_assert(!key_new_alg);
- tt_assert(!key_new_blob);
+ tt_ptr_op(pk, OP_EQ, NULL);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
/* Test loading a invalid key. */
@@ -94,9 +94,9 @@ test_add_onion_helper_keyarg(void *arg)
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
&err_msg);
- tt_assert(!pk);
- tt_assert(!key_new_alg);
- tt_assert(!key_new_blob);
+ tt_ptr_op(pk, OP_EQ, NULL);
+ tt_ptr_op(key_new_alg, OP_EQ, NULL);
+ tt_ptr_op(key_new_blob, OP_EQ, NULL);
tt_assert(err_msg);
done:
@@ -123,13 +123,13 @@ test_getinfo_helper_onion(void *arg)
/* successfully get an empty answer */
rt = getinfo_helper_onions(&dummy, "onions/current", &answer, &errmsg);
- tt_assert(rt == 0);
+ tt_int_op(rt, OP_EQ, 0);
tt_str_op(answer, OP_EQ, "");
tor_free(answer);
/* successfully get an empty answer */
rt = getinfo_helper_onions(&dummy, "onions/detached", &answer, &errmsg);
- tt_assert(rt == 0);
+ tt_int_op(rt, OP_EQ, 0);
tt_str_op(answer, OP_EQ, "");
tor_free(answer);
@@ -138,7 +138,7 @@ test_getinfo_helper_onion(void *arg)
dummy.ephemeral_onion_services = smartlist_new();
smartlist_add(dummy.ephemeral_onion_services, service_id);
rt = getinfo_helper_onions(&dummy, "onions/current", &answer, &errmsg);
- tt_assert(rt == 0);
+ tt_int_op(rt, OP_EQ, 0);
tt_str_op(answer, OP_EQ, "dummy_onion_id");
done:
@@ -159,25 +159,25 @@ test_rend_service_parse_port_config(void *arg)
/* Test "VIRTPORT" only. */
cfg = rend_service_parse_port_config("80", sep, &err_msg);
tt_assert(cfg);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "VIRTPORT,TARGET" (Target is port). */
rend_service_port_config_free(cfg);
cfg = rend_service_parse_port_config("80,8080", sep, &err_msg);
tt_assert(cfg);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "VIRTPORT,TARGET" (Target is IPv4:port). */
rend_service_port_config_free(cfg);
cfg = rend_service_parse_port_config("80,192.0.2.1:8080", sep, &err_msg);
tt_assert(cfg);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "VIRTPORT,TARGET" (Target is IPv6:port). */
rend_service_port_config_free(cfg);
cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg);
tt_assert(cfg);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
rend_service_port_config_free(cfg);
cfg = NULL;
@@ -186,13 +186,13 @@ test_rend_service_parse_port_config(void *arg)
/* Test empty config. */
rend_service_port_config_free(cfg);
cfg = rend_service_parse_port_config("", sep, &err_msg);
- tt_assert(!cfg);
+ tt_ptr_op(cfg, OP_EQ, NULL);
tt_assert(err_msg);
/* Test invalid port. */
tor_free(err_msg);
cfg = rend_service_parse_port_config("90001", sep, &err_msg);
- tt_assert(!cfg);
+ tt_ptr_op(cfg, OP_EQ, NULL);
tt_assert(err_msg);
tor_free(err_msg);
@@ -204,7 +204,7 @@ test_rend_service_parse_port_config(void *arg)
cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
" ", &err_msg);
tt_assert(cfg);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
rend_service_port_config_free(cfg);
cfg = NULL;
@@ -213,14 +213,14 @@ test_rend_service_parse_port_config(void *arg)
cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
" ", &err_msg);
tt_assert(cfg);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
rend_service_port_config_free(cfg);
cfg = NULL;
/* quoted unix port, missing end quote */
cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar",
" ", &err_msg);
- tt_assert(!cfg);
+ tt_ptr_op(cfg, OP_EQ, NULL);
tt_str_op(err_msg, OP_EQ, "Couldn't process address <unix:\"/tmp/foo bar> "
"from hidden service configuration");
tor_free(err_msg);
@@ -230,7 +230,7 @@ test_rend_service_parse_port_config(void *arg)
cfg = rend_service_parse_port_config("100 foo!!.example.com:9000",
" ", &err_msg);
UNMOCK(tor_addr_lookup);
- tt_assert(!cfg);
+ tt_ptr_op(cfg, OP_EQ, NULL);
tt_str_op(err_msg, OP_EQ, "Unparseable address in hidden service port "
"configuration.");
tor_free(err_msg);
@@ -238,7 +238,7 @@ test_rend_service_parse_port_config(void *arg)
/* bogus port port */
cfg = rend_service_parse_port_config("100 99999",
" ", &err_msg);
- tt_assert(!cfg);
+ tt_ptr_op(cfg, OP_EQ, NULL);
tt_str_op(err_msg, OP_EQ, "Unparseable or out-of-range port \"99999\" "
"in hidden service port configuration.");
tor_free(err_msg);
@@ -261,7 +261,7 @@ test_add_onion_helper_clientauth(void *arg)
client = add_onion_helper_clientauth("alice", &created, &err_msg);
tt_assert(client);
tt_assert(created);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
rend_authorized_client_free(client);
/* Test "ClientName:Blob" */
@@ -269,26 +269,26 @@ test_add_onion_helper_clientauth(void *arg)
&created, &err_msg);
tt_assert(client);
tt_assert(!created);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
rend_authorized_client_free(client);
/* Test invalid client names */
client = add_onion_helper_clientauth("no*asterisks*allowed", &created,
&err_msg);
- tt_assert(!client);
+ tt_ptr_op(client, OP_EQ, NULL);
tt_assert(err_msg);
tor_free(err_msg);
/* Test invalid auth cookie */
client = add_onion_helper_clientauth("alice:12345", &created, &err_msg);
- tt_assert(!client);
+ tt_ptr_op(client, OP_EQ, NULL);
tt_assert(err_msg);
tor_free(err_msg);
/* Test invalid syntax */
client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created,
&err_msg);
- tt_assert(!client);
+ tt_ptr_op(client, OP_EQ, NULL);
tt_assert(err_msg);
tor_free(err_msg);
@@ -544,7 +544,7 @@ cert_dl_status_def_for_auth_mock(const char *digest)
download_status_t *dl = NULL;
char digest_str[HEX_DIGEST_LEN+1];
- tt_assert(digest != NULL);
+ tt_ptr_op(digest, OP_NE, NULL);
base16_encode(digest_str, HEX_DIGEST_LEN + 1,
digest, DIGEST_LEN);
digest_str[HEX_DIGEST_LEN] = '\0';
@@ -568,7 +568,7 @@ cert_dl_status_sks_for_auth_id_mock(const char *digest)
char *tmp;
int len;
- tt_assert(digest != NULL);
+ tt_ptr_op(digest, OP_NE, NULL);
base16_encode(digest_str, HEX_DIGEST_LEN + 1,
digest, DIGEST_LEN);
digest_str[HEX_DIGEST_LEN] = '\0';
@@ -622,11 +622,11 @@ cert_dl_status_fp_sk_mock(const char *fp_digest, const char *sk_digest)
* dl status we want.
*/
- tt_assert(fp_digest != NULL);
+ tt_ptr_op(fp_digest, OP_NE, NULL);
base16_encode(fp_digest_str, HEX_DIGEST_LEN + 1,
fp_digest, DIGEST_LEN);
fp_digest_str[HEX_DIGEST_LEN] = '\0';
- tt_assert(sk_digest != NULL);
+ tt_ptr_op(sk_digest, OP_NE, NULL);
base16_encode(sk_digest_str, HEX_DIGEST_LEN + 1,
sk_digest, DIGEST_LEN);
sk_digest_str[HEX_DIGEST_LEN] = '\0';
@@ -706,7 +706,7 @@ descbr_get_dl_by_digest_mock(const char *digest)
char digest_str[HEX_DIGEST_LEN+1];
if (!disable_descbr) {
- tt_assert(digest != NULL);
+ tt_ptr_op(digest, OP_NE, NULL);
base16_encode(digest_str, HEX_DIGEST_LEN + 1,
digest, DIGEST_LEN);
digest_str[HEX_DIGEST_LEN] = '\0';
@@ -773,7 +773,7 @@ test_download_status_consensus(void *arg)
/* Check that the unknown prefix case works; no mocks needed yet */
getinfo_helper_downloads(&dummy, "downloads/foo", &answer, &errmsg);
- tt_assert(answer == NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
tt_str_op(errmsg, OP_EQ, "Unknown download status query");
setup_ns_mocks();
@@ -788,8 +788,8 @@ test_download_status_consensus(void *arg)
sizeof(download_status_t));
getinfo_helper_downloads(&dummy, "downloads/networkstatus/ns",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_1_str);
tor_free(answer);
errmsg = NULL;
@@ -799,8 +799,8 @@ test_download_status_consensus(void *arg)
sizeof(download_status_t));
getinfo_helper_downloads(&dummy, "downloads/networkstatus/microdesc",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_2_str);
tor_free(answer);
errmsg = NULL;
@@ -810,8 +810,8 @@ test_download_status_consensus(void *arg)
sizeof(download_status_t));
getinfo_helper_downloads(&dummy, "downloads/networkstatus/ns/bootstrap",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_3_str);
tor_free(answer);
errmsg = NULL;
@@ -822,8 +822,8 @@ test_download_status_consensus(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/networkstatus/microdesc/bootstrap",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_4_str);
tor_free(answer);
errmsg = NULL;
@@ -834,8 +834,8 @@ test_download_status_consensus(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/networkstatus/ns/running",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_5_str);
tor_free(answer);
errmsg = NULL;
@@ -846,8 +846,8 @@ test_download_status_consensus(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/networkstatus/microdesc/running",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_6_str);
tor_free(answer);
errmsg = NULL;
@@ -855,8 +855,8 @@ test_download_status_consensus(void *arg)
/* Now check the error case */
getinfo_helper_downloads(&dummy, "downloads/networkstatus/foo",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "Unknown flavor");
errmsg = NULL;
@@ -890,8 +890,8 @@ test_download_status_cert(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/cert/fps",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, auth_id_digest_expected_list);
tor_free(answer);
errmsg = NULL;
@@ -900,10 +900,10 @@ test_download_status_cert(void *arg)
memcpy(&auth_def_cert_download_status_1, &dls_sample_1,
sizeof(download_status_t));
tor_asprintf(&question, "downloads/cert/fp/%s", auth_id_digest_1_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_1_str);
tor_free(question);
tor_free(answer);
@@ -913,10 +913,10 @@ test_download_status_cert(void *arg)
memcpy(&auth_def_cert_download_status_2, &dls_sample_2,
sizeof(download_status_t));
tor_asprintf(&question, "downloads/cert/fp/%s", auth_id_digest_2_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_2_str);
tor_free(question);
tor_free(answer);
@@ -924,10 +924,10 @@ test_download_status_cert(void *arg)
/* Case 4 - list of signing key digests for 1st auth id */
tor_asprintf(&question, "downloads/cert/fp/%s/sks", auth_id_digest_1_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, auth_1_sk_digest_expected_list);
tor_free(question);
tor_free(answer);
@@ -935,10 +935,10 @@ test_download_status_cert(void *arg)
/* Case 5 - list of signing key digests for 2nd auth id */
tor_asprintf(&question, "downloads/cert/fp/%s/sks", auth_id_digest_2_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, auth_2_sk_digest_expected_list);
tor_free(question);
tor_free(answer);
@@ -949,10 +949,10 @@ test_download_status_cert(void *arg)
sizeof(download_status_t));
tor_asprintf(&question, "downloads/cert/fp/%s/%s",
auth_id_digest_1_str, auth_1_sk_1_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_3_str);
tor_free(question);
tor_free(answer);
@@ -963,10 +963,10 @@ test_download_status_cert(void *arg)
sizeof(download_status_t));
tor_asprintf(&question, "downloads/cert/fp/%s/%s",
auth_id_digest_1_str, auth_1_sk_2_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_4_str);
tor_free(question);
tor_free(answer);
@@ -977,10 +977,10 @@ test_download_status_cert(void *arg)
sizeof(download_status_t));
tor_asprintf(&question, "downloads/cert/fp/%s/%s",
auth_id_digest_2_str, auth_2_sk_1_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_5_str);
tor_free(question);
tor_free(answer);
@@ -991,10 +991,10 @@ test_download_status_cert(void *arg)
sizeof(download_status_t));
tor_asprintf(&question, "downloads/cert/fp/%s/%s",
auth_id_digest_2_str, auth_2_sk_2_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_6_str);
tor_free(question);
tor_free(answer);
@@ -1005,8 +1005,8 @@ test_download_status_cert(void *arg)
/* Case 1 - query is garbage after downloads/cert/ part */
getinfo_helper_downloads(&dummy, "downloads/cert/blahdeblah",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "Unknown certificate download status query");
errmsg = NULL;
@@ -1016,8 +1016,8 @@ test_download_status_cert(void *arg)
*/
getinfo_helper_downloads(&dummy, "downloads/cert/fp/2B1D36D32B2942406",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
errmsg = NULL;
@@ -1028,8 +1028,8 @@ test_download_status_cert(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/cert/fp/82F52AF55D250115FE44D3GC81D49643241D56A1",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
errmsg = NULL;
@@ -1040,8 +1040,8 @@ test_download_status_cert(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ,
"Failed to get download status for this authority identity digest");
errmsg = NULL;
@@ -1053,8 +1053,8 @@ test_download_status_cert(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/cert/fp/82F52AF55D250115FE44D3GC81D49643241D56A1/blah",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like an identity digest");
errmsg = NULL;
@@ -1065,8 +1065,8 @@ test_download_status_cert(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/cert/fp/82F52AF55D25/blah",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like an identity digest");
errmsg = NULL;
@@ -1077,8 +1077,8 @@ test_download_status_cert(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/sks",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ,
"Failed to get list of signing key digests for this authority "
"identity digest");
@@ -1092,8 +1092,8 @@ test_download_status_cert(void *arg)
"downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/"
"82F52AF55D250115FE44D3GC81D49643241D56A1",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like a signing key digest");
errmsg = NULL;
@@ -1105,8 +1105,8 @@ test_download_status_cert(void *arg)
"downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/"
"82F52AF55D250115FE44D",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like a signing key digest");
errmsg = NULL;
@@ -1118,8 +1118,8 @@ test_download_status_cert(void *arg)
"downloads/cert/fp/C6B05DF332F74DB9A13498EE3BBC7AA2F69FCB45/"
"3A214FC21AE25B012C2ECCB5F4EC8A3602D0545D",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ,
"Failed to get download status for this identity/"
"signing key digest pair");
@@ -1133,8 +1133,8 @@ test_download_status_cert(void *arg)
"downloads/cert/fp/63CDD326DFEF0CA020BDD3FEB45A3286FE13A061/"
"3A214FC21AE25B012C2ECCB5F4EC8A3602D0545D",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ,
"Failed to get download status for this identity/"
"signing key digest pair");
@@ -1148,8 +1148,8 @@ test_download_status_cert(void *arg)
"downloads/cert/fp/63CDD326DFEF0CA020BDD3FEB45A3286FE13A061/"
"9451B8F1B10952384EB58B5F230C0BB701626C9B",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ,
"Failed to get download status for this identity/"
"signing key digest pair");
@@ -1185,8 +1185,8 @@ test_download_status_desc(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/desc/descs",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, descbr_expected_list);
tor_free(answer);
errmsg = NULL;
@@ -1195,10 +1195,10 @@ test_download_status_desc(void *arg)
memcpy(&descbr_digest_1_dl, &dls_sample_1,
sizeof(download_status_t));
tor_asprintf(&question, "downloads/desc/%s", descbr_digest_1_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_1_str);
tor_free(question);
tor_free(answer);
@@ -1208,10 +1208,10 @@ test_download_status_desc(void *arg)
memcpy(&descbr_digest_2_dl, &dls_sample_2,
sizeof(download_status_t));
tor_asprintf(&question, "downloads/desc/%s", descbr_digest_2_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_2_str);
tor_free(question);
tor_free(answer);
@@ -1222,8 +1222,8 @@ test_download_status_desc(void *arg)
/* Case 1 - non-digest-length garbage after downloads/desc */
getinfo_helper_downloads(&dummy, "downloads/desc/blahdeblah",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "Unknown router descriptor download status query");
errmsg = NULL;
@@ -1232,8 +1232,8 @@ test_download_status_desc(void *arg)
&dummy,
"downloads/desc/774EC52FD9A5B80A6FACZE536616E8022E3470AG",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
errmsg = NULL;
@@ -1242,8 +1242,8 @@ test_download_status_desc(void *arg)
&dummy,
"downloads/desc/B05B46135B0B2C04EBE1DD6A6AE4B12D7CD2226A",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "No such descriptor digest found");
errmsg = NULL;
@@ -1252,8 +1252,8 @@ test_download_status_desc(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/desc/descs",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ,
"We don't seem to have a networkstatus-flavored consensus");
errmsg = NULL;
@@ -1289,8 +1289,8 @@ test_download_status_bridge(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/bridge/bridges",
&answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, descbr_expected_list);
tor_free(answer);
errmsg = NULL;
@@ -1299,10 +1299,10 @@ test_download_status_bridge(void *arg)
memcpy(&descbr_digest_1_dl, &dls_sample_3,
sizeof(download_status_t));
tor_asprintf(&question, "downloads/bridge/%s", descbr_digest_1_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_3_str);
tor_free(question);
tor_free(answer);
@@ -1312,10 +1312,10 @@ test_download_status_bridge(void *arg)
memcpy(&descbr_digest_2_dl, &dls_sample_4,
sizeof(download_status_t));
tor_asprintf(&question, "downloads/bridge/%s", descbr_digest_2_str);
- tt_assert(question != NULL);
+ tt_ptr_op(question, OP_NE, NULL);
getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
- tt_assert(answer != NULL);
- tt_assert(errmsg == NULL);
+ tt_ptr_op(answer, OP_NE, NULL);
+ tt_ptr_op(errmsg, OP_EQ, NULL);
tt_str_op(answer, OP_EQ, dls_sample_4_str);
tor_free(question);
tor_free(answer);
@@ -1326,8 +1326,8 @@ test_download_status_bridge(void *arg)
/* Case 1 - non-digest-length garbage after downloads/bridge */
getinfo_helper_downloads(&dummy, "downloads/bridge/blahdeblah",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "Unknown bridge descriptor download status query");
errmsg = NULL;
@@ -1336,8 +1336,8 @@ test_download_status_bridge(void *arg)
&dummy,
"downloads/bridge/774EC52FD9A5B80A6FACZE536616E8022E3470AG",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
errmsg = NULL;
@@ -1346,8 +1346,8 @@ test_download_status_bridge(void *arg)
&dummy,
"downloads/bridge/B05B46135B0B2C04EBE1DD6A6AE4B12D7CD2226A",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "No such bridge identity digest found");
errmsg = NULL;
@@ -1356,8 +1356,8 @@ test_download_status_bridge(void *arg)
getinfo_helper_downloads(&dummy,
"downloads/bridge/bridges",
&answer, &errmsg);
- tt_assert(answer == NULL);
- tt_assert(errmsg != NULL);
+ tt_ptr_op(answer, OP_EQ, NULL);
+ tt_ptr_op(errmsg, OP_NE, NULL);
tt_str_op(errmsg, OP_EQ, "We don't seem to be using bridges");
errmsg = NULL;
disable_descbr = 0;
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 75cd30ebb5..83d97f2867 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -1139,7 +1139,7 @@ test_crypto_mac_sha3(void *arg)
result = crypto_digest256(hmac_manual, all, all_len, DIGEST_SHA3_256);
tor_free(key_msg_concat);
tor_free(all);
- tt_int_op(result, ==, 0);
+ tt_int_op(result, OP_EQ, 0);
}
/* Now compare the two results */
@@ -1208,12 +1208,12 @@ test_crypto_pk(void *arg)
tt_assert(! crypto_pk_write_private_key_to_filename(pk1,
get_fname("pkey1")));
/* failing case for read: can't read. */
- tt_assert(crypto_pk_read_private_key_from_filename(pk2,
- get_fname("xyzzy")) < 0);
+ tt_int_op(crypto_pk_read_private_key_from_filename(pk2, get_fname("xyzzy")),
+ OP_LT, 0);
write_str_to_file(get_fname("xyzzy"), "foobar", 6);
/* Failing case for read: no key. */
- tt_assert(crypto_pk_read_private_key_from_filename(pk2,
- get_fname("xyzzy")) < 0);
+ tt_int_op(crypto_pk_read_private_key_from_filename(pk2, get_fname("xyzzy")),
+ OP_LT, 0);
tt_assert(! crypto_pk_read_private_key_from_filename(pk2,
get_fname("pkey1")));
tt_int_op(15,OP_EQ,
@@ -1245,17 +1245,17 @@ test_crypto_pk(void *arg)
i = crypto_pk_asn1_encode(pk1, data1, 1024);
tt_int_op(i, OP_GT, 0);
pk2 = crypto_pk_asn1_decode(data1, i);
- tt_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
+ tt_int_op(crypto_pk_cmp_keys(pk1, pk2), OP_EQ, 0);
/* Try with hybrid encryption wrappers. */
crypto_rand(data1, 1024);
for (i = 85; i < 140; ++i) {
memset(data2,0,1024);
memset(data3,0,1024);
- len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2),
+ len = crypto_pk_obsolete_public_hybrid_encrypt(pk1,data2,sizeof(data2),
data1,i,PK_PKCS1_OAEP_PADDING,0);
tt_int_op(len, OP_GE, 0);
- len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3),
+ len = crypto_pk_obsolete_private_hybrid_decrypt(pk1,data3,sizeof(data3),
data2,len,PK_PKCS1_OAEP_PADDING,1);
tt_int_op(len,OP_EQ, i);
tt_mem_op(data1,OP_EQ, data3,i);
@@ -1264,9 +1264,9 @@ test_crypto_pk(void *arg)
/* Try copy_full */
crypto_pk_free(pk2);
pk2 = crypto_pk_copy_full(pk1);
- tt_assert(pk2 != NULL);
+ tt_ptr_op(pk2, OP_NE, NULL);
tt_ptr_op(pk1, OP_NE, pk2);
- tt_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
+ tt_int_op(crypto_pk_cmp_keys(pk1, pk2), OP_EQ, 0);
done:
if (pk1)
@@ -1344,17 +1344,17 @@ test_crypto_pk_base64(void *arg)
/* Test decoding a valid key. */
pk2 = crypto_pk_base64_decode(encoded, strlen(encoded));
tt_assert(pk2);
- tt_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
+ tt_int_op(crypto_pk_cmp_keys(pk1, pk2), OP_EQ, 0);
crypto_pk_free(pk2);
/* Test decoding a invalid key (not Base64). */
static const char *invalid_b64 = "The key is in another castle!";
pk2 = crypto_pk_base64_decode(invalid_b64, strlen(invalid_b64));
- tt_assert(!pk2);
+ tt_ptr_op(pk2, OP_EQ, NULL);
/* Test decoding a truncated Base64 blob. */
pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)/2);
- tt_assert(!pk2);
+ tt_ptr_op(pk2, OP_EQ, NULL);
done:
crypto_pk_free(pk1);
@@ -1423,7 +1423,7 @@ do_truncate(const char *fname, size_t len)
tor_free(bytes);
return r;
}
-#endif
+#endif /* defined(HAVE_TRUNCATE) */
/** Sanity check for crypto pk digests */
static void
@@ -1446,6 +1446,7 @@ test_crypto_digests(void *arg)
AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
r = crypto_pk_get_common_digests(k, &pkey_digests);
+ tt_int_op(r, OP_EQ, 0);
tt_mem_op(hex_str(pkey_digests.d[DIGEST_SHA1], DIGEST_LEN),OP_EQ,
AUTHORITY_SIGNKEY_A_DIGEST, HEX_DIGEST_LEN);
@@ -1535,7 +1536,7 @@ test_crypto_formats(void *arg)
tt_mem_op(data1,OP_EQ, data3, DIGEST_LEN);
tt_int_op(99,OP_EQ, data3[DIGEST_LEN+1]);
- tt_assert(digest_from_base64(data3, "###") < 0);
+ tt_int_op(digest_from_base64(data3, "###"), OP_LT, 0);
/* Encoding SHA256 */
crypto_rand(data2, DIGEST256_LEN);
@@ -1946,7 +1947,7 @@ test_crypto_curve25519_impl(void *arg)
"e0544770bc7de853b38f9100489e3e79";
const char e1e2k_expected[] = "cd6e8269104eb5aaee886bd2071fba88"
"bd13861475516bc2cd2b6e005e805064";
-#else
+#else /* !(defined(SLOW_CURVE25519_TEST)) */
const int loop_max=200;
const char e1_expected[] = "bc7112cde03f97ef7008cad1bdc56be3"
"c6a1037d74cceb3712e9206871dcf654";
@@ -1954,7 +1955,7 @@ test_crypto_curve25519_impl(void *arg)
"8e3ee1a63c7d14274ea5d4c67f065467";
const char e1e2k_expected[] = "7ddb98bd89025d2347776b33901b3e7e"
"c0ee98cb2257a4545c0cfb2ca3e1812b";
-#endif
+#endif /* defined(SLOW_CURVE25519_TEST) */
unsigned char e1k[32];
unsigned char e2k[32];
@@ -2210,6 +2211,9 @@ test_crypto_ed25519_simple(void *arg)
tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub1, &sec1));
tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub2, &sec1));
+ tt_int_op(ed25519_validate_pubkey(&pub1), OP_EQ, 0);
+ tt_int_op(ed25519_validate_pubkey(&pub2), OP_EQ, 0);
+
tt_mem_op(pub1.pubkey, OP_EQ, pub2.pubkey, sizeof(pub1.pubkey));
tt_assert(ed25519_pubkey_eq(&pub1, &pub2));
tt_assert(ed25519_pubkey_eq(&pub1, &pub1));
@@ -2581,6 +2585,39 @@ test_crypto_ed25519_blinding(void *arg)
;
}
+/** Test that our blinding functions will fail if we pass them bad pubkeys */
+static void
+test_crypto_ed25519_blinding_fail(void *arg)
+{
+ int retval;
+ uint8_t param[32] = {2};
+ ed25519_public_key_t pub;
+ ed25519_public_key_t pub_blinded;
+
+ (void)arg;
+
+ /* This point is not on the curve: the blind routines should fail */
+ const char badkey[] =
+ "e19c65de75c68cf3b7643ea732ba9eb1a3d20d6d57ba223c2ece1df66feb5af0";
+ retval = base16_decode((char*)pub.pubkey, sizeof(pub.pubkey),
+ badkey, strlen(badkey));
+ tt_int_op(retval, OP_EQ, sizeof(pub.pubkey));
+ retval = ed25519_public_blind(&pub_blinded, &pub, param);
+ tt_int_op(retval, OP_EQ, -1);
+
+ /* This point is legit: blind routines should be happy */
+ const char goodkey[] =
+ "4ba2e44760dff4c559ef3c38768c1c14a8a54740c782c8d70803e9d6e3ad8794";
+ retval = base16_decode((char*)pub.pubkey, sizeof(pub.pubkey),
+ goodkey, strlen(goodkey));
+ tt_int_op(retval, OP_EQ, sizeof(pub.pubkey));
+ retval = ed25519_public_blind(&pub_blinded, &pub, param);
+ tt_int_op(retval, OP_EQ, 0);
+
+ done:
+ ;
+}
+
static void
test_crypto_ed25519_testvectors(void *arg)
{
@@ -2598,6 +2635,8 @@ test_crypto_ed25519_testvectors(void *arg)
ed25519_signature_t sig;
int sign;
+ memset(&curvekp, 0xd0, sizeof(curvekp));
+
#define DECODE(p,s) base16_decode((char*)(p),sizeof(p),(s),strlen(s))
#define EQ(a,h) test_memeq_hex((const char*)(a), (h))
@@ -2679,8 +2718,8 @@ test_crypto_ed25519_storage(void *arg)
tor_free(tag);
/* whitebox test: truncated keys. */
- tt_int_op(0, ==, do_truncate(fname_1, 40));
- tt_int_op(0, ==, do_truncate(fname_2, 40));
+ tt_int_op(0, OP_EQ, do_truncate(fname_1, 40));
+ tt_int_op(0, OP_EQ, do_truncate(fname_2, 40));
tt_int_op(-1, OP_EQ, ed25519_pubkey_read_from_file(&pub, &tag, fname_2));
tt_ptr_op(tag, OP_EQ, NULL);
tor_free(tag);
@@ -2872,6 +2911,67 @@ crypto_rand_check_failure_mode_predict(void)
#undef FAILURE_MODE_BUFFER_SIZE
+/** Test that our ed25519 validation function rejects evil public keys and
+ * accepts good ones. */
+static void
+test_crypto_ed25519_validation(void *arg)
+{
+ (void) arg;
+
+ int retval;
+ ed25519_public_key_t pub1;
+
+ /* See https://lists.torproject.org/pipermail/tor-dev/2017-April/012230.html
+ for a list of points with torsion components in ed25519. */
+
+ { /* Point with torsion component (order 8l) */
+ const char badkey[] =
+ "300ef2e64e588e1df55b48e4da0416ffb64cc85d5b00af6463d5cc6c2b1c185e";
+ retval = base16_decode((char*)pub1.pubkey, sizeof(pub1.pubkey),
+ badkey, strlen(badkey));
+ tt_int_op(retval, OP_EQ, sizeof(pub1.pubkey));
+ tt_int_op(ed25519_validate_pubkey(&pub1), OP_EQ, -1);
+ }
+
+ { /* Point with torsion component (order 4l) */
+ const char badkey[] =
+ "f43e3a046db8749164c6e69b193f1e942c7452e7d888736f40b98093d814d5e7";
+ retval = base16_decode((char*)pub1.pubkey, sizeof(pub1.pubkey),
+ badkey, strlen(badkey));
+ tt_int_op(retval, OP_EQ, sizeof(pub1.pubkey));
+ tt_int_op(ed25519_validate_pubkey(&pub1), OP_EQ, -1);
+ }
+
+ { /* Point with torsion component (order 2l) */
+ const char badkey[] =
+ "c9fff3af0471c28e33e98c2043e44f779d0427b1e37c521a6bddc011ed1869af";
+ retval = base16_decode((char*)pub1.pubkey, sizeof(pub1.pubkey),
+ badkey, strlen(badkey));
+ tt_int_op(retval, OP_EQ, sizeof(pub1.pubkey));
+ tt_int_op(ed25519_validate_pubkey(&pub1), OP_EQ, -1);
+ }
+
+ { /* This point is not even on the curve */
+ const char badkey[] =
+ "e19c65de75c68cf3b7643ea732ba9eb1a3d20d6d57ba223c2ece1df66feb5af0";
+ retval = base16_decode((char*)pub1.pubkey, sizeof(pub1.pubkey),
+ badkey, strlen(badkey));
+ tt_int_op(retval, OP_EQ, sizeof(pub1.pubkey));
+ tt_int_op(ed25519_validate_pubkey(&pub1), OP_EQ, -1);
+ }
+
+ { /* This one is a good key */
+ const char goodkey[] =
+ "4ba2e44760dff4c559ef3c38768c1c14a8a54740c782c8d70803e9d6e3ad8794";
+ retval = base16_decode((char*)pub1.pubkey, sizeof(pub1.pubkey),
+ goodkey, strlen(goodkey));
+ tt_int_op(retval, OP_EQ, sizeof(pub1.pubkey));
+ tt_int_op(ed25519_validate_pubkey(&pub1), OP_EQ, 0);
+ }
+
+ done: ;
+}
+
static void
test_crypto_failure_modes(void *arg)
{
@@ -2879,17 +2979,17 @@ test_crypto_failure_modes(void *arg)
(void)arg;
rv = crypto_early_init();
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
/* Check random works */
rv = crypto_rand_check_failure_mode_zero();
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
rv = crypto_rand_check_failure_mode_identical();
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
rv = crypto_rand_check_failure_mode_predict();
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
done:
;
@@ -2958,7 +3058,9 @@ struct testcase_t crypto_tests[] = {
ED25519_TEST(encode, 0),
ED25519_TEST(convert, 0),
ED25519_TEST(blinding, 0),
+ ED25519_TEST(blinding_fail, 0),
ED25519_TEST(testvectors, 0),
+ ED25519_TEST(validation, 0),
{ "ed25519_storage", test_crypto_ed25519_storage, 0, NULL, NULL },
{ "siphash", test_crypto_siphash, 0, NULL, NULL },
{ "failure_modes", test_crypto_failure_modes, TT_FORK, NULL, NULL },
diff --git a/src/test/test_crypto_openssl.c b/src/test/test_crypto_openssl.c
index 3d7d2b4639..090cb4242b 100644
--- a/src/test/test_crypto_openssl.c
+++ b/src/test/test_crypto_openssl.c
@@ -26,7 +26,7 @@ test_crypto_rng_engine(void *arg)
memset(&dummy_method, 0, sizeof(dummy_method));
/* We should be a no-op if we're already on RAND_OpenSSL */
- tt_int_op(0, ==, crypto_force_rand_ssleay());
+ tt_int_op(0, OP_EQ, crypto_force_rand_ssleay());
tt_assert(RAND_get_rand_method() == RAND_OpenSSL());
/* We should correct the method if it's a dummy. */
@@ -34,11 +34,11 @@ test_crypto_rng_engine(void *arg)
#ifdef LIBRESSL_VERSION_NUMBER
/* On libressl, you can't override the RNG. */
tt_assert(RAND_get_rand_method() == RAND_OpenSSL());
- tt_int_op(0, ==, crypto_force_rand_ssleay());
+ tt_int_op(0, OP_EQ, crypto_force_rand_ssleay());
#else
tt_assert(RAND_get_rand_method() == &dummy_method);
- tt_int_op(1, ==, crypto_force_rand_ssleay());
-#endif
+ tt_int_op(1, OP_EQ, crypto_force_rand_ssleay());
+#endif /* defined(LIBRESSL_VERSION_NUMBER) */
tt_assert(RAND_get_rand_method() == RAND_OpenSSL());
/* Make sure we aren't calling dummy_method */
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
index 75c6ba9aaa..2afb71ff5a 100644
--- a/src/test/test_crypto_slow.c
+++ b/src/test/test_crypto_slow.c
@@ -164,10 +164,10 @@ test_libscrypt_eq_openssl(void *arg)
EVP_PBE_scrypt((const char *)"", 0, (const unsigned char *)"", 0,
N, r, p, maxmem, buf2, dk_len);
- tt_int_op(libscrypt_retval, ==, 0);
- tt_int_op(openssl_retval, ==, 1);
+ tt_int_op(libscrypt_retval, OP_EQ, 0);
+ tt_int_op(openssl_retval, OP_EQ, 1);
- tt_mem_op(buf1, ==, buf2, 64);
+ tt_mem_op(buf1, OP_EQ, buf2, 64);
memset(buf1,0,64);
memset(buf2,0,64);
@@ -185,10 +185,10 @@ test_libscrypt_eq_openssl(void *arg)
(const unsigned char *)"NaCl", strlen("NaCl"),
N, r, p, maxmem, buf2, dk_len);
- tt_int_op(libscrypt_retval, ==, 0);
- tt_int_op(openssl_retval, ==, 1);
+ tt_int_op(libscrypt_retval, OP_EQ, 0);
+ tt_int_op(openssl_retval, OP_EQ, 1);
- tt_mem_op(buf1, ==, buf2, 64);
+ tt_mem_op(buf1, OP_EQ, buf2, 64);
memset(buf1,0,64);
memset(buf2,0,64);
@@ -210,10 +210,10 @@ test_libscrypt_eq_openssl(void *arg)
strlen("SodiumChloride"),
N, r, p, maxmem, buf2, dk_len);
- tt_int_op(libscrypt_retval, ==, 0);
- tt_int_op(openssl_retval, ==, 1);
+ tt_int_op(libscrypt_retval, OP_EQ, 0);
+ tt_int_op(openssl_retval, OP_EQ, 1);
- tt_mem_op(buf1, ==, buf2, 64);
+ tt_mem_op(buf1, OP_EQ, buf2, 64);
memset(buf1,0,64);
memset(buf2,0,64);
@@ -234,15 +234,15 @@ test_libscrypt_eq_openssl(void *arg)
strlen("SodiumChloride"),
N, r, p, maxmem, buf2, dk_len);
- tt_int_op(libscrypt_retval, ==, 0);
- tt_int_op(openssl_retval, ==, 1);
+ tt_int_op(libscrypt_retval, OP_EQ, 0);
+ tt_int_op(openssl_retval, OP_EQ, 1);
- tt_mem_op(buf1, ==, buf2, 64);
+ tt_mem_op(buf1, OP_EQ, buf2, 64);
done:
return;
}
-#endif
+#endif /* defined(HAVE_LIBSCRYPT) && defined(HAVE_EVP_PBE_SCRYPT) */
static void
test_crypto_s2k_errors(void *arg)
@@ -283,7 +283,7 @@ test_crypto_s2k_errors(void *arg)
"ABC", 3, 0));
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 50, &sz,
"ABC", 3, S2K_FLAG_LOW_MEM));
-#endif
+#endif /* defined(HAVE_LIBSCRYPT) */
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 37, &sz,
"ABC", 3, S2K_FLAG_USE_PBKDF2));
tt_int_op(S2K_TRUNCATED, OP_EQ, secret_to_key_new(buf, 29, &sz,
@@ -318,7 +318,7 @@ test_crypto_s2k_errors(void *arg)
tt_int_op(S2K_BAD_PARAMS, OP_EQ,
secret_to_key_derivekey(buf2, sizeof(buf2),
buf, 19, "ABC", 3));
-#endif
+#endif /* defined(HAVE_LIBSCRYPT) */
done:
;
@@ -516,7 +516,7 @@ test_crypto_ed25519_fuzz_donna(void *arg)
unsigned i;
(void)arg;
- tt_assert(sizeof(msg) == iters);
+ tt_uint_op(iters, OP_EQ, sizeof(msg));
crypto_rand((char*) msg, sizeof(msg));
/* Fuzz Ed25519-donna vs ref10, alternating the implementation used to
@@ -600,7 +600,7 @@ struct testcase_t slow_crypto_tests[] = {
#ifdef HAVE_EVP_PBE_SCRYPT
{ "libscrypt_eq_openssl", test_libscrypt_eq_openssl, 0, NULL, NULL },
#endif
-#endif
+#endif /* defined(HAVE_LIBSCRYPT) */
{ "s2k_pbkdf2", test_crypto_s2k_general, 0, &passthrough_setup,
(void*)"pbkdf2" },
{ "s2k_rfc2440_general", test_crypto_s2k_general, 0, &passthrough_setup,
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 1ead6d78c2..b21ce5048c 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -18,6 +18,7 @@
#define RELAY_PRIVATE
#include "or.h"
+#include "bridges.h"
#include "confparse.h"
#include "config.h"
#include "control.h"
@@ -275,8 +276,8 @@ test_dir_formats(void *arg)
tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate);
tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst);
tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity);
- tt_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0);
- tt_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
+ tt_int_op(crypto_pk_cmp_keys(rp1->onion_pkey, pk1), OP_EQ, 0);
+ tt_int_op(crypto_pk_cmp_keys(rp1->identity_pkey, pk2), OP_EQ, 0);
tt_assert(rp1->supports_tunnelled_dir_requests);
//tt_assert(rp1->exit_policy == NULL);
tor_free(buf);
@@ -294,9 +295,9 @@ test_dir_formats(void *arg)
strlcat(buf2, "master-key-ed25519 ", sizeof(buf2));
{
char k[ED25519_BASE64_LEN+1];
- tt_assert(ed25519_public_to_base64(k,
- &r2->cache_info.signing_key_cert->signing_key)
- >= 0);
+ tt_int_op(ed25519_public_to_base64(k,
+ &r2->cache_info.signing_key_cert->signing_key),
+ OP_GE, 0);
strlcat(buf2, k, sizeof(buf2));
strlcat(buf2, "\n", sizeof(buf2));
}
@@ -392,8 +393,8 @@ test_dir_formats(void *arg)
tt_mem_op(rp2->onion_curve25519_pkey->public_key,OP_EQ,
r2->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN);
- tt_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
- tt_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
+ tt_int_op(crypto_pk_cmp_keys(rp2->onion_pkey, pk2), OP_EQ, 0);
+ tt_int_op(crypto_pk_cmp_keys(rp2->identity_pkey, pk1), OP_EQ, 0);
tt_assert(rp2->supports_tunnelled_dir_requests);
tt_int_op(smartlist_len(rp2->exit_policy),OP_EQ, 2);
@@ -422,7 +423,7 @@ test_dir_formats(void *arg)
add_fingerprint_to_dir(buf, fingerprint_list, 0);
}
-#endif
+#endif /* 0 */
dirserv_free_fingerprint_list();
done:
@@ -478,34 +479,34 @@ test_dir_routerinfo_parsing(void *arg)
routerinfo_free(ri);
ri = router_parse_entry_from_string(EX_RI_MINIMAL, NULL, 0, 0,
"@purpose bridge\n", NULL);
- tt_assert(ri != NULL);
+ tt_ptr_op(ri, OP_NE, NULL);
tt_assert(ri->purpose == ROUTER_PURPOSE_BRIDGE);
routerinfo_free(ri);
/* bad annotations prepended. */
ri = router_parse_entry_from_string(EX_RI_MINIMAL,
NULL, 0, 0, "@purpose\n", NULL);
- tt_assert(ri == NULL);
+ tt_ptr_op(ri, OP_EQ, NULL);
/* bad annotations on router. */
ri = router_parse_entry_from_string("@purpose\nrouter x\n", NULL, 0, 1,
NULL, NULL);
- tt_assert(ri == NULL);
+ tt_ptr_op(ri, OP_EQ, NULL);
/* unwanted annotations on router. */
ri = router_parse_entry_from_string("@purpose foo\nrouter x\n", NULL, 0, 0,
NULL, NULL);
- tt_assert(ri == NULL);
+ tt_ptr_op(ri, OP_EQ, NULL);
/* No signature. */
ri = router_parse_entry_from_string("router x\n", NULL, 0, 0,
NULL, NULL);
- tt_assert(ri == NULL);
+ tt_ptr_op(ri, OP_EQ, NULL);
/* Not a router */
routerinfo_free(ri);
ri = router_parse_entry_from_string("hello\n", NULL, 0, 0, NULL, NULL);
- tt_assert(ri == NULL);
+ tt_ptr_op(ri, OP_EQ, NULL);
CHECK_FAIL(EX_RI_BAD_SIG1, 1);
CHECK_FAIL(EX_RI_BAD_SIG2, 1);
@@ -632,11 +633,11 @@ test_dir_extrainfo_parsing(void *arg)
ADD(EX_EI_ED_MISPLACED_SIG);
CHECK_OK(EX_EI_MINIMAL);
- tt_assert(!ei->pending_sig);
+ tt_ptr_op(ei->pending_sig, OP_EQ, NULL);
CHECK_OK(EX_EI_MAXIMAL);
- tt_assert(!ei->pending_sig);
+ tt_ptr_op(ei->pending_sig, OP_EQ, NULL);
CHECK_OK(EX_EI_GOOD_ED_EI);
- tt_assert(!ei->pending_sig);
+ tt_ptr_op(ei->pending_sig, OP_EQ, NULL);
CHECK_FAIL(EX_EI_BAD_SIG1,1);
CHECK_FAIL(EX_EI_BAD_SIG2,1);
@@ -1537,12 +1538,12 @@ test_dir_measured_bw_kb(void *arg)
(void)arg;
for (i = 0; strcmp(lines_fail[i], "end"); i++) {
//fprintf(stderr, "Testing: %s\n", lines_fail[i]);
- tt_assert(measured_bw_line_parse(&mbwl, lines_fail[i]) == -1);
+ tt_int_op(measured_bw_line_parse(&mbwl, lines_fail[i]), OP_EQ, -1);
}
for (i = 0; strcmp(lines_pass[i], "end"); i++) {
//fprintf(stderr, "Testing: %s %d\n", lines_pass[i], TOR_ISSPACE('\n'));
- tt_assert(measured_bw_line_parse(&mbwl, lines_pass[i]) == 0);
+ tt_int_op(measured_bw_line_parse(&mbwl, lines_pass[i]), OP_EQ, 0);
tt_assert(mbwl.bw_kb == 1024);
tt_assert(strcmp(mbwl.node_hex,
"557365204145532d32353620696e73746561642e") == 0);
@@ -1804,7 +1805,7 @@ test_dir_param_voting_lookup(void *arg)
dirvote_get_intermediate_param_value(lst, "moomin", -100));
tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1);
tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ,
- "!(n_found > 1)");
+ "n_found == 0");
tor_end_capture_bugs_();
/* There is no 'fred=', so that is treated as not existing. */
tt_int_op(-100, OP_EQ,
@@ -1892,8 +1893,7 @@ vote_tweaks_for_v3ns(networkstatus_t *v, int voter, time_t now)
measured_bw_line_t mbw;
memset(mbw.node_id, 33, sizeof(mbw.node_id));
mbw.bw_kb = 1024;
- tt_assert(measured_bw_line_apply(&mbw,
- v->routerstatus_list) == 1);
+ tt_int_op(measured_bw_line_apply(&mbw, v->routerstatus_list), OP_EQ, 1);
} else if (voter == 2 || voter == 3) {
/* Monkey around with the list a bit */
vrs = smartlist_get(v->routerstatus_list, 2);
@@ -2009,7 +2009,7 @@ test_consensus_for_v3ns(networkstatus_t *con, time_t now)
(void)now;
tt_assert(con);
- tt_assert(!con->cert);
+ tt_ptr_op(con->cert, OP_EQ, NULL);
tt_int_op(2,OP_EQ, smartlist_len(con->routerstatus_list));
/* There should be two listed routers: one with identity 3, one with
* identity 5. */
@@ -2079,7 +2079,7 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
/* XXXX check version */
} else {
/* Weren't expecting this... */
- tt_assert(0);
+ tt_abort();
}
done:
@@ -2271,6 +2271,7 @@ test_dir_networkstatus_compute_bw_weights_v10(void *arg)
tt_i64_op(G+M+E+D, OP_EQ, T);
ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T,
weight_scale);
+ tt_assert(ret);
tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=883 Wbe=0 "
"Wbg=3673 Wbm=10000 Wdb=10000 Web=10000 Wed=8233 Wee=10000 Weg=8233 "
"Wem=10000 Wgb=10000 Wgd=883 Wgg=6327 Wgm=6327 Wmb=10000 Wmd=883 Wme=0 "
@@ -2287,6 +2288,7 @@ test_dir_networkstatus_compute_bw_weights_v10(void *arg)
tt_i64_op(G+M+E+D, OP_EQ, T);
ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T,
weight_scale);
+ tt_assert(ret);
tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 "
"Wbg=4194 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 "
"Wem=10000 Wgb=10000 Wgd=0 Wgg=5806 Wgm=5806 Wmb=10000 Wmd=0 Wme=0 "
@@ -2303,6 +2305,7 @@ test_dir_networkstatus_compute_bw_weights_v10(void *arg)
tt_i64_op(G+M+E+D, OP_EQ, T);
ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T,
weight_scale);
+ tt_assert(ret);
tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=317 "
"Wbe=5938 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=9366 Wee=4061 "
"Weg=9366 Wem=4061 Wgb=10000 Wgd=317 Wgg=10000 Wgm=10000 Wmb=10000 "
@@ -2323,6 +2326,7 @@ test_dir_networkstatus_compute_bw_weights_v10(void *arg)
"Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=3333 Wee=10000 Weg=3333 "
"Wem=10000 Wgb=10000 Wgd=3333 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=3333 "
"Wme=0 Wmg=0 Wmm=10000\n");
+ tt_assert(ret);
done:
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
@@ -2394,6 +2398,7 @@ test_a_networkstatus(
sign_skey_2 = crypto_pk_new();
sign_skey_3 = crypto_pk_new();
sign_skey_leg1 = pk_generate(4);
+ dirvote_recalculate_timing(get_options(), now);
sr_state_init(0, 0);
tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
@@ -3023,7 +3028,7 @@ gen_routerstatus_for_umbw(int idx, time_t now)
break;
default:
/* Shouldn't happen */
- tt_assert(0);
+ tt_abort();
}
if (vrs) {
vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
@@ -3163,7 +3168,7 @@ test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now)
tt_int_op(rs->bandwidth_kb,OP_EQ, max_unmeasured_bw_kb / 2);
tt_int_op(vrs->measured_bw_kb,OP_EQ, 0);
} else {
- tt_assert(0);
+ tt_abort();
}
done:
@@ -3179,9 +3184,9 @@ test_consensus_for_umbw(networkstatus_t *con, time_t now)
(void)now;
tt_assert(con);
- tt_assert(!con->cert);
+ tt_ptr_op(con->cert, OP_EQ, NULL);
// tt_assert(con->consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW_KB);
- tt_assert(con->consensus_method >= 16);
+ tt_int_op(con->consensus_method, OP_GE, 16);
tt_int_op(4,OP_EQ, smartlist_len(con->routerstatus_list));
/* There should be four listed routers; all voters saw the same in this */
@@ -3278,7 +3283,7 @@ test_routerstatus_for_umbw(routerstatus_t *rs, time_t now)
tt_assert(rs->bw_is_unmeasured);
} else {
/* Weren't expecting this... */
- tt_assert(0);
+ tt_abort();
}
done:
@@ -3385,7 +3390,7 @@ mock_get_options(void)
static void
reset_routerstatus(routerstatus_t *rs,
const char *hex_identity_digest,
- int32_t ipv4_addr)
+ uint32_t ipv4_addr)
{
memset(rs, 0, sizeof(routerstatus_t));
base16_decode(rs->identity_digest, sizeof(rs->identity_digest),
@@ -3446,15 +3451,17 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
* Return values are {2, 3, 4} */
/* We want 3 ("*" means match all addresses) */
- tt_assert(routerset_contains_routerstatus(routerset_all, rs_a, 0) == 3);
- tt_assert(routerset_contains_routerstatus(routerset_all, rs_b, 0) == 3);
+ tt_int_op(routerset_contains_routerstatus(routerset_all, rs_a, 0), OP_EQ, 3);
+ tt_int_op(routerset_contains_routerstatus(routerset_all, rs_b, 0), OP_EQ, 3);
/* We want 4 (match id_digest [or nickname]) */
- tt_assert(routerset_contains_routerstatus(routerset_a, rs_a, 0) == 4);
- tt_assert(routerset_contains_routerstatus(routerset_a, rs_b, 0) == 0);
+ tt_int_op(routerset_contains_routerstatus(routerset_a, rs_a, 0), OP_EQ, 4);
+ tt_int_op(routerset_contains_routerstatus(routerset_a, rs_b, 0), OP_EQ, 0);
- tt_assert(routerset_contains_routerstatus(routerset_none, rs_a, 0) == 0);
- tt_assert(routerset_contains_routerstatus(routerset_none, rs_b, 0) == 0);
+ tt_int_op(routerset_contains_routerstatus(routerset_none, rs_a, 0), OP_EQ,
+ 0);
+ tt_int_op(routerset_contains_routerstatus(routerset_none, rs_b, 0), OP_EQ,
+ 0);
/* Check that "*" sets flags on all routers: Exit
* Check the flags aren't being confused with each other */
@@ -3466,17 +3473,17 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
mock_options->TestingDirAuthVoteExitIsStrict = 0;
dirserv_set_routerstatus_testing(rs_a);
- tt_assert(mock_get_options_calls == 1);
+ tt_int_op(mock_get_options_calls, OP_EQ, 1);
dirserv_set_routerstatus_testing(rs_b);
- tt_assert(mock_get_options_calls == 2);
+ tt_int_op(mock_get_options_calls, OP_EQ, 2);
- tt_assert(rs_a->is_exit == 1);
- tt_assert(rs_b->is_exit == 1);
+ tt_uint_op(rs_a->is_exit, OP_EQ, 1);
+ tt_uint_op(rs_b->is_exit, OP_EQ, 1);
/* Be paranoid - check no other flags are set */
- tt_assert(rs_a->is_possible_guard == 0);
- tt_assert(rs_b->is_possible_guard == 0);
- tt_assert(rs_a->is_hs_dir == 0);
- tt_assert(rs_b->is_hs_dir == 0);
+ tt_uint_op(rs_a->is_possible_guard, OP_EQ, 0);
+ tt_uint_op(rs_b->is_possible_guard, OP_EQ, 0);
+ tt_uint_op(rs_a->is_hs_dir, OP_EQ, 0);
+ tt_uint_op(rs_b->is_hs_dir, OP_EQ, 0);
/* Check that "*" sets flags on all routers: Guard & HSDir
* Cover the remaining flags in one test */
@@ -3490,17 +3497,17 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
mock_options->TestingDirAuthVoteHSDirIsStrict = 0;
dirserv_set_routerstatus_testing(rs_a);
- tt_assert(mock_get_options_calls == 1);
+ tt_int_op(mock_get_options_calls, OP_EQ, 1);
dirserv_set_routerstatus_testing(rs_b);
- tt_assert(mock_get_options_calls == 2);
+ tt_int_op(mock_get_options_calls, OP_EQ, 2);
- tt_assert(rs_a->is_possible_guard == 1);
- tt_assert(rs_b->is_possible_guard == 1);
- tt_assert(rs_a->is_hs_dir == 1);
- tt_assert(rs_b->is_hs_dir == 1);
+ tt_uint_op(rs_a->is_possible_guard, OP_EQ, 1);
+ tt_uint_op(rs_b->is_possible_guard, OP_EQ, 1);
+ tt_uint_op(rs_a->is_hs_dir, OP_EQ, 1);
+ tt_uint_op(rs_b->is_hs_dir, OP_EQ, 1);
/* Be paranoid - check exit isn't set */
- tt_assert(rs_a->is_exit == 0);
- tt_assert(rs_b->is_exit == 0);
+ tt_uint_op(rs_a->is_exit, OP_EQ, 0);
+ tt_uint_op(rs_b->is_exit, OP_EQ, 0);
/* Check routerset A sets all flags on router A,
* but leaves router B unmodified */
@@ -3516,16 +3523,16 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
mock_options->TestingDirAuthVoteHSDirIsStrict = 0;
dirserv_set_routerstatus_testing(rs_a);
- tt_assert(mock_get_options_calls == 1);
+ tt_int_op(mock_get_options_calls, OP_EQ, 1);
dirserv_set_routerstatus_testing(rs_b);
- tt_assert(mock_get_options_calls == 2);
+ tt_int_op(mock_get_options_calls, OP_EQ, 2);
- tt_assert(rs_a->is_exit == 1);
- tt_assert(rs_b->is_exit == 0);
- tt_assert(rs_a->is_possible_guard == 1);
- tt_assert(rs_b->is_possible_guard == 0);
- tt_assert(rs_a->is_hs_dir == 1);
- tt_assert(rs_b->is_hs_dir == 0);
+ tt_uint_op(rs_a->is_exit, OP_EQ, 1);
+ tt_uint_op(rs_b->is_exit, OP_EQ, 0);
+ tt_uint_op(rs_a->is_possible_guard, OP_EQ, 1);
+ tt_uint_op(rs_b->is_possible_guard, OP_EQ, 0);
+ tt_uint_op(rs_a->is_hs_dir, OP_EQ, 1);
+ tt_uint_op(rs_b->is_hs_dir, OP_EQ, 0);
/* Check routerset A unsets all flags on router B when Strict is set */
reset_options(mock_options, &mock_get_options_calls);
@@ -3543,11 +3550,11 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
rs_b->is_hs_dir = 1;
dirserv_set_routerstatus_testing(rs_b);
- tt_assert(mock_get_options_calls == 1);
+ tt_int_op(mock_get_options_calls, OP_EQ, 1);
- tt_assert(rs_b->is_exit == 0);
- tt_assert(rs_b->is_possible_guard == 0);
- tt_assert(rs_b->is_hs_dir == 0);
+ tt_uint_op(rs_b->is_exit, OP_EQ, 0);
+ tt_uint_op(rs_b->is_possible_guard, OP_EQ, 0);
+ tt_uint_op(rs_b->is_hs_dir, OP_EQ, 0);
/* Check routerset A doesn't modify flags on router B without Strict set */
reset_options(mock_options, &mock_get_options_calls);
@@ -3565,11 +3572,11 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
rs_b->is_hs_dir = 1;
dirserv_set_routerstatus_testing(rs_b);
- tt_assert(mock_get_options_calls == 1);
+ tt_int_op(mock_get_options_calls, OP_EQ, 1);
- tt_assert(rs_b->is_exit == 1);
- tt_assert(rs_b->is_possible_guard == 1);
- tt_assert(rs_b->is_hs_dir == 1);
+ tt_uint_op(rs_b->is_exit, OP_EQ, 1);
+ tt_uint_op(rs_b->is_possible_guard, OP_EQ, 1);
+ tt_uint_op(rs_b->is_hs_dir, OP_EQ, 1);
/* Check the empty routerset zeroes all flags
* on routers A & B with Strict set */
@@ -3588,11 +3595,11 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
rs_b->is_hs_dir = 1;
dirserv_set_routerstatus_testing(rs_b);
- tt_assert(mock_get_options_calls == 1);
+ tt_int_op(mock_get_options_calls, OP_EQ, 1);
- tt_assert(rs_b->is_exit == 0);
- tt_assert(rs_b->is_possible_guard == 0);
- tt_assert(rs_b->is_hs_dir == 0);
+ tt_uint_op(rs_b->is_exit, OP_EQ, 0);
+ tt_uint_op(rs_b->is_possible_guard, OP_EQ, 0);
+ tt_uint_op(rs_b->is_hs_dir, OP_EQ, 0);
/* Check the empty routerset doesn't modify any flags
* on A or B without Strict set */
@@ -3612,16 +3619,16 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
rs_b->is_hs_dir = 1;
dirserv_set_routerstatus_testing(rs_a);
- tt_assert(mock_get_options_calls == 1);
+ tt_int_op(mock_get_options_calls, OP_EQ, 1);
dirserv_set_routerstatus_testing(rs_b);
- tt_assert(mock_get_options_calls == 2);
+ tt_int_op(mock_get_options_calls, OP_EQ, 2);
- tt_assert(rs_a->is_exit == 0);
- tt_assert(rs_a->is_possible_guard == 0);
- tt_assert(rs_a->is_hs_dir == 0);
- tt_assert(rs_b->is_exit == 1);
- tt_assert(rs_b->is_possible_guard == 1);
- tt_assert(rs_b->is_hs_dir == 1);
+ tt_uint_op(rs_a->is_exit, OP_EQ, 0);
+ tt_uint_op(rs_a->is_possible_guard, OP_EQ, 0);
+ tt_uint_op(rs_a->is_hs_dir, OP_EQ, 0);
+ tt_uint_op(rs_b->is_exit, OP_EQ, 1);
+ tt_uint_op(rs_b->is_possible_guard, OP_EQ, 1);
+ tt_uint_op(rs_b->is_hs_dir, OP_EQ, 1);
done:
tor_free(mock_options);
@@ -3677,7 +3684,7 @@ test_dir_http_handling(void *args)
"User-Agent: Mozilla/5.0 (Windows;"
" U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n",
&url),OP_EQ, -1);
- tt_assert(!url);
+ tt_ptr_op(url, OP_EQ, NULL);
/* Bad headers */
tt_int_op(parse_http_url("GET /a/b/c.txt\r\n"
@@ -3685,23 +3692,23 @@ test_dir_http_handling(void *args)
"User-Agent: Mozilla/5.0 (Windows;"
" U; Windows NT 6.1; en-US; rv:1.9.1.5)\r\n",
&url),OP_EQ, -1);
- tt_assert(!url);
+ tt_ptr_op(url, OP_EQ, NULL);
tt_int_op(parse_http_url("GET /tor/a/b/c.txt", &url),OP_EQ, -1);
- tt_assert(!url);
+ tt_ptr_op(url, OP_EQ, NULL);
tt_int_op(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1", &url),OP_EQ, -1);
- tt_assert(!url);
+ tt_ptr_op(url, OP_EQ, NULL);
tt_int_op(parse_http_url("GET /tor/a/b/c.txt HTTP/1.1x\r\n", &url),
OP_EQ, -1);
- tt_assert(!url);
+ tt_ptr_op(url, OP_EQ, NULL);
tt_int_op(parse_http_url("GET /tor/a/b/c.txt HTTP/1.", &url),OP_EQ, -1);
- tt_assert(!url);
+ tt_ptr_op(url, OP_EQ, NULL);
tt_int_op(parse_http_url("GET /tor/a/b/c.txt HTTP/1.\r", &url),OP_EQ, -1);
- tt_assert(!url);
+ tt_ptr_op(url, OP_EQ, NULL);
done:
tor_free(url);
@@ -3714,8 +3721,8 @@ test_dir_purpose_needs_anonymity_returns_true_by_default(void *arg)
tor_capture_bugs_(1);
setup_full_capture_of_logs(LOG_WARN);
- tt_int_op(1, ==, purpose_needs_anonymity(0, 0, NULL));
- tt_int_op(1, ==, smartlist_len(tor_get_captured_bug_log_()));
+ tt_int_op(1, OP_EQ, purpose_needs_anonymity(0, 0, NULL));
+ tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_()));
expect_single_log_msg_containing("Called with dir_purpose=0");
tor_end_capture_bugs_();
@@ -3729,11 +3736,12 @@ test_dir_purpose_needs_anonymity_returns_true_for_bridges(void *arg)
{
(void)arg;
- tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE, NULL));
- tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE,
+ tt_int_op(1, OP_EQ, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE, NULL));
+ tt_int_op(1, OP_EQ, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE,
"foobar"));
- tt_int_op(1, ==, purpose_needs_anonymity(DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2,
- ROUTER_PURPOSE_BRIDGE, NULL));
+ tt_int_op(1, OP_EQ,
+ purpose_needs_anonymity(DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2,
+ ROUTER_PURPOSE_BRIDGE, NULL));
done: ;
}
@@ -3741,7 +3749,7 @@ static void
test_dir_purpose_needs_anonymity_returns_false_for_own_bridge_desc(void *arg)
{
(void)arg;
- tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_FETCH_SERVERDESC,
+ tt_int_op(0, OP_EQ, purpose_needs_anonymity(DIR_PURPOSE_FETCH_SERVERDESC,
ROUTER_PURPOSE_BRIDGE,
"authority.z"));
done: ;
@@ -3752,12 +3760,12 @@ test_dir_purpose_needs_anonymity_returns_true_for_sensitive_purpose(void *arg)
{
(void)arg;
- tt_int_op(1, ==, purpose_needs_anonymity(
+ tt_int_op(1, OP_EQ, purpose_needs_anonymity(
DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2,
ROUTER_PURPOSE_GENERAL, NULL));
- tt_int_op(1, ==, purpose_needs_anonymity(
+ tt_int_op(1, OP_EQ, purpose_needs_anonymity(
DIR_PURPOSE_UPLOAD_RENDDESC_V2, 0, NULL));
- tt_int_op(1, ==, purpose_needs_anonymity(
+ tt_int_op(1, OP_EQ, purpose_needs_anonymity(
DIR_PURPOSE_FETCH_RENDDESC_V2, 0, NULL));
done: ;
}
@@ -3767,24 +3775,25 @@ test_dir_purpose_needs_anonymity_ret_false_for_non_sensitive_conn(void *arg)
{
(void)arg;
- tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_DIR,
+ tt_int_op(0, OP_EQ, purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_DIR,
ROUTER_PURPOSE_GENERAL, NULL));
- tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_VOTE, 0, NULL));
- tt_int_op(0, ==,
+ tt_int_op(0, OP_EQ,
+ purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_VOTE, 0, NULL));
+ tt_int_op(0, OP_EQ,
purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_SIGNATURES, 0, NULL));
- tt_int_op(0, ==,
+ tt_int_op(0, OP_EQ,
purpose_needs_anonymity(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL));
- tt_int_op(0, ==, purpose_needs_anonymity(
+ tt_int_op(0, OP_EQ, purpose_needs_anonymity(
DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL));
- tt_int_op(0, ==,
+ tt_int_op(0, OP_EQ,
purpose_needs_anonymity(DIR_PURPOSE_FETCH_CONSENSUS, 0, NULL));
- tt_int_op(0, ==,
+ tt_int_op(0, OP_EQ,
purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0, NULL));
- tt_int_op(0, ==,
+ tt_int_op(0, OP_EQ,
purpose_needs_anonymity(DIR_PURPOSE_FETCH_SERVERDESC, 0, NULL));
- tt_int_op(0, ==,
+ tt_int_op(0, OP_EQ,
purpose_needs_anonymity(DIR_PURPOSE_FETCH_EXTRAINFO, 0, NULL));
- tt_int_op(0, ==,
+ tt_int_op(0, OP_EQ,
purpose_needs_anonymity(DIR_PURPOSE_FETCH_MICRODESC, 0, NULL));
done: ;
}
@@ -3837,9 +3846,9 @@ test_dir_packages(void *arg)
(void)arg;
#define BAD(s) \
- tt_int_op(0, ==, validate_recommended_package_line(s));
+ tt_int_op(0, OP_EQ, validate_recommended_package_line(s));
#define GOOD(s) \
- tt_int_op(1, ==, validate_recommended_package_line(s));
+ tt_int_op(1, OP_EQ, validate_recommended_package_line(s));
GOOD("tor 0.2.6.3-alpha "
"http://torproject.example.com/dist/tor-0.2.6.3-alpha.tar.gz "
"sha256=sssdlkfjdsklfjdskfljasdklfj");
@@ -3956,7 +3965,7 @@ test_dir_packages(void *arg)
res = compute_consensus_package_lines(votes);
tt_assert(res);
- tt_str_op(res, ==,
+ tt_str_op(res, OP_EQ,
"package cbc 99.1.11.1.1 http://example.com/cbc/ cubehash=ahooy sha512=m\n"
"package clownshoes 22alpha3 http://quumble.example.com/ blake2=fooz\n"
"package clownshoes 22alpha4 http://quumble.example.cam/ blake2=fooa\n"
@@ -4128,47 +4137,105 @@ test_dir_download_status_schedule(void *arg)
}
static void
-test_dir_download_status_random_backoff(void *arg)
+download_status_random_backoff_helper(int min_delay, int max_delay)
{
download_status_t dls_random =
{ 0, 0, 0, DL_SCHED_GENERIC, DL_WANT_AUTHORITY,
DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
int increment = -1;
- int old_increment;
+ int old_increment = -1;
time_t current_time = time(NULL);
- const int min_delay = 0;
- const int max_delay = 1000000;
-
- (void)arg;
/* Check the random backoff cases */
- old_increment = 0;
+ int n_attempts = 0;
do {
increment = download_status_schedule_get_delay(&dls_random,
NULL,
min_delay, max_delay,
current_time);
+
+ log_debug(LD_DIR, "Min: %d, Max: %d, Inc: %d, Old Inc: %d",
+ min_delay, max_delay, increment, old_increment);
+
+ /* Regression test for 20534 and friends
+ * increment must always increase after the first */
+ if (dls_random.last_backoff_position > 0 && max_delay > 0) {
+ /* Always increment the exponential backoff */
+ tt_int_op(increment, OP_GE, 1);
+ }
+
/* Test */
tt_int_op(increment, OP_GE, min_delay);
tt_int_op(increment, OP_LE, max_delay);
- tt_int_op(increment, OP_GE, old_increment);
- /* We at most quadruple, and maybe add one */
- tt_int_op(increment, OP_LE, 4 * old_increment + 1);
/* Advance */
- current_time += increment;
- ++(dls_random.n_download_attempts);
- ++(dls_random.n_download_failures);
+ if (dls_random.n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD - 1) {
+ ++(dls_random.n_download_attempts);
+ ++(dls_random.n_download_failures);
+ }
/* Try another maybe */
old_increment = increment;
- } while (increment < max_delay);
+ } while (increment < max_delay && ++n_attempts < 1000);
done:
return;
}
static void
+test_dir_download_status_random_backoff(void *arg)
+{
+ (void)arg;
+
+ /* Do a standard test */
+ download_status_random_backoff_helper(0, 1000000);
+ /* Regression test for 20534 and friends:
+ * try tighter bounds */
+ download_status_random_backoff_helper(0, 100);
+ /* regression tests for 17750: initial delay */
+ download_status_random_backoff_helper(10, 1000);
+ download_status_random_backoff_helper(20, 30);
+
+ /* Pathological cases */
+ download_status_random_backoff_helper(0, 0);
+ download_status_random_backoff_helper(1, 1);
+ download_status_random_backoff_helper(0, INT_MAX);
+ download_status_random_backoff_helper(INT_MAX/2, INT_MAX);
+}
+
+static void
+test_dir_download_status_random_backoff_ranges(void *arg)
+{
+ (void)arg;
+ int lo, hi;
+ next_random_exponential_delay_range(&lo, &hi, 0, 10);
+ tt_int_op(lo, OP_EQ, 10);
+ tt_int_op(hi, OP_EQ, 11);
+
+ next_random_exponential_delay_range(&lo, &hi, 6, 10);
+ tt_int_op(lo, OP_EQ, 10);
+ tt_int_op(hi, OP_EQ, 6*3);
+
+ next_random_exponential_delay_range(&lo, &hi, 13, 10);
+ tt_int_op(lo, OP_EQ, 10);
+ tt_int_op(hi, OP_EQ, 13 * 3);
+
+ next_random_exponential_delay_range(&lo, &hi, 37, 10);
+ tt_int_op(lo, OP_EQ, 10);
+ tt_int_op(hi, OP_EQ, 111);
+
+ next_random_exponential_delay_range(&lo, &hi, 123, 10);
+ tt_int_op(lo, OP_EQ, 10);
+ tt_int_op(hi, OP_EQ, 369);
+
+ next_random_exponential_delay_range(&lo, &hi, INT_MAX-5, 10);
+ tt_int_op(lo, OP_EQ, 10);
+ tt_int_op(hi, OP_EQ, INT_MAX);
+ done:
+ ;
+}
+
+static void
test_dir_download_status_increment(void *arg)
{
(void)arg;
@@ -4180,32 +4247,97 @@ test_dir_download_status_increment(void *arg)
DL_WANT_ANY_DIRSERVER,
DL_SCHED_INCREMENT_ATTEMPT,
DL_SCHED_DETERMINISTIC, 0, 0 };
+ download_status_t dls_exp = { 0, 0, 0, DL_SCHED_GENERIC,
+ DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT,
+ DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
+ int no_delay = 0;
int delay0 = -1;
int delay1 = -1;
int delay2 = -1;
smartlist_t *schedule = smartlist_new();
+ smartlist_t *schedule_no_initial_delay = smartlist_new();
or_options_t test_options;
time_t next_at = TIME_MAX;
time_t current_time = time(NULL);
- /* Provide some values for the schedule */
+ /* Provide some values for the schedules */
delay0 = 10;
delay1 = 99;
delay2 = 20;
- /* Make the schedule */
+ /* Make the schedules */
smartlist_add(schedule, (void *)&delay0);
smartlist_add(schedule, (void *)&delay1);
smartlist_add(schedule, (void *)&delay2);
+ smartlist_add(schedule_no_initial_delay, (void *)&no_delay);
+ smartlist_add(schedule_no_initial_delay, (void *)&delay1);
+ smartlist_add(schedule_no_initial_delay, (void *)&delay2);
+
/* Put it in the options */
mock_options = &test_options;
reset_options(mock_options, &mock_get_options_calls);
+ mock_options->TestingBridgeBootstrapDownloadSchedule = schedule;
mock_options->TestingClientDownloadSchedule = schedule;
- mock_options->TestingBridgeDownloadSchedule = schedule;
MOCK(get_options, mock_get_options);
+ /* Check that the initial value of the schedule is the first value used,
+ * whether or not it was reset before being used */
+
+ /* regression test for 17750: no initial delay */
+ mock_options->TestingClientDownloadSchedule = schedule_no_initial_delay;
+ mock_get_options_calls = 0;
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ >= current_time + no_delay);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ != TIME_MAX);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
+
+ /* regression test for 17750: initial delay */
+ mock_options->TestingClientDownloadSchedule = schedule;
+ mock_get_options_calls = 0;
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_failure)
+ != TIME_MAX);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
+
+ /* regression test for 17750: exponential, no initial delay */
+ mock_options->TestingClientDownloadSchedule = schedule_no_initial_delay;
+ mock_get_options_calls = 0;
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_exp)
+ >= current_time + no_delay);
+ tt_assert(download_status_get_next_attempt_at(&dls_exp)
+ != TIME_MAX);
+ tt_int_op(download_status_get_n_failures(&dls_exp), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_exp), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
+
+ /* regression test for 17750: exponential, initial delay */
+ mock_options->TestingClientDownloadSchedule = schedule;
+ mock_get_options_calls = 0;
+ /* we really want to test that it's equal to time(NULL) + delay0, but that's
+ * an unrealiable test, because time(NULL) might change. */
+ tt_assert(download_status_get_next_attempt_at(&dls_exp)
+ >= current_time + delay0);
+ tt_assert(download_status_get_next_attempt_at(&dls_exp)
+ != TIME_MAX);
+ tt_int_op(download_status_get_n_failures(&dls_exp), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_exp), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
+
/* Check that a failure reset works */
mock_get_options_calls = 0;
download_status_reset(&dls_failure);
@@ -4215,76 +4347,76 @@ test_dir_download_status_increment(void *arg)
>= current_time + delay0);
tt_assert(download_status_get_next_attempt_at(&dls_failure)
!= TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_failure) == 0);
- tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* avoid timing inconsistencies */
dls_failure.next_attempt_at = current_time + delay0;
/* check that a reset schedule becomes ready at the right time */
- tt_assert(download_status_is_ready(&dls_failure,
- current_time + delay0 - 1,
- 1) == 0);
- tt_assert(download_status_is_ready(&dls_failure,
- current_time + delay0,
- 1) == 1);
- tt_assert(download_status_is_ready(&dls_failure,
- current_time + delay0 + 1,
- 1) == 1);
+ tt_int_op(download_status_is_ready(&dls_failure,
+ current_time + delay0 - 1, 1),
+ OP_EQ, 0);
+ tt_int_op(download_status_is_ready(&dls_failure,
+ current_time + delay0, 1),
+ OP_EQ, 1);
+ tt_int_op(download_status_is_ready(&dls_failure,
+ current_time + delay0 + 1, 1),
+ OP_EQ, 1);
/* Check that a failure increment works */
mock_get_options_calls = 0;
next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
current_time);
tt_assert(next_at == current_time + delay1);
- tt_assert(download_status_get_n_failures(&dls_failure) == 1);
- tt_assert(download_status_get_n_attempts(&dls_failure) == 1);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 1);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 1);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* check that an incremented schedule becomes ready at the right time */
- tt_assert(download_status_is_ready(&dls_failure,
- current_time + delay1 - 1,
- 1) == 0);
- tt_assert(download_status_is_ready(&dls_failure,
- current_time + delay1,
- 1) == 1);
- tt_assert(download_status_is_ready(&dls_failure,
- current_time + delay1 + 1,
- 1) == 1);
+ tt_int_op(download_status_is_ready(&dls_failure,
+ current_time + delay1 - 1, 1),
+ OP_EQ, 0);
+ tt_int_op(download_status_is_ready(&dls_failure,
+ current_time + delay1, 1),
+ OP_EQ, 1);
+ tt_int_op(download_status_is_ready(&dls_failure,
+ current_time + delay1 + 1, 1),
+ OP_EQ, 1);
/* check that a schedule isn't ready if it's had too many failures */
- tt_assert(download_status_is_ready(&dls_failure,
- current_time + delay1 + 10,
- 0) == 0);
+ tt_int_op(download_status_is_ready(&dls_failure,
+ current_time + delay1 + 10, 0),
+ OP_EQ, 0);
/* Check that failure increments do happen on 503 for clients, and
* attempt increments do too. */
mock_get_options_calls = 0;
next_at = download_status_increment_failure(&dls_failure, 503, "test", 0,
current_time);
- tt_i64_op(next_at, ==, current_time + delay2);
- tt_int_op(download_status_get_n_failures(&dls_failure), ==, 2);
- tt_int_op(download_status_get_n_attempts(&dls_failure), ==, 2);
- tt_assert(mock_get_options_calls >= 1);
+ tt_i64_op(next_at, OP_EQ, current_time + delay2);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 2);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 2);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check that failure increments do happen on 503 for servers */
mock_get_options_calls = 0;
next_at = download_status_increment_failure(&dls_failure, 503, "test", 1,
current_time);
tt_assert(next_at == current_time + delay2);
- tt_assert(download_status_get_n_failures(&dls_failure) == 3);
- tt_assert(download_status_get_n_attempts(&dls_failure) == 3);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 3);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 3);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check what happens when we run off the end of the schedule */
mock_get_options_calls = 0;
next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
current_time);
tt_assert(next_at == current_time + delay2);
- tt_assert(download_status_get_n_failures(&dls_failure) == 4);
- tt_assert(download_status_get_n_attempts(&dls_failure) == 4);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 4);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 4);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check what happens when we hit the failure limit */
mock_get_options_calls = 0;
@@ -4292,22 +4424,22 @@ test_dir_download_status_increment(void *arg)
next_at = download_status_increment_failure(&dls_failure, 404, "test", 0,
current_time);
tt_assert(next_at == TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_failure)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(download_status_get_n_attempts(&dls_failure)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check that a failure reset doesn't reset at the limit */
mock_get_options_calls = 0;
download_status_reset(&dls_failure);
tt_assert(download_status_get_next_attempt_at(&dls_failure)
== TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_failure)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(download_status_get_n_attempts(&dls_failure)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(mock_get_options_calls == 0);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(mock_get_options_calls, OP_EQ, 0);
/* Check that a failure reset resets just before the limit */
mock_get_options_calls = 0;
@@ -4320,19 +4452,20 @@ test_dir_download_status_increment(void *arg)
>= current_time + delay0);
tt_assert(download_status_get_next_attempt_at(&dls_failure)
!= TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_failure) == 0);
- tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check that failure increments do happen on attempt-based schedules,
* but that the retry is set at the end of time */
+ mock_options->UseBridges = 1;
mock_get_options_calls = 0;
next_at = download_status_increment_failure(&dls_attempt, 404, "test", 0,
current_time);
tt_assert(next_at == TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_attempt) == 1);
- tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
- tt_assert(mock_get_options_calls == 0);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 1);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check that an attempt reset works */
mock_get_options_calls = 0;
@@ -4343,65 +4476,65 @@ test_dir_download_status_increment(void *arg)
>= current_time + delay0);
tt_assert(download_status_get_next_attempt_at(&dls_attempt)
!= TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
- tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* avoid timing inconsistencies */
dls_attempt.next_attempt_at = current_time + delay0;
/* check that a reset schedule becomes ready at the right time */
- tt_assert(download_status_is_ready(&dls_attempt,
- current_time + delay0 - 1,
- 1) == 0);
- tt_assert(download_status_is_ready(&dls_attempt,
- current_time + delay0,
- 1) == 1);
- tt_assert(download_status_is_ready(&dls_attempt,
- current_time + delay0 + 1,
- 1) == 1);
+ tt_int_op(download_status_is_ready(&dls_attempt,
+ current_time + delay0 - 1, 1),
+ OP_EQ, 0);
+ tt_int_op(download_status_is_ready(&dls_attempt,
+ current_time + delay0, 1),
+ OP_EQ, 1);
+ tt_int_op(download_status_is_ready(&dls_attempt,
+ current_time + delay0 + 1, 1),
+ OP_EQ, 1);
/* Check that an attempt increment works */
mock_get_options_calls = 0;
next_at = download_status_increment_attempt(&dls_attempt, "test",
current_time);
tt_assert(next_at == current_time + delay1);
- tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
- tt_assert(download_status_get_n_attempts(&dls_attempt) == 1);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 1);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* check that an incremented schedule becomes ready at the right time */
- tt_assert(download_status_is_ready(&dls_attempt,
- current_time + delay1 - 1,
- 1) == 0);
- tt_assert(download_status_is_ready(&dls_attempt,
- current_time + delay1,
- 1) == 1);
- tt_assert(download_status_is_ready(&dls_attempt,
- current_time + delay1 + 1,
- 1) == 1);
+ tt_int_op(download_status_is_ready(&dls_attempt,
+ current_time + delay1 - 1, 1),
+ OP_EQ, 0);
+ tt_int_op(download_status_is_ready(&dls_attempt,
+ current_time + delay1, 1),
+ OP_EQ, 1);
+ tt_int_op(download_status_is_ready(&dls_attempt,
+ current_time + delay1 + 1, 1),
+ OP_EQ, 1);
/* check that a schedule isn't ready if it's had too many attempts */
- tt_assert(download_status_is_ready(&dls_attempt,
- current_time + delay1 + 10,
- 0) == 0);
+ tt_int_op(download_status_is_ready(&dls_attempt,
+ current_time + delay1 + 10, 0),
+ OP_EQ, 0);
/* Check what happens when we reach then run off the end of the schedule */
mock_get_options_calls = 0;
next_at = download_status_increment_attempt(&dls_attempt, "test",
current_time);
tt_assert(next_at == current_time + delay2);
- tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
- tt_assert(download_status_get_n_attempts(&dls_attempt) == 2);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 2);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
mock_get_options_calls = 0;
next_at = download_status_increment_attempt(&dls_attempt, "test",
current_time);
tt_assert(next_at == current_time + delay2);
- tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
- tt_assert(download_status_get_n_attempts(&dls_attempt) == 3);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 3);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check what happens when we hit the attempt limit */
mock_get_options_calls = 0;
@@ -4409,22 +4542,22 @@ test_dir_download_status_increment(void *arg)
next_at = download_status_increment_attempt(&dls_attempt, "test",
current_time);
tt_assert(next_at == TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_attempt)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(download_status_get_n_attempts(&dls_attempt)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
/* Check that an attempt reset doesn't reset at the limit */
mock_get_options_calls = 0;
download_status_reset(&dls_attempt);
tt_assert(download_status_get_next_attempt_at(&dls_attempt)
== TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_attempt)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(download_status_get_n_attempts(&dls_attempt)
- == IMPOSSIBLE_TO_DOWNLOAD);
- tt_assert(mock_get_options_calls == 0);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ,
+ IMPOSSIBLE_TO_DOWNLOAD);
+ tt_int_op(mock_get_options_calls, OP_EQ, 0);
/* Check that an attempt reset resets just before the limit */
mock_get_options_calls = 0;
@@ -4437,9 +4570,10 @@ test_dir_download_status_increment(void *arg)
>= current_time + delay0);
tt_assert(download_status_get_next_attempt_at(&dls_attempt)
!= TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_attempt) == 0);
- tt_assert(download_status_get_n_attempts(&dls_attempt) == 0);
- tt_assert(mock_get_options_calls >= 1);
+ tt_int_op(download_status_get_n_failures(&dls_attempt), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_attempt), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_GE, 1);
+ mock_options->UseBridges = 0;
/* Check that attempt increments don't happen on failure-based schedules,
* and that the attempt is set at the end of time */
@@ -4452,13 +4586,14 @@ test_dir_download_status_increment(void *arg)
"schedule.");
teardown_capture_of_logs();
tt_assert(next_at == TIME_MAX);
- tt_assert(download_status_get_n_failures(&dls_failure) == 0);
- tt_assert(download_status_get_n_attempts(&dls_failure) == 0);
- tt_assert(mock_get_options_calls == 0);
+ tt_int_op(download_status_get_n_failures(&dls_failure), OP_EQ, 0);
+ tt_int_op(download_status_get_n_attempts(&dls_failure), OP_EQ, 0);
+ tt_int_op(mock_get_options_calls, OP_EQ, 0);
done:
/* the pointers in schedule are allocated on the stack */
smartlist_free(schedule);
+ smartlist_free(schedule_no_initial_delay);
UNMOCK(get_options);
mock_options = NULL;
mock_get_options_calls = 0;
@@ -4767,13 +4902,13 @@ mock_get_datadir_fname(const or_options_t *options,
* Assert we were called like get_datadir_fname2() or get_datadir_fname(),
* since that's all we implement here.
*/
- tt_assert(options != NULL);
- tt_assert(sub1 != NULL);
+ tt_ptr_op(options, OP_NE, NULL);
+ tt_ptr_op(sub1, OP_NE, NULL);
/*
* No particular assertions about sub2, since we could be in the
* get_datadir_fname() or get_datadir_fname2() case.
*/
- tt_assert(suffix == NULL);
+ tt_ptr_op(suffix, OP_EQ, NULL);
/* Just duplicate the basename and return it for this mock */
if (sub2) {
@@ -4800,7 +4935,7 @@ mock_unlink_reset(void)
static int
mock_unlink(const char *path)
{
- tt_assert(path != NULL);
+ tt_ptr_op(path, OP_NE, NULL);
tor_free(last_unlinked_path);
last_unlinked_path = tor_strdup(path);
@@ -4829,8 +4964,8 @@ mock_write_str_to_file(const char *path, const char *str, int bin)
(void)bin;
- tt_assert(path != NULL);
- tt_assert(str != NULL);
+ tt_ptr_op(path, OP_NE, NULL);
+ tt_ptr_op(str, OP_NE, NULL);
len = strlen(str);
crypto_digest256((char *)hash, str, len, DIGEST_SHA256);
@@ -4956,7 +5091,7 @@ test_dir_dump_unparseable_descriptors(void *data)
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -4969,21 +5104,21 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1));
+ tt_u64_op(len_descs_dumped, OP_EQ, strlen(test_desc_1));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 1);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 1);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256);
/*
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -4991,8 +5126,8 @@ test_dir_dump_unparseable_descriptors(void *data)
*/
mock_unlink_reset();
mock_write_str_to_file_reset();
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 0);
/*
* (2) Fire off dump_desc() twice; this still should trigger no cleanup.
@@ -5004,14 +5139,14 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2));
+ tt_u64_op(len_descs_dumped, OP_EQ, strlen(test_desc_2));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 1);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 1);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
/* Second time */
@@ -5020,21 +5155,22 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_3));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_2) + strlen(test_desc_3));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
/*
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -5042,8 +5178,8 @@ test_dir_dump_unparseable_descriptors(void *data)
*/
mock_unlink_reset();
mock_write_str_to_file_reset();
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 0);
/*
* (3) Three calls to dump_desc cause a FIFO cleanup
@@ -5055,14 +5191,14 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_4));
+ tt_u64_op(len_descs_dumped, OP_EQ, strlen(test_desc_4));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 1);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 1);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
/* Second time */
@@ -5071,14 +5207,15 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_4) + strlen(test_desc_1));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_4) + strlen(test_desc_1));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256);
/* Third time - we should unlink the dump of test_desc_4 here */
@@ -5087,21 +5224,22 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1) + strlen(test_desc_2));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_1) + strlen(test_desc_2));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 1);
- tt_int_op(write_str_count, ==, 3);
+ tt_int_op(unlinked_count, OP_EQ, 1);
+ tt_int_op(write_str_count, OP_EQ, 3);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
/*
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -5109,8 +5247,8 @@ test_dir_dump_unparseable_descriptors(void *data)
*/
mock_unlink_reset();
mock_write_str_to_file_reset();
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 0);
/*
* (4) But repeating one (A B B) doesn't overflow and cleanup
@@ -5122,14 +5260,14 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3));
+ tt_u64_op(len_descs_dumped, OP_EQ, strlen(test_desc_3));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 1);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 1);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
/* Second time */
@@ -5138,14 +5276,15 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_3) + strlen(test_desc_4));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
/* Third time */
@@ -5154,21 +5293,22 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_3) + strlen(test_desc_4));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
/*
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -5176,8 +5316,8 @@ test_dir_dump_unparseable_descriptors(void *data)
*/
mock_unlink_reset();
mock_write_str_to_file_reset();
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 0);
/*
* (5) Same for the (A B A) repetition
@@ -5189,14 +5329,14 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1));
+ tt_u64_op(len_descs_dumped, OP_EQ, strlen(test_desc_1));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 1);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 1);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256);
/* Second time */
@@ -5205,14 +5345,15 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1) + strlen(test_desc_2));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_1) + strlen(test_desc_2));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
/* Third time */
@@ -5221,21 +5362,22 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_1) + strlen(test_desc_2));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_1) + strlen(test_desc_2));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
/*
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -5243,8 +5385,8 @@ test_dir_dump_unparseable_descriptors(void *data)
*/
mock_unlink_reset();
mock_write_str_to_file_reset();
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 0);
/*
* (6) (A B B C) triggering overflow on C causes A, not B to be unlinked
@@ -5256,14 +5398,14 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3));
+ tt_u64_op(len_descs_dumped, OP_EQ, strlen(test_desc_3));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 1);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 1);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
/* Second time */
@@ -5272,14 +5414,15 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_3) + strlen(test_desc_4));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
/* Third time */
@@ -5288,14 +5431,15 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_3) + strlen(test_desc_4));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_3) + strlen(test_desc_4));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
/* Fourth time - we should unlink the dump of test_desc_3 here */
@@ -5304,21 +5448,22 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_4) + strlen(test_desc_1));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_4) + strlen(test_desc_1));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 1);
- tt_int_op(write_str_count, ==, 3);
+ tt_int_op(unlinked_count, OP_EQ, 1);
+ tt_int_op(write_str_count, OP_EQ, 3);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256);
/*
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -5326,8 +5471,8 @@ test_dir_dump_unparseable_descriptors(void *data)
*/
mock_unlink_reset();
mock_write_str_to_file_reset();
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 0);
/*
* (7) (A B A C) triggering overflow on C causes B, not A to be unlinked
@@ -5339,14 +5484,14 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2));
+ tt_u64_op(len_descs_dumped, OP_EQ, strlen(test_desc_2));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 1);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 1);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 1);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
/* Second time */
@@ -5355,14 +5500,15 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_3));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_2) + strlen(test_desc_3));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
/* Third time */
@@ -5371,14 +5517,15 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_3));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_2) + strlen(test_desc_3));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 2);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 2);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
/* Fourth time - we should unlink the dump of test_desc_3 here */
@@ -5387,21 +5534,22 @@ test_dir_dump_unparseable_descriptors(void *data)
/*
* Assert things about the FIFO state
*/
- tt_u64_op(len_descs_dumped, ==, strlen(test_desc_2) + strlen(test_desc_4));
+ tt_u64_op(len_descs_dumped, OP_EQ,
+ strlen(test_desc_2) + strlen(test_desc_4));
tt_assert(descs_dumped != NULL && smartlist_len(descs_dumped) == 2);
/*
* Assert things about the mocks
*/
- tt_int_op(unlinked_count, ==, 1);
- tt_int_op(write_str_count, ==, 3);
+ tt_int_op(unlinked_count, OP_EQ, 1);
+ tt_int_op(write_str_count, OP_EQ, 3);
tt_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
/*
* Reset the FIFO and check its state
*/
dump_desc_fifo_cleanup();
- tt_u64_op(len_descs_dumped, ==, 0);
+ tt_u64_op(len_descs_dumped, OP_EQ, 0);
tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
/*
@@ -5409,8 +5557,8 @@ test_dir_dump_unparseable_descriptors(void *data)
*/
mock_unlink_reset();
mock_write_str_to_file_reset();
- tt_int_op(unlinked_count, ==, 0);
- tt_int_op(write_str_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
+ tt_int_op(write_str_count, OP_EQ, 0);
done:
@@ -5457,7 +5605,7 @@ read_file_to_str_mock(const char *filename, int flags,
char *result = NULL;
/* Insist we got a filename */
- tt_assert(filename != NULL);
+ tt_ptr_op(filename, OP_NE, NULL);
/* We ignore flags */
(void)flags;
@@ -5520,53 +5668,53 @@ test_dir_populate_dump_desc_fifo(void *data)
reset_read_file_to_str_mock();
/* Check state of unlink mock */
- tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
/* Some cases that should fail before trying to read the file */
ent = dump_desc_populate_one_file(dirname, "bar");
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 1);
- tt_int_op(read_count, ==, 0);
- tt_int_op(read_call_count, ==, 0);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 1);
+ tt_int_op(read_count, OP_EQ, 0);
+ tt_int_op(read_call_count, OP_EQ, 0);
ent = dump_desc_populate_one_file(dirname, "unparseable-desc");
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 2);
- tt_int_op(read_count, ==, 0);
- tt_int_op(read_call_count, ==, 0);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 2);
+ tt_int_op(read_count, OP_EQ, 0);
+ tt_int_op(read_call_count, OP_EQ, 0);
ent = dump_desc_populate_one_file(dirname, "unparseable-desc.baz");
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 3);
- tt_int_op(read_count, ==, 0);
- tt_int_op(read_call_count, ==, 0);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 3);
+ tt_int_op(read_count, OP_EQ, 0);
+ tt_int_op(read_call_count, OP_EQ, 0);
ent = dump_desc_populate_one_file(
dirname,
"unparseable-desc.08AE85E90461F59E");
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 4);
- tt_int_op(read_count, ==, 0);
- tt_int_op(read_call_count, ==, 0);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 4);
+ tt_int_op(read_count, OP_EQ, 0);
+ tt_int_op(read_call_count, OP_EQ, 0);
ent = dump_desc_populate_one_file(
dirname,
"unparseable-desc.08AE85E90461F59EDF0981323F3A70D02B55AB54B44B04F"
"287D72F7B72F242E85C8CB0EDA8854A99");
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 5);
- tt_int_op(read_count, ==, 0);
- tt_int_op(read_call_count, ==, 0);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 5);
+ tt_int_op(read_count, OP_EQ, 0);
+ tt_int_op(read_call_count, OP_EQ, 0);
/* This is a correct-length digest but base16_decode() will fail */
ent = dump_desc_populate_one_file(
dirname,
"unparseable-desc.68219B8BGE64B705A6FFC728C069DC596216D60A7D7520C"
"D5ECE250D912E686B");
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 6);
- tt_int_op(read_count, ==, 0);
- tt_int_op(read_call_count, ==, 0);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 6);
+ tt_int_op(read_count, OP_EQ, 0);
+ tt_int_op(read_call_count, OP_EQ, 0);
/* This one has a correctly formed filename and should try reading */
@@ -5575,10 +5723,10 @@ test_dir_populate_dump_desc_fifo(void *data)
dirname,
"unparseable-desc.DF0981323F3A70D02B55AB54B44B04F287D72F7B72F242E"
"85C8CB0EDA8854A99");
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 7);
- tt_int_op(read_count, ==, 0);
- tt_int_op(read_call_count, ==, 1);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 7);
+ tt_int_op(read_count, OP_EQ, 0);
+ tt_int_op(read_call_count, OP_EQ, 1);
/* This read will succeed but the digest won't match the file content */
fname =
@@ -5591,10 +5739,10 @@ test_dir_populate_dump_desc_fifo(void *data)
file_stat.st_mtime = 123456;
ent = dump_desc_populate_one_file(dirname, fname);
enforce_expected_filename = 0;
- tt_assert(ent == NULL);
- tt_int_op(unlinked_count, ==, 8);
- tt_int_op(read_count, ==, 1);
- tt_int_op(read_call_count, ==, 2);
+ tt_ptr_op(ent, OP_EQ, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 8);
+ tt_int_op(read_count, OP_EQ, 1);
+ tt_int_op(read_call_count, OP_EQ, 2);
tor_free(expected_filename);
tor_free(file_content);
@@ -5607,13 +5755,13 @@ test_dir_populate_dump_desc_fifo(void *data)
file_content_len = strlen(file_content);
file_stat.st_mtime = 789012;
ent = dump_desc_populate_one_file(dirname, fname);
- tt_assert(ent != NULL);
- tt_int_op(unlinked_count, ==, 8);
- tt_int_op(read_count, ==, 2);
- tt_int_op(read_call_count, ==, 3);
+ tt_ptr_op(ent, OP_NE, NULL);
+ tt_int_op(unlinked_count, OP_EQ, 8);
+ tt_int_op(read_count, OP_EQ, 2);
+ tt_int_op(read_call_count, OP_EQ, 3);
tt_str_op(ent->filename, OP_EQ, expected_filename);
- tt_int_op(ent->len, ==, file_content_len);
- tt_int_op(ent->when, ==, file_stat.st_mtime);
+ tt_int_op(ent->len, OP_EQ, file_content_len);
+ tt_int_op(ent->when, OP_EQ, file_stat.st_mtime);
tor_free(ent->filename);
tor_free(ent);
tor_free(expected_filename);
@@ -5622,9 +5770,9 @@ test_dir_populate_dump_desc_fifo(void *data)
* Reset the mocks and check their state
*/
mock_unlink_reset();
- tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(unlinked_count, OP_EQ, 0);
reset_read_file_to_str_mock();
- tt_int_op(read_count, ==, 0);
+ tt_int_op(read_count, OP_EQ, 0);
done:
@@ -5746,9 +5894,18 @@ mock_networkstatus_consensus_can_use_extra_fallbacks(
return mock_networkstatus_consensus_can_use_extra_fallbacks_value;
}
-/* data is a 2 character nul-terminated string.
+static int mock_num_bridges_usable_value = 0;
+static int
+mock_num_bridges_usable(int use_maybe_reachable)
+{
+ (void)use_maybe_reachable;
+ return mock_num_bridges_usable_value;
+}
+
+/* data is a 3 character nul-terminated string.
* If data[0] is 'b', set bootstrapping, anything else means not bootstrapping
* If data[1] is 'f', set extra fallbacks, anything else means no extra
+ * If data[2] is 'f', set running bridges, anything else means no extra
* fallbacks.
*/
static void
@@ -5756,7 +5913,7 @@ test_dir_find_dl_schedule(void* data)
{
const char *str = (const char *)data;
- tt_assert(strlen(data) == 2);
+ tt_assert(strlen(data) == 3);
if (str[0] == 'b') {
mock_networkstatus_consensus_is_bootstrapping_value = 1;
@@ -5770,15 +5927,24 @@ test_dir_find_dl_schedule(void* data)
mock_networkstatus_consensus_can_use_extra_fallbacks_value = 0;
}
+ if (str[2] == 'r') {
+ /* Any positive, non-zero value should work */
+ mock_num_bridges_usable_value = 2;
+ } else {
+ mock_num_bridges_usable_value = 0;
+ }
+
MOCK(networkstatus_consensus_is_bootstrapping,
mock_networkstatus_consensus_is_bootstrapping);
MOCK(networkstatus_consensus_can_use_extra_fallbacks,
mock_networkstatus_consensus_can_use_extra_fallbacks);
+ MOCK(num_bridges_usable,
+ mock_num_bridges_usable);
download_status_t dls;
smartlist_t server, client, server_cons, client_cons;
smartlist_t client_boot_auth_only_cons, client_boot_auth_cons;
- smartlist_t client_boot_fallback_cons, bridge;
+ smartlist_t client_boot_fallback_cons, bridge, bridge_bootstrap;
mock_options = tor_malloc(sizeof(or_options_t));
reset_options(mock_options, &mock_get_options_calls);
@@ -5795,6 +5961,7 @@ test_dir_find_dl_schedule(void* data)
mock_options->ClientBootstrapConsensusFallbackDownloadSchedule =
&client_boot_fallback_cons;
mock_options->TestingBridgeDownloadSchedule = &bridge;
+ mock_options->TestingBridgeBootstrapDownloadSchedule = &bridge_bootstrap;
dls.schedule = DL_SCHED_GENERIC;
/* client */
@@ -5883,11 +6050,17 @@ test_dir_find_dl_schedule(void* data)
dls.schedule = DL_SCHED_BRIDGE;
/* client */
mock_options->ClientOnly = 1;
- tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
+ mock_options->UseBridges = 1;
+ if (num_bridges_usable(0) > 0) {
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
+ } else {
+ tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge_bootstrap);
+ }
done:
UNMOCK(networkstatus_consensus_is_bootstrapping);
UNMOCK(networkstatus_consensus_can_use_extra_fallbacks);
+ UNMOCK(num_bridges_usable);
UNMOCK(get_options);
tor_free(mock_options);
mock_options = NULL;
@@ -5997,6 +6170,29 @@ test_dir_post_parsing(void *arg)
;
}
+static void
+test_dir_platform_str(void *arg)
+{
+ char platform[256];
+ (void)arg;
+ platform[0] = 0;
+ get_platform_str(platform, sizeof(platform));
+ tt_int_op((int)strlen(platform), OP_GT, 0);
+ tt_assert(!strcmpstart(platform, "Tor "));
+
+ tor_version_t ver;
+ // make sure this is a tor version, a real actual tor version.
+ tt_int_op(tor_version_parse_platform(platform, &ver, 1), OP_EQ, 1);
+
+ TT_BLATHER(("%d.%d.%d.%d", ver.major, ver.minor, ver.micro, ver.patchlevel));
+
+ // Handle an example version.
+ tt_int_op(tor_version_parse_platform(
+ "Tor 0.3.3.3 (foo) (git-xyzzy) on a potato", &ver, 1), OP_EQ, 1);
+ done:
+ ;
+}
+
#define DIR_LEGACY(name) \
{ #name, test_dir_ ## name , TT_FORK, NULL, NULL }
@@ -6042,7 +6238,8 @@ struct testcase_t dir_tests[] = {
DIR(packages, 0),
DIR(download_status_schedule, 0),
DIR(download_status_random_backoff, 0),
- DIR(download_status_increment, 0),
+ DIR(download_status_random_backoff_ranges, 0),
+ DIR(download_status_increment, TT_FORK),
DIR(authdir_type_to_string, 0),
DIR(conn_purpose_to_string, 0),
DIR(should_use_directory_guards, 0),
@@ -6053,12 +6250,17 @@ struct testcase_t dir_tests[] = {
DIR(dump_unparseable_descriptors, 0),
DIR(populate_dump_desc_fifo, 0),
DIR(populate_dump_desc_fifo_2, 0),
- DIR_ARG(find_dl_schedule, TT_FORK, "bf"),
- DIR_ARG(find_dl_schedule, TT_FORK, "ba"),
- DIR_ARG(find_dl_schedule, TT_FORK, "cf"),
- DIR_ARG(find_dl_schedule, TT_FORK, "ca"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "bfd"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "bad"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "cfd"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "cad"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "bfr"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "bar"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "cfr"),
+ DIR_ARG(find_dl_schedule, TT_FORK, "car"),
DIR(assumed_flags, 0),
DIR(networkstatus_compute_bw_weights_v10, 0),
+ DIR(platform_str, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
index fca70249bd..fdf43533a8 100644
--- a/src/test/test_dir_common.c
+++ b/src/test/test_dir_common.c
@@ -146,7 +146,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
break;
default:
/* Shouldn't happen */
- tt_assert(0);
+ tt_abort();
}
if (vrs) {
vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 75fe6249ad..fe26657ad8 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -28,6 +28,7 @@
#include "entrynodes.h"
#include "routerparse.h"
#include "networkstatus.h"
+#include "proto_http.h"
#include "geoip.h"
#include "dirserv.h"
#include "dirvote.h"
@@ -38,7 +39,7 @@
#include <direct.h>
#else
#include <dirent.h>
-#endif
+#endif /* defined(_WIN32) */
#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
DISABLE_GCC_WARNING(overlength-strings)
@@ -869,7 +870,7 @@ test_dir_handle_get_server_descriptors_authority(void* data)
mock_routerinfo->cache_info.signed_descriptor_body =
tor_strdup(TEST_DESCRIPTOR);
mock_routerinfo->cache_info.signed_descriptor_len =
- strlen(TEST_DESCRIPTOR) - annotation_len;;
+ strlen(TEST_DESCRIPTOR) - annotation_len;
mock_routerinfo->cache_info.annotations_len = annotation_len;
mock_routerinfo->cache_info.published_on = time(NULL);
@@ -1794,7 +1795,7 @@ status_vote_current_consensus_ns_test(char **header, char **body,
dirserv_set_cached_consensus_networkstatus(NETWORK_STATUS, "ns", &digests,
sha3,
time(NULL));
-#endif
+#endif /* 0 */
networkstatus_t *ns = tor_malloc_zero(sizeof(networkstatus_t));
ns->type = NS_TYPE_CONSENSUS;
ns->flavor = FLAV_NS;
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index 6a8e92cb47..19dcb02931 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -18,9 +18,9 @@ NS(test_main)(void *arg)
uint32_t ttl_mid = MIN_DNS_TTL_AT_EXIT / 2 + MAX_DNS_TTL_AT_EXIT / 2;
- tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT - 1),==,MIN_DNS_TTL_AT_EXIT);
- tt_int_op(dns_clip_ttl(ttl_mid),==,MAX_DNS_TTL_AT_EXIT);
- tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT + 1),==,MAX_DNS_TTL_AT_EXIT);
+ tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT - 1),OP_EQ,MIN_DNS_TTL_AT_EXIT);
+ tt_int_op(dns_clip_ttl(ttl_mid),OP_EQ,MAX_DNS_TTL_AT_EXIT);
+ tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT + 1),OP_EQ,MAX_DNS_TTL_AT_EXIT);
done:
return;
@@ -172,10 +172,10 @@ NS(test_main)(void *arg)
retval = dns_resolve(exitconn);
- tt_int_op(retval,==,1);
- tt_str_op(resolved_name,==,last_resolved_hostname);
+ tt_int_op(retval,OP_EQ,1);
+ tt_str_op(resolved_name,OP_EQ,last_resolved_hostname);
tt_assert(conn_for_resolved_cell == exitconn);
- tt_int_op(n_send_resolved_hostname_cell_replacement,==,
+ tt_int_op(n_send_resolved_hostname_cell_replacement,OP_EQ,
prev_n_send_resolved_hostname_cell_replacement + 1);
tt_assert(exitconn->on_circuit == NULL);
@@ -201,12 +201,12 @@ NS(test_main)(void *arg)
retval = dns_resolve(exitconn);
- tt_int_op(retval,==,1);
+ tt_int_op(retval,OP_EQ,1);
tt_assert(conn_for_resolved_cell == exitconn);
- tt_int_op(n_send_resolved_cell_replacement,==,
+ tt_int_op(n_send_resolved_cell_replacement,OP_EQ,
prev_n_send_resolved_cell_replacement + 1);
tt_assert(last_resolved == fake_resolved);
- tt_int_op(last_answer_type,==,0xff);
+ tt_int_op(last_answer_type,OP_EQ,0xff);
tt_assert(exitconn->on_circuit == NULL);
/* CASE 3: The purpose of exit connection is not EXIT_PURPOSE_RESOLVE
@@ -229,12 +229,12 @@ NS(test_main)(void *arg)
retval = dns_resolve(exitconn);
- tt_int_op(retval,==,1);
+ tt_int_op(retval,OP_EQ,1);
tt_assert(on_circuit->n_streams == exitconn);
tt_assert(exitconn->next_stream == nextconn);
- tt_int_op(prev_n_send_resolved_cell_replacement,==,
+ tt_int_op(prev_n_send_resolved_cell_replacement,OP_EQ,
n_send_resolved_cell_replacement);
- tt_int_op(prev_n_send_resolved_hostname_cell_replacement,==,
+ tt_int_op(prev_n_send_resolved_hostname_cell_replacement,OP_EQ,
n_send_resolved_hostname_cell_replacement);
/* CASE 4: _impl returns 0.
@@ -253,8 +253,8 @@ NS(test_main)(void *arg)
retval = dns_resolve(exitconn);
- tt_int_op(retval,==,0);
- tt_int_op(exitconn->base_.state,==,EXIT_CONN_STATE_RESOLVING);
+ tt_int_op(retval,OP_EQ,0);
+ tt_int_op(exitconn->base_.state,OP_EQ,EXIT_CONN_STATE_RESOLVING);
tt_assert(on_circuit->resolving_streams == exitconn);
tt_assert(exitconn->next_stream == nextconn);
@@ -278,12 +278,12 @@ NS(test_main)(void *arg)
retval = dns_resolve(exitconn);
- tt_int_op(retval,==,-1);
- tt_int_op(n_send_resolved_cell_replacement,==,
+ tt_int_op(retval,OP_EQ,-1);
+ tt_int_op(n_send_resolved_cell_replacement,OP_EQ,
prev_n_send_resolved_cell_replacement + 1);
- tt_int_op(last_answer_type,==,RESOLVED_TYPE_ERROR);
- tt_int_op(n_dns_cancel_pending_resolve_replacement,==,1);
- tt_int_op(n_connection_free,==,prev_n_connection_free + 1);
+ tt_int_op(last_answer_type,OP_EQ,RESOLVED_TYPE_ERROR);
+ tt_int_op(n_dns_cancel_pending_resolve_replacement,OP_EQ,1);
+ tt_int_op(n_connection_free,OP_EQ,prev_n_connection_free + 1);
tt_assert(last_freed_conn == TO_CONN(exitconn));
done:
@@ -351,9 +351,9 @@ NS(test_main)(void *arg)
resolved_addr = &(exitconn->base_.addr);
- tt_int_op(retval,==,1);
+ tt_int_op(retval,OP_EQ,1);
tt_assert(tor_addr_eq(resolved_addr, (const tor_addr_t *)&addr_to_compare));
- tt_int_op(exitconn->address_ttl,==,DEFAULT_DNS_TTL);
+ tt_int_op(exitconn->address_ttl,OP_EQ,DEFAULT_DNS_TTL);
done:
tor_free(on_circ);
@@ -393,7 +393,7 @@ NS(test_main)(void *arg)
retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
NULL);
- tt_int_op(retval,==,-1);
+ tt_int_op(retval,OP_EQ,-1);
done:
tor_free(TO_CONN(exitconn)->address);
@@ -436,7 +436,7 @@ NS(test_main)(void *arg)
retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
NULL);
- tt_int_op(retval,==,-1);
+ tt_int_op(retval,OP_EQ,-1);
done:
NS_UNMOCK(router_my_exit_policy_is_reject_star);
@@ -478,7 +478,7 @@ NS(test_main)(void *arg)
retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
NULL);
- tt_int_op(retval,==,-1);
+ tt_int_op(retval,OP_EQ,-1);
tor_free(TO_CONN(exitconn)->address);
@@ -488,7 +488,7 @@ NS(test_main)(void *arg)
retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
NULL);
- tt_int_op(retval,==,-1);
+ tt_int_op(retval,OP_EQ,-1);
done:
NS_UNMOCK(router_my_exit_policy_is_reject_star);
@@ -546,8 +546,8 @@ NS(test_main)(void *arg)
retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
NULL);
- tt_int_op(retval,==,0);
- tt_int_op(made_pending,==,1);
+ tt_int_op(retval,OP_EQ,0);
+ tt_int_op(made_pending,OP_EQ,1);
pending_conn = cache_entry->pending_connections;
@@ -628,8 +628,8 @@ NS(test_main)(void *arg)
retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
&resolve_out);
- tt_int_op(retval,==,0);
- tt_int_op(made_pending,==,0);
+ tt_int_op(retval,OP_EQ,0);
+ tt_int_op(made_pending,OP_EQ,0);
tt_assert(resolve_out == cache_entry);
tt_assert(last_exitconn == exitconn);
@@ -699,8 +699,8 @@ NS(test_main)(void *arg)
retval = dns_resolve_impl(exitconn, 1, on_circ, NULL, &made_pending,
NULL);
- tt_int_op(retval,==,0);
- tt_int_op(made_pending,==,1);
+ tt_int_op(retval,OP_EQ,0);
+ tt_int_op(made_pending,OP_EQ,1);
cache_entry = dns_get_cache_entry(&query);
@@ -712,7 +712,7 @@ NS(test_main)(void *arg)
tt_assert(pending_conn->conn == exitconn);
tt_assert(last_launched_resolve == cache_entry);
- tt_str_op(cache_entry->address,==,TO_CONN(exitconn)->address);
+ tt_str_op(cache_entry->address,OP_EQ,TO_CONN(exitconn)->address);
done:
NS_UNMOCK(router_my_exit_policy_is_reject_star);
diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c
index 12a631630b..c29b1a7126 100644
--- a/src/test/test_entryconn.c
+++ b/src/test/test_entryconn.c
@@ -14,6 +14,10 @@
#include "confparse.h"
#include "connection.h"
#include "connection_edge.h"
+#include "nodelist.h"
+
+#include "hs_cache.h"
+#include "rendcache.h"
static void *
entryconn_rewrite_setup(const struct testcase_t *tc)
@@ -72,7 +76,6 @@ test_entryconn_rewrite_bad_dotexit(void *arg)
entry_connection_t *ec = arg;
rewrite_result_t rr;
- get_options_mutable()->AllowDotExit = 0;
tt_assert(ec->socks_request);
strlcpy(ec->socks_request->address, "www.TORproject.org.foo.exit",
sizeof(ec->socks_request->address));
@@ -282,7 +285,7 @@ test_entryconn_rewrite_automap_reverse(void *arg)
done:
connection_free_(ENTRY_TO_CONN(ec2));
}
-#endif
+#endif /* 0 */
/* Rewrite because of cached DNS entry. */
static void
@@ -476,7 +479,7 @@ test_entryconn_rewrite_reject_internal_reverse(void *arg)
;
}
-/* Rewrite into .exit because of virtual address mapping */
+/* Rewrite into .exit because of virtual address mapping. */
static void
test_entryconn_rewrite_automap_exit(void *arg)
{
@@ -487,43 +490,21 @@ test_entryconn_rewrite_automap_exit(void *arg)
ec2 = entry_connection_new(CONN_TYPE_AP, AF_INET);
- get_options_mutable()->AutomapHostsOnResolve = 1;
- get_options_mutable()->AllowDotExit = 1;
smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes,
".EXIT");
parse_virtual_addr_network("127.1.0.0/16", AF_INET, 0, &msg);
- /* Automap this on resolve. */
+ /* Try to automap this on resolve. */
strlcpy(ec->socks_request->address, "website.example.exit",
sizeof(ec->socks_request->address));
ec->socks_request->command = SOCKS_COMMAND_RESOLVE;
connection_ap_handshake_rewrite(ec, &rr);
- tt_int_op(rr.automap, OP_EQ, 1);
- tt_int_op(rr.should_close, OP_EQ, 0);
- tt_int_op(rr.end_reason, OP_EQ, 0);
- tt_i64_op(rr.map_expires, OP_EQ, TIME_MAX);
- tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_NONE);
- tt_str_op(rr.orig_address, OP_EQ, "website.example.exit");
- tt_str_op(ec->original_dest_address, OP_EQ, "website.example.exit");
-
- tt_assert(!strcmpstart(ec->socks_request->address,"127.1."));
-
- /* Connect to it and make sure we get the original address back. */
- strlcpy(ec2->socks_request->address, ec->socks_request->address,
- sizeof(ec2->socks_request->address));
-
- ec2->socks_request->command = SOCKS_COMMAND_CONNECT;
- connection_ap_handshake_rewrite(ec2, &rr);
-
+ /* Make sure it isn't allowed -- there is no longer an AllowDotExit
+ * option. */
tt_int_op(rr.automap, OP_EQ, 0);
- tt_int_op(rr.should_close, OP_EQ, 0);
- tt_int_op(rr.end_reason, OP_EQ, 0);
- tt_i64_op(rr.map_expires, OP_EQ, TIME_MAX);
- tt_int_op(rr.exit_source, OP_EQ, ADDRMAPSRC_AUTOMAP);
- tt_str_op(rr.orig_address, OP_EQ, ec->socks_request->address);
- tt_str_op(ec2->original_dest_address, OP_EQ, ec->socks_request->address);
- tt_str_op(ec2->socks_request->address, OP_EQ, "website.example.exit");
+ tt_int_op(rr.should_close, OP_EQ, 1);
+ tt_int_op(rr.end_reason, OP_EQ, END_STREAM_REASON_TORPROTOCOL);
done:
connection_free_(ENTRY_TO_CONN(ec2));
@@ -573,7 +554,6 @@ test_entryconn_rewrite_mapaddress_automap_onion(void *arg)
ec4 = entry_connection_new(CONN_TYPE_AP, AF_INET);
get_options_mutable()->AutomapHostsOnResolve = 1;
- get_options_mutable()->AllowDotExit = 1;
smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes,
".onion");
parse_virtual_addr_network("192.168.0.0/16", AF_INET, 0, &msg);
@@ -743,6 +723,88 @@ test_entryconn_rewrite_mapaddress_automap_onion4(void *arg)
test_entryconn_rewrite_mapaddress_automap_onion_common(arg, 0, 1);
}
+/** Test that rewrite functions can handle v2 addresses */
+static void
+test_entryconn_rewrite_onion_v2(void *arg)
+{
+ int retval;
+ entry_connection_t *conn = arg;
+
+ (void) arg;
+
+ rend_cache_init();
+
+ /* Make a SOCKS request */
+ conn->socks_request->command = SOCKS_COMMAND_CONNECT;
+ strlcpy(conn->socks_request->address,
+ "pqeed46efnwmfuid.onion",
+ sizeof(conn->socks_request->address));
+
+ /* Make an onion connection using the SOCKS request */
+ conn->entry_cfg.onion_traffic = 1;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_SOCKS_WAIT;
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
+
+ /* Handle SOCKS and rewrite! */
+ retval = connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check connection state after rewrite */
+ tt_int_op(ENTRY_TO_CONN(conn)->state, OP_EQ, AP_CONN_STATE_RENDDESC_WAIT);
+ /* check that the address got rewritten */
+ tt_str_op(conn->socks_request->address, OP_EQ,
+ "pqeed46efnwmfuid");
+ /* check that HS information got attached to the connection */
+ tt_assert(ENTRY_TO_EDGE_CONN(conn)->rend_data);
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->hs_ident);
+
+ done:
+ rend_cache_free_all();
+ /* 'conn' is cleaned by handler */
+}
+
+/** Test that rewrite functions can handle v3 onion addresses */
+static void
+test_entryconn_rewrite_onion_v3(void *arg)
+{
+ int retval;
+ entry_connection_t *conn = arg;
+
+ (void) arg;
+
+ hs_cache_init();
+
+ /* Make a SOCKS request */
+ conn->socks_request->command = SOCKS_COMMAND_CONNECT;
+ strlcpy(conn->socks_request->address,
+ "git.25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion",
+ sizeof(conn->socks_request->address));
+
+ /* Make an onion connection using the SOCKS request */
+ conn->entry_cfg.onion_traffic = 1;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_SOCKS_WAIT;
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->hs_ident);
+
+ /* Handle SOCKS and rewrite! */
+ retval = connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check connection state after rewrite. It should be in waiting for
+ * descriptor state. */
+ tt_int_op(ENTRY_TO_CONN(conn)->state, OP_EQ, AP_CONN_STATE_RENDDESC_WAIT);
+ /* check that the address got rewritten */
+ tt_str_op(conn->socks_request->address, OP_EQ,
+ "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid");
+ /* check that HS information got attached to the connection */
+ tt_assert(ENTRY_TO_EDGE_CONN(conn)->hs_ident);
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
+
+ done:
+ hs_free_all();
+ /* 'conn' is cleaned by handler */
+}
+
#define REWRITE(name) \
{ #name, test_entryconn_##name, TT_FORK, &test_rewrite_setup, NULL }
@@ -763,6 +825,8 @@ struct testcase_t entryconn_tests[] = {
REWRITE(rewrite_mapaddress_automap_onion2),
REWRITE(rewrite_mapaddress_automap_onion3),
REWRITE(rewrite_mapaddress_automap_onion4),
+ REWRITE(rewrite_onion_v2),
+ REWRITE(rewrite_onion_v3),
END_OF_TESTCASES
};
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 1f008d93b3..80ebebe3f8 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -7,6 +7,7 @@
#define STATEFILE_PRIVATE
#define ENTRYNODES_PRIVATE
#define ROUTERLIST_PRIVATE
+#define DIRECTORY_PRIVATE
#include "or.h"
#include "test.h"
@@ -15,6 +16,7 @@
#include "circuitlist.h"
#include "config.h"
#include "confparse.h"
+#include "directory.h"
#include "entrynodes.h"
#include "nodelist.h"
#include "networkstatus.h"
@@ -129,6 +131,14 @@ big_fake_network_setup(const struct testcase_t *testcase)
n->rs->has_bandwidth = 1;
n->rs->bandwidth_kb = 30;
+ /* Make a random nickname for each node */
+ {
+ char nickname_binary[8];
+ crypto_rand(nickname_binary, sizeof(nickname_binary));
+ base64_encode(n->rs->nickname, sizeof(n->rs->nickname),
+ nickname_binary, sizeof(nickname_binary), 0);
+ }
+
/* Call half of the nodes a possible guard. */
if (i % 2 == 0) {
n->is_possible_guard = 1;
@@ -455,29 +465,29 @@ test_entry_guard_parse_from_state_failure(void *arg)
/* no selection */
eg = entry_guard_parse_from_state(
"rsa_id=596f75206d6179206e656564206120686f626270");
- tt_assert(! eg);
+ tt_ptr_op(eg, OP_EQ, NULL);
/* no RSA ID. */
eg = entry_guard_parse_from_state("in=default nickname=Fred");
- tt_assert(! eg);
+ tt_ptr_op(eg, OP_EQ, NULL);
/* Bad RSA ID: bad character. */
eg = entry_guard_parse_from_state(
"in=default "
"rsa_id=596f75206d6179206e656564206120686f62627q");
- tt_assert(! eg);
+ tt_ptr_op(eg, OP_EQ, NULL);
/* Bad RSA ID: too long.*/
eg = entry_guard_parse_from_state(
"in=default "
"rsa_id=596f75206d6179206e656564206120686f6262703");
- tt_assert(! eg);
+ tt_ptr_op(eg, OP_EQ, NULL);
/* Bad RSA ID: too short.*/
eg = entry_guard_parse_from_state(
"in=default "
"rsa_id=596f75206d6179206e65656420612");
- tt_assert(! eg);
+ tt_ptr_op(eg, OP_EQ, NULL);
done:
entry_guard_free(eg);
@@ -595,20 +605,20 @@ test_entry_guard_parse_from_state_full(void *arg)
MOCK(get_or_state,
get_or_state_replacement);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_assert(lines);
state->Guard = lines;
/* Try it first without setting the result. */
r = entry_guards_parse_state(state, 0, &msg);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
guard_selection_t *gs_br =
get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0);
- tt_assert(!gs_br);
+ tt_ptr_op(gs_br, OP_EQ, NULL);
r = entry_guards_parse_state(state, 1, &msg);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
gs_br = get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0);
guard_selection_t *gs_df =
get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0);
@@ -625,7 +635,7 @@ test_entry_guard_parse_from_state_full(void *arg)
/* Try again; make sure it doesn't double-add the guards. */
r = entry_guards_parse_state(state, 1, &msg);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
gs_br = get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0);
gs_df = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0);
tt_assert(gs_br);
@@ -730,7 +740,7 @@ test_entry_guard_parse_from_state_broken(void *arg)
MOCK(get_or_state,
get_or_state_replacement);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_assert(lines);
state->Guard = lines;
@@ -742,7 +752,7 @@ test_entry_guard_parse_from_state_broken(void *arg)
/* And we shouldn't have made anything. */
guard_selection_t *gs_df =
get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0);
- tt_assert(gs_df == NULL);
+ tt_ptr_op(gs_df, OP_EQ, NULL);
tor_free(msg);
/* Now see about the set case (which shouldn't happen IRL) */
@@ -750,7 +760,7 @@ test_entry_guard_parse_from_state_broken(void *arg)
tt_int_op(r, OP_LT, 0);
tt_ptr_op(msg, OP_NE, NULL);
gs_df = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0);
- tt_assert(gs_df != NULL);
+ tt_ptr_op(gs_df, OP_NE, NULL);
tt_int_op(smartlist_len(gs_df->sampled_entry_guards), OP_EQ, 1);
done:
@@ -767,26 +777,26 @@ test_entry_guard_get_guard_selection_by_name(void *arg)
guard_selection_t *gs1, *gs2, *gs3;
gs1 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 0);
- tt_assert(gs1 == NULL);
+ tt_ptr_op(gs1, OP_EQ, NULL);
gs1 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 1);
- tt_assert(gs1 != NULL);
+ tt_ptr_op(gs1, OP_NE, NULL);
gs2 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 1);
tt_assert(gs2 == gs1);
gs2 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 0);
tt_assert(gs2 == gs1);
gs2 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 0);
- tt_assert(gs2 == NULL);
+ tt_ptr_op(gs2, OP_EQ, NULL);
gs2 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 1);
- tt_assert(gs2 != NULL);
+ tt_ptr_op(gs2, OP_NE, NULL);
tt_assert(gs2 != gs1);
gs3 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 0);
tt_assert(gs3 == gs2);
gs3 = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0);
- tt_assert(gs3 == NULL);
+ tt_ptr_op(gs3, OP_EQ, NULL);
gs3 = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 1);
- tt_assert(gs3 != NULL);
+ tt_ptr_op(gs3, OP_NE, NULL);
tt_assert(gs3 != gs2);
tt_assert(gs3 != gs1);
tt_assert(gs3 == get_guard_selection_info());
@@ -847,16 +857,16 @@ test_entry_guard_add_single_guard(void *arg)
tt_i64_op(g1->sampled_on_date, OP_GE, now - 12*86400);
tt_i64_op(g1->sampled_on_date, OP_LE, now);
tt_str_op(g1->sampled_by_version, OP_EQ, VERSION);
- tt_assert(g1->currently_listed == 1);
+ tt_uint_op(g1->currently_listed, OP_EQ, 1);
tt_i64_op(g1->confirmed_on_date, OP_EQ, 0);
tt_int_op(g1->confirmed_idx, OP_EQ, -1);
tt_int_op(g1->last_tried_to_connect, OP_EQ, 0);
tt_uint_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE);
tt_i64_op(g1->failing_since, OP_EQ, 0);
- tt_assert(g1->is_filtered_guard == 1);
- tt_assert(g1->is_usable_filtered_guard == 1);
- tt_assert(g1->is_primary == 0);
- tt_assert(g1->extra_state_fields == NULL);
+ tt_uint_op(g1->is_filtered_guard, OP_EQ, 1);
+ tt_uint_op(g1->is_usable_filtered_guard, OP_EQ, 1);
+ tt_uint_op(g1->is_primary, OP_EQ, 0);
+ tt_ptr_op(g1->extra_state_fields, OP_EQ, NULL);
/* Make sure it got added. */
tt_int_op(1, OP_EQ, smartlist_len(gs->sampled_entry_guards));
@@ -886,16 +896,16 @@ test_entry_guard_node_filter(void *arg)
g[i] = entry_guard_add_to_sample(gs, n[i]);
// everything starts out filtered-in
- tt_assert(g[i]->is_filtered_guard == 1);
- tt_assert(g[i]->is_usable_filtered_guard == 1);
+ tt_uint_op(g[i]->is_filtered_guard, OP_EQ, 1);
+ tt_uint_op(g[i]->is_usable_filtered_guard, OP_EQ, 1);
}
tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM);
/* Make sure refiltering doesn't hurt */
entry_guards_update_filtered_sets(gs);
for (i = 0; i < NUM; ++i) {
- tt_assert(g[i]->is_filtered_guard == 1);
- tt_assert(g[i]->is_usable_filtered_guard == 1);
+ tt_uint_op(g[i]->is_filtered_guard, OP_EQ, 1);
+ tt_uint_op(g[i]->is_usable_filtered_guard, OP_EQ, 1);
}
tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM);
@@ -917,6 +927,7 @@ test_entry_guard_node_filter(void *arg)
routerset_parse(get_options_mutable()->ExcludeNodes, "144.144.0.0/16", "");
/* 4: Bridge. */
+ get_options_mutable()->UseBridges = 1;
sweep_bridge_list();
bl = tor_malloc_zero(sizeof(bridge_line_t));
tor_addr_from_ipv4h(&bl->addr, n[4]->rs->addr);
@@ -924,6 +935,7 @@ test_entry_guard_node_filter(void *arg)
memcpy(bl->digest, n[4]->identity, 20);
bridge_add_from_config(bl);
bl = NULL; // prevent free.
+ get_options_mutable()->UseBridges = 0;
/* 5: Unreachable. This stays in the filter, but isn't in usable-filtered */
g[5]->last_tried_to_connect = approx_time(); // prevent retry.
@@ -948,8 +960,8 @@ test_entry_guard_node_filter(void *arg)
});
entry_guards_update_filtered_sets(gs);
for (i = 0; i < NUM; ++i) {
- tt_assert(g[i]->is_filtered_guard == 0);
- tt_assert(g[i]->is_usable_filtered_guard == 0);
+ tt_uint_op(g[i]->is_filtered_guard, OP_EQ, 0);
+ tt_uint_op(g[i]->is_usable_filtered_guard, OP_EQ, 0);
}
tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 0);
@@ -992,7 +1004,7 @@ test_entry_guard_expand_sample(void *arg)
// Nothing became unusable/unfiltered, so a subsequent expand should
// make no changes.
guard = entry_guards_expand_sample(gs);
- tt_assert(! guard); // no guard was added.
+ tt_ptr_op(guard, OP_EQ, NULL); // no guard was added.
tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ,
num_reachable_filtered_guards(gs, NULL));
@@ -1016,7 +1028,7 @@ test_entry_guard_expand_sample(void *arg)
// Still idempotent.
guard = entry_guards_expand_sample(gs);
- tt_assert(! guard); // no guard was added.
+ tt_ptr_op(guard, OP_EQ, NULL); // no guard was added.
tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ,
num_reachable_filtered_guards(gs, NULL));
@@ -1520,7 +1532,7 @@ test_entry_guard_retry_unreachable(void *arg)
entry_guards_expand_sample(gs);
/* Let's say that we have two guards, and they're down.
*/
- time_t start = approx_time();;
+ time_t start = approx_time();
entry_guard_t *g1 = smartlist_get(gs->sampled_entry_guards, 0);
entry_guard_t *g2 = smartlist_get(gs->sampled_entry_guards, 1);
entry_guard_t *g3 = smartlist_get(gs->sampled_entry_guards, 2);
@@ -1637,6 +1649,27 @@ test_entry_guard_manage_primary(void *arg)
tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx));
});
+ /* Do some dirinfo checks */
+ {
+ /* Check that we have all required dirinfo for the primaries (that's done
+ * in big_fake_network_setup()) */
+ char *dir_info_str =
+ guard_selection_get_err_str_if_dir_info_missing(gs, 0, 0, 0);
+ tt_assert(!dir_info_str);
+
+ /* Now artificially remove the first primary's descriptor and re-check */
+ entry_guard_t *first_primary;
+ first_primary = smartlist_get(gs->primary_entry_guards, 0);
+ /* Change the first primary's identity digest so that the mocked functions
+ * can't find its descriptor */
+ memset(first_primary->identity, 9, sizeof(first_primary->identity));
+ dir_info_str =guard_selection_get_err_str_if_dir_info_missing(gs, 1, 2, 3);
+ tt_str_op(dir_info_str, OP_EQ,
+ "We're missing descriptors for 1/2 of our primary entry guards "
+ "(total microdescriptors: 2/3).");
+ tor_free(dir_info_str);
+ }
+
done:
guard_selection_free(gs);
smartlist_free(prev_guards);
@@ -1696,6 +1729,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg)
/* Simpler cases: no gaurds are confirmed yet. */
(void)arg;
guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL);
+ entry_guard_restriction_t *rst = NULL;
/* simple starting configuration */
entry_guards_update_primary(gs);
@@ -1707,7 +1741,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg)
tt_assert(g);
tt_assert(g->is_primary);
tt_int_op(g->confirmed_idx, OP_EQ, -1);
- tt_assert(g->is_pending == 0); // primary implies non-pending.
+ tt_uint_op(g->is_pending, OP_EQ, 0); // primary implies non-pending.
tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time());
@@ -1727,7 +1761,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg)
tt_assert(g2);
tt_assert(g2->is_primary);
tt_int_op(g2->confirmed_idx, OP_EQ, -1);
- tt_assert(g2->is_pending == 0); // primary implies non-pending.
+ tt_uint_op(g2->is_pending, OP_EQ, 0); // primary implies non-pending.
tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time());
@@ -1755,7 +1789,7 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg)
tt_assert(g2);
tt_assert(!g2->is_primary);
tt_int_op(g2->confirmed_idx, OP_EQ, -1);
- tt_assert(g2->is_pending == 1);
+ tt_uint_op(g2->is_pending, OP_EQ, 1);
tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD);
tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time());
tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE);
@@ -1777,14 +1811,13 @@ test_entry_guard_select_for_circuit_no_confirmed(void *arg)
tt_ptr_op(g2, OP_EQ, g);
/* But if we impose a restriction, we don't get the same guard */
- entry_guard_restriction_t rst;
- memset(&rst, 0, sizeof(rst));
- memcpy(rst.exclude_id, g->identity, DIGEST_LEN);
- g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state);
+ rst = guard_create_exit_restriction((uint8_t*)g->identity);
+ g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state);
tt_ptr_op(g2, OP_NE, g);
done:
guard_selection_free(gs);
+ entry_guard_restriction_free(rst);
}
static void
@@ -1794,6 +1827,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg)
guards, we use a confirmed guard. */
(void)arg;
int i;
+ entry_guard_restriction_t *rst = NULL;
guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL);
const int N_CONFIRMED = 10;
@@ -1813,7 +1847,7 @@ test_entry_guard_select_for_circuit_confirmed(void *arg)
tt_assert(g);
tt_assert(g->is_primary);
tt_int_op(g->confirmed_idx, OP_EQ, 0);
- tt_assert(g->is_pending == 0); // primary implies non-pending.
+ tt_uint_op(g->is_pending, OP_EQ, 0); // primary implies non-pending.
tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time());
tt_ptr_op(g, OP_EQ, smartlist_get(gs->primary_entry_guards, 0));
@@ -1854,10 +1888,8 @@ test_entry_guard_select_for_circuit_confirmed(void *arg)
get_options_mutable()->EnforceDistinctSubnets = 0;
g = smartlist_get(gs->confirmed_entry_guards,
smartlist_len(gs->primary_entry_guards)+2);
- entry_guard_restriction_t rst;
- memset(&rst, 0, sizeof(rst));
- memcpy(rst.exclude_id, g->identity, DIGEST_LEN);
- g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state);
+ rst = guard_create_exit_restriction((uint8_t*)g->identity);
+ g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state);
tt_ptr_op(g2, OP_NE, NULL);
tt_ptr_op(g2, OP_NE, g);
tt_int_op(g2->confirmed_idx, OP_EQ,
@@ -1883,13 +1915,13 @@ test_entry_guard_select_for_circuit_confirmed(void *arg)
// Regression test for bug 22753/TROVE-2017-006.
get_options_mutable()->EnforceDistinctSubnets = 1;
g = smartlist_get(gs->confirmed_entry_guards, 0);
- memset(&rst, 0, sizeof(rst));
- memcpy(rst.exclude_id, g->identity, DIGEST_LEN);
- g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state);
+ memcpy(rst->exclude_id, g->identity, DIGEST_LEN);
+ g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, rst, &state);
tt_ptr_op(g2, OP_EQ, NULL);
done:
guard_selection_free(gs);
+ entry_guard_restriction_free(rst);
}
static void
@@ -1913,7 +1945,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg)
int r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL,
&node, &guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_assert(node);
tt_assert(guard);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
@@ -1945,7 +1977,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg)
update_approx_time(start+35);
r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL,
&node, &guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_assert(node);
tt_assert(guard);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
@@ -1981,7 +2013,7 @@ test_entry_guard_select_for_circuit_highlevel_primary(void *arg)
update_approx_time(start+60);
r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL,
&node, &guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_assert(node);
tt_assert(guard);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
@@ -2036,7 +2068,7 @@ test_entry_guard_select_for_circuit_highlevel_confirm_other(void *arg)
&node, &guard);
tt_assert(node);
tt_assert(guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
entry_guard_failed(&guard);
circuit_guard_state_free(guard);
@@ -2050,7 +2082,7 @@ test_entry_guard_select_for_circuit_highlevel_confirm_other(void *arg)
&node, &guard);
tt_assert(node);
tt_assert(guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
entry_guard_t *g = entry_guard_handle_get(guard->guard);
tt_assert(g);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD);
@@ -2102,7 +2134,7 @@ test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg)
&node, &guard);
tt_assert(node);
tt_assert(guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
g = entry_guard_handle_get(guard->guard);
make_guard_confirmed(gs, g);
@@ -2119,7 +2151,7 @@ test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg)
&node, &guard);
tt_assert(node);
tt_assert(guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD);
g = entry_guard_handle_get(guard->guard);
tt_int_op(g->is_primary, OP_EQ, 0);
@@ -2145,7 +2177,7 @@ test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg)
/* Have a circuit to a primary guard succeed. */
r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL,
&node, &guard2);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION);
u = entry_guard_succeeded(&guard2);
tt_assert(u == GUARD_USABLE_NOW);
@@ -2194,7 +2226,7 @@ test_entry_guard_select_and_cancel(void *arg)
&node, &guard);
tt_assert(node);
tt_assert(guard);
- tt_assert(r == 0);
+ tt_int_op(r, OP_EQ, 0);
tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD);
g = entry_guard_handle_get(guard->guard);
tt_int_op(g->is_primary, OP_EQ, 0);
@@ -2202,7 +2234,7 @@ test_entry_guard_select_and_cancel(void *arg)
/* Whoops! We should never have asked for this guard. Cancel the request! */
entry_guard_cancel(&guard);
- tt_assert(guard == NULL);
+ tt_ptr_op(guard, OP_EQ, NULL);
tt_int_op(g->is_primary, OP_EQ, 0);
tt_int_op(g->is_pending, OP_EQ, 0);
@@ -2470,9 +2502,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_complete(void *arg)
/* Once more, let circ1 become complete. But this time, we'll claim
* that circ2 was restricted to not use the same guard as circ1. */
data->guard2_state->restrictions =
- tor_malloc_zero(sizeof(entry_guard_restriction_t));
- memcpy(data->guard2_state->restrictions->exclude_id,
- data->guard1->identity, DIGEST_LEN);
+ guard_create_exit_restriction((uint8_t*)data->guard1->identity);
smartlist_t *result = smartlist_new();
int r;
@@ -2581,9 +2611,7 @@ test_entry_guard_upgrade_not_blocked_by_restricted_circ_pending(void *arg)
}
data->guard2_state->restrictions =
- tor_malloc_zero(sizeof(entry_guard_restriction_t));
- memcpy(data->guard2_state->restrictions->exclude_id,
- data->guard1->identity, DIGEST_LEN);
+ guard_create_exit_restriction((uint8_t*)data->guard1->identity);
smartlist_t *result = smartlist_new();
int r;
@@ -2651,6 +2679,114 @@ test_enty_guard_should_expire_waiting(void *arg)
tor_free(fake_state);
}
+static void
+mock_directory_initiate_request(directory_request_t *req)
+{
+ if (req->guard_state) {
+ circuit_guard_state_free(req->guard_state);
+ }
+}
+
+static networkstatus_t *mock_ns_val = NULL;
+static networkstatus_t *
+mock_ns_get_by_flavor(consensus_flavor_t f)
+{
+ (void)f;
+ return mock_ns_val;
+}
+
+/** Test that when we fetch microdescriptors we skip guards that have
+ * previously failed to serve us needed microdescriptors. */
+static void
+test_entry_guard_outdated_dirserver_exclusion(void *arg)
+{
+ int retval;
+ response_handler_args_t *args = NULL;
+ dir_connection_t *conn = NULL;
+ (void) arg;
+
+ /* Test prep: Make a new guard selection */
+ guard_selection_t *gs = get_guard_selection_by_name("default",
+ GS_TYPE_NORMAL, 1);
+
+ /* ... we want to use entry guards */
+ or_options_t *options = get_options_mutable();
+ options->UseEntryGuards = 1;
+ options->UseBridges = 0;
+
+ /* ... prepare some md digests we want to download in the future */
+ smartlist_t *digests = smartlist_new();
+ const char *prose = "unhurried and wise, we perceive.";
+ for (int i = 0; i < 20; i++) {
+ smartlist_add(digests, (char*)prose);
+ }
+
+ tt_int_op(smartlist_len(digests), OP_EQ, 20);
+
+ /* ... now mock some functions */
+ mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
+ MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
+ MOCK(directory_initiate_request, mock_directory_initiate_request);
+
+ /* Test logic:
+ * 0. Create a proper guard set and primary guard list.
+ * 1. Pretend to fail microdescriptor fetches from all the primary guards.
+ * 2. Order another microdescriptor fetch and make sure that primary guards
+ * get skipped since they failed previous fetches.
+ */
+
+ { /* Setup primary guard list */
+ int i;
+ entry_guards_update_primary(gs);
+ for (i = 0; i < DFLT_N_PRIMARY_GUARDS; ++i) {
+ entry_guard_t *guard = smartlist_get(gs->sampled_entry_guards, i);
+ make_guard_confirmed(gs, guard);
+ }
+ entry_guards_update_primary(gs);
+ }
+
+ {
+ /* Fail microdesc fetches with all the primary guards */
+ args = tor_malloc_zero(sizeof(response_handler_args_t));
+ args->status_code = 404;
+ args->reason = NULL;
+ args->body = NULL;
+ args->body_len = 0;
+
+ conn = tor_malloc_zero(sizeof(dir_connection_t));
+ conn->requested_resource = tor_strdup("d/jlinblackorigami");
+ conn->base_.purpose = DIR_PURPOSE_FETCH_MICRODESC;
+
+ /* Pretend to fail fetches with all primary guards */
+ SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards,const entry_guard_t *,g) {
+ memcpy(conn->identity_digest, g->identity, DIGEST_LEN);
+
+ retval = handle_response_fetch_microdesc(conn, args);
+ tt_int_op(retval, OP_EQ, 0);
+ } SMARTLIST_FOREACH_END(g);
+ }
+
+ {
+ /* Now order the final md download */
+ setup_full_capture_of_logs(LOG_INFO);
+ initiate_descriptor_downloads(NULL, DIR_PURPOSE_FETCH_MICRODESC,
+ digests, 3, 7, 0);
+
+ /* ... and check that because we failed to fetch microdescs from all our
+ * primaries, we didnt end up selecting a primary for fetching dir info */
+ expect_log_msg_containing("No primary or confirmed guards available.");
+ teardown_capture_of_logs();
+ }
+
+ done:
+ smartlist_free(digests);
+ tor_free(args);
+ if (conn) {
+ tor_free(conn->requested_resource);
+ tor_free(conn);
+ }
+}
+
static const struct testcase_setup_t big_fake_network = {
big_fake_network_setup, big_fake_network_cleanup
};
@@ -2711,6 +2847,7 @@ struct testcase_t entrynodes_tests[] = {
BFN_TEST(select_for_circuit_highlevel_primary_retry),
BFN_TEST(select_and_cancel),
BFN_TEST(drop_guards),
+ BFN_TEST(outdated_dirserver_exclusion),
UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"),
UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"),
diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c
index fc9f27a5ac..e18deb2700 100644
--- a/src/test/test_extorport.c
+++ b/src/test/test_extorport.c
@@ -78,7 +78,7 @@ connection_write_to_buf_impl_replacement(const char *string, size_t len,
tor_assert(string);
tor_assert(conn);
- write_to_buf(string, len, conn->outbuf);
+ buf_add(conn->outbuf, string, len);
}
static char *
@@ -89,7 +89,7 @@ buf_get_contents(buf_t *buf, size_t *sz_out)
if (*sz_out >= ULONG_MAX)
return NULL; /* C'mon, really? */
out = tor_malloc(*sz_out + 1);
- if (fetch_from_buf(out, (unsigned long)*sz_out, buf) != 0) {
+ if (buf_get_bytes(buf, out, (unsigned long)*sz_out) != 0) {
tor_free(out);
return NULL;
}
@@ -399,14 +399,14 @@ handshake_start(or_connection_t *conn, int receiving)
#define WRITE(s,n) \
do { \
- write_to_buf((s), (n), TO_CONN(conn)->inbuf); \
+ buf_add(TO_CONN(conn)->inbuf, (s), (n)); \
} while (0)
#define CONTAINS(s,n) \
do { \
tt_int_op((n), OP_LE, sizeof(b)); \
tt_int_op(buf_datalen(TO_CONN(conn)->outbuf), OP_EQ, (n)); \
if ((n)) { \
- fetch_from_buf(b, (n), TO_CONN(conn)->outbuf); \
+ buf_get_bytes(TO_CONN(conn)->outbuf, b, (n)); \
tt_mem_op(b, OP_EQ, (s), (n)); \
} \
} while (0)
@@ -497,14 +497,14 @@ test_ext_or_handshake(void *arg)
"te road There is always another ", 64);
/* Send the wrong response. */
WRITE("not with a bang but a whimper...", 32);
- MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ MOCK(control_event_bootstrap_prob_or, ignore_bootstrap_problem);
tt_int_op(-1, OP_EQ, connection_ext_or_process_inbuf(conn));
CONTAINS("\x00", 1);
tt_assert(TO_CONN(conn)->marked_for_close);
/* XXXX Hold-open-until-flushed. */
close_closeable_connections();
conn = NULL;
- UNMOCK(control_event_bootstrap_problem);
+ UNMOCK(control_event_bootstrap_prob_or);
MOCK(connection_start_reading, note_read_started);
MOCK(connection_stop_reading, note_read_stopped);
@@ -552,26 +552,26 @@ test_ext_or_handshake(void *arg)
do_ext_or_handshake(conn);
/* USERADDR command with an extra NUL byte */
WRITE("\x00\x01\x00\x0d""1.2.3.4:5678\x00", 17);
- MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ MOCK(control_event_bootstrap_prob_or, ignore_bootstrap_problem);
tt_int_op(-1, OP_EQ, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_assert(TO_CONN(conn)->marked_for_close);
close_closeable_connections();
conn = NULL;
- UNMOCK(control_event_bootstrap_problem);
+ UNMOCK(control_event_bootstrap_prob_or);
/* Now fail the TRANSPORT command. */
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
do_ext_or_handshake(conn);
/* TRANSPORT command with an extra NUL byte */
WRITE("\x00\x02\x00\x08""rfc1149\x00", 12);
- MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ MOCK(control_event_bootstrap_prob_or, ignore_bootstrap_problem);
tt_int_op(-1, OP_EQ, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_assert(TO_CONN(conn)->marked_for_close);
close_closeable_connections();
conn = NULL;
- UNMOCK(control_event_bootstrap_problem);
+ UNMOCK(control_event_bootstrap_prob_or);
/* Now fail the TRANSPORT command. */
conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
@@ -579,13 +579,13 @@ test_ext_or_handshake(void *arg)
/* TRANSPORT command with transport name with symbols (not a
C-identifier) */
WRITE("\x00\x02\x00\x07""rf*1149", 11);
- MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+ MOCK(control_event_bootstrap_prob_or, ignore_bootstrap_problem);
tt_int_op(-1, OP_EQ, connection_ext_or_process_inbuf(conn));
CONTAINS("", 0);
tt_assert(TO_CONN(conn)->marked_for_close);
close_closeable_connections();
conn = NULL;
- UNMOCK(control_event_bootstrap_problem);
+ UNMOCK(control_event_bootstrap_prob_or);
done:
UNMOCK(connection_write_to_buf_impl_);
diff --git a/src/test/test_guardfraction.c b/src/test/test_guardfraction.c
index 56006f3cc3..51ca8f08ec 100644
--- a/src/test/test_guardfraction.c
+++ b/src/test/test_guardfraction.c
@@ -38,10 +38,10 @@ gen_vote_routerstatus_for_tests(const char *digest_in_hex, int is_guard)
rs->is_possible_guard = is_guard;
/* Fill in the fpr */
- tt_int_op(strlen(digest_in_hex), ==, HEX_DIGEST_LEN);
+ tt_int_op(strlen(digest_in_hex), OP_EQ, HEX_DIGEST_LEN);
retval = base16_decode(digest_tmp, sizeof(digest_tmp),
digest_in_hex, HEX_DIGEST_LEN);
- tt_int_op(retval, ==, sizeof(digest_tmp));
+ tt_int_op(retval, OP_EQ, sizeof(digest_tmp));
memcpy(rs->identity_digest, digest_tmp, DIGEST_LEN);
}
@@ -88,7 +88,7 @@ test_parse_guardfraction_file_bad(void *arg)
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
tor_free(guardfraction_bad);
/* This one does not have a date! Parsing should fail. */
@@ -100,7 +100,7 @@ test_parse_guardfraction_file_bad(void *arg)
"guard-seen 07B5547026DF3E229806E135CFA8552D56AFBABC 5 420\n");
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
tor_free(guardfraction_bad);
/* This one has an incomplete n-inputs line, but parsing should
@@ -114,7 +114,7 @@ test_parse_guardfraction_file_bad(void *arg)
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
- tt_int_op(retval, ==, 2);
+ tt_int_op(retval, OP_EQ, 2);
tor_free(guardfraction_bad);
/* This one does not have a fingerprint in the guard line! */
@@ -126,7 +126,7 @@ test_parse_guardfraction_file_bad(void *arg)
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
tor_free(guardfraction_bad);
/* This one does not even have an integer guardfraction value. */
@@ -139,7 +139,7 @@ test_parse_guardfraction_file_bad(void *arg)
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
- tt_int_op(retval, ==, 1);
+ tt_int_op(retval, OP_EQ, 1);
tor_free(guardfraction_bad);
/* This one is not a percentage (not in [0, 100]) */
@@ -152,7 +152,7 @@ test_parse_guardfraction_file_bad(void *arg)
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
- tt_int_op(retval, ==, 1);
+ tt_int_op(retval, OP_EQ, 1);
tor_free(guardfraction_bad);
/* This one is not a percentage either (not in [0, 100]) */
@@ -164,7 +164,7 @@ test_parse_guardfraction_file_bad(void *arg)
yesterday_date_str);
retval = dirserv_read_guardfraction_file_from_str(guardfraction_bad, NULL);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
done:
tor_free(guardfraction_bad);
@@ -216,14 +216,14 @@ test_parse_guardfraction_file_good(void *arg)
/* Read the guardfraction file */
retval = dirserv_read_guardfraction_file_from_str(guardfraction_good,
routerstatuses);
- tt_int_op(retval, ==, 1);
+ tt_int_op(retval, OP_EQ, 1);
{ /* Test that routerstatus fields got filled properly */
/* The guardfraction fields of the guard should be filled. */
tt_assert(vrs_guard->status.has_guardfraction);
tt_int_op(vrs_guard->status.guardfraction_percentage,
- ==,
+ OP_EQ,
guardfraction_value);
/* The guard that was not in the guardfraction file should not have
@@ -252,12 +252,12 @@ test_get_guardfraction_bandwidth(void *arg)
guard_get_guardfraction_bandwidth(&gf_bw,
orig_bw, 25);
- tt_int_op(gf_bw.guard_bw, ==, 250);
- tt_int_op(gf_bw.non_guard_bw, ==, 750);
+ tt_int_op(gf_bw.guard_bw, OP_EQ, 250);
+ tt_int_op(gf_bw.non_guard_bw, OP_EQ, 750);
/* Also check the 'guard_bw + non_guard_bw == original_bw'
* invariant. */
- tt_int_op(gf_bw.non_guard_bw + gf_bw.guard_bw, ==, orig_bw);
+ tt_int_op(gf_bw.non_guard_bw + gf_bw.guard_bw, OP_EQ, orig_bw);
done:
;
@@ -295,9 +295,9 @@ test_parse_guardfraction_consensus(void *arg)
retval = routerstatus_parse_guardfraction(guardfraction_str_good,
NULL, NULL,
&rs_good);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
tt_assert(rs_good.has_guardfraction);
- tt_int_op(rs_good.guardfraction_percentage, ==, 66);
+ tt_int_op(rs_good.guardfraction_percentage, OP_EQ, 66);
}
{ /* Properly formatted GuardFraction but router is not a
@@ -309,7 +309,7 @@ test_parse_guardfraction_consensus(void *arg)
retval = routerstatus_parse_guardfraction(guardfraction_str_good,
NULL, NULL,
&rs_no_guard);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
tt_assert(!rs_no_guard.has_guardfraction);
expect_single_log_msg_containing("Got GuardFraction for non-guard . "
"This is not supposed to happen.");
@@ -323,7 +323,7 @@ test_parse_guardfraction_consensus(void *arg)
retval = routerstatus_parse_guardfraction(guardfraction_str_bad1,
NULL, NULL,
&rs_bad1);
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
tt_assert(!rs_bad1.has_guardfraction);
}
@@ -334,7 +334,7 @@ test_parse_guardfraction_consensus(void *arg)
retval = routerstatus_parse_guardfraction(guardfraction_str_bad2,
NULL, NULL,
&rs_bad2);
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
tt_assert(!rs_bad2.has_guardfraction);
}
@@ -375,27 +375,27 @@ test_should_apply_guardfraction(void *arg)
/* If torrc option is set to yes, we should always use
* guardfraction.*/
options->UseGuardFraction = 1;
- tt_int_op(should_apply_guardfraction(&vote_disabled), ==, 1);
+ tt_int_op(should_apply_guardfraction(&vote_disabled), OP_EQ, 1);
/* If torrc option is set to no, we should never use
* guardfraction.*/
options->UseGuardFraction = 0;
- tt_int_op(should_apply_guardfraction(&vote_enabled), ==, 0);
+ tt_int_op(should_apply_guardfraction(&vote_enabled), OP_EQ, 0);
/* Now let's test torrc option set to auto. */
options->UseGuardFraction = -1;
/* If torrc option is set to auto, and consensus parameter is set to
* yes, we should use guardfraction. */
- tt_int_op(should_apply_guardfraction(&vote_enabled), ==, 1);
+ tt_int_op(should_apply_guardfraction(&vote_enabled), OP_EQ, 1);
/* If torrc option is set to auto, and consensus parameter is set to
* no, we should use guardfraction. */
- tt_int_op(should_apply_guardfraction(&vote_disabled), ==, 0);
+ tt_int_op(should_apply_guardfraction(&vote_disabled), OP_EQ, 0);
/* If torrc option is set to auto, and consensus parameter is not
* set, we should fallback to "no". */
- tt_int_op(should_apply_guardfraction(&vote_missing), ==, 0);
+ tt_int_op(should_apply_guardfraction(&vote_missing), OP_EQ, 0);
done:
SMARTLIST_FOREACH(vote_enabled.net_params, char *, cp, tor_free(cp));
diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c
index 9fada5a675..0da9cf64d0 100644
--- a/src/test/test_helpers.c
+++ b/src/test/test_helpers.c
@@ -7,16 +7,25 @@
*/
#define ROUTERLIST_PRIVATE
+#define CONFIG_PRIVATE
+#define CONNECTION_PRIVATE
+#define MAIN_PRIVATE
+
#include "orconfig.h"
#include "or.h"
+#include "buffers.h"
+#include "config.h"
+#include "confparse.h"
+#include "connection.h"
+#include "main.h"
+#include "nodelist.h"
#include "relay.h"
#include "routerlist.h"
-#include "nodelist.h"
-#include "buffers.h"
#include "test.h"
#include "test_helpers.h"
+#include "test_connection.h"
#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
DISABLE_GCC_WARNING(overlength-strings)
@@ -74,16 +83,16 @@ helper_setup_fake_routerlist(void)
retval = router_load_routers_from_string(TEST_DESCRIPTORS,
NULL, SAVED_IN_JOURNAL,
NULL, 0, NULL);
- tt_int_op(retval, ==, HELPER_NUMBER_OF_DESCRIPTORS);
+ tt_int_op(retval, OP_EQ, HELPER_NUMBER_OF_DESCRIPTORS);
/* Sanity checking of routerlist and nodelist. */
our_routerlist = router_get_routerlist();
- tt_int_op(smartlist_len(our_routerlist->routers), ==,
+ tt_int_op(smartlist_len(our_routerlist->routers), OP_EQ,
HELPER_NUMBER_OF_DESCRIPTORS);
routerlist_assert_ok(our_routerlist);
our_nodelist = nodelist_get_list();
- tt_int_op(smartlist_len(our_nodelist), ==, HELPER_NUMBER_OF_DESCRIPTORS);
+ tt_int_op(smartlist_len(our_nodelist), OP_EQ, HELPER_NUMBER_OF_DESCRIPTORS);
/* Mark all routers as non-guards but up and running! */
SMARTLIST_FOREACH_BEGIN(our_nodelist, node_t *, node) {
@@ -105,7 +114,7 @@ connection_write_to_buf_mock(const char *string, size_t len,
tor_assert(string);
tor_assert(conn);
- write_to_buf(string, len, conn->outbuf);
+ buf_add(conn->outbuf, string, len);
}
/* Set up a fake origin circuit with the specified number of cells,
@@ -143,3 +152,128 @@ mock_tor_addr_lookup__fail_on_bad_addrs(const char *name,
return tor_addr_lookup__real(name, family, out);
}
+/*********** Helper funcs for making new connections/streams *****************/
+
+/* Helper for test_conn_get_connection() */
+static int
+fake_close_socket(evutil_socket_t sock)
+{
+ (void)sock;
+ return 0;
+}
+
+static int mock_connection_connect_sockaddr_called = 0;
+static int fake_socket_number = TEST_CONN_FD_INIT;
+
+/* Helper for test_conn_get_connection() */
+static int
+mock_connection_connect_sockaddr(connection_t *conn,
+ const struct sockaddr *sa,
+ socklen_t sa_len,
+ const struct sockaddr *bindaddr,
+ socklen_t bindaddr_len,
+ int *socket_error)
+{
+ (void)sa_len;
+ (void)bindaddr;
+ (void)bindaddr_len;
+
+ tor_assert(conn);
+ tor_assert(sa);
+ tor_assert(socket_error);
+
+ mock_connection_connect_sockaddr_called++;
+
+ conn->s = fake_socket_number++;
+ tt_assert(SOCKET_OK(conn->s));
+ /* We really should call tor_libevent_initialize() here. Because we don't,
+ * we are relying on other parts of the code not checking if the_event_base
+ * (and therefore event->ev_base) is NULL. */
+ tt_int_op(connection_add_connecting(conn), OP_EQ, 0);
+
+ done:
+ /* Fake "connected" status */
+ return 1;
+}
+
+/** Create and return a new connection/stream */
+connection_t *
+test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
+{
+ connection_t *conn = NULL;
+ tor_addr_t addr;
+ int socket_err = 0;
+ int in_progress = 0;
+
+ MOCK(connection_connect_sockaddr,
+ mock_connection_connect_sockaddr);
+ MOCK(tor_close_socket, fake_close_socket);
+
+ init_connection_lists();
+
+ conn = connection_new(type, TEST_CONN_FAMILY);
+ tt_assert(conn);
+
+ test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
+ tt_assert(!tor_addr_is_null(&addr));
+
+ tor_addr_copy_tight(&conn->addr, &addr);
+ conn->port = TEST_CONN_PORT;
+ mock_connection_connect_sockaddr_called = 0;
+ in_progress = connection_connect(conn, TEST_CONN_ADDRESS_PORT, &addr,
+ TEST_CONN_PORT, &socket_err);
+ tt_int_op(mock_connection_connect_sockaddr_called, OP_EQ, 1);
+ tt_assert(!socket_err);
+ tt_assert(in_progress == 0 || in_progress == 1);
+
+ /* fake some of the attributes so the connection looks OK */
+ conn->state = state;
+ conn->purpose = purpose;
+ assert_connection_ok(conn, time(NULL));
+
+ UNMOCK(connection_connect_sockaddr);
+ UNMOCK(tor_close_socket);
+ return conn;
+
+ /* On failure */
+ done:
+ UNMOCK(connection_connect_sockaddr);
+ UNMOCK(tor_close_socket);
+ return NULL;
+}
+
+/* Helper function to parse a set of torrc options in a text format and return
+ * a newly allocated or_options_t object containing the configuration. On
+ * error, NULL is returned indicating that the conf couldn't be parsed
+ * properly. */
+or_options_t *
+helper_parse_options(const char *conf)
+{
+ int ret = 0;
+ char *msg = NULL;
+ or_options_t *opt = NULL;
+ config_line_t *line = NULL;
+
+ /* Kind of pointless to call this with a NULL value. */
+ tt_assert(conf);
+
+ opt = options_new();
+ tt_assert(opt);
+ ret = config_get_lines(conf, &line, 1);
+ if (ret != 0) {
+ goto done;
+ }
+ ret = config_assign(&options_format, opt, line, 0, &msg);
+ if (ret != 0) {
+ goto done;
+ }
+
+ done:
+ config_free_lines(line);
+ if (ret != 0) {
+ or_options_free(opt);
+ opt = NULL;
+ }
+ return opt;
+}
+
diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h
index 4621631cc1..9bc8553257 100644
--- a/src/test/test_helpers.h
+++ b/src/test/test_helpers.h
@@ -1,9 +1,11 @@
-/* Copyright (c) 2014-2017, The Tor Project, Inc. */
+/* Copyright (c) 2017, The Tor Project, Inc. */
/* See LICENSE for licensing information */
#ifndef TOR_TEST_HELPERS_H
#define TOR_TEST_HELPERS_H
+#include "or.h"
+
const char *get_yesterday_date_str(void);
circuit_t * dummy_origin_circuit_new(int num_cells);
@@ -20,7 +22,11 @@ void connection_write_to_buf_mock(const char *string, size_t len,
int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name,
uint16_t family, tor_addr_t *out);
+connection_t *test_conn_get_connection(uint8_t state,
+ uint8_t type, uint8_t purpose);
+or_options_t *helper_parse_options(const char *conf);
+
extern const char TEST_DESCRIPTORS[];
-#endif
+#endif /* !defined(TOR_TEST_HELPERS_H) */
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 5aae6c5b97..7737499f50 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -8,7 +8,9 @@
#define CONTROL_PRIVATE
#define CIRCUITBUILD_PRIVATE
+#define RENDCOMMON_PRIVATE
#define RENDSERVICE_PRIVATE
+#define HS_SERVICE_PRIVATE
#include "or.h"
#include "test.h"
@@ -32,8 +34,9 @@
#define STR_HSDIR_NONE_EXIST_LONGNAME \
"$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
-/* DuckDuckGo descriptor as an example. */
-static const char *hs_desc_content = "\
+/* DuckDuckGo descriptor as an example. This one has extra "\r" at the end so
+ * the control port is happy. */
+static const char *hs_desc_content_control = "\
rendezvous-service-descriptor g5ojobzupf275beh5ra72uyhb3dkpxwg\r\n\
version 2\r\n\
permanent-key\r\n\
@@ -94,6 +97,68 @@ PcftsZf2ztN0sbNCtPgDL3d0PqvxY3iHTQAI8EbaGq/IAJUZ8U4y963dD5+Bn6JQ\r\n\
myE3ctmh0vy5+QxSiRjmQBkuEpCyks7LvWvHYrhnmcg=\r\n\
-----END SIGNATURE-----";
+/* DuckDuckGo descriptor as an example. */
+static const char *hs_desc_content = "\
+rendezvous-service-descriptor g5ojobzupf275beh5ra72uyhb3dkpxwg\n\
+version 2\n\
+permanent-key\n\
+-----BEGIN RSA PUBLIC KEY-----\n\
+MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE\n\
+aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg\n\
+I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE=\n\
+-----END RSA PUBLIC KEY-----\n\
+secret-id-part anmjoxxwiupreyajjt5yasimfmwcnxlf\n\
+publication-time 2015-03-11 19:00:00\n\
+protocol-versions 2,3\n\
+introduction-points\n\
+-----BEGIN MESSAGE-----\n\
+aW50cm9kdWN0aW9uLXBvaW50IDd1bnd4cmg2dG5kNGh6eWt1Z3EzaGZzdHduc2ll\n\
+cmhyCmlwLWFkZHJlc3MgMTg4LjEzOC4xMjEuMTE4Cm9uaW9uLXBvcnQgOTAwMQpv\n\
+bmlvbi1rZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dC\n\
+QUxGRVVyeVpDbk9ROEhURmV5cDVjMTRObWVqL1BhekFLTTBxRENTNElKUWh0Y3g1\n\
+NXpRSFdOVWIKQ2hHZ0JqR1RjV3ZGRnA0N3FkdGF6WUZhVXE2c0lQKzVqeWZ5b0Q4\n\
+UmJ1bzBwQmFWclJjMmNhYUptWWM0RDh6Vgpuby9sZnhzOVVaQnZ1cWY4eHIrMDB2\n\
+S0JJNmFSMlA2OE1WeDhrMExqcUpUU2RKOE9idm9yQWdNQkFBRT0KLS0tLS1FTkQg\n\
+UlNBIFBVQkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQ\n\
+VUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTnJHb0ozeTlHNXQzN2F2ekI1cTlwN1hG\n\
+VUplRUVYMUNOaExnWmJXWGJhVk5OcXpoZFhyL0xTUQppM1Z6dW5OaUs3cndUVnE2\n\
+K2QyZ1lRckhMMmIvMXBBY3ZKWjJiNSs0bTRRc0NibFpjRENXTktRbHJnRWN5WXRJ\n\
+CkdscXJTbFFEaXA0ZnNrUFMvNDVkWTI0QmJsQ3NGU1k3RzVLVkxJck4zZFpGbmJr\n\
+NEZIS1hBZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJv\n\
+ZHVjdGlvbi1wb2ludCBiNGM3enlxNXNheGZzN2prNXFibG1wN3I1b3pwdHRvagpp\n\
+cC1hZGRyZXNzIDEwOS4xNjkuNDUuMjI2Cm9uaW9uLXBvcnQgOTAwMQpvbmlvbi1r\n\
+ZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQU8xSXpw\n\
+WFFUTUY3RXZUb1NEUXpzVnZiRVFRQUQrcGZ6NzczMVRXZzVaUEJZY1EyUkRaeVp4\n\
+OEQKNUVQSU1FeUE1RE83cGd0ak5LaXJvYXJGMC8yempjMkRXTUlSaXZyU29YUWVZ\n\
+ZXlMM1pzKzFIajJhMDlCdkYxZAp6MEswblRFdVhoNVR5V3lyMHdsbGI1SFBnTlI0\n\
+MS9oYkprZzkwZitPVCtIeGhKL1duUml2QWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBV\n\
+QkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQVUJMSUMg\n\
+S0VZLS0tLS0KTUlHSkFvR0JBSzNWZEJ2ajFtQllLL3JrcHNwcm9Ub0llNUtHVmth\n\
+QkxvMW1tK1I2YUVJek1VZFE1SjkwNGtyRwpCd3k5NC8rV0lGNFpGYXh5Z2phejl1\n\
+N2pKY1k3ZGJhd1pFeG1hYXFCRlRwL2h2ZG9rcHQ4a1ByRVk4OTJPRHJ1CmJORUox\n\
+N1FPSmVMTVZZZk5Kcjl4TWZCQ3JQai8zOGh2RUdrbWVRNmRVWElvbVFNaUJGOVRB\n\
+Z01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJvZHVjdGlv\n\
+bi1wb2ludCBhdjVtcWl0Y2Q3cjJkandsYmN0c2Jlc2R3eGt0ZWtvegppcC1hZGRy\n\
+ZXNzIDE0NC43Ni44LjczCm9uaW9uLXBvcnQgNDQzCm9uaW9uLWtleQotLS0tLUJF\n\
+R0lOIFJTQSBQVUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTzVweVZzQmpZQmNmMXBE\n\
+dklHUlpmWXUzQ05nNldka0ZLMGlvdTBXTGZtejZRVDN0NWhzd3cyVwpjejlHMXhx\n\
+MmN0Nkd6VWkrNnVkTDlITTRVOUdHTi9BbW8wRG9GV1hKWHpBQkFXd2YyMVdsd1lW\n\
+eFJQMHRydi9WCkN6UDkzcHc5OG5vSmdGUGRUZ05iMjdKYmVUZENLVFBrTEtscXFt\n\
+b3NveUN2RitRa25vUS9BZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0t\n\
+LS0tCnNlcnZpY2Uta2V5Ci0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLQpN\n\
+SUdKQW9HQkFMVjNKSmtWN3lTNU9jc1lHMHNFYzFQOTVRclFRR3ZzbGJ6Wi9zRGxl\n\
+RlpKYXFSOUYvYjRUVERNClNGcFMxcU1GbldkZDgxVmRGMEdYRmN2WVpLamRJdHU2\n\
+SndBaTRJeEhxeXZtdTRKdUxrcXNaTEFLaXRLVkx4eGsKeERlMjlDNzRWMmJrOTRJ\n\
+MEgybTNKS2tzTHVwc3VxWWRVUmhOVXN0SElKZmgyZmNIalF0bEFnTUJBQUU9Ci0t\n\
+LS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0KCg==\n\
+-----END MESSAGE-----\n\
+signature\n\
+-----BEGIN SIGNATURE-----\n\
+d4OuCE5OLAOnRB6cQN6WyMEmg/BHem144Vec+eYgeWoKwx3MxXFplUjFxgnMlmwN\n\
+PcftsZf2ztN0sbNCtPgDL3d0PqvxY3iHTQAI8EbaGq/IAJUZ8U4y963dD5+Bn6JQ\n\
+myE3ctmh0vy5+QxSiRjmQBkuEpCyks7LvWvHYrhnmcg=\n\
+-----END SIGNATURE-----";
+
/* Helper global variable for hidden service descriptor event test.
* It's used as a pointer to dynamically created message buffer in
* send_control_event_string_replacement function, which mocks
@@ -125,6 +190,30 @@ node_describe_longname_by_id_replacement(const char *id_digest)
}
}
+/** Test that we can parse a hardcoded v2 HS desc. */
+static void
+test_hs_parse_static_v2_desc(void *arg)
+{
+ int ret;
+ rend_encoded_v2_service_descriptor_t desc;
+
+ (void) arg;
+
+ /* Test an obviously not parseable string */
+ desc.desc_str = tor_strdup("ceci n'est pas un HS descriptor");
+ ret = rend_desc_v2_is_parsable(&desc);
+ tor_free(desc.desc_str);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Test an actual descriptor */
+ desc.desc_str = tor_strdup(hs_desc_content);
+ ret = rend_desc_v2_is_parsable(&desc);
+ tor_free(desc.desc_str);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done: ;
+}
+
/** Make sure each hidden service descriptor async event generation
*
* function generates the message in expected format.
@@ -161,7 +250,7 @@ test_hs_desc_event(void *arg)
ret = rend_compute_v2_desc_id(rend_query.descriptor_id[0],
rend_query.onion_address,
NULL, 0, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
base32_encode(desc_id_base32, sizeof(desc_id_base32),
rend_query.descriptor_id[0], DIGEST_LEN);
/* Make sure rend_compute_v2_desc_id works properly. */
@@ -235,10 +324,10 @@ test_hs_desc_event(void *arg)
/* test valid content. */
control_event_hs_descriptor_content(rend_query.onion_address,
STR_HS_CONTENT_DESC_ID, HSDIR_EXIST_ID,
- hs_desc_content);
+ hs_desc_content_control);
tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " "\
STR_HS_CONTENT_DESC_ID " " STR_HSDIR_EXIST_LONGNAME\
- "\r\n%s\r\n.\r\n650 OK\r\n", hs_desc_content);
+ "\r\n%s\r\n.\r\n650 OK\r\n", hs_desc_content_control);
tt_assert(received_msg);
tt_str_op(received_msg, OP_EQ, exp_msg);
@@ -274,14 +363,14 @@ test_pick_tor2web_rendezvous_node(void *arg)
retval = routerset_parse(options->Tor2webRendezvousPoints,
tor2web_rendezvous_str,
"test_tor2web_rp");
- tt_int_op(retval, >=, 0);
+ tt_int_op(retval, OP_GE, 0);
/* Pick rendezvous point. Make sure the correct one is
picked. Repeat many times to make sure it works properly. */
for (i = 0; i < 50 ; i++) {
chosen_rp = pick_tor2web_rendezvous_node(flags, options);
tt_assert(chosen_rp);
- tt_str_op(chosen_rp->ri->nickname, ==, tor2web_rendezvous_str);
+ tt_str_op(chosen_rp->ri->nickname, OP_EQ, tor2web_rendezvous_str);
}
done:
@@ -309,13 +398,13 @@ test_pick_bad_tor2web_rendezvous_node(void *arg)
retval = routerset_parse(options->Tor2webRendezvousPoints,
tor2web_rendezvous_str,
"test_tor2web_rp");
- tt_int_op(retval, >=, 0);
+ tt_int_op(retval, OP_GE, 0);
/* Pick rendezvous point. Since Tor2webRendezvousPoints was set to a
dummy value, we shouldn't find any eligible RPs. */
for (i = 0; i < 50 ; i++) {
chosen_rp = pick_tor2web_rendezvous_node(flags, options);
- tt_assert(!chosen_rp);
+ tt_ptr_op(chosen_rp, OP_EQ, NULL);
}
done:
@@ -346,30 +435,30 @@ test_hs_rend_data(void *arg)
REND_NO_AUTH);
tt_assert(client);
rend_data_v2_t *client_v2 = TO_REND_DATA_V2(client);
- tt_int_op(client_v2->auth_type, ==, REND_NO_AUTH);
+ tt_int_op(client_v2->auth_type, OP_EQ, REND_NO_AUTH);
tt_str_op(client_v2->onion_address, OP_EQ, STR_HS_ADDR);
tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
tt_mem_op(client_v2->descriptor_cookie, OP_EQ, client_cookie,
sizeof(client_cookie));
tt_assert(client->hsdirs_fp);
- tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0);
+ tt_int_op(smartlist_len(client->hsdirs_fp), OP_EQ, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
int ret = rend_compute_v2_desc_id(desc_id, client_v2->onion_address,
client_v2->descriptor_cookie, now, rep);
/* That shouldn't never fail. */
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
tt_mem_op(client_v2->descriptor_id[rep], OP_EQ, desc_id,
sizeof(desc_id));
}
/* The rest should be zeroed because this is a client request. */
- tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1);
- tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1);
+ tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), OP_EQ, 1);
+ tt_int_op(tor_digest_is_zero(client->rend_cookie), OP_EQ, 1);
/* Test dup(). */
client_dup = rend_data_dup(client);
tt_assert(client_dup);
rend_data_v2_t *client_dup_v2 = TO_REND_DATA_V2(client_dup);
- tt_int_op(client_dup_v2->auth_type, ==, client_v2->auth_type);
+ tt_int_op(client_dup_v2->auth_type, OP_EQ, client_v2->auth_type);
tt_str_op(client_dup_v2->onion_address, OP_EQ, client_v2->onion_address);
tt_mem_op(client_dup_v2->desc_id_fetch, OP_EQ, client_v2->desc_id_fetch,
sizeof(client_dup_v2->desc_id_fetch));
@@ -378,14 +467,14 @@ test_hs_rend_data(void *arg)
sizeof(client_dup_v2->descriptor_cookie));
tt_assert(client_dup->hsdirs_fp);
- tt_int_op(smartlist_len(client_dup->hsdirs_fp), ==, 0);
+ tt_int_op(smartlist_len(client_dup->hsdirs_fp), OP_EQ, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
tt_mem_op(client_dup_v2->descriptor_id[rep], OP_EQ,
client_v2->descriptor_id[rep], DIGEST_LEN);
}
/* The rest should be zeroed because this is a client request. */
- tt_int_op(tor_digest_is_zero(client_dup_v2->rend_pk_digest), ==, 1);
- tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), ==, 1);
+ tt_int_op(tor_digest_is_zero(client_dup_v2->rend_pk_digest), OP_EQ, 1);
+ tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), OP_EQ, 1);
rend_data_free(client);
client = NULL;
rend_data_free(client_dup);
@@ -401,19 +490,19 @@ test_hs_rend_data(void *arg)
client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH);
tt_assert(client);
client_v2 = TO_REND_DATA_V2(client);
- tt_int_op(client_v2->auth_type, ==, REND_BASIC_AUTH);
- tt_int_op(strlen(client_v2->onion_address), ==, 0);
+ tt_int_op(client_v2->auth_type, OP_EQ, REND_BASIC_AUTH);
+ tt_int_op(strlen(client_v2->onion_address), OP_EQ, 0);
tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
tt_int_op(tor_mem_is_zero(client_v2->descriptor_cookie,
- sizeof(client_v2->descriptor_cookie)), ==, 1);
+ sizeof(client_v2->descriptor_cookie)), OP_EQ, 1);
tt_assert(client->hsdirs_fp);
- tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0);
+ tt_int_op(smartlist_len(client->hsdirs_fp), OP_EQ, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
- tt_int_op(tor_digest_is_zero(client_v2->descriptor_id[rep]), ==, 1);
+ tt_int_op(tor_digest_is_zero(client_v2->descriptor_id[rep]), OP_EQ, 1);
}
/* The rest should be zeroed because this is a client request. */
- tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1);
- tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1);
+ tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), OP_EQ, 1);
+ tt_int_op(tor_digest_is_zero(client->rend_cookie), OP_EQ, 1);
rend_data_free(client);
client = NULL;
@@ -427,38 +516,38 @@ test_hs_rend_data(void *arg)
rend_cookie, REND_NO_AUTH);
tt_assert(service);
rend_data_v2_t *service_v2 = TO_REND_DATA_V2(service);
- tt_int_op(service_v2->auth_type, ==, REND_NO_AUTH);
+ tt_int_op(service_v2->auth_type, OP_EQ, REND_NO_AUTH);
tt_str_op(service_v2->onion_address, OP_EQ, STR_HS_ADDR);
tt_mem_op(service_v2->rend_pk_digest, OP_EQ, rend_pk_digest,
sizeof(rend_pk_digest));
tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie));
tt_assert(service->hsdirs_fp);
- tt_int_op(smartlist_len(service->hsdirs_fp), ==, 0);
+ tt_int_op(smartlist_len(service->hsdirs_fp), OP_EQ, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
- tt_int_op(tor_digest_is_zero(service_v2->descriptor_id[rep]), ==, 1);
+ tt_int_op(tor_digest_is_zero(service_v2->descriptor_id[rep]), OP_EQ, 1);
}
/* The rest should be zeroed because this is a service request. */
- tt_int_op(tor_digest_is_zero(service_v2->descriptor_cookie), ==, 1);
- tt_int_op(tor_digest_is_zero(service_v2->desc_id_fetch), ==, 1);
+ tt_int_op(tor_digest_is_zero(service_v2->descriptor_cookie), OP_EQ, 1);
+ tt_int_op(tor_digest_is_zero(service_v2->desc_id_fetch), OP_EQ, 1);
/* Test dup(). */
service_dup = rend_data_dup(service);
rend_data_v2_t *service_dup_v2 = TO_REND_DATA_V2(service_dup);
tt_assert(service_dup);
- tt_int_op(service_dup_v2->auth_type, ==, service_v2->auth_type);
+ tt_int_op(service_dup_v2->auth_type, OP_EQ, service_v2->auth_type);
tt_str_op(service_dup_v2->onion_address, OP_EQ, service_v2->onion_address);
tt_mem_op(service_dup_v2->rend_pk_digest, OP_EQ, service_v2->rend_pk_digest,
sizeof(service_dup_v2->rend_pk_digest));
tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie,
sizeof(service_dup->rend_cookie));
tt_assert(service_dup->hsdirs_fp);
- tt_int_op(smartlist_len(service_dup->hsdirs_fp), ==, 0);
+ tt_int_op(smartlist_len(service_dup->hsdirs_fp), OP_EQ, 0);
for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
- tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_id[rep]), ==, 1);
+ tt_assert(tor_digest_is_zero(service_dup_v2->descriptor_id[rep]));
}
/* The rest should be zeroed because this is a service request. */
- tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_cookie), ==, 1);
- tt_int_op(tor_digest_is_zero(service_dup_v2->desc_id_fetch), ==, 1);
+ tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_cookie), OP_EQ, 1);
+ tt_int_op(tor_digest_is_zero(service_dup_v2->desc_id_fetch), OP_EQ, 1);
done:
rend_data_free(service);
@@ -497,7 +586,7 @@ test_hs_auth_cookies(void *arg)
re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED, raw_cookie, &auth_type,
&err_msg);
tt_assert(!re);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN);
tt_int_op(auth_type, OP_EQ, REND_BASIC_AUTH);
memset(raw_cookie, 0, sizeof(raw_cookie));
@@ -505,7 +594,7 @@ test_hs_auth_cookies(void *arg)
re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED_STEALTH, raw_cookie,
&auth_type, &err_msg);
tt_assert(!re);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN);
tt_int_op(auth_type, OP_EQ, REND_STEALTH_AUTH);
memset(raw_cookie, 0, sizeof(raw_cookie));
@@ -514,7 +603,7 @@ test_hs_auth_cookies(void *arg)
re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED "==", raw_cookie, NULL,
&err_msg);
tt_assert(!re);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN);
/* Decoding with an unknown type should fail */
@@ -574,31 +663,22 @@ test_single_onion_poisoning(void *arg)
char *poison_path = NULL;
char *err_msg = NULL;
- /* No services, no service to verify, no problem! */
- mock_options->HiddenServiceSingleHopMode = 0;
- mock_options->HiddenServiceNonAnonymousMode = 0;
- ret = rend_config_services(mock_options, 1);
- tt_assert(ret == 0);
-
- /* Either way, no problem. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
- ret = rend_config_services(mock_options, 1);
- tt_assert(ret == 0);
/* Create the data directory, and, if the correct bit in arg is set,
* create a directory for that service.
* The data directory is required for the lockfile, which is used when
* loading keys. */
ret = check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
if (create_dir_mask & CREATE_HS_DIR1) {
ret = check_private_dir(dir1, CPD_CREATE, NULL);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
}
if (create_dir_mask & CREATE_HS_DIR2) {
ret = check_private_dir(dir2, CPD_CREATE, NULL);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
}
service_1->directory = dir1;
@@ -611,190 +691,194 @@ test_single_onion_poisoning(void *arg)
rend_service_port_config_t *port1 = rend_service_parse_port_config("80", " ",
&err_msg);
tt_assert(port1);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
smartlist_add(service_1->ports, port1);
rend_service_port_config_t *port2 = rend_service_parse_port_config("90", " ",
&err_msg);
/* Add port to service 2 */
tt_assert(port2);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
smartlist_add(service_2->ports, port2);
/* No services, a service to verify, no problem! */
mock_options->HiddenServiceSingleHopMode = 0;
mock_options->HiddenServiceNonAnonymousMode = 0;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Either way, no problem. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Add the first service */
- ret = rend_service_check_dir_and_add(services, mock_options, service_1, 0);
- tt_assert(ret == 0);
+ ret = hs_check_service_private_dir(mock_options->User, service_1->directory,
+ service_1->dir_group_readable, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ smartlist_add(services, service_1);
/* But don't add the second service yet. */
/* Service directories, but no previous keys, no problem! */
mock_options->HiddenServiceSingleHopMode = 0;
mock_options->HiddenServiceNonAnonymousMode = 0;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Either way, no problem. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Poison! Poison! Poison!
* This can only be done in HiddenServiceSingleHopMode. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Poisoning twice is a no-op. */
ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Poisoned service directories, but no previous keys, no problem! */
mock_options->HiddenServiceSingleHopMode = 0;
mock_options->HiddenServiceNonAnonymousMode = 0;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Either way, no problem. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Now add some keys, and we'll have a problem. */
ret = rend_service_load_all_keys(services);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Poisoned service directories with previous keys are not allowed. */
mock_options->HiddenServiceSingleHopMode = 0;
mock_options->HiddenServiceNonAnonymousMode = 0;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret < 0);
+ tt_int_op(ret, OP_LT, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* But they are allowed if we're in non-anonymous mode. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Re-poisoning directories with existing keys is a no-op, because
* directories with existing keys are ignored. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* And it keeps the poison. */
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Now add the second service: it has no key and no poison file */
- ret = rend_service_check_dir_and_add(services, mock_options, service_2, 0);
- tt_assert(ret == 0);
+ ret = hs_check_service_private_dir(mock_options->User, service_2->directory,
+ service_2->dir_group_readable, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ smartlist_add(services, service_2);
/* A new service, and an existing poisoned service. Not ok. */
mock_options->HiddenServiceSingleHopMode = 0;
mock_options->HiddenServiceNonAnonymousMode = 0;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret < 0);
+ tt_int_op(ret, OP_LT, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* But ok to add in non-anonymous mode. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Now remove the poisoning from the first service, and we have the opposite
* problem. */
poison_path = rend_service_sos_poison_path(service_1);
tt_assert(poison_path);
ret = unlink(poison_path);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Unpoisoned service directories with previous keys are ok, as are empty
* directories. */
mock_options->HiddenServiceSingleHopMode = 0;
mock_options->HiddenServiceNonAnonymousMode = 0;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* But the existing unpoisoned key is not ok in non-anonymous mode, even if
* there is an empty service. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret < 0);
+ tt_int_op(ret, OP_LT, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Poisoning directories with existing keys is a no-op, because directories
* with existing keys are ignored. But the new directory should poison. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* And the old directory remains unpoisoned. */
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret < 0);
+ tt_int_op(ret, OP_LT, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* And the new directory should be ignored, because it has no key. */
mock_options->HiddenServiceSingleHopMode = 0;
mock_options->HiddenServiceNonAnonymousMode = 0;
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Re-poisoning directories without existing keys is a no-op. */
mock_options->HiddenServiceSingleHopMode = 1;
mock_options->HiddenServiceNonAnonymousMode = 1;
ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* And the old directory remains unpoisoned. */
ret = rend_service_verify_single_onion_poison(service_1, mock_options);
- tt_assert(ret < 0);
+ tt_int_op(ret, OP_LT, 0);
ret = rend_service_verify_single_onion_poison(service_2, mock_options);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
done:
/* The test harness deletes the directories at exit */
@@ -942,6 +1026,8 @@ test_prune_services_on_reload(void *arg)
struct testcase_t hs_tests[] = {
{ "hs_rend_data", test_hs_rend_data, TT_FORK,
NULL, NULL },
+ { "hs_parse_static_v2_desc", test_hs_parse_static_v2_desc, TT_FORK,
+ NULL, NULL },
{ "hs_desc_event", test_hs_desc_event, TT_FORK,
NULL, NULL },
{ "pick_tor2web_rendezvous_node", test_pick_tor2web_rendezvous_node, TT_FORK,
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 40f50b322a..91b13be862 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -7,13 +7,16 @@
*/
#define CONNECTION_PRIVATE
+#define DIRECTORY_PRIVATE
#define HS_CACHE_PRIVATE
#include "ed25519_cert.h"
#include "hs_cache.h"
#include "rendcache.h"
#include "directory.h"
+#include "networkstatus.h"
#include "connection.h"
+#include "proto_http.h"
#include "hs_test_helpers.h"
#include "test_helpers.h"
@@ -54,7 +57,7 @@ test_directory(void *arg)
init_test();
/* Generate a valid descriptor with normal values. */
ret = ed25519_keypair_generate(&signing_kp1, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1);
tt_assert(desc1);
ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str);
@@ -78,7 +81,7 @@ test_directory(void *arg)
/* Tell our OOM to run and to at least remove a byte which will result in
* removing the descriptor from our cache. */
oom_size = hs_cache_handle_oom(time(NULL), 1);
- tt_int_op(oom_size, >=, 1);
+ tt_int_op(oom_size, OP_GE, 1);
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 0);
}
@@ -87,7 +90,7 @@ test_directory(void *arg)
{
ed25519_keypair_t signing_kp_zero;
ret = ed25519_keypair_generate(&signing_kp_zero, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
hs_descriptor_t *desc_zero_lifetime;
desc_zero_lifetime = hs_helper_build_hs_desc_with_ip(&signing_kp_zero);
tt_assert(desc_zero_lifetime);
@@ -115,7 +118,7 @@ test_directory(void *arg)
tt_int_op(ret, OP_EQ, 0);
/* Cleanup our entire cache. */
oom_size = hs_cache_handle_oom(time(NULL), 1);
- tt_int_op(oom_size, >=, 1);
+ tt_int_op(oom_size, OP_GE, 1);
hs_descriptor_free(desc_zero_lifetime);
tor_free(desc_zero_lifetime_str);
}
@@ -177,7 +180,7 @@ test_clean_as_dir(void *arg)
/* Generate a valid descriptor with values. */
ret = ed25519_keypair_generate(&signing_kp1, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
desc1 = hs_helper_build_hs_desc_with_ip(&signing_kp1);
tt_assert(desc1);
ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str);
@@ -187,21 +190,21 @@ test_clean_as_dir(void *arg)
/* With the lifetime being 3 hours, a cleanup shouldn't remove it. */
ret = cache_clean_v3_as_dir(now, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Should be present after clean up. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 1);
/* Set a cutoff 100 seconds in the past. It should not remove the entry
* since the entry is still recent enough. */
ret = cache_clean_v3_as_dir(now, now - 100);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Should be present after clean up. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 1);
/* Set a cutoff of 100 seconds in the future. It should remove the entry
* that we've just added since it's not too old for the cutoff. */
ret = cache_clean_v3_as_dir(now, now + 100);
- tt_int_op(ret, >, 0);
+ tt_int_op(ret, OP_GT, 0);
/* Shouldn't be present after clean up. */
ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL);
tt_int_op(ret, OP_EQ, 0);
@@ -231,7 +234,7 @@ helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key)
retval = ed25519_public_to_base64(hsdir_cache_key,
blinded_key);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
tor_asprintf(&hsdir_query_str, GET("/tor/hs/3/%s"), hsdir_cache_key);
}
@@ -290,7 +293,7 @@ test_upload_and_download_hs_desc(void *arg)
{
ed25519_keypair_t signing_kp;
retval = ed25519_keypair_generate(&signing_kp, 0);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
tt_assert(published_desc);
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
@@ -301,7 +304,7 @@ test_upload_and_download_hs_desc(void *arg)
/* Publish descriptor to the HSDir */
{
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
- tt_int_op(retval, ==, 200);
+ tt_int_op(retval, OP_EQ, 200);
}
/* Simulate a fetch of the previously published descriptor */
@@ -342,6 +345,7 @@ test_hsdir_revision_counter_check(void *arg)
hs_descriptor_t *published_desc = NULL;
char *published_desc_str = NULL;
+ uint8_t subcredential[DIGEST256_LEN];
char *received_desc_str = NULL;
hs_descriptor_t *received_desc = NULL;
@@ -353,7 +357,7 @@ test_hsdir_revision_counter_check(void *arg)
/* Generate a valid descriptor with normal values. */
{
retval = ed25519_keypair_generate(&signing_kp, 0);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
tt_assert(published_desc);
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
@@ -364,13 +368,13 @@ test_hsdir_revision_counter_check(void *arg)
/* Publish descriptor to the HSDir */
{
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
- tt_int_op(retval, ==, 200);
+ tt_int_op(retval, OP_EQ, 200);
}
/* Try publishing again with the same revision counter: Should fail. */
{
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
- tt_int_op(retval, ==, 400);
+ tt_int_op(retval, OP_EQ, 400);
}
/* Fetch the published descriptor and validate the revision counter. */
@@ -378,14 +382,16 @@ test_hsdir_revision_counter_check(void *arg)
const ed25519_public_key_t *blinded_key;
blinded_key = &published_desc->plaintext_data.blinded_pubkey;
+ hs_get_subcredential(&signing_kp.pubkey, blinded_key, subcredential);
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
- retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc);
- tt_int_op(retval, ==, 0);
+ retval = hs_desc_decode_descriptor(received_desc_str,
+ subcredential, &received_desc);
+ tt_int_op(retval, OP_EQ, 0);
tt_assert(received_desc);
/* Check that the revision counter is correct */
- tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 42);
+ tt_u64_op(received_desc->plaintext_data.revision_counter, OP_EQ, 42);
hs_descriptor_free(received_desc);
received_desc = NULL;
@@ -401,7 +407,7 @@ test_hsdir_revision_counter_check(void *arg)
tt_int_op(retval, OP_EQ, 0);
retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str);
- tt_int_op(retval, ==, 200);
+ tt_int_op(retval, OP_EQ, 200);
}
/* Again, fetch the published descriptor and perform the revision counter
@@ -412,12 +418,13 @@ test_hsdir_revision_counter_check(void *arg)
blinded_key = &published_desc->plaintext_data.blinded_pubkey;
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
- retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc);
- tt_int_op(retval, ==, 0);
+ retval = hs_desc_decode_descriptor(received_desc_str,
+ subcredential, &received_desc);
+ tt_int_op(retval, OP_EQ, 0);
tt_assert(received_desc);
/* Check that the revision counter is the latest */
- tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 1313);
+ tt_u64_op(received_desc->plaintext_data.revision_counter, OP_EQ, 1313);
}
done:
@@ -427,6 +434,115 @@ test_hsdir_revision_counter_check(void *arg)
tor_free(published_desc_str);
}
+static networkstatus_t mock_ns;
+
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void) now;
+ return &mock_ns;
+}
+
+/** Test that we can store HS descriptors in the client HS cache. */
+static void
+test_client_cache(void *arg)
+{
+ int retval;
+ ed25519_keypair_t signing_kp;
+ hs_descriptor_t *published_desc = NULL;
+ char *published_desc_str = NULL;
+ uint8_t wanted_subcredential[DIGEST256_LEN];
+ response_handler_args_t *args = NULL;
+ dir_connection_t *conn = NULL;
+
+ (void) arg;
+
+ /* Initialize HSDir cache subsystem */
+ init_test();
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ /* Set consensus time */
+ parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ parse_rfc1123_time("Sat, 26 Oct 1985 16:00:00 UTC",
+ &mock_ns.valid_until);
+
+ /* Generate a valid descriptor with normal values. */
+ {
+ retval = ed25519_keypair_generate(&signing_kp, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
+ tt_assert(published_desc);
+ retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
+ &published_desc_str);
+ tt_int_op(retval, OP_EQ, 0);
+ memcpy(wanted_subcredential, published_desc->subcredential, DIGEST256_LEN);
+ tt_assert(!tor_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
+ }
+
+ /* Test handle_response_fetch_hsdesc_v3() */
+ {
+ args = tor_malloc_zero(sizeof(response_handler_args_t));
+ args->status_code = 200;
+ args->reason = NULL;
+ args->body = published_desc_str;
+ args->body_len = strlen(published_desc_str);
+
+ conn = tor_malloc_zero(sizeof(dir_connection_t));
+ conn->hs_ident = tor_malloc_zero(sizeof(hs_ident_dir_conn_t));
+ ed25519_pubkey_copy(&conn->hs_ident->identity_pk, &signing_kp.pubkey);
+ }
+
+ /* store the descriptor! */
+ retval = handle_response_fetch_hsdesc_v3(conn, args);
+ tt_int_op(retval, == , 0);
+
+ /* Progress time a bit and attempt to clean cache: our desc should not be
+ * cleaned since we still in the same TP. */
+ {
+ parse_rfc1123_time("Sat, 27 Oct 1985 02:00:00 UTC",
+ &mock_ns.valid_after);
+ parse_rfc1123_time("Sat, 27 Oct 1985 03:00:00 UTC",
+ &mock_ns.fresh_until);
+ parse_rfc1123_time("Sat, 27 Oct 1985 05:00:00 UTC",
+ &mock_ns.valid_until);
+
+ /* fetch the descriptor and make sure it's there */
+ const hs_descriptor_t *cached_desc = NULL;
+ cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
+ tt_assert(cached_desc);
+ tt_mem_op(cached_desc->subcredential, OP_EQ, wanted_subcredential,
+ DIGEST256_LEN);
+ }
+
+ /* Progress time to next TP and check that desc was cleaned */
+ {
+ parse_rfc1123_time("Sat, 27 Oct 1985 12:00:00 UTC",
+ &mock_ns.valid_after);
+ parse_rfc1123_time("Sat, 27 Oct 1985 13:00:00 UTC",
+ &mock_ns.fresh_until);
+ parse_rfc1123_time("Sat, 27 Oct 1985 15:00:00 UTC",
+ &mock_ns.valid_until);
+
+ const hs_descriptor_t *cached_desc = NULL;
+ cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
+ tt_assert(!cached_desc);
+ }
+
+ done:
+ tor_free(args);
+ hs_descriptor_free(published_desc);
+ tor_free(published_desc_str);
+ if (conn) {
+ tor_free(conn->hs_ident);
+ tor_free(conn);
+ }
+}
+
struct testcase_t hs_cache[] = {
/* Encoding tests. */
{ "directory", test_directory, TT_FORK,
@@ -437,6 +553,8 @@ struct testcase_t hs_cache[] = {
NULL, NULL },
{ "upload_and_download_hs_desc", test_upload_and_download_hs_desc, TT_FORK,
NULL, NULL },
+ { "client_cache", test_client_cache, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c
new file mode 100644
index 0000000000..1b3c788a67
--- /dev/null
+++ b/src/test/test_hs_cell.c
@@ -0,0 +1,130 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_cell.c
+ * \brief Test hidden service cell functionality.
+ */
+
+#define HS_INTROPOINT_PRIVATE
+#define HS_SERVICE_PRIVATE
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+
+#include "crypto_ed25519.h"
+#include "hs_cell.h"
+#include "hs_intropoint.h"
+#include "hs_service.h"
+
+/* Trunnel. */
+#include "hs/cell_establish_intro.h"
+
+/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
+ * parse it from the receiver side. */
+static void
+test_gen_establish_intro_cell(void *arg)
+{
+ (void) arg;
+ ssize_t ret;
+ char circ_nonce[DIGEST_LEN] = {0};
+ uint8_t buf[RELAY_PAYLOAD_SIZE];
+ trn_cell_establish_intro_t *cell_in = NULL;
+
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+
+ /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
+ attempt to parse it. */
+ {
+ /* We only need the auth key pair here. */
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0);
+ /* Auth key pair is generated in the constructor so we are all set for
+ * using this IP object. */
+ ret = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ service_intro_point_free(ip);
+ tt_u64_op(ret, OP_GT, 0);
+ }
+
+ /* Check the contents of the cell */
+ {
+ /* First byte is the auth key type: make sure its correct */
+ tt_int_op(buf[0], OP_EQ, HS_INTRO_AUTH_KEY_TYPE_ED25519);
+ /* Next two bytes is auth key len */
+ tt_int_op(ntohs(get_uint16(buf+1)), OP_EQ, ED25519_PUBKEY_LEN);
+ /* Skip to the number of extensions: no extensions */
+ tt_int_op(buf[35], OP_EQ, 0);
+ /* Skip to the sig len. Make sure it's the size of an ed25519 sig */
+ tt_int_op(ntohs(get_uint16(buf+35+1+32)), OP_EQ, ED25519_SIG_LEN);
+ }
+
+ /* Parse it as the receiver */
+ {
+ ret = trn_cell_establish_intro_parse(&cell_in, buf, sizeof(buf));
+ tt_u64_op(ret, OP_GT, 0);
+
+ ret = verify_establish_intro_cell(cell_in,
+ (const uint8_t *) circ_nonce,
+ sizeof(circ_nonce));
+ tt_u64_op(ret, OP_EQ, 0);
+ }
+
+ done:
+ trn_cell_establish_intro_free(cell_in);
+}
+
+/* Mocked ed25519_sign_prefixed() function that always fails :) */
+static int
+mock_ed25519_sign_prefixed(ed25519_signature_t *signature_out,
+ const uint8_t *msg, size_t msg_len,
+ const char *prefix_str,
+ const ed25519_keypair_t *keypair) {
+ (void) signature_out;
+ (void) msg;
+ (void) msg_len;
+ (void) prefix_str;
+ (void) keypair;
+ return -1;
+}
+
+/** We simulate a failure to create an ESTABLISH_INTRO cell */
+static void
+test_gen_establish_intro_cell_bad(void *arg)
+{
+ (void) arg;
+ ssize_t cell_len = 0;
+ trn_cell_establish_intro_t *cell = NULL;
+ char circ_nonce[DIGEST_LEN] = {0};
+ hs_service_intro_point_t *ip = NULL;
+
+ MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
+
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+
+ setup_full_capture_of_logs(LOG_WARN);
+ /* Easiest way to make that function fail is to mock the
+ ed25519_sign_prefixed() function and make it fail. */
+ cell = trn_cell_establish_intro_new();
+ tt_assert(cell);
+ ip = service_intro_point_new(NULL, 0);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL);
+ service_intro_point_free(ip);
+ expect_log_msg_containing("Unable to make signature for "
+ "ESTABLISH_INTRO cell.");
+ teardown_capture_of_logs();
+ tt_i64_op(cell_len, OP_EQ, -1);
+
+ done:
+ trn_cell_establish_intro_free(cell);
+ UNMOCK(ed25519_sign_prefixed);
+}
+
+struct testcase_t hs_cell_tests[] = {
+ { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
+ NULL, NULL },
+ { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
new file mode 100644
index 0000000000..750920fac0
--- /dev/null
+++ b/src/test/test_hs_client.c
@@ -0,0 +1,599 @@
+/* Copyright (c) 2016-2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_client.c
+ * \brief Test prop224 HS client functionality.
+ */
+
+#define CRYPTO_PRIVATE
+#define MAIN_PRIVATE
+#define HS_CLIENT_PRIVATE
+#define TOR_CHANNEL_INTERNAL_
+#define CIRCUITBUILD_PRIVATE
+#define CIRCUITLIST_PRIVATE
+#define CONNECTION_PRIVATE
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+#include "rend_test_helpers.h"
+#include "hs_test_helpers.h"
+
+#include "config.h"
+#include "crypto.h"
+#include "channeltls.h"
+#include "main.h"
+#include "nodelist.h"
+#include "routerset.h"
+
+#include "hs_circuit.h"
+#include "hs_client.h"
+#include "hs_ident.h"
+#include "hs_cache.h"
+#include "circuitlist.h"
+#include "circuitbuild.h"
+#include "connection.h"
+#include "connection_edge.h"
+#include "networkstatus.h"
+
+static int
+mock_connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
+{
+ (void) ap_conn;
+ return 0;
+}
+
+static networkstatus_t mock_ns;
+
+/* Always return NULL. */
+static networkstatus_t *
+mock_networkstatus_get_live_consensus_false(time_t now)
+{
+ (void) now;
+ return NULL;
+}
+
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void) now;
+ return &mock_ns;
+}
+
+/* Test helper function: Setup a circuit and a stream with the same hidden
+ * service destination, and put them in <b>circ_out</b> and
+ * <b>conn_out</b>. Make the stream wait for circuits to be established to the
+ * hidden service. */
+static int
+helper_get_circ_and_stream_for_test(origin_circuit_t **circ_out,
+ connection_t **conn_out,
+ int is_legacy)
+{
+ int retval;
+ channel_tls_t *n_chan=NULL;
+ rend_data_t *conn_rend_data = NULL;
+ origin_circuit_t *or_circ = NULL;
+ connection_t *conn = NULL;
+ ed25519_public_key_t service_pk;
+
+ /* Make a dummy connection stream and make it wait for our circuit */
+ conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT,
+ CONN_TYPE_AP /* ??? */,
+ 0);
+ if (is_legacy) {
+ /* Legacy: Setup rend_data of stream */
+ char service_id[REND_SERVICE_ID_LEN_BASE32+1] = {0};
+ TO_EDGE_CONN(conn)->rend_data = mock_rend_data(service_id);
+ conn_rend_data = TO_EDGE_CONN(conn)->rend_data;
+ } else {
+ /* prop224: Setup hs conn identifier on the stream */
+ ed25519_secret_key_t sk;
+ tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
+ tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
+
+ /* Setup hs_conn_identifier of stream */
+ TO_EDGE_CONN(conn)->hs_ident = hs_ident_edge_conn_new(&service_pk);
+ }
+
+ /* Make it wait for circuit */
+ connection_ap_mark_as_pending_circuit(TO_ENTRY_CONN(conn));
+
+ /* This is needed to silence a BUG warning from
+ connection_edge_update_circuit_isolation() */
+ TO_ENTRY_CONN(conn)->original_dest_address =
+ tor_strdup(TO_ENTRY_CONN(conn)->socks_request->address);
+
+ /****************************************************/
+
+ /* Now make dummy circuit */
+ or_circ = origin_circuit_new();
+
+ or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED;
+
+ or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
+ or_circ->build_state->is_internal = 1;
+
+ if (is_legacy) {
+ /* Legacy: Setup rend data and final cpath */
+ or_circ->build_state->pending_final_cpath =
+ tor_malloc_zero(sizeof(crypt_path_t));
+ or_circ->build_state->pending_final_cpath->magic = CRYPT_PATH_MAGIC;
+ or_circ->build_state->pending_final_cpath->rend_dh_handshake_state =
+ crypto_dh_new(DH_TYPE_REND);
+ tt_assert(
+ or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
+ retval = crypto_dh_generate_public(
+ or_circ->build_state->pending_final_cpath->rend_dh_handshake_state);
+ tt_int_op(retval, OP_EQ, 0);
+ or_circ->rend_data = rend_data_dup(conn_rend_data);
+ } else {
+ /* prop224: Setup hs ident on the circuit */
+ or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
+ HS_IDENT_CIRCUIT_RENDEZVOUS);
+ }
+
+ TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
+
+ /* fake n_chan */
+ n_chan = tor_malloc_zero(sizeof(channel_tls_t));
+ n_chan->base_.global_identifier = 1;
+ or_circ->base_.n_chan = &(n_chan->base_);
+
+ *circ_out = or_circ;
+ *conn_out = conn;
+
+ return 0;
+
+ done:
+ /* something failed */
+ return -1;
+}
+
+/* Test: Ensure that setting up legacy e2e rendezvous circuits works
+ * correctly. */
+static void
+test_e2e_rend_circuit_setup_legacy(void *arg)
+{
+ ssize_t retval;
+ origin_circuit_t *or_circ = NULL;
+ connection_t *conn = NULL;
+
+ (void) arg;
+
+ /** In this test we create a v2 legacy HS stream and a circuit with the same
+ * hidden service destination. We make the stream wait for circuits to be
+ * established to the hidden service, and then we complete the circuit using
+ * the hs_circuit_setup_e2e_rend_circ_legacy_client() function. We then
+ * check that the end-to-end cpath was setup correctly and that the stream
+ * was attached to the circuit as expected. */
+
+ MOCK(connection_ap_handshake_send_begin,
+ mock_connection_ap_handshake_send_begin);
+
+ /* Setup */
+ retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 1);
+ tt_int_op(retval, OP_EQ, 0);
+ tt_assert(or_circ);
+ tt_assert(conn);
+
+ /* Check number of hops */
+ retval = cpath_get_n_hops(&or_circ->cpath);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check that our stream is not attached on any circuits */
+ tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, NULL);
+
+ /********************************************** */
+
+ /* Make a good RENDEZVOUS1 cell body because it needs to pass key exchange
+ * digest verification... */
+ uint8_t rend_cell_body[DH_KEY_LEN+DIGEST_LEN] = {2};
+ {
+ char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN];
+ crypto_dh_t *dh_state =
+ or_circ->build_state->pending_final_cpath->rend_dh_handshake_state;
+ /* compute and overwrite digest of cell body with the right value */
+ retval = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh_state,
+ (char*)rend_cell_body, DH_KEY_LEN,
+ keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN);
+ tt_int_op(retval, OP_GT, 0);
+ memcpy(rend_cell_body+DH_KEY_LEN, keys, DIGEST_LEN);
+ }
+
+ /* Setup the circuit */
+ retval = hs_circuit_setup_e2e_rend_circ_legacy_client(or_circ,
+ rend_cell_body);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /**********************************************/
+
+ /* See that a hop was added to the circuit's cpath */
+ retval = cpath_get_n_hops(&or_circ->cpath);
+ tt_int_op(retval, OP_EQ, 1);
+
+ /* Check the digest algo */
+ tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
+ OP_EQ, DIGEST_SHA1);
+ tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
+ OP_EQ, DIGEST_SHA1);
+ tt_assert(or_circ->cpath->f_crypto);
+ tt_assert(or_circ->cpath->b_crypto);
+
+ /* Ensure that circ purpose was changed */
+ tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
+
+ /* Test that stream got attached */
+ tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
+
+ done:
+ connection_free_(conn);
+ if (or_circ)
+ tor_free(TO_CIRCUIT(or_circ)->n_chan);
+ circuit_free(TO_CIRCUIT(or_circ));
+}
+
+/* Test: Ensure that setting up v3 rendezvous circuits works correctly. */
+static void
+test_e2e_rend_circuit_setup(void *arg)
+{
+ uint8_t ntor_key_seed[DIGEST256_LEN] = {0};
+ origin_circuit_t *or_circ = NULL;
+ int retval;
+ connection_t *conn = NULL;
+
+ (void) arg;
+
+ /** In this test we create a prop224 v3 HS stream and a circuit with the same
+ * hidden service destination. We make the stream wait for circuits to be
+ * established to the hidden service, and then we complete the circuit using
+ * the hs_circuit_setup_e2e_rend_circ() function. We then check that the
+ * end-to-end cpath was setup correctly and that the stream was attached to
+ * the circuit as expected. */
+
+ MOCK(connection_ap_handshake_send_begin,
+ mock_connection_ap_handshake_send_begin);
+
+ /* Setup */
+ retval = helper_get_circ_and_stream_for_test( &or_circ, &conn, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ tt_assert(or_circ);
+ tt_assert(conn);
+
+ /* Check number of hops: There should be no hops yet to this circ */
+ retval = cpath_get_n_hops(&or_circ->cpath);
+ tt_int_op(retval, OP_EQ, 0);
+ tt_ptr_op(or_circ->cpath, OP_EQ, NULL);
+
+ /* Check that our stream is not attached on any circuits */
+ tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, NULL);
+
+ /**********************************************/
+
+ /* Setup the circuit */
+ retval = hs_circuit_setup_e2e_rend_circ(or_circ,
+ ntor_key_seed, sizeof(ntor_key_seed),
+ 0);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /**********************************************/
+
+ /* See that a hop was added to the circuit's cpath */
+ retval = cpath_get_n_hops(&or_circ->cpath);
+ tt_int_op(retval, OP_EQ, 1);
+
+ /* Check that the crypt path has prop224 algorithm parameters */
+ tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
+ OP_EQ, DIGEST_SHA3_256);
+ tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
+ OP_EQ, DIGEST_SHA3_256);
+ tt_assert(or_circ->cpath->f_crypto);
+ tt_assert(or_circ->cpath->b_crypto);
+
+ /* Ensure that circ purpose was changed */
+ tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_REND_JOINED);
+
+ /* Test that stream got attached */
+ tt_ptr_op(TO_EDGE_CONN(conn)->on_circuit, OP_EQ, TO_CIRCUIT(or_circ));
+
+ done:
+ connection_free_(conn);
+ if (or_circ)
+ tor_free(TO_CIRCUIT(or_circ)->n_chan);
+ circuit_free(TO_CIRCUIT(or_circ));
+}
+
+/** Test client logic for picking intro points from a descriptor. Also test how
+ * ExcludeNodes and intro point failures affect picking intro points. */
+static void
+test_client_pick_intro(void *arg)
+{
+ int ret;
+ ed25519_keypair_t service_kp;
+ hs_descriptor_t *desc = NULL;
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ (void) arg;
+
+ hs_init();
+
+ /* Generate service keypair */
+ tt_int_op(0, OP_EQ, ed25519_keypair_generate(&service_kp, 0));
+
+ /* Set time */
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+
+ update_approx_time(mock_ns.fresh_until-10);
+ time_t now = approx_time();
+
+ /* Test logic:
+ *
+ * 1) Add our desc with intro points to the HS cache.
+ *
+ * 2) Mark all descriptor intro points except _the chosen one_ as
+ * failed. Then query the desc to get a random intro: check that we got
+ * _the chosen one_. Then fail the chosen one as well, and see that no
+ * intros are returned.
+ *
+ * 3) Then clean the intro state cache and get an intro point.
+ *
+ * 4) Try fetching an intro with the wrong service key: shouldn't work
+ *
+ * 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that
+ * nothing is returned.
+ */
+
+ /* 1) Add desc to HS cache */
+ {
+ char *encoded = NULL;
+ desc = hs_helper_build_hs_desc_with_ip(&service_kp);
+ ret = hs_desc_encode_descriptor(desc, &service_kp, &encoded);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_assert(encoded);
+
+ /* store it */
+ hs_cache_store_as_client(encoded, &service_kp.pubkey);
+
+ /* fetch it to make sure it works */
+ const hs_descriptor_t *fetched_desc =
+ hs_cache_lookup_as_client(&service_kp.pubkey);
+ tt_assert(fetched_desc);
+ tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential,
+ DIGEST256_LEN);
+ tt_assert(!tor_mem_is_zero((char*)fetched_desc->subcredential,
+ DIGEST256_LEN));
+ tor_free(encoded);
+ }
+
+ /* 2) Mark all intro points except _the chosen one_ as failed. Then query the
+ * desc and get a random intro: check that we got _the chosen one_. */
+ {
+ /* Pick the chosen intro point and get its ei */
+ hs_desc_intro_point_t *chosen_intro_point =
+ smartlist_get(desc->encrypted_data.intro_points, 0);
+ extend_info_t *chosen_intro_ei =
+ desc_intro_point_to_extend_info(chosen_intro_point);
+ tt_assert(chosen_intro_point);
+ tt_assert(chosen_intro_ei);
+
+ /* Now mark all other intro points as failed */
+ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
+ hs_desc_intro_point_t *, ip) {
+ /* Skip the chosen intro point */
+ if (ip == chosen_intro_point) {
+ continue;
+ }
+ ed25519_public_key_t *intro_auth_key = &ip->auth_key_cert->signed_key;
+ hs_cache_client_intro_state_note(&service_kp.pubkey,
+ intro_auth_key,
+ INTRO_POINT_FAILURE_GENERIC);
+ } SMARTLIST_FOREACH_END(ip);
+
+ /* Try to get a random intro: Should return the chosen one! */
+ extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
+ tor_assert(ip);
+ tt_assert(!tor_mem_is_zero((char*)ip->identity_digest, DIGEST_LEN));
+ tt_mem_op(ip->identity_digest, OP_EQ, chosen_intro_ei->identity_digest,
+ DIGEST_LEN);
+
+ extend_info_free(chosen_intro_ei);
+ extend_info_free(ip);
+
+ /* Now also mark the chosen one as failed: See that we can't get any intro
+ points anymore. */
+ hs_cache_client_intro_state_note(&service_kp.pubkey,
+ &chosen_intro_point->auth_key_cert->signed_key,
+ INTRO_POINT_FAILURE_TIMEOUT);
+ ip = client_get_random_intro(&service_kp.pubkey);
+ tor_assert(!ip);
+ }
+
+ /* 3) Clean the intro state cache and get an intro point */
+ {
+ /* Pretend we are 5 mins in the future and order a cleanup of the intro
+ * state. This should clean up the intro point failures and allow us to get
+ * an intro. */
+ hs_cache_client_intro_state_clean(now + 5*60);
+
+ /* Get an intro. It should work! */
+ extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
+ tor_assert(ip);
+ extend_info_free(ip);
+ }
+
+ /* 4) Try fetching an intro with the wrong service key: shouldn't work */
+ {
+ ed25519_keypair_t dummy_kp;
+ tt_int_op(0, OP_EQ, ed25519_keypair_generate(&dummy_kp, 0));
+ extend_info_t *ip = client_get_random_intro(&dummy_kp.pubkey);
+ tor_assert(!ip);
+ }
+
+ /* 5) Set StrictNodes and put all our intro points in ExcludeNodes: see that
+ * nothing is returned. */
+ {
+ get_options_mutable()->ExcludeNodes = routerset_new();
+ get_options_mutable()->StrictNodes = 1;
+ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
+ hs_desc_intro_point_t *, ip) {
+ extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip);
+ if (intro_ei) {
+ const char *ptr;
+ char ip_addr[TOR_ADDR_BUF_LEN];
+ /* We need to decorate in case it is an IPv6 else routerset_parse()
+ * doesn't like it. */
+ ptr = tor_addr_to_str(ip_addr, &intro_ei->addr, sizeof(ip_addr), 1);
+ tt_assert(ptr == ip_addr);
+ ret = routerset_parse(get_options_mutable()->ExcludeNodes,
+ ip_addr, "");
+ tt_int_op(ret, OP_EQ, 0);
+ extend_info_free(intro_ei);
+ }
+ } SMARTLIST_FOREACH_END(ip);
+
+ extend_info_t *ip = client_get_random_intro(&service_kp.pubkey);
+ tt_assert(!ip);
+ }
+
+ done:
+ hs_descriptor_free(desc);
+}
+
+static int
+mock_router_have_minimum_dir_info_false(void)
+{
+ return 0;
+}
+static int
+mock_router_have_minimum_dir_info_true(void)
+{
+ return 1;
+}
+
+static hs_client_fetch_status_t
+mock_fetch_v3_desc_error(const ed25519_public_key_t *key)
+{
+ (void) key;
+ return HS_CLIENT_FETCH_ERROR;
+}
+
+static void
+mock_connection_mark_unattached_ap_(entry_connection_t *conn, int endreason,
+ int line, const char *file)
+{
+ (void) line;
+ (void) file;
+ conn->edge_.end_reason = endreason;
+}
+
+static void
+test_descriptor_fetch(void *arg)
+{
+ int ret;
+ entry_connection_t *ec = NULL;
+ ed25519_public_key_t service_pk;
+ ed25519_secret_key_t service_sk;
+
+ (void) arg;
+
+ hs_init();
+ memset(&service_sk, 'A', sizeof(service_sk));
+ ret = ed25519_public_key_generate(&service_pk, &service_sk);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Initialize this so get_voting_interval() doesn't freak out. */
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ec = entry_connection_new(CONN_TYPE_AP, AF_INET);
+ tt_assert(ec);
+ ENTRY_TO_EDGE_CONN(ec)->hs_ident = hs_ident_edge_conn_new(&service_pk);
+ tt_assert(ENTRY_TO_EDGE_CONN(ec)->hs_ident);
+ TO_CONN(ENTRY_TO_EDGE_CONN(ec))->state = AP_CONN_STATE_RENDDESC_WAIT;
+ smartlist_add(get_connection_array(), &ec->edge_.base_);
+
+ /* 1. FetchHidServDescriptors is false so we shouldn't be able to fetch. */
+ get_options_mutable()->FetchHidServDescriptors = 0;
+ ret = hs_client_refetch_hsdesc(&service_pk);
+ tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_NOT_ALLOWED);
+ get_options_mutable()->FetchHidServDescriptors = 1;
+
+ /* 2. We don't have a live consensus. */
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus_false);
+ ret = hs_client_refetch_hsdesc(&service_pk);
+ UNMOCK(networkstatus_get_live_consensus);
+ tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_MISSING_INFO);
+
+ /* From now on, return a live consensus. */
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ /* 3. Not enough dir information. */
+ MOCK(router_have_minimum_dir_info,
+ mock_router_have_minimum_dir_info_false);
+ ret = hs_client_refetch_hsdesc(&service_pk);
+ UNMOCK(router_have_minimum_dir_info);
+ tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_MISSING_INFO);
+
+ /* From now on, we do have enough directory information. */
+ MOCK(router_have_minimum_dir_info,
+ mock_router_have_minimum_dir_info_true);
+
+ /* 4. We do have a pending directory request. */
+ {
+ dir_connection_t *dir_conn = dir_connection_new(AF_INET);
+ dir_conn->hs_ident = tor_malloc_zero(sizeof(hs_ident_dir_conn_t));
+ TO_CONN(dir_conn)->purpose = DIR_PURPOSE_FETCH_HSDESC;
+ ed25519_pubkey_copy(&dir_conn->hs_ident->identity_pk, &service_pk);
+ smartlist_add(get_connection_array(), TO_CONN(dir_conn));
+ ret = hs_client_refetch_hsdesc(&service_pk);
+ smartlist_remove(get_connection_array(), TO_CONN(dir_conn));
+ connection_free_(TO_CONN(dir_conn));
+ tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_PENDING);
+ }
+
+ /* 5. We'll trigger an error on the fetch_desc_v3 and force to close all
+ * pending SOCKS request. */
+ MOCK(router_have_minimum_dir_info,
+ mock_router_have_minimum_dir_info_true);
+ MOCK(fetch_v3_desc, mock_fetch_v3_desc_error);
+ MOCK(connection_mark_unattached_ap_,
+ mock_connection_mark_unattached_ap_);
+ ret = hs_client_refetch_hsdesc(&service_pk);
+ UNMOCK(fetch_v3_desc);
+ UNMOCK(connection_mark_unattached_ap_);
+ tt_int_op(ret, OP_EQ, HS_CLIENT_FETCH_ERROR);
+ /* The close waiting for descriptor function has been called. */
+ tt_int_op(ec->edge_.end_reason, OP_EQ, END_STREAM_REASON_RESOLVEFAILED);
+
+ done:
+ connection_free_(ENTRY_TO_CONN(ec));
+ UNMOCK(networkstatus_get_live_consensus);
+ UNMOCK(router_have_minimum_dir_info);
+ hs_free_all();
+}
+
+struct testcase_t hs_client_tests[] = {
+ { "e2e_rend_circuit_setup_legacy", test_e2e_rend_circuit_setup_legacy,
+ TT_FORK, NULL, NULL },
+ { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup,
+ TT_FORK, NULL, NULL },
+ { "client_pick_intro", test_client_pick_intro,
+ TT_FORK, NULL, NULL },
+ { "descriptor_fetch", test_descriptor_fetch,
+ TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
new file mode 100644
index 0000000000..21daa58abd
--- /dev/null
+++ b/src/test/test_hs_common.c
@@ -0,0 +1,1822 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_common.c
+ * \brief Test hidden service common functionalities.
+ */
+
+#define HS_COMMON_PRIVATE
+#define HS_CLIENT_PRIVATE
+#define HS_SERVICE_PRIVATE
+#define NODELIST_PRIVATE
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+#include "hs_test_helpers.h"
+
+#include "connection_edge.h"
+#include "hs_common.h"
+#include "hs_client.h"
+#include "hs_service.h"
+#include "config.h"
+#include "networkstatus.h"
+#include "directory.h"
+#include "dirvote.h"
+#include "nodelist.h"
+#include "routerlist.h"
+#include "statefile.h"
+#include "circuitlist.h"
+#include "shared_random.h"
+#include "util.h"
+
+/** Test the validation of HS v3 addresses */
+static void
+test_validate_address(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ /* Address too short and too long. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid("blah");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("has an invalid length");
+ teardown_capture_of_logs();
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("has an invalid length");
+ teardown_capture_of_logs();
+
+ /* Invalid checksum (taken from prop224) */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "l5satjgud6gucryazcyvyvhuxhr74u6ygigiuyixe3a6ysis67ororad");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("invalid checksum");
+ teardown_capture_of_logs();
+
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "btojiu7nu5y5iwut64eufevogqdw4wmqzugnoluw232r4t3ecsfv37ad");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("invalid checksum");
+ teardown_capture_of_logs();
+
+ /* Non base32 decodable string. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_address_is_valid(
+ "????????????????????????????????????????????????????????");
+ tt_int_op(ret, OP_EQ, 0);
+ expect_log_msg_containing("can't be decoded");
+ teardown_capture_of_logs();
+
+ /* Valid address. */
+ ret = hs_address_is_valid(
+ "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid");
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ ;
+}
+
+static int
+mock_write_str_to_file(const char *path, const char *str, int bin)
+{
+ (void)bin;
+ tt_str_op(path, OP_EQ, "/double/five"PATH_SEPARATOR"squared");
+ tt_str_op(str, OP_EQ,
+ "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion\n");
+
+ done:
+ return 0;
+}
+
+/** Test building HS v3 onion addresses. Uses test vectors from the
+ * ./hs_build_address.py script. */
+static void
+test_build_address(void *arg)
+{
+ int ret;
+ char onion_addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ ed25519_public_key_t pubkey;
+ /* hex-encoded ed25519 pubkey used in hs_build_address.py */
+ char pubkey_hex[] =
+ "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
+ hs_service_t *service = NULL;
+
+ (void) arg;
+
+ MOCK(write_str_to_file, mock_write_str_to_file);
+
+ /* The following has been created with hs_build_address.py script that
+ * follows proposal 224 specification to build an onion address. */
+ static const char *test_addr =
+ "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid";
+
+ /* Let's try to build the same onion address as the script */
+ base16_decode((char*)pubkey.pubkey, sizeof(pubkey.pubkey),
+ pubkey_hex, strlen(pubkey_hex));
+ hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr);
+ tt_str_op(test_addr, OP_EQ, onion_addr);
+ /* Validate that address. */
+ ret = hs_address_is_valid(onion_addr);
+ tt_int_op(ret, OP_EQ, 1);
+
+ service = tor_malloc_zero(sizeof(hs_service_t));
+ memcpy(service->onion_address, onion_addr, sizeof(service->onion_address));
+ tor_asprintf(&service->config.directory_path, "/double/five");
+ ret = write_address_to_file(service, "squared");
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ hs_service_free(service);
+}
+
+/** Test that our HS time period calculation functions work properly */
+static void
+test_time_period(void *arg)
+{
+ (void) arg;
+ uint64_t tn;
+ int retval;
+ time_t fake_time, correct_time, start_time;
+
+ /* Let's do the example in prop224 section [TIME-PERIODS] */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check that the time period number is right */
+ tn = hs_get_time_period_num(fake_time);
+ tt_u64_op(tn, OP_EQ, 16903);
+
+ /* Increase current time to 11:59:59 UTC and check that the time period
+ number is still the same */
+ fake_time += 3599;
+ tn = hs_get_time_period_num(fake_time);
+ tt_u64_op(tn, OP_EQ, 16903);
+
+ { /* Check start time of next time period */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 12:00:00 UTC",
+ &correct_time);
+ tt_int_op(retval, OP_EQ, 0);
+
+ start_time = hs_get_start_time_of_next_time_period(fake_time);
+ tt_int_op(start_time, OP_EQ, correct_time);
+ }
+
+ /* Now take time to 12:00:00 UTC and check that the time period rotated */
+ fake_time += 1;
+ tn = hs_get_time_period_num(fake_time);
+ tt_u64_op(tn, OP_EQ, 16904);
+
+ /* Now also check our hs_get_next_time_period_num() function */
+ tn = hs_get_next_time_period_num(fake_time);
+ tt_u64_op(tn, OP_EQ, 16905);
+
+ { /* Check start time of next time period again */
+ retval = parse_rfc1123_time("Wed, 14 Apr 2016 12:00:00 UTC",
+ &correct_time);
+ tt_int_op(retval, OP_EQ, 0);
+
+ start_time = hs_get_start_time_of_next_time_period(fake_time);
+ tt_int_op(start_time, OP_EQ, correct_time);
+ }
+
+ /* Now do another sanity check: The time period number at the start of the
+ * next time period, must be the same time period number as the one returned
+ * from hs_get_next_time_period_num() */
+ {
+ time_t next_tp_start = hs_get_start_time_of_next_time_period(fake_time);
+ tt_u64_op(hs_get_time_period_num(next_tp_start), OP_EQ,
+ hs_get_next_time_period_num(fake_time));
+ }
+
+ done:
+ ;
+}
+
+/** Test that we can correctly find the start time of the next time period */
+static void
+test_start_time_of_next_time_period(void *arg)
+{
+ (void) arg;
+ int retval;
+ time_t fake_time;
+ char tbuf[ISO_TIME_LEN + 1];
+ time_t next_tp_start_time;
+
+ /* Do some basic tests */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, OP_EQ, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-13 12:00:00", OP_EQ, tbuf);
+
+ /* Another test with an edge-case time (start of TP) */
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 12:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, OP_EQ, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-14 12:00:00", OP_EQ, tbuf);
+
+ {
+ /* Now pretend we are on a testing network and alter the voting schedule to
+ be every 10 seconds. This means that a time period has length 10*24
+ seconds (4 minutes). It also means that we apply a rotational offset of
+ 120 seconds to the time period, so that it starts at 00:02:00 instead of
+ 00:00:00. */
+ or_options_t *options = get_options_mutable();
+ options->TestingTorNetwork = 1;
+ options->V3AuthVotingInterval = 10;
+ options->TestingV3AuthInitialVotingInterval = 10;
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:00:00 UTC",
+ &fake_time);
+ tt_int_op(retval, OP_EQ, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-13 00:02:00", OP_EQ, tbuf);
+
+ retval = parse_rfc1123_time("Wed, 13 Apr 2016 00:02:00 UTC",
+ &fake_time);
+ tt_int_op(retval, OP_EQ, 0);
+ next_tp_start_time = hs_get_start_time_of_next_time_period(fake_time);
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, next_tp_start_time);
+ tt_str_op("2016-04-13 00:06:00", OP_EQ, tbuf);
+ }
+
+ done:
+ ;
+}
+
+/* Cleanup the global nodelist. It also frees the "md" in the node_t because
+ * we allocate the memory in helper_add_hsdir_to_networkstatus(). */
+static void
+cleanup_nodelist(void)
+{
+ smartlist_t *nodelist = nodelist_get_list();
+ SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) {
+ tor_free(node->md);
+ node->md = NULL;
+ } SMARTLIST_FOREACH_END(node);
+ nodelist_free_all();
+}
+
+static void
+helper_add_hsdir_to_networkstatus(networkstatus_t *ns,
+ int identity_idx,
+ const char *nickname,
+ int is_hsdir)
+{
+ routerstatus_t *rs = tor_malloc_zero(sizeof(routerstatus_t));
+ routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t));
+ uint8_t identity[DIGEST_LEN];
+ tor_addr_t ipv4_addr;
+
+ memset(identity, identity_idx, sizeof(identity));
+
+ memcpy(rs->identity_digest, identity, DIGEST_LEN);
+ rs->is_hs_dir = is_hsdir;
+ rs->supports_v3_hsdir = 1;
+ strlcpy(rs->nickname, nickname, sizeof(rs->nickname));
+ tor_addr_parse(&ipv4_addr, "1.2.3.4");
+ ri->addr = tor_addr_to_ipv4h(&ipv4_addr);
+ rs->addr = tor_addr_to_ipv4h(&ipv4_addr);
+ ri->nickname = tor_strdup(nickname);
+ ri->protocol_list = tor_strdup("HSDir=1-2 LinkAuth=3");
+ memcpy(ri->cache_info.identity_digest, identity, DIGEST_LEN);
+ ri->cache_info.signing_key_cert = tor_malloc_zero(sizeof(tor_cert_t));
+ /* Needed for the HSDir index computation. */
+ memset(&ri->cache_info.signing_key_cert->signing_key,
+ identity_idx, ED25519_PUBKEY_LEN);
+ tt_assert(nodelist_set_routerinfo(ri, NULL));
+ node_t *node = node_get_mutable_by_id(ri->cache_info.identity_digest);
+ tt_assert(node);
+ node->rs = rs;
+ /* We need this to exist for node_has_descriptor() to return true. */
+ node->md = tor_malloc_zero(sizeof(microdesc_t));
+ /* Do this now the nodelist_set_routerinfo() function needs a "rs" to set
+ * the indexes which it doesn't have when it is called. */
+ node_set_hsdir_index(node, ns);
+ node->ri = NULL;
+ smartlist_add(ns->routerstatus_list, rs);
+
+ done:
+ routerinfo_free(ri);
+}
+
+static networkstatus_t *mock_ns = NULL;
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus(void)
+{
+ time_t now = approx_time();
+
+ /* If initialized, return it */
+ if (mock_ns) {
+ return mock_ns;
+ }
+
+ /* Initialize fake consensus */
+ mock_ns = tor_malloc_zero(sizeof(networkstatus_t));
+
+ /* This consensus is live */
+ mock_ns->valid_after = now-1;
+ mock_ns->fresh_until = now+1;
+ mock_ns->valid_until = now+2;
+ /* Create routerstatus list */
+ mock_ns->routerstatus_list = smartlist_new();
+ mock_ns->type = NS_TYPE_CONSENSUS;
+
+ return mock_ns;
+}
+
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void) now;
+
+ tt_assert(mock_ns);
+
+ done:
+ return mock_ns;
+}
+
+/** Test the responsible HSDirs calculation function */
+static void
+test_responsible_hsdirs(void *arg)
+{
+ time_t now = approx_time();
+ smartlist_t *responsible_dirs = smartlist_new();
+ networkstatus_t *ns = NULL;
+ int retval;
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+
+ ns = networkstatus_get_latest_consensus();
+
+ { /* First router: HSdir */
+ helper_add_hsdir_to_networkstatus(ns, 1, "igor", 1);
+ }
+
+ { /* Second HSDir */
+ helper_add_hsdir_to_networkstatus(ns, 2, "victor", 1);
+ }
+
+ { /* Third relay but not HSDir */
+ helper_add_hsdir_to_networkstatus(ns, 3, "spyro", 0);
+ }
+
+ ed25519_keypair_t kp;
+ retval = ed25519_keypair_generate(&kp, 0);
+ tt_int_op(retval, OP_EQ , 0);
+
+ uint64_t time_period_num = hs_get_time_period_num(now);
+ hs_get_responsible_hsdirs(&kp.pubkey, time_period_num,
+ 0, 0, responsible_dirs);
+
+ /* Make sure that we only found 2 responsible HSDirs.
+ * The third relay was not an hsdir! */
+ tt_int_op(smartlist_len(responsible_dirs), OP_EQ, 2);
+
+ /** TODO: Build a bigger network and do more tests here */
+
+ done:
+ SMARTLIST_FOREACH(ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_free(responsible_dirs);
+ smartlist_clear(ns->routerstatus_list);
+ networkstatus_vote_free(mock_ns);
+ cleanup_nodelist();
+}
+
+static void
+mock_directory_initiate_request(directory_request_t *req)
+{
+ (void)req;
+ return;
+}
+
+static int
+mock_hs_desc_encode_descriptor(const hs_descriptor_t *desc,
+ const ed25519_keypair_t *signing_kp,
+ char **encoded_out)
+{
+ (void)desc;
+ (void)signing_kp;
+
+ tor_asprintf(encoded_out, "lulu");
+ return 0;
+}
+
+static or_state_t dummy_state;
+
+/* Mock function to get fake or state (used for rev counters) */
+static or_state_t *
+get_or_state_replacement(void)
+{
+ return &dummy_state;
+}
+
+static int
+mock_router_have_minimum_dir_info(void)
+{
+ return 1;
+}
+
+/** Test that we correctly detect when the HSDir hash ring changes so that we
+ * reupload our descriptor. */
+static void
+test_desc_reupload_logic(void *arg)
+{
+ networkstatus_t *ns = NULL;
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(router_have_minimum_dir_info,
+ mock_router_have_minimum_dir_info);
+ MOCK(get_or_state,
+ get_or_state_replacement);
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+ MOCK(directory_initiate_request,
+ mock_directory_initiate_request);
+ MOCK(hs_desc_encode_descriptor,
+ mock_hs_desc_encode_descriptor);
+
+ ns = networkstatus_get_latest_consensus();
+
+ /** Test logic:
+ * 1) Upload descriptor to HSDirs
+ * CHECK that previous_hsdirs list was populated.
+ * 2) Then call router_dir_info_changed() without an HSDir set change.
+ * CHECK that no reuplod occurs.
+ * 3) Now change the HSDir set, and call dir_info_changed() again.
+ * CHECK that reupload occurs.
+ * 4) Finally call service_desc_schedule_upload().
+ * CHECK that previous_hsdirs list was cleared.
+ **/
+
+ /* Let's start by building our descriptor and service */
+ hs_service_descriptor_t *desc = service_descriptor_new();
+ hs_service_t *service = NULL;
+ /* hex-encoded ed25519 pubkey used in hs_build_address.py */
+ char pubkey_hex[] =
+ "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a";
+ char onion_addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ ed25519_public_key_t pubkey;
+ base16_decode((char*)pubkey.pubkey, sizeof(pubkey.pubkey),
+ pubkey_hex, strlen(pubkey_hex));
+ hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr);
+ service = tor_malloc_zero(sizeof(hs_service_t));
+ memcpy(service->onion_address, onion_addr, sizeof(service->onion_address));
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ service->desc_current = desc;
+ /* Also add service to service map */
+ hs_service_ht *service_map = get_hs_service_map();
+ tt_assert(service_map);
+ tt_int_op(hs_service_get_num_services(), OP_EQ, 0);
+ register_service(service_map, service);
+ tt_int_op(hs_service_get_num_services(), OP_EQ, 1);
+
+ /* Now let's create our hash ring: */
+ {
+ helper_add_hsdir_to_networkstatus(ns, 1, "dingus", 1);
+ helper_add_hsdir_to_networkstatus(ns, 2, "clive", 1);
+ helper_add_hsdir_to_networkstatus(ns, 3, "aaron", 1);
+ helper_add_hsdir_to_networkstatus(ns, 4, "lizzie", 1);
+ helper_add_hsdir_to_networkstatus(ns, 5, "daewon", 1);
+ helper_add_hsdir_to_networkstatus(ns, 6, "clarke", 1);
+ }
+
+ /* Now let's upload our desc to all hsdirs */
+ upload_descriptor_to_all(service, desc);
+ /* Check that previous hsdirs were populated */
+ tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 6);
+
+ /* Poison next upload time so that we can see if it was changed by
+ * router_dir_info_changed(). No changes in hash ring so far, so the upload
+ * time should stay as is. */
+ desc->next_upload_time = 42;
+ router_dir_info_changed();
+ tt_int_op(desc->next_upload_time, OP_EQ, 42);
+
+ /* Now change the HSDir hash ring by swapping nora for aaron.
+ * Start by clearing the hash ring */
+ {
+ SMARTLIST_FOREACH(ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_clear(ns->routerstatus_list);
+ cleanup_nodelist();
+ routerlist_free_all();
+ }
+
+ { /* Now add back all the nodes */
+ helper_add_hsdir_to_networkstatus(ns, 1, "dingus", 1);
+ helper_add_hsdir_to_networkstatus(ns, 2, "clive", 1);
+ helper_add_hsdir_to_networkstatus(ns, 4, "lizzie", 1);
+ helper_add_hsdir_to_networkstatus(ns, 5, "daewon", 1);
+ helper_add_hsdir_to_networkstatus(ns, 6, "clarke", 1);
+ helper_add_hsdir_to_networkstatus(ns, 7, "nora", 1);
+ }
+
+ /* Now call service_desc_hsdirs_changed() and see that it detected the hash
+ ring change */
+ time_t now = approx_time();
+ tt_assert(now);
+ tt_int_op(service_desc_hsdirs_changed(service, desc), OP_EQ, 1);
+ tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 6);
+
+ /* Now order another upload and see that we keep having 6 prev hsdirs */
+ upload_descriptor_to_all(service, desc);
+ /* Check that previous hsdirs were populated */
+ tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 6);
+
+ /* Now restore the HSDir hash ring to its original state by swapping back
+ aaron for nora */
+ /* First clear up the hash ring */
+ {
+ SMARTLIST_FOREACH(ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_clear(ns->routerstatus_list);
+ cleanup_nodelist();
+ routerlist_free_all();
+ }
+
+ { /* Now populate the hash ring again */
+ helper_add_hsdir_to_networkstatus(ns, 1, "dingus", 1);
+ helper_add_hsdir_to_networkstatus(ns, 2, "clive", 1);
+ helper_add_hsdir_to_networkstatus(ns, 3, "aaron", 1);
+ helper_add_hsdir_to_networkstatus(ns, 4, "lizzie", 1);
+ helper_add_hsdir_to_networkstatus(ns, 5, "daewon", 1);
+ helper_add_hsdir_to_networkstatus(ns, 6, "clarke", 1);
+ }
+
+ /* Check that our algorithm catches this change of hsdirs */
+ tt_int_op(service_desc_hsdirs_changed(service, desc), OP_EQ, 1);
+
+ /* Now pretend that the descriptor changed, and order a reupload to all
+ HSDirs. Make sure that the set of previous HSDirs was cleared. */
+ service_desc_schedule_upload(desc, now, 1);
+ tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 0);
+
+ /* Now reupload again: see that the prev hsdir set got populated again. */
+ upload_descriptor_to_all(service, desc);
+ tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 6);
+
+ done:
+ SMARTLIST_FOREACH(ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_clear(ns->routerstatus_list);
+ networkstatus_vote_free(ns);
+ cleanup_nodelist();
+ hs_free_all();
+}
+
+/** Test disaster SRV computation and caching */
+static void
+test_disaster_srv(void *arg)
+{
+ uint8_t *cached_disaster_srv_one = NULL;
+ uint8_t *cached_disaster_srv_two = NULL;
+ uint8_t srv_one[DIGEST256_LEN] = {0};
+ uint8_t srv_two[DIGEST256_LEN] = {0};
+ uint8_t srv_three[DIGEST256_LEN] = {0};
+ uint8_t srv_four[DIGEST256_LEN] = {0};
+ uint8_t srv_five[DIGEST256_LEN] = {0};
+
+ (void) arg;
+
+ /* Get the cached SRVs: we gonna use them later for verification */
+ cached_disaster_srv_one = get_first_cached_disaster_srv();
+ cached_disaster_srv_two = get_second_cached_disaster_srv();
+
+ /* Compute some srvs */
+ get_disaster_srv(1, srv_one);
+ get_disaster_srv(2, srv_two);
+
+ /* Check that the cached ones where updated */
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
+
+ /* Ask for an SRV that has already been computed */
+ get_disaster_srv(2, srv_two);
+ /* and check that the cache entries have not changed */
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_one, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
+
+ /* Ask for a new SRV */
+ get_disaster_srv(3, srv_three);
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_three, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_two, DIGEST256_LEN);
+
+ /* Ask for another SRV: none of the original SRVs should now be cached */
+ get_disaster_srv(4, srv_four);
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_three, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_four, DIGEST256_LEN);
+
+ /* Ask for yet another SRV */
+ get_disaster_srv(5, srv_five);
+ tt_mem_op(cached_disaster_srv_one, OP_EQ, srv_five, DIGEST256_LEN);
+ tt_mem_op(cached_disaster_srv_two, OP_EQ, srv_four, DIGEST256_LEN);
+
+ done:
+ ;
+}
+
+/** Test our HS descriptor request tracker by making various requests and
+ * checking whether they get tracked properly. */
+static void
+test_hid_serv_request_tracker(void *arg)
+{
+ (void) arg;
+ time_t retval;
+ routerstatus_t *hsdir = NULL, *hsdir2 = NULL, *hsdir3 = NULL;
+ time_t now = approx_time();
+
+ const char *req_key_str_first =
+ "vd4zb6zesaubtrjvdqcr2w7x7lhw2up4Xnw4526ThUNbL5o1go+EdUuEqlKxHkNbnK41pRzizzs";
+ const char *req_key_str_second =
+ "g53o7iavcd62oihswhr24u6czmqws5kpXnw4526ThUNbL5o1go+EdUuEqlKxHkNbnK41pRzizzs";
+ const char *req_key_str_small = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ";
+
+ /*************************** basic test *******************************/
+
+ /* Get request tracker and make sure it's empty */
+ strmap_t *request_tracker = get_last_hid_serv_requests();
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 0);
+
+ /* Let's register a hid serv request */
+ hsdir = tor_malloc_zero(sizeof(routerstatus_t));
+ memset(hsdir->identity_digest, 'Z', DIGEST_LEN);
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_first,
+ now, 1);
+ tt_int_op(retval, OP_EQ, now);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+
+ /* Let's lookup a non-existent hidserv request */
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_second,
+ now+1, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+
+ /* Let's lookup a real hidserv request */
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_first,
+ now+2, 0);
+ tt_int_op(retval, OP_EQ, now); /* we got it */
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+
+ /**********************************************************************/
+
+ /* Let's add another request for the same HS but on a different HSDir. */
+ hsdir2 = tor_malloc_zero(sizeof(routerstatus_t));
+ memset(hsdir2->identity_digest, 2, DIGEST_LEN);
+ retval = hs_lookup_last_hid_serv_request(hsdir2, req_key_str_first,
+ now+3, 1);
+ tt_int_op(retval, OP_EQ, now+3);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 2);
+
+ /* Check that we can clean the first request based on time */
+ hs_clean_last_hid_serv_requests(now+3+REND_HID_SERV_DIR_REQUERY_PERIOD);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+ /* Check that it doesn't exist anymore */
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_first,
+ now+2, 0);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Now let's add a smaller req key str */
+ hsdir3 = tor_malloc_zero(sizeof(routerstatus_t));
+ memset(hsdir3->identity_digest, 3, DIGEST_LEN);
+ retval = hs_lookup_last_hid_serv_request(hsdir3, req_key_str_small,
+ now+4, 1);
+ tt_int_op(retval, OP_EQ, now+4);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 2);
+
+ /*************************** deleting entries **************************/
+
+ /* Add another request with very short key */
+ retval = hs_lookup_last_hid_serv_request(hsdir, "l", now, 1);
+ tt_int_op(retval, OP_EQ, now);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 3);
+
+ /* Try deleting entries with a dummy key. Check that our previous requests
+ * are still there */
+ tor_capture_bugs_(1);
+ hs_purge_hid_serv_from_last_hid_serv_requests("a");
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 3);
+ tor_end_capture_bugs_();
+
+ /* Try another dummy key. Check that requests are still there */
+ {
+ char dummy[2000];
+ memset(dummy, 'Z', 2000);
+ dummy[1999] = '\x00';
+ hs_purge_hid_serv_from_last_hid_serv_requests(dummy);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 3);
+ }
+
+ /* Another dummy key! */
+ hs_purge_hid_serv_from_last_hid_serv_requests(req_key_str_second);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 3);
+
+ /* Now actually delete a request! */
+ hs_purge_hid_serv_from_last_hid_serv_requests(req_key_str_first);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 2);
+
+ /* Purge it all! */
+ hs_purge_last_hid_serv_requests();
+ request_tracker = get_last_hid_serv_requests();
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 0);
+
+ done:
+ tor_free(hsdir);
+ tor_free(hsdir2);
+ tor_free(hsdir3);
+}
+
+static void
+test_parse_extended_hostname(void *arg)
+{
+ (void) arg;
+
+ char address1[] = "fooaddress.onion";
+ char address2[] = "aaaaaaaaaaaaaaaa.onion";
+ char address3[] = "fooaddress.exit";
+ char address4[] = "www.torproject.org";
+ char address5[] = "foo.abcdefghijklmnop.onion";
+ char address6[] = "foo.bar.abcdefghijklmnop.onion";
+ char address7[] = ".abcdefghijklmnop.onion";
+ char address8[] =
+ "www.25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion";
+
+ tt_assert(BAD_HOSTNAME == parse_extended_hostname(address1));
+ tt_assert(ONION_V2_HOSTNAME == parse_extended_hostname(address2));
+ tt_str_op(address2,OP_EQ, "aaaaaaaaaaaaaaaa");
+ tt_assert(EXIT_HOSTNAME == parse_extended_hostname(address3));
+ tt_assert(NORMAL_HOSTNAME == parse_extended_hostname(address4));
+ tt_assert(ONION_V2_HOSTNAME == parse_extended_hostname(address5));
+ tt_str_op(address5,OP_EQ, "abcdefghijklmnop");
+ tt_assert(ONION_V2_HOSTNAME == parse_extended_hostname(address6));
+ tt_str_op(address6,OP_EQ, "abcdefghijklmnop");
+ tt_assert(BAD_HOSTNAME == parse_extended_hostname(address7));
+ tt_assert(ONION_V3_HOSTNAME == parse_extended_hostname(address8));
+ tt_str_op(address8, OP_EQ,
+ "25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid");
+
+ done: ;
+}
+
+static void
+test_time_between_tp_and_srv(void *arg)
+{
+ int ret;
+ networkstatus_t ns;
+ (void) arg;
+
+ /* This function should be returning true where "^" are:
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^^^^^^^^^^^^ ^^^^^^^^^^^^ |
+ * | |
+ * +------------------------------------------------------------------+
+ */
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 00:00:00 UTC", &ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 01:00:00 UTC", &ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), ns.valid_after);
+ ret = hs_in_period_between_tp_and_srv(&ns, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 11:00:00 UTC", &ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 12:00:00 UTC", &ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), ns.valid_after);
+ ret = hs_in_period_between_tp_and_srv(&ns, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 12:00:00 UTC", &ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", &ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), ns.valid_after);
+ ret = hs_in_period_between_tp_and_srv(&ns, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 23:00:00 UTC", &ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 27 Oct 1985 00:00:00 UTC", &ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), ns.valid_after);
+ ret = hs_in_period_between_tp_and_srv(&ns, 0);
+ tt_int_op(ret, OP_EQ, 1);
+
+ ret = parse_rfc1123_time("Sat, 27 Oct 1985 00:00:00 UTC", &ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 27 Oct 1985 01:00:00 UTC", &ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), ns.valid_after);
+ ret = hs_in_period_between_tp_and_srv(&ns, 0);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ ;
+}
+
+/************ Reachability Test (it is huge) ****************/
+
+/* Simulate different consensus for client and service. Used by the
+ * reachability test. The SRV and responsible HSDir list are used by all
+ * reachability tests so make them common to simplify setup and teardown. */
+static networkstatus_t *mock_service_ns = NULL;
+static networkstatus_t *mock_client_ns = NULL;
+static sr_srv_t current_srv, previous_srv;
+static smartlist_t *service_responsible_hsdirs = NULL;
+static smartlist_t *client_responsible_hsdirs = NULL;
+
+static networkstatus_t *
+mock_networkstatus_get_live_consensus_service(time_t now)
+{
+ (void) now;
+
+ if (mock_service_ns) {
+ return mock_service_ns;
+ }
+
+ mock_service_ns = tor_malloc_zero(sizeof(networkstatus_t));
+ mock_service_ns->routerstatus_list = smartlist_new();
+ mock_service_ns->type = NS_TYPE_CONSENSUS;
+
+ return mock_service_ns;
+}
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus_service(void)
+{
+ return mock_networkstatus_get_live_consensus_service(0);
+}
+
+static networkstatus_t *
+mock_networkstatus_get_live_consensus_client(time_t now)
+{
+ (void) now;
+
+ if (mock_client_ns) {
+ return mock_client_ns;
+ }
+
+ mock_client_ns = tor_malloc_zero(sizeof(networkstatus_t));
+ mock_client_ns->routerstatus_list = smartlist_new();
+ mock_client_ns->type = NS_TYPE_CONSENSUS;
+
+ return mock_client_ns;
+}
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus_client(void)
+{
+ return mock_networkstatus_get_live_consensus_client(0);
+}
+
+/* Mock function because we are not trying to test the close circuit that does
+ * an awful lot of checks on the circuit object. */
+static void
+mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
+ const char *file)
+{
+ (void) circ;
+ (void) reason;
+ (void) line;
+ (void) file;
+ return;
+}
+
+/* Initialize a big HSDir V3 hash ring. */
+static void
+helper_initialize_big_hash_ring(networkstatus_t *ns)
+{
+ int ret;
+
+ /* Generate 250 hsdirs! :) */
+ for (int counter = 1 ; counter < 251 ; counter++) {
+ /* Let's generate random nickname for each hsdir... */
+ char nickname_binary[8];
+ char nickname_str[13] = {0};
+ crypto_rand(nickname_binary, sizeof(nickname_binary));
+ ret = base64_encode(nickname_str, sizeof(nickname_str),
+ nickname_binary, sizeof(nickname_binary), 0);
+ tt_int_op(ret, OP_EQ, 12);
+ helper_add_hsdir_to_networkstatus(ns, counter, nickname_str, 1);
+ }
+
+ /* Make sure we have 200 hsdirs in our list */
+ tt_int_op(smartlist_len(ns->routerstatus_list), OP_EQ, 250);
+
+ done:
+ ;
+}
+
+/** Initialize service and publish its descriptor as needed. Return the newly
+ * allocated service object to the caller. */
+static hs_service_t *
+helper_init_service(time_t now)
+{
+ int retval;
+ hs_service_t *service = hs_service_new(get_options());
+ tt_assert(service);
+ service->config.version = HS_VERSION_THREE;
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ /* Register service to global map. */
+ retval = register_service(get_hs_service_map(), service);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Initialize service descriptor */
+ build_all_descriptors(now);
+ tt_assert(service->desc_current);
+ tt_assert(service->desc_next);
+
+ done:
+ return service;
+}
+
+/* Helper function to set the RFC 1123 time string into t. */
+static void
+set_consensus_times(const char *timestr, time_t *t)
+{
+ tt_assert(timestr);
+ tt_assert(t);
+
+ int ret = parse_rfc1123_time(timestr, t);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ return;
+}
+
+/* Helper function to cleanup the mock consensus (client and service) */
+static void
+cleanup_mock_ns(void)
+{
+ if (mock_service_ns) {
+ SMARTLIST_FOREACH(mock_service_ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_clear(mock_service_ns->routerstatus_list);
+ mock_service_ns->sr_info.current_srv = NULL;
+ mock_service_ns->sr_info.previous_srv = NULL;
+ networkstatus_vote_free(mock_service_ns);
+ mock_service_ns = NULL;
+ }
+
+ if (mock_client_ns) {
+ SMARTLIST_FOREACH(mock_client_ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_clear(mock_client_ns->routerstatus_list);
+ mock_client_ns->sr_info.current_srv = NULL;
+ mock_client_ns->sr_info.previous_srv = NULL;
+ networkstatus_vote_free(mock_client_ns);
+ mock_client_ns = NULL;
+ }
+}
+
+/* Helper function to setup a reachability test. Once called, the
+ * cleanup_reachability_test MUST be called at the end. */
+static void
+setup_reachability_test(void)
+{
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(get_or_state, get_or_state_replacement);
+
+ hs_init();
+
+ /* Baseline to start with. */
+ memset(&current_srv, 0, sizeof(current_srv));
+ memset(&previous_srv, 1, sizeof(previous_srv));
+
+ /* Initialize the consensuses. */
+ mock_networkstatus_get_latest_consensus_service();
+ mock_networkstatus_get_latest_consensus_client();
+
+ service_responsible_hsdirs = smartlist_new();
+ client_responsible_hsdirs = smartlist_new();
+}
+
+/* Helper function to cleanup a reachability test initial setup. */
+static void
+cleanup_reachability_test(void)
+{
+ smartlist_free(service_responsible_hsdirs);
+ service_responsible_hsdirs = NULL;
+ smartlist_free(client_responsible_hsdirs);
+ client_responsible_hsdirs = NULL;
+ hs_free_all();
+ cleanup_mock_ns();
+ UNMOCK(get_or_state);
+ UNMOCK(circuit_mark_for_close_);
+}
+
+/* A reachability test always check if the resulting service and client
+ * responsible HSDir for the given parameters are equal.
+ *
+ * Return true iff the same exact nodes are in both list. */
+static int
+are_responsible_hsdirs_equal(void)
+{
+ int count = 0;
+ tt_int_op(smartlist_len(client_responsible_hsdirs), OP_EQ, 6);
+ tt_int_op(smartlist_len(service_responsible_hsdirs), OP_EQ, 8);
+
+ SMARTLIST_FOREACH_BEGIN(client_responsible_hsdirs,
+ const routerstatus_t *, c_rs) {
+ SMARTLIST_FOREACH_BEGIN(service_responsible_hsdirs,
+ const routerstatus_t *, s_rs) {
+ if (tor_memeq(c_rs->identity_digest, s_rs->identity_digest,
+ DIGEST_LEN)) {
+ count++;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(s_rs);
+ } SMARTLIST_FOREACH_END(c_rs);
+
+ done:
+ return (count == 6);
+}
+
+/* Tor doesn't use such a function to get the previous HSDir, it is only used
+ * in node_set_hsdir_index(). We need it here so we can test the reachability
+ * scenario 6 that requires the previous time period to compute the list of
+ * responsible HSDir because of the client state timing. */
+static uint64_t
+get_previous_time_period(time_t now)
+{
+ return hs_get_time_period_num(now) - 1;
+}
+
+/* Configuration of a reachability test scenario. */
+typedef struct reachability_cfg_t {
+ /* Consensus timings to be set. They have to be compliant with
+ * RFC 1123 time format. */
+ const char *service_valid_after;
+ const char *service_valid_until;
+ const char *client_valid_after;
+ const char *client_valid_until;
+
+ /* SRVs that the service and client should use. */
+ sr_srv_t *service_current_srv;
+ sr_srv_t *service_previous_srv;
+ sr_srv_t *client_current_srv;
+ sr_srv_t *client_previous_srv;
+
+ /* A time period function for the service to use for this scenario. For a
+ * successful reachability test, the client always use the current time
+ * period thus why no client function. */
+ uint64_t (*service_time_period_fn)(time_t);
+
+ /* Is the client and service expected to be in a new time period. After
+ * setting the consensus time, the reachability test checks
+ * hs_in_period_between_tp_and_srv() and test the returned value against
+ * this. */
+ unsigned int service_in_new_tp;
+ unsigned int client_in_new_tp;
+
+ /* Some scenario requires a hint that the client, because of its consensus
+ * time, will request the "next" service descriptor so this indicates if it
+ * is the case or not. */
+ unsigned int client_fetch_next_desc;
+} reachability_cfg_t;
+
+/* Some defines to help with semantic while reading a configuration below. */
+#define NOT_IN_NEW_TP 0
+#define IN_NEW_TP 1
+#define DONT_NEED_NEXT_DESC 0
+#define NEED_NEXT_DESC 1
+
+static reachability_cfg_t reachability_scenarios[] = {
+ /* Scenario 1
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ *
+ * S: Service, C: Client
+ *
+ * Service consensus valid_after time is set to 13:00 and client to 15:00,
+ * both are after TP#1 thus have access to SRV#1. Service and client should
+ * be using TP#1.
+ */
+
+ { "Sat, 26 Oct 1985 13:00:00 UTC", /* Service valid_after */
+ "Sat, 26 Oct 1985 14:00:00 UTC", /* Service valid_until */
+ "Sat, 26 Oct 1985 15:00:00 UTC", /* Client valid_after */
+ "Sat, 26 Oct 1985 16:00:00 UTC", /* Client valid_until. */
+ &current_srv, NULL, /* Service current and previous SRV */
+ &current_srv, NULL, /* Client current and previous SRV */
+ hs_get_time_period_num, /* Service time period function. */
+ IN_NEW_TP, /* Is service in new TP? */
+ IN_NEW_TP, /* Is client in new TP? */
+ NEED_NEXT_DESC },
+
+ /* Scenario 2
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ *
+ * S: Service, C: Client
+ *
+ * Service consensus valid_after time is set to 23:00 and client to 01:00,
+ * which makes the client after the SRV#2 and the service just before. The
+ * service should only be using TP#1. The client should be using TP#1.
+ */
+
+ { "Sat, 26 Oct 1985 23:00:00 UTC", /* Service valid_after */
+ "Sat, 27 Oct 1985 00:00:00 UTC", /* Service valid_until */
+ "Sat, 27 Oct 1985 01:00:00 UTC", /* Client valid_after */
+ "Sat, 27 Oct 1985 02:00:00 UTC", /* Client valid_until. */
+ &previous_srv, NULL, /* Service current and previous SRV */
+ &current_srv, &previous_srv, /* Client current and previous SRV */
+ hs_get_time_period_num, /* Service time period function. */
+ IN_NEW_TP, /* Is service in new TP? */
+ NOT_IN_NEW_TP, /* Is client in new TP? */
+ NEED_NEXT_DESC },
+
+ /* Scenario 3
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ *
+ * S: Service, C: Client
+ *
+ * Service consensus valid_after time is set to 03:00 and client to 05:00,
+ * which makes both after SRV#2. The service should be using TP#1 as its
+ * current time period. The client should be using TP#1.
+ */
+
+ { "Sat, 27 Oct 1985 03:00:00 UTC", /* Service valid_after */
+ "Sat, 27 Oct 1985 04:00:00 UTC", /* Service valid_until */
+ "Sat, 27 Oct 1985 05:00:00 UTC", /* Client valid_after */
+ "Sat, 27 Oct 1985 06:00:00 UTC", /* Client valid_until. */
+ &current_srv, &previous_srv, /* Service current and previous SRV */
+ &current_srv, &previous_srv, /* Client current and previous SRV */
+ hs_get_time_period_num, /* Service time period function. */
+ NOT_IN_NEW_TP, /* Is service in new TP? */
+ NOT_IN_NEW_TP, /* Is client in new TP? */
+ DONT_NEED_NEXT_DESC },
+
+ /* Scenario 4
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ *
+ * S: Service, C: Client
+ *
+ * Service consensus valid_after time is set to 11:00 and client to 13:00,
+ * which makes the service before TP#2 and the client just after. The
+ * service should be using TP#1 as its current time period and TP#2 as the
+ * next. The client should be using TP#2 time period.
+ */
+
+ { "Sat, 27 Oct 1985 11:00:00 UTC", /* Service valid_after */
+ "Sat, 27 Oct 1985 12:00:00 UTC", /* Service valid_until */
+ "Sat, 27 Oct 1985 13:00:00 UTC", /* Client valid_after */
+ "Sat, 27 Oct 1985 14:00:00 UTC", /* Client valid_until. */
+ &current_srv, &previous_srv, /* Service current and previous SRV */
+ &current_srv, &previous_srv, /* Client current and previous SRV */
+ hs_get_next_time_period_num, /* Service time period function. */
+ NOT_IN_NEW_TP, /* Is service in new TP? */
+ IN_NEW_TP, /* Is client in new TP? */
+ NEED_NEXT_DESC },
+
+ /* Scenario 5
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | C S |
+ * +------------------------------------------------------------------+
+ *
+ * S: Service, C: Client
+ *
+ * Service consensus valid_after time is set to 01:00 and client to 23:00,
+ * which makes the service after SRV#2 and the client just before. The
+ * service should be using TP#1 as its current time period and TP#2 as the
+ * next. The client should be using TP#1 time period.
+ */
+
+ { "Sat, 27 Oct 1985 01:00:00 UTC", /* Service valid_after */
+ "Sat, 27 Oct 1985 02:00:00 UTC", /* Service valid_until */
+ "Sat, 26 Oct 1985 23:00:00 UTC", /* Client valid_after */
+ "Sat, 27 Oct 1985 00:00:00 UTC", /* Client valid_until. */
+ &current_srv, &previous_srv, /* Service current and previous SRV */
+ &previous_srv, NULL, /* Client current and previous SRV */
+ hs_get_time_period_num, /* Service time period function. */
+ NOT_IN_NEW_TP, /* Is service in new TP? */
+ IN_NEW_TP, /* Is client in new TP? */
+ DONT_NEED_NEXT_DESC },
+
+ /* Scenario 6
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | C S |
+ * +------------------------------------------------------------------+
+ *
+ * S: Service, C: Client
+ *
+ * Service consensus valid_after time is set to 13:00 and client to 11:00,
+ * which makes the service outside after TP#2 and the client just before.
+ * The service should be using TP#1 as its current time period and TP#2 as
+ * its next. The client should be using TP#1 time period.
+ */
+
+ { "Sat, 27 Oct 1985 13:00:00 UTC", /* Service valid_after */
+ "Sat, 27 Oct 1985 14:00:00 UTC", /* Service valid_until */
+ "Sat, 27 Oct 1985 11:00:00 UTC", /* Client valid_after */
+ "Sat, 27 Oct 1985 12:00:00 UTC", /* Client valid_until. */
+ &current_srv, &previous_srv, /* Service current and previous SRV */
+ &current_srv, &previous_srv, /* Client current and previous SRV */
+ get_previous_time_period, /* Service time period function. */
+ IN_NEW_TP, /* Is service in new TP? */
+ NOT_IN_NEW_TP, /* Is client in new TP? */
+ DONT_NEED_NEXT_DESC },
+
+ /* End marker. */
+ { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}
+};
+
+/* Run a single reachability scenario. num_scenario is the corresponding
+ * scenario number from the documentation. It is used to log it in case of
+ * failure so we know which scenario fails. */
+static int
+run_reachability_scenario(const reachability_cfg_t *cfg, int num_scenario)
+{
+ int ret = -1;
+ hs_service_t *service;
+ uint64_t service_tp, client_tp;
+ ed25519_public_key_t service_blinded_pk, client_blinded_pk;
+
+ setup_reachability_test();
+
+ tt_assert(cfg);
+
+ /* Set service consensus time. */
+ set_consensus_times(cfg->service_valid_after,
+ &mock_service_ns->valid_after);
+ set_consensus_times(cfg->service_valid_until,
+ &mock_service_ns->valid_until);
+ set_consensus_times(cfg->service_valid_until,
+ &mock_service_ns->fresh_until);
+ dirvote_recalculate_timing(get_options(), mock_service_ns->valid_after);
+ /* Set client consensus time. */
+ set_consensus_times(cfg->client_valid_after,
+ &mock_client_ns->valid_after);
+ set_consensus_times(cfg->client_valid_until,
+ &mock_client_ns->valid_until);
+ set_consensus_times(cfg->client_valid_until,
+ &mock_client_ns->fresh_until);
+ dirvote_recalculate_timing(get_options(), mock_client_ns->valid_after);
+
+ /* New time period checks for this scenario. */
+ tt_int_op(hs_in_period_between_tp_and_srv(mock_service_ns, 0), OP_EQ,
+ cfg->service_in_new_tp);
+ tt_int_op(hs_in_period_between_tp_and_srv(mock_client_ns, 0), OP_EQ,
+ cfg->client_in_new_tp);
+
+ /* Set the SRVs for this scenario. */
+ mock_client_ns->sr_info.current_srv = cfg->client_current_srv;
+ mock_client_ns->sr_info.previous_srv = cfg->client_previous_srv;
+ mock_service_ns->sr_info.current_srv = cfg->service_current_srv;
+ mock_service_ns->sr_info.previous_srv = cfg->service_previous_srv;
+
+ /* Initialize a service to get keys. */
+ service = helper_init_service(time(NULL));
+
+ /*
+ * === Client setup ===
+ */
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus_client);
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus_client);
+
+ /* Make networkstatus_is_live() happy. */
+ update_approx_time(mock_client_ns->valid_after);
+ /* Initialize a big hashring for this consensus with the hsdir index set. */
+ helper_initialize_big_hash_ring(mock_client_ns);
+
+ /* Client ONLY use the current time period. This is the whole point of these
+ * reachability test that is to make sure the client can always reach the
+ * service using only its current time period. */
+ client_tp = hs_get_time_period_num(0);
+
+ hs_build_blinded_pubkey(&service->keys.identity_pk, NULL, 0,
+ client_tp, &client_blinded_pk);
+ hs_get_responsible_hsdirs(&client_blinded_pk, client_tp, 0, 1,
+ client_responsible_hsdirs);
+ /* Cleanup the nodelist so we can let the service computes its own set of
+ * node with its own hashring. */
+ cleanup_nodelist();
+ tt_int_op(smartlist_len(client_responsible_hsdirs), OP_EQ, 6);
+
+ UNMOCK(networkstatus_get_latest_consensus);
+ UNMOCK(networkstatus_get_live_consensus);
+
+ /*
+ * === Service setup ===
+ */
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus_service);
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus_service);
+
+ /* Make networkstatus_is_live() happy. */
+ update_approx_time(mock_service_ns->valid_after);
+ /* Initialize a big hashring for this consensus with the hsdir index set. */
+ helper_initialize_big_hash_ring(mock_service_ns);
+
+ service_tp = cfg->service_time_period_fn(0);
+
+ hs_build_blinded_pubkey(&service->keys.identity_pk, NULL, 0,
+ service_tp, &service_blinded_pk);
+
+ /* A service builds two lists of responsible HSDir, for the current and the
+ * next descriptor. Depending on the scenario, the client timing indicate if
+ * it is fetching the current or the next descriptor so we use the
+ * "client_fetch_next_desc" to know which one the client is trying to get to
+ * confirm that the service computes the same hashring for the same blinded
+ * key and service time period function. */
+ hs_get_responsible_hsdirs(&service_blinded_pk, service_tp,
+ cfg->client_fetch_next_desc, 0,
+ service_responsible_hsdirs);
+ cleanup_nodelist();
+ tt_int_op(smartlist_len(service_responsible_hsdirs), OP_EQ, 8);
+
+ UNMOCK(networkstatus_get_latest_consensus);
+ UNMOCK(networkstatus_get_live_consensus);
+
+ /* Some testing of the values we just got from the client and service. */
+ tt_mem_op(&client_blinded_pk, OP_EQ, &service_blinded_pk,
+ ED25519_PUBKEY_LEN);
+ tt_int_op(are_responsible_hsdirs_equal(), OP_EQ, 1);
+
+ /* Everything went well. */
+ ret = 0;
+
+ done:
+ cleanup_reachability_test();
+ if (ret == -1) {
+ /* Do this so we can know which scenario failed. */
+ char msg[32];
+ tor_snprintf(msg, sizeof(msg), "Scenario %d failed", num_scenario);
+ tt_fail_msg(msg);
+ }
+ return ret;
+}
+
+static void
+test_reachability(void *arg)
+{
+ (void) arg;
+
+ /* NOTE: An important axiom to understand here is that SRV#N must only be
+ * used with TP#N value. For example, SRV#2 with TP#1 should NEVER be used
+ * together. The HSDir index computation is based on this axiom.*/
+
+ for (int i = 0; reachability_scenarios[i].service_valid_after; ++i) {
+ int ret = run_reachability_scenario(&reachability_scenarios[i], i + 1);
+ if (ret < 0) {
+ return;
+ }
+ }
+}
+
+/** Pick an HSDir for service with <b>onion_identity_pk</b> as a client. Put
+ * its identity digest in <b>hsdir_digest_out</b>. */
+static void
+helper_client_pick_hsdir(const ed25519_public_key_t *onion_identity_pk,
+ char *hsdir_digest_out)
+{
+ tt_assert(onion_identity_pk);
+
+ routerstatus_t *client_hsdir = pick_hsdir_v3(onion_identity_pk);
+ tt_assert(client_hsdir);
+ digest_to_base64(hsdir_digest_out, client_hsdir->identity_digest);
+
+ done:
+ ;
+}
+
+static void
+test_hs_indexes(void *arg)
+{
+ int ret;
+ uint64_t period_num = 42;
+ ed25519_public_key_t pubkey;
+
+ (void) arg;
+
+ /* Build the hs_index */
+ {
+ uint8_t hs_index[DIGEST256_LEN];
+ const char *b32_test_vector =
+ "37e5cbbd56a22823714f18f1623ece5983a0d64c78495a8cfab854245e5f9a8a";
+ char test_vector[DIGEST256_LEN];
+ ret = base16_decode(test_vector, sizeof(test_vector), b32_test_vector,
+ strlen(b32_test_vector));
+ tt_int_op(ret, OP_EQ, sizeof(test_vector));
+ /* Our test vector uses a public key set to 32 bytes of \x42. */
+ memset(&pubkey, '\x42', sizeof(pubkey));
+ hs_build_hs_index(1, &pubkey, period_num, hs_index);
+ tt_mem_op(hs_index, OP_EQ, test_vector, sizeof(hs_index));
+ }
+
+ /* Build the hsdir_index */
+ {
+ uint8_t srv[DIGEST256_LEN];
+ uint8_t hsdir_index[DIGEST256_LEN];
+ const char *b32_test_vector =
+ "db475361014a09965e7e5e4d4a25b8f8d4b8f16cb1d8a7e95eed50249cc1a2d5";
+ char test_vector[DIGEST256_LEN];
+ ret = base16_decode(test_vector, sizeof(test_vector), b32_test_vector,
+ strlen(b32_test_vector));
+ tt_int_op(ret, OP_EQ, sizeof(test_vector));
+ /* Our test vector uses a public key set to 32 bytes of \x42. */
+ memset(&pubkey, '\x42', sizeof(pubkey));
+ memset(srv, '\x43', sizeof(srv));
+ hs_build_hsdir_index(&pubkey, srv, period_num, hsdir_index);
+ tt_mem_op(hsdir_index, OP_EQ, test_vector, sizeof(hsdir_index));
+ }
+
+ done:
+ ;
+}
+
+#define EARLY_IN_SRV_TO_TP 0
+#define LATE_IN_SRV_TO_TP 1
+#define EARLY_IN_TP_TO_SRV 2
+#define LATE_IN_TP_TO_SRV 3
+
+/** Set the consensus and system time based on <b>position</b>. See the
+ * following diagram for details:
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|----------$===========| |
+ * | |
+ * | |
+ * +------------------------------------------------------------------+
+ */
+static time_t
+helper_set_consensus_and_system_time(networkstatus_t *ns, int position)
+{
+ time_t real_time = 0;
+
+ /* The period between SRV#N and TP#N is from 00:00 to 12:00 UTC. Consensus
+ * valid_after is what matters here, the rest is just to specify the voting
+ * period correctly. */
+ if (position == LATE_IN_SRV_TO_TP) {
+ parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC", &ns->valid_after);
+ parse_rfc1123_time("Wed, 13 Apr 2016 12:00:00 UTC", &ns->fresh_until);
+ parse_rfc1123_time("Wed, 13 Apr 2016 14:00:00 UTC", &ns->valid_until);
+ } else if (position == EARLY_IN_TP_TO_SRV) {
+ parse_rfc1123_time("Wed, 13 Apr 2016 13:00:00 UTC", &ns->valid_after);
+ parse_rfc1123_time("Wed, 13 Apr 2016 14:00:00 UTC", &ns->fresh_until);
+ parse_rfc1123_time("Wed, 13 Apr 2016 16:00:00 UTC", &ns->valid_until);
+ } else if (position == LATE_IN_TP_TO_SRV) {
+ parse_rfc1123_time("Wed, 13 Apr 2016 23:00:00 UTC", &ns->valid_after);
+ parse_rfc1123_time("Wed, 14 Apr 2016 00:00:00 UTC", &ns->fresh_until);
+ parse_rfc1123_time("Wed, 14 Apr 2016 02:00:00 UTC", &ns->valid_until);
+ } else if (position == EARLY_IN_SRV_TO_TP) {
+ parse_rfc1123_time("Wed, 14 Apr 2016 01:00:00 UTC", &ns->valid_after);
+ parse_rfc1123_time("Wed, 14 Apr 2016 02:00:00 UTC", &ns->fresh_until);
+ parse_rfc1123_time("Wed, 14 Apr 2016 04:00:00 UTC", &ns->valid_until);
+ } else {
+ tt_assert(0);
+ }
+ dirvote_recalculate_timing(get_options(), ns->valid_after);
+
+ /* Set system time: pretend to be just 2 minutes before consensus expiry */
+ real_time = ns->valid_until - 120;
+ update_approx_time(real_time);
+
+ done:
+ return real_time;
+}
+
+/** Helper function that carries out the actual test for
+ * test_client_service_sync() */
+static void
+helper_test_hsdir_sync(networkstatus_t *ns,
+ int service_position, int client_position,
+ int client_fetches_next_desc)
+{
+ hs_service_descriptor_t *desc;
+ int retval;
+
+ /** Test logic:
+ * 1) Initialize service time: consensus and system time.
+ * 1.1) Initialize service hash ring
+ * 2) Initialize service and publish descriptors.
+ * 3) Initialize client time: consensus and system time.
+ * 3.1) Initialize client hash ring
+ * 4) Try to fetch descriptor as client, and CHECK that the HSDir picked by
+ * the client was also picked by service.
+ */
+
+ /* 1) Initialize service time: consensus and real time */
+ time_t now = helper_set_consensus_and_system_time(ns, service_position);
+ helper_initialize_big_hash_ring(ns);
+
+ /* 2) Initialize service */
+ hs_service_t *service = helper_init_service(now);
+ desc = client_fetches_next_desc ? service->desc_next : service->desc_current;
+
+ /* Now let's upload our desc to all hsdirs */
+ upload_descriptor_to_all(service, desc);
+ /* Cleanup right now so we don't memleak on error. */
+ cleanup_nodelist();
+ /* Check that previous hsdirs were populated */
+ tt_int_op(smartlist_len(desc->previous_hsdirs), OP_EQ, 8);
+
+ /* 3) Initialize client time */
+ helper_set_consensus_and_system_time(ns, client_position);
+
+ cleanup_nodelist();
+ SMARTLIST_FOREACH(ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_clear(ns->routerstatus_list);
+ helper_initialize_big_hash_ring(ns);
+
+ /* 4) Pick 6 HSDirs as a client and check that they were also chosen by the
+ service. */
+ for (int y = 0 ; y < 6 ; y++) {
+ char client_hsdir_b64_digest[BASE64_DIGEST_LEN+1] = {0};
+ helper_client_pick_hsdir(&service->keys.identity_pk,
+ client_hsdir_b64_digest);
+
+ /* CHECK: Go through the hsdirs chosen by the service and make sure that it
+ * contains the one picked by the client! */
+ retval = smartlist_contains_string(desc->previous_hsdirs,
+ client_hsdir_b64_digest);
+ tt_int_op(retval, OP_EQ, 1);
+ }
+
+ /* Finally, try to pick a 7th hsdir and see that NULL is returned since we
+ * exhausted all of them: */
+ tt_assert(!pick_hsdir_v3(&service->keys.identity_pk));
+
+ done:
+ /* At the end: free all services and initialize the subsystem again, we will
+ * need it for next scenario. */
+ cleanup_nodelist();
+ hs_service_free_all();
+ hs_service_init();
+ SMARTLIST_FOREACH(ns->routerstatus_list,
+ routerstatus_t *, rs, routerstatus_free(rs));
+ smartlist_clear(ns->routerstatus_list);
+}
+
+/** This test ensures that client and service will pick the same HSDirs, under
+ * various timing scenarios:
+ * a) Scenario where both client and service are in the time segment between
+ * SRV#N and TP#N:
+ * b) Scenario where both client and service are in the time segment between
+ * TP#N and SRV#N+1.
+ * c) Scenario where service is between SRV#N and TP#N, but client is between
+ * TP#N and SRV#N+1.
+ * d) Scenario where service is between TP#N and SRV#N+1, but client is
+ * between SRV#N and TP#N.
+ *
+ * This test is important because it tests that upload_descriptor_to_all() is
+ * in synch with pick_hsdir_v3(). That's not the case for the
+ * test_reachability() test which only compares the responsible hsdir sets.
+ */
+static void
+test_client_service_hsdir_set_sync(void *arg)
+{
+ networkstatus_t *ns = NULL;
+
+ (void) arg;
+
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+ MOCK(get_or_state,
+ get_or_state_replacement);
+ MOCK(hs_desc_encode_descriptor,
+ mock_hs_desc_encode_descriptor);
+ MOCK(directory_initiate_request,
+ mock_directory_initiate_request);
+
+ hs_init();
+
+ /* Initialize a big hash ring: we want it to be big so that client and
+ * service cannot accidentally select the same HSDirs */
+ ns = networkstatus_get_latest_consensus();
+ tt_assert(ns);
+
+ /** Now test the various synch scenarios. See the helper function for more
+ details: */
+
+ /* a) Scenario where both client and service are in the time segment between
+ * SRV#N and TP#N. At this time the client fetches the first HS desc:
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ */
+ helper_test_hsdir_sync(ns, LATE_IN_SRV_TO_TP, LATE_IN_SRV_TO_TP, 0);
+
+ /* b) Scenario where both client and service are in the time segment between
+ * TP#N and SRV#N+1. At this time the client fetches the second HS
+ * desc:
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ */
+ helper_test_hsdir_sync(ns, LATE_IN_TP_TO_SRV, LATE_IN_TP_TO_SRV, 1);
+
+ /* c) Scenario where service is between SRV#N and TP#N, but client is
+ * between TP#N and SRV#N+1. Client is forward in time so it fetches the
+ * second HS desc.
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ */
+ helper_test_hsdir_sync(ns, LATE_IN_SRV_TO_TP, EARLY_IN_TP_TO_SRV, 1);
+
+ /* d) Scenario where service is between TP#N and SRV#N+1, but client is
+ * between SRV#N and TP#N. Client is backwards in time so it fetches the
+ * first HS desc.
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | C S |
+ * +------------------------------------------------------------------+
+ */
+ helper_test_hsdir_sync(ns, EARLY_IN_TP_TO_SRV, LATE_IN_SRV_TO_TP, 0);
+
+ /* e) Scenario where service is between SRV#N and TP#N, but client is
+ * between TP#N-1 and SRV#3. Client is backwards in time so it fetches
+ * the first HS desc.
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | C S |
+ * +------------------------------------------------------------------+
+ */
+ helper_test_hsdir_sync(ns, EARLY_IN_SRV_TO_TP, LATE_IN_TP_TO_SRV, 0);
+
+ /* f) Scenario where service is between TP#N and SRV#N+1, but client is
+ * between SRV#N+1 and TP#N+1. Client is forward in time so it fetches
+ * the second HS desc.
+ *
+ * +------------------------------------------------------------------+
+ * | |
+ * | 00:00 12:00 00:00 12:00 00:00 12:00 |
+ * | SRV#1 TP#1 SRV#2 TP#2 SRV#3 TP#3 |
+ * | |
+ * | $==========|-----------$===========|-----------$===========| |
+ * | ^ ^ |
+ * | S C |
+ * +------------------------------------------------------------------+
+ */
+ helper_test_hsdir_sync(ns, LATE_IN_TP_TO_SRV, EARLY_IN_SRV_TO_TP, 1);
+
+ done:
+ networkstatus_vote_free(ns);
+ nodelist_free_all();
+ hs_free_all();
+}
+
+struct testcase_t hs_common_tests[] = {
+ { "build_address", test_build_address, TT_FORK,
+ NULL, NULL },
+ { "validate_address", test_validate_address, TT_FORK,
+ NULL, NULL },
+ { "time_period", test_time_period, TT_FORK,
+ NULL, NULL },
+ { "start_time_of_next_time_period", test_start_time_of_next_time_period,
+ TT_FORK, NULL, NULL },
+ { "responsible_hsdirs", test_responsible_hsdirs, TT_FORK,
+ NULL, NULL },
+ { "desc_reupload_logic", test_desc_reupload_logic, TT_FORK,
+ NULL, NULL },
+ { "disaster_srv", test_disaster_srv, TT_FORK,
+ NULL, NULL },
+ { "hid_serv_request_tracker", test_hid_serv_request_tracker, TT_FORK,
+ NULL, NULL },
+ { "parse_extended_hostname", test_parse_extended_hostname, TT_FORK,
+ NULL, NULL },
+ { "time_between_tp_and_srv", test_time_between_tp_and_srv, TT_FORK,
+ NULL, NULL },
+ { "reachability", test_reachability, TT_FORK,
+ NULL, NULL },
+ { "client_service_hsdir_set_sync", test_client_service_hsdir_set_sync,
+ TT_FORK, NULL, NULL },
+ { "hs_indexes", test_hs_indexes, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c
new file mode 100644
index 0000000000..a76be301d3
--- /dev/null
+++ b/src/test/test_hs_config.c
@@ -0,0 +1,487 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_config.c
+ * \brief Test hidden service configuration functionality.
+ */
+
+#define CONFIG_PRIVATE
+#define HS_SERVICE_PRIVATE
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+
+#include "config.h"
+#include "hs_common.h"
+#include "hs_config.h"
+#include "hs_service.h"
+#include "rendservice.h"
+
+static int
+helper_config_service(const char *conf, int validate_only)
+{
+ int ret = 0;
+ or_options_t *options = NULL;
+ tt_assert(conf);
+ options = helper_parse_options(conf);
+ tt_assert(options);
+ ret = hs_config_service_all(options, validate_only);
+ done:
+ or_options_free(options);
+ return ret;
+}
+
+static void
+test_invalid_service(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ /* Try with a missing port configuration. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 1\n"; /* Wrong not supported version. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceVersion must be between 2 and 3");
+ teardown_capture_of_logs();
+ }
+
+ /* Bad value of HiddenServiceAllowUnknownPorts. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServiceAllowUnknownPorts 2\n"; /* Should be 0 or 1. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceAllowUnknownPorts must be "
+ "between 0 and 1, not 2");
+ teardown_capture_of_logs();
+ }
+
+ /* Bad value of HiddenServiceDirGroupReadable */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServiceDirGroupReadable 2\n"; /* Should be 0 or 1. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceDirGroupReadable must be "
+ "between 0 and 1, not 2");
+ teardown_capture_of_logs();
+ }
+
+ /* Bad value of HiddenServiceMaxStreamsCloseCircuit */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServiceMaxStreamsCloseCircuit 2\n"; /* Should be 0 or 1. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceMaxStreamsCloseCircuit must "
+ "be between 0 and 1, not 2");
+ teardown_capture_of_logs();
+ }
+
+ /* Too much max streams. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceMaxStreams 65536\n"; /* One too many. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceMaxStreams must be between "
+ "0 and 65535, not 65536");
+ teardown_capture_of_logs();
+ }
+
+ /* Duplicate directory directive. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 81\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Another hidden service is already "
+ "configured for directory");
+ teardown_capture_of_logs();
+ }
+
+ /* Bad port. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 65536\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Missing or invalid port");
+ teardown_capture_of_logs();
+ }
+
+ /* Out of order directives. */
+ {
+ const char *conf =
+ "HiddenServiceVersion 2\n"
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServicePort 80\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceVersion with no preceding "
+ "HiddenServiceDir directive");
+ teardown_capture_of_logs();
+ }
+
+ done:
+ ;
+}
+
+static void
+test_valid_service(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ /* Mix of v2 and v3. Still valid. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 81\n"
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 82\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ done:
+ ;
+}
+
+static void
+test_invalid_service_v2(void *arg)
+{
+ int validate_only = 1, ret;
+
+ (void) arg;
+
+ /* Try with a missing port configuration. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, validate_only);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("with no ports configured.");
+ teardown_capture_of_logs();
+ }
+
+ /* Too many introduction points. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceNumIntroductionPoints 11\n"; /* One too many. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, validate_only);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceNumIntroductionPoints should "
+ "be between 0 and 10, not 11");
+ teardown_capture_of_logs();
+ }
+
+ /* Too little introduction points. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceNumIntroductionPoints -1\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, validate_only);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceNumIntroductionPoints should "
+ "be between 0 and 10, not -1");
+ teardown_capture_of_logs();
+ }
+
+ /* Bad authorized client type. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceAuthorizeClient blah alice,bob\n"; /* blah is no good. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, validate_only);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceAuthorizeClient contains "
+ "unrecognized auth-type");
+ teardown_capture_of_logs();
+ }
+
+ done:
+ ;
+}
+
+static void
+test_valid_service_v2(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ /* Valid complex configuration. Basic client authorization. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServicePort 22 localhost:22\n"
+#ifdef HAVE_SYS_UN_H
+ "HiddenServicePort 42 unix:/path/to/socket\n"
+#endif
+ "HiddenServiceAuthorizeClient basic alice,bob,eve\n"
+ "HiddenServiceAllowUnknownPorts 1\n"
+ "HiddenServiceMaxStreams 42\n"
+ "HiddenServiceMaxStreamsCloseCircuit 0\n"
+ "HiddenServiceDirGroupReadable 1\n"
+ "HiddenServiceNumIntroductionPoints 7\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ /* Valid complex configuration. Stealth client authorization. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 65535\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+#ifdef HAVE_SYS_UN_H
+ "HiddenServicePort 9000 unix:/path/to/socket\n"
+#endif
+ "HiddenServiceAuthorizeClient stealth charlie,romeo\n"
+ "HiddenServiceAllowUnknownPorts 0\n"
+ "HiddenServiceMaxStreams 42\n"
+ "HiddenServiceMaxStreamsCloseCircuit 0\n"
+ "HiddenServiceDirGroupReadable 1\n"
+ "HiddenServiceNumIntroductionPoints 8\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ done:
+ ;
+}
+
+static void
+test_invalid_service_v3(void *arg)
+{
+ int validate_only = 1, ret;
+
+ (void) arg;
+
+ /* Try with a missing port configuration. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 3\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, validate_only);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("with no ports configured.");
+ teardown_capture_of_logs();
+ }
+
+ /* Too many introduction points. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceNumIntroductionPoints 21\n"; /* One too many. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, validate_only);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceNumIntroductionPoints must "
+ "be between 3 and 20, not 21.");
+ teardown_capture_of_logs();
+ }
+
+ /* Too little introduction points. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceNumIntroductionPoints 1\n";
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = helper_config_service(conf, validate_only);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("HiddenServiceNumIntroductionPoints must "
+ "be between 3 and 20, not 1.");
+ teardown_capture_of_logs();
+ }
+
+ done:
+ ;
+}
+
+static void
+test_valid_service_v3(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ /* Valid complex configuration. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 80\n"
+ "HiddenServicePort 22 localhost:22\n"
+#ifdef HAVE_SYS_UN_H
+ "HiddenServicePort 42 unix:/path/to/socket\n"
+#endif
+ "HiddenServiceAllowUnknownPorts 1\n"
+ "HiddenServiceMaxStreams 42\n"
+ "HiddenServiceMaxStreamsCloseCircuit 0\n"
+ "HiddenServiceDirGroupReadable 1\n"
+ "HiddenServiceNumIntroductionPoints 7\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ /* Valid complex configuration. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 65535\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+#ifdef HAVE_SYS_UN_H
+ "HiddenServicePort 9000 unix:/path/to/socket\n"
+#endif
+ "HiddenServiceAllowUnknownPorts 0\n"
+ "HiddenServiceMaxStreams 42\n"
+ "HiddenServiceMaxStreamsCloseCircuit 0\n"
+ "HiddenServiceDirGroupReadable 1\n"
+ "HiddenServiceNumIntroductionPoints 20\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ /* Mix of v2 and v3. Still valid. */
+ {
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 80\n"
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 81\n"
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
+ "HiddenServiceVersion 2\n"
+ "HiddenServicePort 82\n";
+ ret = helper_config_service(conf, 1);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ done:
+ ;
+}
+
+static void
+test_staging_service_v3(void *arg)
+{
+ int ret;
+
+ (void) arg;
+
+ /* We don't validate a service object, this is the service test that are in
+ * charge of doing so. We just check for the stable state after
+ * registration. */
+
+ hs_init();
+
+ /* Time for a valid v3 service that should get staged. */
+ const char *conf =
+ "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
+ "HiddenServiceVersion 3\n"
+ "HiddenServicePort 65535\n"
+ "HiddenServicePort 22 1.1.1.1:22\n"
+#ifdef HAVE_SYS_UN_H
+ "HiddenServicePort 9000 unix:/path/to/socket\n"
+#endif
+ "HiddenServiceAllowUnknownPorts 0\n"
+ "HiddenServiceMaxStreams 42\n"
+ "HiddenServiceMaxStreamsCloseCircuit 0\n"
+ "HiddenServiceDirGroupReadable 1\n"
+ "HiddenServiceNumIntroductionPoints 20\n";
+ ret = helper_config_service(conf, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ /* Ok, we have a service in our map! Registration went well. */
+ tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
+ /* Make sure we don't have a magic v2 service out of this. */
+ tt_int_op(rend_num_services(), OP_EQ, 0);
+
+ done:
+ hs_free_all();
+}
+
+struct testcase_t hs_config_tests[] = {
+ /* Invalid service not specific to any version. */
+ { "invalid_service", test_invalid_service, TT_FORK,
+ NULL, NULL },
+ { "valid_service", test_valid_service, TT_FORK,
+ NULL, NULL },
+
+ /* Test case only for version 2. */
+ { "invalid_service_v2", test_invalid_service_v2, TT_FORK,
+ NULL, NULL },
+ { "valid_service_v2", test_valid_service_v2, TT_FORK,
+ NULL, NULL },
+
+ /* Test case only for version 3. */
+ { "invalid_service_v3", test_invalid_service_v3, TT_FORK,
+ NULL, NULL },
+ { "valid_service_v3", test_valid_service_v3, TT_FORK,
+ NULL, NULL },
+
+ /* Test service staging. */
+ { "staging_service_v3", test_staging_service_v3, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index 7d7ec7d9db..9ec183db06 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -54,7 +54,7 @@ test_cert_encoding(void *arg)
/* Test the certificate encoding function. */
ret = tor_cert_encode_ed22519(cert, &encoded);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Validated the certificate string. */
{
@@ -63,7 +63,7 @@ test_cert_encoding(void *arg)
size_t b64_cert_len;
tor_cert_t *parsed_cert;
- tt_int_op(strcmpstart(pos, "-----BEGIN ED25519 CERT-----\n"), ==, 0);
+ tt_int_op(strcmpstart(pos, "-----BEGIN ED25519 CERT-----\n"), OP_EQ, 0);
pos += strlen("-----BEGIN ED25519 CERT-----\n");
/* Isolate the base64 encoded certificate and try to decode it. */
@@ -72,24 +72,25 @@ test_cert_encoding(void *arg)
b64_cert = pos;
b64_cert_len = end - pos;
ret = base64_decode(buf, sizeof(buf), b64_cert, b64_cert_len);
- tt_int_op(ret, >, 0);
+ tt_int_op(ret, OP_GT, 0);
/* Parseable? */
parsed_cert = tor_cert_parse((uint8_t *) buf, ret);
tt_assert(parsed_cert);
/* Signature is valid? */
ret = tor_cert_checksig(parsed_cert, &kp.pubkey, now + 10);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = tor_cert_eq(cert, parsed_cert);
- tt_int_op(ret, ==, 1);
+ tt_int_op(ret, OP_EQ, 1);
/* The cert did have the signing key? */
ret= ed25519_pubkey_eq(&parsed_cert->signing_key, &kp.pubkey);
- tt_int_op(ret, ==, 1);
+ tt_int_op(ret, OP_EQ, 1);
tor_cert_free(parsed_cert);
/* Get to the end part of the certificate. */
pos += b64_cert_len;
- tt_int_op(strcmpstart(pos, "-----END ED25519 CERT-----"), ==, 0);
+ tt_int_op(strcmpstart(pos, "-----END ED25519 CERT-----"), OP_EQ, 0);
pos += strlen("-----END ED25519 CERT-----");
+ tt_str_op(pos, OP_EQ, "");
}
done:
@@ -188,22 +189,22 @@ test_link_specifier(void *arg)
spec.type = LS_IPV4;
ret = tor_addr_parse(&spec.u.ap.addr, "1.2.3.4");
- tt_int_op(ret, ==, AF_INET);
+ tt_int_op(ret, OP_EQ, AF_INET);
b64 = encode_link_specifiers(link_specifiers);
tt_assert(b64);
/* Decode it and validate the format. */
ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, >, 0);
+ tt_int_op(ret, OP_GT, 0);
/* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), ==, 1);
+ tt_int_op(get_uint8(buf), OP_EQ, 1);
ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- tt_int_op(ret, ==, 8);
+ tt_int_op(ret, OP_EQ, 8);
/* Should be 2 bytes for port and 4 bytes for IPv4. */
- tt_int_op(link_specifier_get_ls_len(ls), ==, 6);
+ tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, 6);
ipv4 = link_specifier_get_un_ipv4_addr(ls);
- tt_int_op(tor_addr_to_ipv4h(&spec.u.ap.addr), ==, ipv4);
- tt_int_op(link_specifier_get_un_ipv4_port(ls), ==, spec.u.ap.port);
+ tt_int_op(tor_addr_to_ipv4h(&spec.u.ap.addr), OP_EQ, ipv4);
+ tt_int_op(link_specifier_get_un_ipv4_port(ls), OP_EQ, spec.u.ap.port);
link_specifier_free(ls);
tor_free(b64);
@@ -217,24 +218,25 @@ test_link_specifier(void *arg)
spec.type = LS_IPV6;
ret = tor_addr_parse(&spec.u.ap.addr, "[1:2:3:4::]");
- tt_int_op(ret, ==, AF_INET6);
+ tt_int_op(ret, OP_EQ, AF_INET6);
b64 = encode_link_specifiers(link_specifiers);
tt_assert(b64);
/* Decode it and validate the format. */
ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, >, 0);
+ tt_int_op(ret, OP_GT, 0);
/* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), ==, 1);
+ tt_int_op(get_uint8(buf), OP_EQ, 1);
ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
- tt_int_op(ret, ==, 20);
+ tt_int_op(ret, OP_EQ, 20);
/* Should be 2 bytes for port and 16 bytes for IPv6. */
- tt_int_op(link_specifier_get_ls_len(ls), ==, 18);
+ tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, 18);
for (unsigned int i = 0; i < sizeof(ipv6); i++) {
ipv6[i] = link_specifier_get_un_ipv6_addr(ls, i);
}
- tt_mem_op(tor_addr_to_in6_addr8(&spec.u.ap.addr), ==, ipv6, sizeof(ipv6));
- tt_int_op(link_specifier_get_un_ipv6_port(ls), ==, spec.u.ap.port);
+ tt_mem_op(tor_addr_to_in6_addr8(&spec.u.ap.addr), OP_EQ, ipv6,
+ sizeof(ipv6));
+ tt_int_op(link_specifier_get_un_ipv6_port(ls), OP_EQ, spec.u.ap.port);
link_specifier_free(ls);
tor_free(b64);
@@ -253,12 +255,12 @@ test_link_specifier(void *arg)
/* Decode it and validate the format. */
ret = base64_decode(buf, sizeof(buf), b64, strlen(b64));
- tt_int_op(ret, >, 0);
+ tt_int_op(ret, OP_GT, 0);
/* First byte is the number of link specifier. */
- tt_int_op(get_uint8(buf), ==, 1);
+ tt_int_op(get_uint8(buf), OP_EQ, 1);
ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1);
/* 20 bytes digest + 1 byte type + 1 byte len. */
- tt_int_op(ret, ==, 22);
+ tt_int_op(ret, OP_EQ, 22);
tt_int_op(link_specifier_getlen_un_legacy_id(ls), OP_EQ, DIGEST_LEN);
/* Digest length is 20 bytes. */
tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, DIGEST_LEN);
@@ -284,10 +286,10 @@ test_encode_descriptor(void *arg)
(void) arg;
ret = ed25519_keypair_generate(&signing_kp, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
done:
@@ -304,23 +306,27 @@ test_decode_descriptor(void *arg)
hs_descriptor_t *desc = NULL;
hs_descriptor_t *decoded = NULL;
hs_descriptor_t *desc_no_ip = NULL;
+ uint8_t subcredential[DIGEST256_LEN];
(void) arg;
ret = ed25519_keypair_generate(&signing_kp, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
+ hs_helper_get_subcred_from_identity_keypair(&signing_kp,
+ subcredential);
+
/* Give some bad stuff to the decoding function. */
- ret = hs_desc_decode_descriptor("hladfjlkjadf", NULL, &decoded);
+ ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential, &decoded);
tt_int_op(ret, OP_EQ, -1);
ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
- ret = hs_desc_decode_descriptor(encoded, NULL, &decoded);
- tt_int_op(ret, ==, 0);
+ ret = hs_desc_decode_descriptor(encoded, subcredential, &decoded);
+ tt_int_op(ret, OP_EQ, 0);
tt_assert(decoded);
hs_helper_desc_equal(desc, decoded);
@@ -329,16 +335,18 @@ test_decode_descriptor(void *arg)
{
ed25519_keypair_t signing_kp_no_ip;
ret = ed25519_keypair_generate(&signing_kp_no_ip, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ hs_helper_get_subcred_from_identity_keypair(&signing_kp_no_ip,
+ subcredential);
desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip);
tt_assert(desc_no_ip);
tor_free(encoded);
ret = hs_desc_encode_descriptor(desc_no_ip, &signing_kp_no_ip, &encoded);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
hs_descriptor_free(decoded);
- ret = hs_desc_decode_descriptor(encoded, NULL, &decoded);
- tt_int_op(ret, ==, 0);
+ ret = hs_desc_decode_descriptor(encoded, subcredential, &decoded);
+ tt_int_op(ret, OP_EQ, 0);
tt_assert(decoded);
}
@@ -430,12 +438,12 @@ test_decode_invalid_intro_point(void *arg)
hs_descriptor_free(desc);
desc = NULL;
ret = ed25519_keypair_generate(&signing_kp, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
const char *junk = "this is not a descriptor";
ip = decode_introduction_point(desc, junk);
- tt_assert(!ip);
- desc_intro_point_free(ip);
+ tt_ptr_op(ip, OP_EQ, NULL);
+ hs_desc_intro_point_free(ip);
ip = NULL;
}
@@ -450,10 +458,10 @@ test_decode_invalid_intro_point(void *arg)
encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out);
tt_assert(encoded_ip);
ip = decode_introduction_point(desc, encoded_ip);
- tt_assert(!ip);
+ tt_ptr_op(ip, OP_EQ, NULL);
tor_free(encoded_ip);
smartlist_free(lines);
- desc_intro_point_free(ip);
+ hs_desc_intro_point_free(ip);
ip = NULL;
}
@@ -477,7 +485,7 @@ test_decode_invalid_intro_point(void *arg)
encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out);
tt_assert(encoded_ip);
ip = decode_introduction_point(desc, encoded_ip);
- tt_assert(!ip);
+ tt_ptr_op(ip, OP_EQ, NULL);
tor_free(encoded_ip);
smartlist_free(lines);
}
@@ -495,7 +503,7 @@ test_decode_invalid_intro_point(void *arg)
encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out);
tt_assert(encoded_ip);
ip = decode_introduction_point(desc, encoded_ip);
- tt_assert(!ip);
+ tt_ptr_op(ip, OP_EQ, NULL);
tor_free(encoded_ip);
smartlist_free(lines);
}
@@ -512,7 +520,7 @@ test_decode_invalid_intro_point(void *arg)
encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out);
tt_assert(encoded_ip);
ip = decode_introduction_point(desc, encoded_ip);
- tt_assert(!ip);
+ tt_ptr_op(ip, OP_EQ, NULL);
tor_free(encoded_ip);
smartlist_free(lines);
}
@@ -529,7 +537,7 @@ test_decode_invalid_intro_point(void *arg)
encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out);
tt_assert(encoded_ip);
ip = decode_introduction_point(desc, encoded_ip);
- tt_assert(!ip);
+ tt_ptr_op(ip, OP_EQ, NULL);
tor_free(encoded_ip);
smartlist_free(lines);
}
@@ -546,14 +554,14 @@ test_decode_invalid_intro_point(void *arg)
encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out);
tt_assert(encoded_ip);
ip = decode_introduction_point(desc, encoded_ip);
- tt_assert(!ip);
+ tt_ptr_op(ip, OP_EQ, NULL);
tor_free(encoded_ip);
smartlist_free(lines);
}
done:
hs_descriptor_free(desc);
- desc_intro_point_free(ip);
+ hs_desc_intro_point_free(ip);
}
/** Make sure we fail gracefully when decoding the bad desc from #23233. */
@@ -624,7 +632,7 @@ test_decode_plaintext(void *arg)
{
size_t big = 64000;
/* Must always be bigger than HS_DESC_MAX_LEN. */
- tt_int_op(HS_DESC_MAX_LEN, <, big);
+ tt_int_op(HS_DESC_MAX_LEN, OP_LT, big);
char *plaintext = tor_malloc_zero(big);
memset(plaintext, 'a', big);
plaintext[big - 1] = '\0';
@@ -684,7 +692,7 @@ test_validate_cert(void *arg)
(void) arg;
ret = ed25519_keypair_generate(&kp, 0);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Cert of type CERT_TYPE_AUTH_HS_IP_KEY. */
cert = tor_cert_create(&kp, CERT_TYPE_AUTH_HS_IP_KEY,
@@ -735,16 +743,16 @@ test_desc_signature(void *arg)
tor_asprintf(&data, "This is a signed descriptor\n");
ret = ed25519_sign_prefixed(&sig, (const uint8_t *) data, strlen(data),
"Tor onion service descriptor sig v3", &kp);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = ed25519_signature_to_base64(sig_b64, &sig);
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Build the descriptor that should be valid. */
tor_asprintf(&desc, "%ssignature %s\n", data, sig_b64);
ret = desc_sig_is_valid(sig_b64, &kp.pubkey, desc, strlen(desc));
- tt_int_op(ret, ==, 1);
+ tt_int_op(ret, OP_EQ, 1);
/* Junk signature. */
ret = desc_sig_is_valid("JUNK", &kp.pubkey, desc, strlen(desc));
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
done:
tor_free(desc);
@@ -806,8 +814,8 @@ test_parse_hs_desc_superencrypted(void *arg)
retval = decode_superencrypted(bad_superencrypted_text1,
strlen(bad_superencrypted_text1),
&encrypted_out);
- tt_u64_op(retval, ==, 0);
- tt_assert(!encrypted_out);
+ tt_u64_op(retval, OP_EQ, 0);
+ tt_ptr_op(encrypted_out, OP_EQ, NULL);
expect_log_msg_containing("Unrecognized desc auth type");
teardown_capture_of_logs();
}
@@ -817,8 +825,8 @@ test_parse_hs_desc_superencrypted(void *arg)
retval = decode_superencrypted(bad_superencrypted_text2,
strlen(bad_superencrypted_text2),
&encrypted_out);
- tt_u64_op(retval, ==, 0);
- tt_assert(!encrypted_out);
+ tt_u64_op(retval, OP_EQ, 0);
+ tt_ptr_op(encrypted_out, OP_EQ, NULL);
expect_log_msg_containing("Bogus desc auth key in HS desc");
teardown_capture_of_logs();
}
@@ -828,8 +836,8 @@ test_parse_hs_desc_superencrypted(void *arg)
retval = decode_superencrypted(bad_superencrypted_text3,
strlen(bad_superencrypted_text3),
&encrypted_out);
- tt_u64_op(retval, ==, 0);
- tt_assert(!encrypted_out);
+ tt_u64_op(retval, OP_EQ, 0);
+ tt_ptr_op(encrypted_out, OP_EQ, NULL);
expect_log_msg_containing("Length of descriptor\'s encrypted data "
"is too small.");
teardown_capture_of_logs();
@@ -840,7 +848,7 @@ test_parse_hs_desc_superencrypted(void *arg)
strlen(correct_superencrypted_text),
&encrypted_out);
- tt_u64_op(retval, ==, strlen(correct_encrypted_plaintext));
+ tt_u64_op(retval, OP_EQ, strlen(correct_encrypted_plaintext));
tt_mem_op(encrypted_out, OP_EQ, correct_encrypted_plaintext,
strlen(correct_encrypted_plaintext));
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index c6197875b5..0cae2de7e1 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -17,21 +17,66 @@
#include "log_test_helpers.h"
#include "or.h"
+#include "circuitlist.h"
+#include "circuituse.h"
#include "ht.h"
+#include "relay.h"
+#include "rendservice.h"
+
+#include "hs_cell.h"
+#include "hs_circuitmap.h"
+#include "hs_common.h"
+#include "hs_intropoint.h"
+#include "hs_service.h"
/* Trunnel. */
#include "hs/cell_establish_intro.h"
#include "hs/cell_introduce1.h"
#include "hs/cell_common.h"
-#include "hs_service.h"
-#include "hs_common.h"
-#include "hs_circuitmap.h"
-#include "hs_intropoint.h"
-#include "circuitlist.h"
-#include "circuituse.h"
-#include "rendservice.h"
-#include "relay.h"
+static size_t
+new_establish_intro_cell(const char *circ_nonce,
+ trn_cell_establish_intro_t **cell_out)
+{
+ ssize_t cell_len = 0;
+ uint8_t buf[RELAY_PAYLOAD_SIZE] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
+ hs_service_intro_point_t *ip = NULL;
+
+ /* Auth key pair is generated in the constructor so we are all set for
+ * using this IP object. */
+ ip = service_intro_point_new(NULL, 0);
+ tt_assert(ip);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ tt_i64_op(cell_len, OP_GT, 0);
+
+ cell_len = trn_cell_establish_intro_parse(&cell, buf, sizeof(buf));
+ tt_i64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+ *cell_out = cell;
+
+ done:
+ service_intro_point_free(ip);
+ return cell_len;
+}
+
+static ssize_t
+new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out)
+{
+ ssize_t cell_len = 0;
+ hs_service_intro_point_t *ip = NULL;
+
+ /* Auth key pair is generated in the constructor so we are all set for
+ * using this IP object. */
+ ip = service_intro_point_new(NULL, 0);
+ tt_assert(ip);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out);
+ tt_i64_op(cell_len, OP_GT, 0);
+
+ done:
+ service_intro_point_free(ip);
+ return cell_len;
+}
/* Mock function to avoid networking in unittests */
static int
@@ -122,50 +167,43 @@ static void
test_establish_intro_wrong_purpose(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ char circ_nonce[DIGEST_LEN] = {0};
+ uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
(void)arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- memcpy(intro_circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ memcpy(intro_circ->rend_circ_nonce, circ_nonce, DIGEST_LEN);
/* Set a bad circuit purpose!! :) */
circuit_change_purpose(TO_CIRCUIT(intro_circ), CIRCUIT_PURPOSE_INTRO_POINT);
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
attempt to parse it. */
- establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(establish_intro_cell);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body);
+ tt_i64_op(cell_len, OP_GT, 0);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
expect_log_msg_containing("Rejecting ESTABLISH_INTRO on non-OR circuit.");
teardown_capture_of_logs();
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
/* Prepare a circuit for accepting an ESTABLISH_INTRO cell */
static void
-helper_prepare_circ_for_intro(or_circuit_t *circ,
- uint8_t *circuit_key_material)
+helper_prepare_circ_for_intro(or_circuit_t *circ, const char *circ_nonce)
{
/* Prepare the circuit for the incoming ESTABLISH_INTRO */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
- memcpy(circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN);
+ memcpy(circ->rend_circ_nonce, circ_nonce, DIGEST_LEN);
}
/* Send an empty ESTABLISH_INTRO cell. Should fail. */
@@ -173,21 +211,21 @@ static void
test_establish_intro_wrong_keytype(void *arg)
{
int retval;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
+ char circ_nonce[DIGEST_LEN] = {0};
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
- retval = hs_intro_received_establish_intro(intro_circ, (uint8_t*)"", 0);
+ retval = hs_intro_received_establish_intro(intro_circ, (uint8_t *) "", 0);
expect_log_msg_containing("Empty ESTABLISH_INTRO cell.");
teardown_capture_of_logs();
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
circuit_free(TO_CIRCUIT(intro_circ));
@@ -198,26 +236,21 @@ static void
test_establish_intro_wrong_keytype2(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
- attempt to parse it. */
- establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(establish_intro_cell);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body);
+ tt_i64_op(cell_len, OP_GT, 0);
/* Mutate the auth key type! :) */
cell_body[0] = 42;
@@ -227,10 +260,9 @@ test_establish_intro_wrong_keytype2(void *arg)
retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
expect_log_msg_containing("Unrecognized AUTH_KEY_TYPE 42.");
teardown_capture_of_logs();
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -239,26 +271,27 @@ static void
test_establish_intro_wrong_mac(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
- uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+ char circ_nonce[DIGEST_LEN] = {0};
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ uint8_t cell_body[RELAY_PAYLOAD_SIZE];
+ trn_cell_establish_intro_t *cell = NULL;
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
- attempt to parse it. */
- establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(establish_intro_cell);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_i64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+
/* Mangle one byte of the MAC. */
uint8_t *handshake_ptr =
- trn_cell_establish_intro_getarray_handshake_mac(establish_intro_cell);
+ trn_cell_establish_intro_getarray_handshake_mac(cell);
handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++;
/* We need to resign the payload with that change. */
{
@@ -269,26 +302,26 @@ test_establish_intro_wrong_mac(void *arg)
retval = ed25519_keypair_generate(&key_struct, 0);
tt_int_op(retval, OP_EQ, 0);
uint8_t *auth_key_ptr =
- trn_cell_establish_intro_getarray_auth_key(establish_intro_cell);
+ trn_cell_establish_intro_getarray_auth_key(cell);
memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN);
/* Encode payload so we can sign it. */
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_i64_op(cell_len, OP_GT, 0);
retval = ed25519_sign_prefixed(&sig, cell_body,
cell_len -
- (ED25519_SIG_LEN +
- sizeof(establish_intro_cell->sig_len)),
+ (ED25519_SIG_LEN + sizeof(cell->sig_len)),
ESTABLISH_INTRO_SIG_PREFIX, &key_struct);
tt_int_op(retval, OP_EQ, 0);
/* And write the signature to the cell */
uint8_t *sig_ptr =
- trn_cell_establish_intro_getarray_sig(establish_intro_cell);
- memcpy(sig_ptr, sig.sig, establish_intro_cell->sig_len);
+ trn_cell_establish_intro_getarray_sig(cell);
+ memcpy(sig_ptr, sig.sig, cell->sig_len);
/* Re-encode with the new signature. */
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_i64_op(cell_len, OP_GT, 0);
}
/* Receive the cell. Should fail because our MAC is wrong. */
@@ -296,10 +329,10 @@ test_establish_intro_wrong_mac(void *arg)
retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
expect_log_msg_containing("ESTABLISH_INTRO handshake_auth not as expected");
teardown_capture_of_logs();
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
+ trn_cell_establish_intro_free(cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -309,42 +342,42 @@ static void
test_establish_intro_wrong_auth_key_len(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
size_t bad_auth_key_len = ED25519_PUBKEY_LEN - 1;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
- attempt to parse it. */
- establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(establish_intro_cell);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_i64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+
/* Mangle the auth key length. */
- trn_cell_establish_intro_set_auth_key_len(establish_intro_cell,
- bad_auth_key_len);
- trn_cell_establish_intro_setlen_auth_key(establish_intro_cell,
- bad_auth_key_len);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ trn_cell_establish_intro_set_auth_key_len(cell, bad_auth_key_len);
+ trn_cell_establish_intro_setlen_auth_key(cell, bad_auth_key_len);
+ /* Encode cell. */
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 0);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
expect_log_msg_containing("ESTABLISH_INTRO auth key length is invalid");
teardown_capture_of_logs();
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
+ trn_cell_establish_intro_free(cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -354,40 +387,42 @@ static void
test_establish_intro_wrong_sig_len(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
size_t bad_sig_len = ED25519_SIG_LEN - 1;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
- attempt to parse it. */
- establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(establish_intro_cell);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_i64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+
/* Mangle the signature length. */
- trn_cell_establish_intro_set_sig_len(establish_intro_cell, bad_sig_len);
- trn_cell_establish_intro_setlen_sig(establish_intro_cell, bad_sig_len);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ trn_cell_establish_intro_set_sig_len(cell, bad_sig_len);
+ trn_cell_establish_intro_setlen_sig(cell, bad_sig_len);
+ /* Encode cell. */
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 0);
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
expect_log_msg_containing("ESTABLISH_INTRO sig len is invalid");
teardown_capture_of_logs();
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
+ trn_cell_establish_intro_free(cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -397,39 +432,34 @@ static void
test_establish_intro_wrong_sig(void *arg)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
- or_circuit_t *intro_circ = or_circuit_new(0,NULL);;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ or_circuit_t *intro_circ = or_circuit_new(0,NULL);
- (void)arg;
+ (void) arg;
/* Get the auth key of the intro point */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
attempt to parse it. */
- establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(establish_intro_cell);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ cell_len = new_establish_intro_encoded_cell(circ_nonce, cell_body);
+ tt_i64_op(cell_len, OP_GT, 0);
/* Mutate the last byte (signature)! :) */
- cell_body[cell_len-1]++;
+ cell_body[cell_len - 1]++;
/* Receive the cell. Should fail. */
setup_full_capture_of_logs(LOG_INFO);
- retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
+ retval = hs_intro_received_establish_intro(intro_circ, cell_body,
+ (size_t)cell_len);
expect_log_msg_containing("Failed to verify ESTABLISH_INTRO cell.");
teardown_capture_of_logs();
- tt_int_op(retval, ==, -1);
+ tt_int_op(retval, OP_EQ, -1);
done:
- trn_cell_establish_intro_free(establish_intro_cell);
circuit_free(TO_CIRCUIT(intro_circ));
}
@@ -439,32 +469,33 @@ static trn_cell_establish_intro_t *
helper_establish_intro_v3(or_circuit_t *intro_circ)
{
int retval;
- trn_cell_establish_intro_t *establish_intro_cell = NULL;
+ char circ_nonce[DIGEST_LEN] = {0};
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ trn_cell_establish_intro_t *cell = NULL;
tt_assert(intro_circ);
/* Prepare the circuit for the incoming ESTABLISH_INTRO */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
- attempt to parse it. */
- establish_intro_cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(establish_intro_cell);
- cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body),
- establish_intro_cell);
- tt_int_op(cell_len, >, 0);
+ * attempt to parse it. */
+ cell_len = new_establish_intro_cell(circ_nonce, &cell);
+ tt_i64_op(cell_len, OP_GT, 0);
+ tt_assert(cell);
+ cell_len = trn_cell_establish_intro_encode(cell_body, sizeof(cell_body),
+ cell);
+ tt_int_op(cell_len, OP_GT, 0);
/* Receive the cell */
- retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len);
- tt_int_op(retval, ==, 0);
+ retval = hs_intro_received_establish_intro(intro_circ, cell_body,
+ (size_t) cell_len);
+ tt_int_op(retval, OP_EQ, 0);
done:
- return establish_intro_cell;
+ return cell;
}
/* Helper function: Send a well-formed v2 ESTABLISH_INTRO cell to
@@ -476,28 +507,28 @@ helper_establish_intro_v2(or_circuit_t *intro_circ)
int retval;
uint8_t cell_body[RELAY_PAYLOAD_SIZE];
ssize_t cell_len = 0;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
+ char circ_nonce[DIGEST_LEN] = {0};
tt_assert(intro_circ);
/* Prepare the circuit for the incoming ESTABLISH_INTRO */
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
- helper_prepare_circ_for_intro(intro_circ, circuit_key_material);
+ crypto_rand(circ_nonce, sizeof(circ_nonce));
+ helper_prepare_circ_for_intro(intro_circ, circ_nonce);
/* Send legacy establish_intro */
key1 = pk_generate(0);
- /* Use old circuit_key_material why not */
- cell_len = encode_establish_intro_cell_legacy((char*)cell_body,
- sizeof(cell_body),
- key1,
- (char *) circuit_key_material);
- tt_int_op(cell_len, >, 0);
+ /* Use old circ_nonce why not */
+ cell_len = rend_service_encode_establish_intro_cell(
+ (char*)cell_body,
+ sizeof(cell_body), key1,
+ circ_nonce);
+ tt_int_op(cell_len, OP_GT, 0);
/* Receive legacy establish_intro */
retval = hs_intro_received_establish_intro(intro_circ,
- cell_body, cell_len);
- tt_int_op(retval, ==, 0);
+ cell_body, (size_t) cell_len);
+ tt_int_op(retval, OP_EQ, 0);
done:
return key1;
@@ -516,7 +547,7 @@ test_circuitmap_free_all(void)
tt_assert(the_hs_circuitmap);
hs_circuitmap_free_all();
the_hs_circuitmap = get_hs_circuitmap();
- tt_assert(!the_hs_circuitmap);
+ tt_ptr_op(the_hs_circuitmap, OP_EQ, NULL);
done:
;
}
@@ -548,10 +579,10 @@ test_intro_point_registration(void *arg)
{
the_hs_circuitmap = get_hs_circuitmap();
tt_assert(the_hs_circuitmap);
- tt_int_op(0, ==, HT_SIZE(the_hs_circuitmap));
+ tt_int_op(0, OP_EQ, HT_SIZE(the_hs_circuitmap));
/* Do a circuitmap query in any case */
returned_intro_circ =hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key);
- tt_ptr_op(returned_intro_circ, ==, NULL);
+ tt_ptr_op(returned_intro_circ, OP_EQ, NULL);
}
/* Create a v3 intro point */
@@ -563,12 +594,12 @@ test_intro_point_registration(void *arg)
/* Check that the intro point was registered on the HS circuitmap */
the_hs_circuitmap = get_hs_circuitmap();
tt_assert(the_hs_circuitmap);
- tt_int_op(1, ==, HT_SIZE(the_hs_circuitmap));
+ tt_int_op(1, OP_EQ, HT_SIZE(the_hs_circuitmap));
get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO,
establish_intro_cell);
returned_intro_circ =
hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key);
- tt_ptr_op(intro_circ, ==, returned_intro_circ);
+ tt_ptr_op(intro_circ, OP_EQ, returned_intro_circ);
}
/* Create a v2 intro point */
@@ -583,14 +614,14 @@ test_intro_point_registration(void *arg)
/* Check that the circuitmap now has two elements */
the_hs_circuitmap = get_hs_circuitmap();
tt_assert(the_hs_circuitmap);
- tt_int_op(2, ==, HT_SIZE(the_hs_circuitmap));
+ tt_int_op(2, OP_EQ, HT_SIZE(the_hs_circuitmap));
/* Check that the new element is our legacy intro circuit. */
retval = crypto_pk_get_digest(legacy_auth_key, key_digest);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
returned_intro_circ =
hs_circuitmap_get_intro_circ_v2_relay_side((uint8_t*)key_digest);
- tt_ptr_op(legacy_intro_circ, ==, returned_intro_circ);
+ tt_ptr_op(legacy_intro_circ, OP_EQ, returned_intro_circ);
}
/* XXX Continue test and try to register a second v3 intro point with the
@@ -765,6 +796,7 @@ test_received_introduce1_handling(void *arg)
/* Too small request length. An INTRODUCE1 expect at the very least a
* DIGEST_LEN size. */
{
+ memset(buf, 0, sizeof(buf));
circ = helper_create_intro_circuit();
ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1);
tt_int_op(ret, OP_EQ, -1);
@@ -778,6 +810,7 @@ test_received_introduce1_handling(void *arg)
{
circ = helper_create_intro_circuit();
uint8_t test[2]; /* Too small request. */
+ memset(test, 0, sizeof(test));
ret = handle_introduce1(circ, test, sizeof(test));
tor_free(circ->p_chan);
circuit_free(TO_CIRCUIT(circ));
diff --git a/src/test/test_hs_ntor.c b/src/test/test_hs_ntor.c
new file mode 100644
index 0000000000..8eee54d4b4
--- /dev/null
+++ b/src/test/test_hs_ntor.c
@@ -0,0 +1,114 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_ntor.c
+ * \brief Test hidden service ntor functionality.
+ */
+
+#include "test.h"
+#include "test_helpers.h"
+#include "log_test_helpers.h"
+
+#include "hs_ntor.h"
+
+/* Test the HS ntor handshake. Simulate the sending of an encrypted INTRODUCE1
+ * cell, and verify the proper derivation of decryption keys on the other end.
+ * Then simulate the sending of an authenticated RENDEZVOUS1 cell and verify
+ * the proper verification on the other end. */
+static void
+test_hs_ntor(void *arg)
+{
+ int retval;
+
+ uint8_t subcredential[DIGEST256_LEN];
+
+ ed25519_keypair_t service_intro_auth_keypair;
+ curve25519_keypair_t service_intro_enc_keypair;
+ curve25519_keypair_t service_ephemeral_rend_keypair;
+
+ curve25519_keypair_t client_ephemeral_enc_keypair;
+
+ hs_ntor_intro_cell_keys_t client_hs_ntor_intro_cell_keys;
+ hs_ntor_intro_cell_keys_t service_hs_ntor_intro_cell_keys;
+
+ hs_ntor_rend_cell_keys_t service_hs_ntor_rend_cell_keys;
+ hs_ntor_rend_cell_keys_t client_hs_ntor_rend_cell_keys;
+
+ (void) arg;
+
+ /* Generate fake data for this unittest */
+ {
+ /* Generate fake subcredential */
+ memset(subcredential, 'Z', DIGEST256_LEN);
+
+ /* service */
+ curve25519_keypair_generate(&service_intro_enc_keypair, 0);
+ ed25519_keypair_generate(&service_intro_auth_keypair, 0);
+ curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);
+ /* client */
+ curve25519_keypair_generate(&client_ephemeral_enc_keypair, 0);
+ }
+
+ /* Client: Simulate the sending of an encrypted INTRODUCE1 cell */
+ retval =
+ hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
+ &service_intro_enc_keypair.pubkey,
+ &client_ephemeral_enc_keypair,
+ subcredential,
+ &client_hs_ntor_intro_cell_keys);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Service: Simulate the decryption of the received INTRODUCE1 */
+ retval =
+ hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
+ &service_intro_enc_keypair,
+ &client_ephemeral_enc_keypair.pubkey,
+ subcredential,
+ &service_hs_ntor_intro_cell_keys);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Test that the INTRODUCE1 encryption/mac keys match! */
+ tt_mem_op(client_hs_ntor_intro_cell_keys.enc_key, OP_EQ,
+ service_hs_ntor_intro_cell_keys.enc_key,
+ CIPHER256_KEY_LEN);
+ tt_mem_op(client_hs_ntor_intro_cell_keys.mac_key, OP_EQ,
+ service_hs_ntor_intro_cell_keys.mac_key,
+ DIGEST256_LEN);
+
+ /* Service: Simulate creation of RENDEZVOUS1 key material. */
+ retval =
+ hs_ntor_service_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
+ &service_intro_enc_keypair,
+ &service_ephemeral_rend_keypair,
+ &client_ephemeral_enc_keypair.pubkey,
+ &service_hs_ntor_rend_cell_keys);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Client: Simulate the verification of a received RENDEZVOUS1 cell */
+ retval =
+ hs_ntor_client_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
+ &client_ephemeral_enc_keypair,
+ &service_intro_enc_keypair.pubkey,
+ &service_ephemeral_rend_keypair.pubkey,
+ &client_hs_ntor_rend_cell_keys);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Test that the RENDEZVOUS1 key material match! */
+ tt_mem_op(client_hs_ntor_rend_cell_keys.rend_cell_auth_mac, OP_EQ,
+ service_hs_ntor_rend_cell_keys.rend_cell_auth_mac,
+ DIGEST256_LEN);
+ tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ,
+ service_hs_ntor_rend_cell_keys.ntor_key_seed,
+ DIGEST256_LEN);
+ done:
+ ;
+}
+
+struct testcase_t hs_ntor_tests[] = {
+ { "hs_ntor", test_hs_ntor, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index fcfb3b992d..d040f10e0c 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -6,243 +6,1618 @@
* \brief Test hidden service functionality.
*/
+#define CIRCUITBUILD_PRIVATE
+#define CIRCUITLIST_PRIVATE
+#define CONFIG_PRIVATE
+#define CONNECTION_PRIVATE
+#define CRYPTO_PRIVATE
#define HS_COMMON_PRIVATE
#define HS_SERVICE_PRIVATE
#define HS_INTROPOINT_PRIVATE
+#define HS_CIRCUIT_PRIVATE
+#define MAIN_PRIVATE
+#define NETWORKSTATUS_PRIVATE
+#define STATEFILE_PRIVATE
+#define TOR_CHANNEL_INTERNAL_
+#define HS_CLIENT_PRIVATE
#include "test.h"
+#include "test_helpers.h"
#include "log_test_helpers.h"
+#include "rend_test_helpers.h"
+#include "hs_test_helpers.h"
+
+#include "or.h"
+#include "config.h"
+#include "circuitbuild.h"
+#include "circuitlist.h"
+#include "circuituse.h"
#include "crypto.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "relay.h"
-#include "hs/cell_establish_intro.h"
#include "hs_common.h"
-#include "hs_service.h"
+#include "hs_config.h"
+#include "hs_ident.h"
#include "hs_intropoint.h"
-
#include "hs_ntor.h"
+#include "hs_circuit.h"
+#include "hs_service.h"
+#include "hs_client.h"
+#include "main.h"
+#include "rendservice.h"
+#include "statefile.h"
+#include "shared_random_state.h"
+
+/* Trunnel */
+#include "hs/cell_establish_intro.h"
+
+static networkstatus_t mock_ns;
+
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void) now;
+ return &mock_ns;
+}
+
+static or_state_t *dummy_state = NULL;
+
+/* Mock function to get fake or state (used for rev counters) */
+static or_state_t *
+get_or_state_replacement(void)
+{
+ return dummy_state;
+}
+
+/* Mock function because we are not trying to test the close circuit that does
+ * an awful lot of checks on the circuit object. */
+static void
+mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
+ const char *file)
+{
+ (void) circ;
+ (void) reason;
+ (void) line;
+ (void) file;
+ return;
+}
+
+static int
+mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
+ uint8_t relay_command, const char *payload,
+ size_t payload_len,
+ crypt_path_t *cpath_layer,
+ const char *filename, int lineno)
+{
+ (void) stream_id;
+ (void) circ;
+ (void) relay_command;
+ (void) payload;
+ (void) payload_len;
+ (void) cpath_layer;
+ (void) filename;
+ (void) lineno;
+ return 0;
+}
-/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we
- * parse it from the receiver side. */
+/* Helper: from a set of options in conf, configure a service which will add
+ * it to the staging list of the HS subsytem. */
+static int
+helper_config_service(const char *conf)
+{
+ int ret = 0;
+ or_options_t *options = NULL;
+ tt_assert(conf);
+ options = helper_parse_options(conf);
+ tt_assert(options);
+ ret = hs_config_service_all(options, 0);
+ done:
+ or_options_free(options);
+ return ret;
+}
+
+/* Test: Ensure that setting up rendezvous circuits works correctly. */
static void
-test_gen_establish_intro_cell(void *arg)
+test_e2e_rend_circuit_setup(void *arg)
{
+ ed25519_public_key_t service_pk;
+ origin_circuit_t *or_circ;
+ int retval;
+
+ /** In this test we create a v3 prop224 service-side rendezvous circuit.
+ * We simulate an HS ntor key exchange with a client, and check that
+ * the circuit was setup correctly and is ready to accept rendezvous data */
+
(void) arg;
- ssize_t retval;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
- uint8_t buf[RELAY_PAYLOAD_SIZE];
- trn_cell_establish_intro_t *cell_out = NULL;
- trn_cell_establish_intro_t *cell_in = NULL;
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
+ /* Now make dummy circuit */
+ {
+ or_circ = origin_circuit_new();
- /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
- attempt to parse it. */
+ or_circ->base_.purpose = CIRCUIT_PURPOSE_S_CONNECT_REND;
+
+ or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
+ or_circ->build_state->is_internal = 1;
+
+ /* prop224: Setup hs conn identifier on the stream */
+ ed25519_secret_key_t sk;
+ tt_int_op(0, OP_EQ, ed25519_secret_key_generate(&sk, 0));
+ tt_int_op(0, OP_EQ, ed25519_public_key_generate(&service_pk, &sk));
+
+ or_circ->hs_ident = hs_ident_circuit_new(&service_pk,
+ HS_IDENT_CIRCUIT_RENDEZVOUS);
+
+ TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN;
+ }
+
+ /* Check number of hops */
+ retval = cpath_get_n_hops(&or_circ->cpath);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Setup the circuit: do the ntor key exchange */
{
- cell_out = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- tt_assert(cell_out);
+ uint8_t ntor_key_seed[DIGEST256_LEN] = {2};
+ retval = hs_circuit_setup_e2e_rend_circ(or_circ,
+ ntor_key_seed, sizeof(ntor_key_seed),
+ 1);
+ tt_int_op(retval, OP_EQ, 0);
+ }
+
+ /* See that a hop was added to the circuit's cpath */
+ retval = cpath_get_n_hops(&or_circ->cpath);
+ tt_int_op(retval, OP_EQ, 1);
+
+ /* Check the digest algo */
+ tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->f_digest),
+ OP_EQ, DIGEST_SHA3_256);
+ tt_int_op(crypto_digest_get_algorithm(or_circ->cpath->b_digest),
+ OP_EQ, DIGEST_SHA3_256);
+ tt_assert(or_circ->cpath->f_crypto);
+ tt_assert(or_circ->cpath->b_crypto);
+
+ /* Ensure that circ purpose was changed */
+ tt_int_op(or_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
+
+ done:
+ circuit_free(TO_CIRCUIT(or_circ));
+}
+
+/* Helper: Return a newly allocated and initialized origin circuit with
+ * purpose and flags. A default HS identifier is set to an ed25519
+ * authentication key for introduction point. */
+static origin_circuit_t *
+helper_create_origin_circuit(int purpose, int flags)
+{
+ origin_circuit_t *circ = NULL;
+
+ circ = origin_circuit_init(purpose, flags);
+ tt_assert(circ);
+ circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
+ circ->cpath->magic = CRYPT_PATH_MAGIC;
+ circ->cpath->state = CPATH_STATE_OPEN;
+ circ->cpath->package_window = circuit_initial_package_window();
+ circ->cpath->deliver_window = CIRCWINDOW_START;
+ circ->cpath->prev = circ->cpath;
+ /* Random nonce. */
+ crypto_rand(circ->cpath->prev->rend_circ_nonce, DIGEST_LEN);
+ /* Create a default HS identifier. */
+ circ->hs_ident = tor_malloc_zero(sizeof(hs_ident_circuit_t));
+
+ done:
+ return circ;
+}
+
+/* Helper: Return a newly allocated service object with the identity keypair
+ * sets and the current descriptor. Then register it to the global map.
+ * Caller should us hs_free_all() to free this service or remove it from the
+ * global map before freeing. */
+static hs_service_t *
+helper_create_service(void)
+{
+ /* Set a service for this circuit. */
+ hs_service_t *service = hs_service_new(get_options());
+ tt_assert(service);
+ service->config.version = HS_VERSION_THREE;
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ service->desc_current = service_descriptor_new();
+ tt_assert(service->desc_current);
+ /* Register service to global map. */
+ int ret = register_service(get_hs_service_map(), service);
+ tt_int_op(ret, OP_EQ, 0);
+
+ done:
+ return service;
+}
+
+/* Helper: Return a newly allocated service intro point with two link
+ * specifiers, one IPv4 and one legacy ID set to As. */
+static hs_service_intro_point_t *
+helper_create_service_ip(void)
+{
+ hs_desc_link_specifier_t *ls;
+ hs_service_intro_point_t *ip = service_intro_point_new(NULL, 0);
+ tt_assert(ip);
+ /* Add a first unused link specifier. */
+ ls = tor_malloc_zero(sizeof(*ls));
+ ls->type = LS_IPV4;
+ smartlist_add(ip->base.link_specifiers, ls);
+ /* Add a second link specifier used by a test. */
+ ls = tor_malloc_zero(sizeof(*ls));
+ ls->type = LS_LEGACY_ID;
+ memset(ls->u.legacy_id, 'A', sizeof(ls->u.legacy_id));
+ smartlist_add(ip->base.link_specifiers, ls);
+
+ done:
+ return ip;
+}
+
+static void
+test_load_keys(void *arg)
+{
+ int ret;
+ char *conf = NULL;
+ char *hsdir_v2 = tor_strdup(get_fname("hs2"));
+ char *hsdir_v3 = tor_strdup(get_fname("hs3"));
+ char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+ (void) arg;
+
+ /* We'll register two services, a v2 and a v3, then we'll load keys and
+ * validate that both are in a correct state. */
+
+ hs_init();
+
+#define conf_fmt \
+ "HiddenServiceDir %s\n" \
+ "HiddenServiceVersion %d\n" \
+ "HiddenServicePort 65535\n"
+
+ /* v2 service. */
+ tor_asprintf(&conf, conf_fmt, hsdir_v2, HS_VERSION_TWO);
+ ret = helper_config_service(conf);
+ tor_free(conf);
+ tt_int_op(ret, OP_EQ, 0);
+ /* This one should now be registered into the v2 list. */
+ tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 0);
+ tt_int_op(rend_num_services(), OP_EQ, 1);
+
+ /* v3 service. */
+ tor_asprintf(&conf, conf_fmt, hsdir_v3, HS_VERSION_THREE);
+ ret = helper_config_service(conf);
+ tor_free(conf);
+ tt_int_op(ret, OP_EQ, 0);
+ /* It's in staging? */
+ tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
+
+ /* Load the keys for these. After that, the v3 service should be registered
+ * in the global map. */
+ hs_service_load_all_keys();
+ tt_int_op(get_hs_service_map_size(), OP_EQ, 1);
+ hs_service_t *s = get_first_service();
+ tt_assert(s);
+
+ /* Ok we have the service object. Validate few things. */
+ tt_assert(!tor_mem_is_zero(s->onion_address, sizeof(s->onion_address)));
+ tt_int_op(hs_address_is_valid(s->onion_address), OP_EQ, 1);
+ tt_assert(!tor_mem_is_zero((char *) s->keys.identity_sk.seckey,
+ ED25519_SECKEY_LEN));
+ tt_assert(!tor_mem_is_zero((char *) s->keys.identity_pk.pubkey,
+ ED25519_PUBKEY_LEN));
+ /* Check onion address from identity key. */
+ hs_build_address(&s->keys.identity_pk, s->config.version, addr);
+ tt_int_op(hs_address_is_valid(addr), OP_EQ, 1);
+ tt_str_op(addr, OP_EQ, s->onion_address);
+
+ done:
+ tor_free(hsdir_v2);
+ tor_free(hsdir_v3);
+ hs_free_all();
+}
+
+static void
+test_access_service(void *arg)
+{
+ int ret;
+ char *conf = NULL;
+ char *hsdir_v3 = tor_strdup(get_fname("hs3"));
+ hs_service_ht *global_map;
+ hs_service_t *s = NULL;
+
+ (void) arg;
+
+ /* We'll register two services, a v2 and a v3, then we'll load keys and
+ * validate that both are in a correct state. */
- retval = get_establish_intro_payload(buf, sizeof(buf), cell_out);
- tt_int_op(retval, >=, 0);
+ hs_init();
+
+#define conf_fmt \
+ "HiddenServiceDir %s\n" \
+ "HiddenServiceVersion %d\n" \
+ "HiddenServicePort 65535\n"
+
+ /* v3 service. */
+ tor_asprintf(&conf, conf_fmt, hsdir_v3, HS_VERSION_THREE);
+ ret = helper_config_service(conf);
+ tor_free(conf);
+ tt_int_op(ret, OP_EQ, 0);
+ /* It's in staging? */
+ tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
+
+ /* Load the keys for these. After that, the v3 service should be registered
+ * in the global map. */
+ hs_service_load_all_keys();
+ tt_int_op(get_hs_service_map_size(), OP_EQ, 1);
+ s = get_first_service();
+ tt_assert(s);
+ global_map = get_hs_service_map();
+ tt_assert(global_map);
+
+ /* From here, we'll try the service accessors. */
+ hs_service_t *query = find_service(global_map, &s->keys.identity_pk);
+ tt_assert(query);
+ tt_mem_op(query, OP_EQ, s, sizeof(hs_service_t));
+ /* Remove service, check if it actually works and then put it back. */
+ remove_service(global_map, s);
+ tt_int_op(get_hs_service_map_size(), OP_EQ, 0);
+ query = find_service(global_map, &s->keys.identity_pk);
+ tt_ptr_op(query, OP_EQ, NULL);
+
+ /* Register back the service in the map. */
+ ret = register_service(global_map, s);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_int_op(get_hs_service_map_size(), OP_EQ, 1);
+ /* Twice should fail. */
+ ret = register_service(global_map, s);
+ tt_int_op(ret, OP_EQ, -1);
+ /* Remove service from map so we don't double free on cleanup. */
+ remove_service(global_map, s);
+ tt_int_op(get_hs_service_map_size(), OP_EQ, 0);
+ query = find_service(global_map, &s->keys.identity_pk);
+ tt_ptr_op(query, OP_EQ, NULL);
+ /* Let's try to remove twice for fun. */
+ setup_full_capture_of_logs(LOG_WARN);
+ remove_service(global_map, s);
+ expect_log_msg_containing("Could not find service in the global map");
+ teardown_capture_of_logs();
+
+ done:
+ hs_service_free(s);
+ tor_free(hsdir_v3);
+ hs_free_all();
+}
+
+/** Test that we can create intro point objects, index them and find them */
+static void
+test_service_intro_point(void *arg)
+{
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+
+ (void) arg;
+
+ /* Test simple creation of an object. */
+ {
+ time_t now = time(NULL);
+ ip = helper_create_service_ip();
+ tt_assert(ip);
+ /* Make sure the authentication keypair is not zeroes. */
+ tt_int_op(tor_mem_is_zero((const char *) &ip->auth_key_kp,
+ sizeof(ed25519_keypair_t)), OP_EQ, 0);
+ /* The introduce2_max MUST be in that range. */
+ tt_u64_op(ip->introduce2_max, OP_GE,
+ INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS);
+ tt_u64_op(ip->introduce2_max, OP_LE,
+ INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS);
+ /* Time to expire MUST also be in that range. We subtract 500 seconds
+ * because there could be a gap between setting now and the time taken in
+ * service_intro_point_new. On ARM and other older CPUs, it can be
+ * surprisingly slow... */
+ tt_u64_op(ip->time_to_expire, OP_GE,
+ now + INTRO_POINT_LIFETIME_MIN_SECONDS - 500);
+ /* We add 500 seconds, because this time we're testing against the
+ * maximum allowed time. */
+ tt_u64_op(ip->time_to_expire, OP_LE,
+ now + INTRO_POINT_LIFETIME_MAX_SECONDS + 500);
+ tt_assert(ip->replay_cache);
+ tt_assert(ip->base.link_specifiers);
+ /* By default, this is NOT a legacy object. */
+ tt_int_op(ip->base.is_only_legacy, OP_EQ, 0);
}
- /* Parse it as the receiver */
+ /* Test functions that uses a service intropoints map with that previously
+ * created object (non legacy). */
{
- ssize_t parse_result = trn_cell_establish_intro_parse(&cell_in,
- buf, sizeof(buf));
- tt_int_op(parse_result, >=, 0);
+ ed25519_public_key_t garbage = { {0} };
+ hs_service_intro_point_t *query;
+
+ service = hs_service_new(get_options());
+ tt_assert(service);
+ service->desc_current = service_descriptor_new();
+ tt_assert(service->desc_current);
+ /* Add intropoint to descriptor map. */
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ query = service_intro_point_find(service, &ip->auth_key_kp.pubkey);
+ tt_mem_op(query, OP_EQ, ip, sizeof(hs_service_intro_point_t));
+ query = service_intro_point_find(service, &garbage);
+ tt_ptr_op(query, OP_EQ, NULL);
+
+ /* While at it, can I find the descriptor with the intro point? */
+ hs_service_descriptor_t *desc_lookup =
+ service_desc_find_by_intro(service, ip);
+ tt_mem_op(service->desc_current, OP_EQ, desc_lookup,
+ sizeof(hs_service_descriptor_t));
- retval = verify_establish_intro_cell(cell_in,
- circuit_key_material,
- sizeof(circuit_key_material));
- tt_int_op(retval, >=, 0);
+ /* Remove object from service descriptor and make sure it is out. */
+ service_intro_point_remove(service, ip);
+ query = service_intro_point_find(service, &ip->auth_key_kp.pubkey);
+ tt_ptr_op(query, OP_EQ, NULL);
}
done:
- trn_cell_establish_intro_free(cell_out);
- trn_cell_establish_intro_free(cell_in);
+ /* If the test succeed, this object is no longer referenced in the service
+ * so we can free it without use after free. Else, it might explode because
+ * it's still in the service descriptor map. */
+ service_intro_point_free(ip);
+ hs_service_free(service);
}
-/* Mocked ed25519_sign_prefixed() function that always fails :) */
-static int
-mock_ed25519_sign_prefixed(ed25519_signature_t *signature_out,
- const uint8_t *msg, size_t msg_len,
- const char *prefix_str,
- const ed25519_keypair_t *keypair) {
- (void) signature_out;
- (void) msg;
- (void) msg_len;
- (void) prefix_str;
- (void) keypair;
- return -1;
+static node_t mock_node;
+static const node_t *
+mock_node_get_by_id(const char *digest)
+{
+ (void) digest;
+ memset(mock_node.identity, 'A', DIGEST_LEN);
+ /* Only return the matchin identity of As */
+ if (!tor_memcmp(mock_node.identity, digest, DIGEST_LEN)) {
+ return &mock_node;
+ }
+ return NULL;
}
-/** We simulate a failure to create an ESTABLISH_INTRO cell */
static void
-test_gen_establish_intro_cell_bad(void *arg)
+test_helper_functions(void *arg)
{
+ int ret;
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL;
+ hs_ident_circuit_t ident;
+
(void) arg;
- trn_cell_establish_intro_t *cell = NULL;
- uint8_t circuit_key_material[DIGEST_LEN] = {0};
- MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
+ MOCK(node_get_by_id, mock_node_get_by_id);
+
+ hs_service_init();
+
+ service = helper_create_service();
+
+ ip = helper_create_service_ip();
+ /* Immediately add the intro point to the service so the free service at the
+ * end cleans it as well. */
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+
+ /* Setup the circuit identifier. */
+ ed25519_pubkey_copy(&ident.intro_auth_pk, &ip->auth_key_kp.pubkey);
+ ed25519_pubkey_copy(&ident.identity_pk, &service->keys.identity_pk);
- crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material));
+ /* Testing get_objects_from_ident(). */
+ {
+ hs_service_t *s_lookup = NULL;
+ hs_service_intro_point_t *ip_lookup = NULL;
+ hs_service_descriptor_t *desc_lookup = NULL;
+
+ get_objects_from_ident(&ident, &s_lookup, &ip_lookup, &desc_lookup);
+ tt_mem_op(s_lookup, OP_EQ, service, sizeof(hs_service_t));
+ tt_mem_op(ip_lookup, OP_EQ, ip, sizeof(hs_service_intro_point_t));
+ tt_mem_op(desc_lookup, OP_EQ, service->desc_current,
+ sizeof(hs_service_descriptor_t));
+ /* Reset */
+ s_lookup = NULL; ip_lookup = NULL; desc_lookup = NULL;
+
+ /* NULL parameter should work. */
+ get_objects_from_ident(&ident, NULL, &ip_lookup, &desc_lookup);
+ tt_mem_op(ip_lookup, OP_EQ, ip, sizeof(hs_service_intro_point_t));
+ tt_mem_op(desc_lookup, OP_EQ, service->desc_current,
+ sizeof(hs_service_descriptor_t));
+ /* Reset. */
+ s_lookup = NULL; ip_lookup = NULL; desc_lookup = NULL;
+
+ /* Break the ident and we should find nothing. */
+ memset(&ident, 0, sizeof(ident));
+ get_objects_from_ident(&ident, &s_lookup, &ip_lookup, &desc_lookup);
+ tt_ptr_op(s_lookup, OP_EQ, NULL);
+ tt_ptr_op(ip_lookup, OP_EQ, NULL);
+ tt_ptr_op(desc_lookup, OP_EQ, NULL);
+ }
+
+ /* Testing get_node_from_intro_point() */
+ {
+ const node_t *node = get_node_from_intro_point(ip);
+ tt_ptr_op(node, OP_EQ, &mock_node);
+ SMARTLIST_FOREACH_BEGIN(ip->base.link_specifiers,
+ hs_desc_link_specifier_t *, ls) {
+ if (ls->type == LS_LEGACY_ID) {
+ /* Change legacy id in link specifier which is not the mock node. */
+ memset(ls->u.legacy_id, 'B', sizeof(ls->u.legacy_id));
+ }
+ } SMARTLIST_FOREACH_END(ls);
+ node = get_node_from_intro_point(ip);
+ tt_ptr_op(node, OP_EQ, NULL);
+ }
+
+ /* Testing can_service_launch_intro_circuit() */
+ {
+ time_t now = time(NULL);
+ /* Put the start of the retry period back in time, we should be allowed.
+ * to launch intro circuit. */
+ service->state.num_intro_circ_launched = 2;
+ service->state.intro_circ_retry_started_time =
+ (now - INTRO_CIRC_RETRY_PERIOD - 1);
+ ret = can_service_launch_intro_circuit(service, now);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_u64_op(service->state.intro_circ_retry_started_time, OP_EQ, now);
+ tt_u64_op(service->state.num_intro_circ_launched, OP_EQ, 0);
+ /* Call it again, we should still be allowed because we are under
+ * MAX_INTRO_CIRCS_PER_PERIOD which been set to 0 previously. */
+ ret = can_service_launch_intro_circuit(service, now);
+ tt_int_op(ret, OP_EQ, 1);
+ tt_u64_op(service->state.intro_circ_retry_started_time, OP_EQ, now);
+ tt_u64_op(service->state.num_intro_circ_launched, OP_EQ, 0);
+ /* Too many intro circuit launched means we are not allowed. */
+ service->state.num_intro_circ_launched = 20;
+ ret = can_service_launch_intro_circuit(service, now);
+ tt_int_op(ret, OP_EQ, 0);
+ }
+
+ /* Testing intro_point_should_expire(). */
+ {
+ time_t now = time(NULL);
+ /* Just some basic test of the current state. */
+ tt_u64_op(ip->introduce2_max, OP_GE,
+ INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS);
+ tt_u64_op(ip->introduce2_max, OP_LE,
+ INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS);
+ tt_u64_op(ip->time_to_expire, OP_GE,
+ now + INTRO_POINT_LIFETIME_MIN_SECONDS);
+ tt_u64_op(ip->time_to_expire, OP_LE,
+ now + INTRO_POINT_LIFETIME_MAX_SECONDS);
+
+ /* This newly created IP from above shouldn't expire now. */
+ ret = intro_point_should_expire(ip, now);
+ tt_int_op(ret, OP_EQ, 0);
+ /* Maximum number of INTRODUCE2 cell reached, it should expire. */
+ ip->introduce2_count = INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS + 1;
+ ret = intro_point_should_expire(ip, now);
+ tt_int_op(ret, OP_EQ, 1);
+ ip->introduce2_count = 0;
+ /* It should expire if time to expire has been reached. */
+ ip->time_to_expire = now - 1000;
+ ret = intro_point_should_expire(ip, now);
+ tt_int_op(ret, OP_EQ, 1);
+ }
+ done:
+ /* This will free the service and all objects associated to it. */
+ hs_service_free_all();
+ UNMOCK(node_get_by_id);
+}
+
+/** Test that we do the right operations when an intro circuit opens */
+static void
+test_intro_circuit_opened(void *arg)
+{
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ hs_service_t *service;
+ origin_circuit_t *circ = NULL;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
+ flags);
+
+ /* No service associated with this circuit. */
setup_full_capture_of_logs(LOG_WARN);
- /* Easiest way to make that function fail is to mock the
- ed25519_sign_prefixed() function and make it fail. */
- cell = generate_establish_intro_cell(circuit_key_material,
- sizeof(circuit_key_material));
- expect_log_msg_containing("Unable to gen signature for "
- "ESTABLISH_INTRO cell.");
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+
+ /* Set a service for this circuit. */
+ {
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+
+ /* No intro point associated with this circuit. */
+ setup_full_capture_of_logs(LOG_WARN);
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Unknown introduction point auth key");
+ teardown_capture_of_logs();
+ }
+
+ /* Set an IP object now for this circuit. */
+ {
+ hs_service_intro_point_t *ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* Update ident to contain the intro point auth key. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ }
+
+ /* This one should go all the way. */
+ setup_full_capture_of_logs(LOG_INFO);
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Introduction circuit 0 established for service");
teardown_capture_of_logs();
- tt_assert(!cell);
done:
- trn_cell_establish_intro_free(cell);
- UNMOCK(ed25519_sign_prefixed);
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+ UNMOCK(relay_send_command_from_edge_);
}
-/** Test the HS ntor handshake. Simulate the sending of an encrypted INTRODUCE1
- * cell, and verify the proper derivation of decryption keys on the other end.
- * Then simulate the sending of an authenticated RENDEZVOUS1 cell and verify
- * the proper verification on the other end. */
+/** Test the operations we do on a circuit after we learn that we successfuly
+ * established an intro point on it */
static void
-test_hs_ntor(void *arg)
+test_intro_established(void *arg)
{
- int retval;
+ int ret;
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
+ origin_circuit_t *circ = NULL;
+ hs_service_t *service;
+ hs_service_intro_point_t *ip = NULL;
- uint8_t subcredential[DIGEST256_LEN];
+ (void) arg;
- ed25519_keypair_t service_intro_auth_keypair;
- curve25519_keypair_t service_intro_enc_keypair;
- curve25519_keypair_t service_ephemeral_rend_keypair;
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
- curve25519_keypair_t client_ephemeral_enc_keypair;
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
+ flags);
+ tt_assert(circ);
- hs_ntor_intro_cell_keys_t client_hs_ntor_intro_cell_keys;
- hs_ntor_intro_cell_keys_t service_hs_ntor_intro_cell_keys;
+ /* Test a wrong purpose. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_INTRO;
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Received an INTRO_ESTABLISHED cell on a "
+ "non introduction circuit of purpose");
+ teardown_capture_of_logs();
+
+ /* Back to normal. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO;
+
+ /* No service associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+ /* No introduction point associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Introduction circuit established without an "
+ "intro point object on circuit");
+ teardown_capture_of_logs();
+
+ /* Set an IP object now for this circuit. */
+ {
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* Update ident to contain the intro point auth key. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ }
+
+ /* Send an empty payload. INTRO_ESTABLISHED cells are basically zeroes. */
+ ret = hs_service_receive_intro_established(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, 0);
+ tt_u64_op(ip->circuit_established, OP_EQ, 1);
+ tt_int_op(TO_CIRCUIT(circ)->purpose, OP_EQ, CIRCUIT_PURPOSE_S_INTRO);
+
+ done:
+ if (circ)
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+}
- hs_ntor_rend_cell_keys_t service_hs_ntor_rend_cell_keys;
- hs_ntor_rend_cell_keys_t client_hs_ntor_rend_cell_keys;
+/** Check the operations we do on a rendezvous circuit after we learn it's
+ * open */
+static void
+test_rdv_circuit_opened(void *arg)
+{
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ origin_circuit_t *circ = NULL;
+ hs_service_t *service;
(void) arg;
- /* Generate fake data for this unittest */
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_CONNECT_REND, flags);
+ crypto_rand((char *) circ->hs_ident->rendezvous_cookie, REND_COOKIE_LEN);
+ crypto_rand((char *) circ->hs_ident->rendezvous_handshake_info,
+ sizeof(circ->hs_ident->rendezvous_handshake_info));
+
+ /* No service associated with this circuit. */
+ setup_full_capture_of_logs(LOG_WARN);
+ hs_service_circuit_has_opened(circ);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+ /* This should be set to a non zero timestamp. */
+ tt_u64_op(TO_CIRCUIT(circ)->timestamp_dirty, OP_NE, 0);
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+ /* Should be all good. */
+ hs_service_circuit_has_opened(circ);
+ tt_int_op(TO_CIRCUIT(circ)->purpose, OP_EQ, CIRCUIT_PURPOSE_S_REND_JOINED);
+
+ done:
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+ UNMOCK(relay_send_command_from_edge_);
+}
+
+static void
+mock_assert_circuit_ok(const circuit_t *c)
+{
+ (void) c;
+ return;
+}
+
+/** Test for the general mechanism for closing intro circs.
+ * Also a way to identify that #23603 has been fixed. */
+static void
+test_closing_intro_circs(void *arg)
+{
+ hs_service_t *service = NULL;
+ hs_service_intro_point_t *ip = NULL, *entry = NULL;
+ origin_circuit_t *intro_circ = NULL, *tmp_circ;
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+
+ (void) arg;
+
+ MOCK(assert_circuit_ok, mock_assert_circuit_ok);
+
+ hs_init();
+
+ /* Initialize service */
+ service = helper_create_service();
+ /* Initialize intro point */
+ ip = helper_create_service_ip();
+ tt_assert(ip);
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+
+ /* Initialize intro circuit */
+ intro_circ = origin_circuit_init(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, flags);
+ intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk,
+ HS_IDENT_CIRCUIT_INTRO);
+ /* Register circuit in the circuitmap . */
+ hs_circuitmap_register_intro_circ_v3_service_side(intro_circ,
+ &ip->auth_key_kp.pubkey);
+ tmp_circ =
+ hs_circuitmap_get_intro_circ_v3_service_side(&ip->auth_key_kp.pubkey);
+ tt_ptr_op(tmp_circ, OP_EQ, intro_circ);
+
+ /* Pretend that intro point has failed too much */
+ ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES+1;
+
+ /* Now pretend we are freeing this intro circuit. We want to see that our
+ * destructor is not gonna kill our intro point structure since that's the
+ * job of the cleanup routine. */
+ circuit_free(TO_CIRCUIT(intro_circ));
+ intro_circ = NULL;
+ entry = service_intro_point_find(service, &ip->auth_key_kp.pubkey);
+ tt_assert(entry);
+ /* The free should also remove the circuit from the circuitmap. */
+ tmp_circ =
+ hs_circuitmap_get_intro_circ_v3_service_side(&ip->auth_key_kp.pubkey);
+ tt_assert(!tmp_circ);
+
+ /* Now pretend that a new intro point circ was launched and opened. Check
+ * that the intro point will be established correctly. */
+ intro_circ = origin_circuit_init(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, flags);
+ intro_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk,
+ HS_IDENT_CIRCUIT_INTRO);
+ ed25519_pubkey_copy(&intro_circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ /* Register circuit in the circuitmap . */
+ hs_circuitmap_register_intro_circ_v3_service_side(intro_circ,
+ &ip->auth_key_kp.pubkey);
+ tmp_circ =
+ hs_circuitmap_get_intro_circ_v3_service_side(&ip->auth_key_kp.pubkey);
+ tt_ptr_op(tmp_circ, OP_EQ, intro_circ);
+ tt_int_op(TO_CIRCUIT(intro_circ)->marked_for_close, OP_EQ, 0);
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ), END_CIRC_REASON_INTERNAL);
+ tt_int_op(TO_CIRCUIT(intro_circ)->marked_for_close, OP_NE, 0);
+ /* At this point, we should not be able to find it in the circuitmap. */
+ tmp_circ =
+ hs_circuitmap_get_intro_circ_v3_service_side(&ip->auth_key_kp.pubkey);
+ tt_assert(!tmp_circ);
+
+ done:
+ if (intro_circ) {
+ circuit_free(TO_CIRCUIT(intro_circ));
+ }
+ /* Frees the service object. */
+ hs_free_all();
+ UNMOCK(assert_circuit_ok);
+}
+
+/** Test sending and receiving introduce2 cells */
+static void
+test_introduce2(void *arg)
+{
+ int ret;
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
+ origin_circuit_t *circ = NULL;
+ hs_service_t *service;
+ hs_service_intro_point_t *ip = NULL;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(get_or_state,
+ get_or_state_replacement);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_INTRO, flags);
+ tt_assert(circ);
+
+ /* Test a wrong purpose. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_ESTABLISH_INTRO;
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Received an INTRODUCE2 cell on a "
+ "non introduction circuit of purpose");
+ teardown_capture_of_logs();
+
+ /* Back to normal. */
+ TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_S_INTRO;
+
+ /* No service associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Unknown service identity key");
+ teardown_capture_of_logs();
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+ /* No introduction point associated to it. */
+ setup_full_capture_of_logs(LOG_WARN);
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("Unknown introduction auth key when handling "
+ "an INTRODUCE2 cell on circuit");
+ teardown_capture_of_logs();
+
+ /* Set an IP object now for this circuit. */
{
- /* Generate fake subcredential */
- memset(subcredential, 'Z', DIGEST256_LEN);
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* Update ident to contain the intro point auth key. */
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ }
+
+ /* This will fail because receiving an INTRODUCE2 cell implies a valid cell
+ * and then launching circuits so let's not do that and instead test that
+ * behaviour differently. */
+ ret = hs_service_receive_introduce2(circ, payload, sizeof(payload));
+ tt_int_op(ret, OP_EQ, -1);
+ tt_u64_op(ip->introduce2_count, OP_EQ, 0);
+
+ done:
+ or_state_free(dummy_state);
+ dummy_state = NULL;
+ if (circ)
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+}
+
+/** Test basic hidden service housekeeping operations (maintaining intro
+ * points, etc) */
+static void
+test_service_event(void *arg)
+{
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
+ time_t now = time(NULL);
+ hs_service_t *service;
+ origin_circuit_t *circ = NULL;
- /* service */
- curve25519_keypair_generate(&service_intro_enc_keypair, 0);
- ed25519_keypair_generate(&service_intro_auth_keypair, 0);
- curve25519_keypair_generate(&service_ephemeral_rend_keypair, 0);
- /* client */
- curve25519_keypair_generate(&client_ephemeral_enc_keypair, 0);
+ (void) arg;
+
+ hs_init();
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+
+ circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_INTRO, flags);
+
+ /* Set a service for this circuit. */
+ service = helper_create_service();
+ ed25519_pubkey_copy(&circ->hs_ident->identity_pk,
+ &service->keys.identity_pk);
+
+ /* Currently this consists of cleaning invalid intro points. So adding IPs
+ * here that should get cleaned up. */
+ {
+ hs_service_intro_point_t *ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ /* This run will remove the IP because we have no circuits nor node_t
+ * associated with it. */
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ /* We'll trigger a removal because we've reached our maximum amount of
+ * times we should retry a circuit. For this, we need to have a node_t
+ * that matches the identity of this IP. */
+ routerinfo_t ri;
+ memset(&ri, 0, sizeof(ri));
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ memset(ri.cache_info.identity_digest, 'A', DIGEST_LEN);
+ /* This triggers a node_t creation. */
+ tt_assert(nodelist_set_routerinfo(&ri, NULL));
+ ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1;
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ /* No removal but no circuit so this means the IP object will stay in the
+ * descriptor map so we can retry it. */
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ ip->circuit_established = 1; /* We'll test that, it MUST be 0 after. */
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
+ /* Remove the IP object at once for the next test. */
+ ip->circuit_retries = MAX_INTRO_POINT_CIRCUIT_RETRIES + 1;
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ /* Now, we'll create an IP with a registered circuit. The IP object
+ * shouldn't go away. */
+ ip = helper_create_service_ip();
+ service_intro_point_add(service->desc_current->intro_points.map, ip);
+ ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk,
+ &ip->auth_key_kp.pubkey);
+ hs_circuitmap_register_intro_circ_v3_service_side(
+ circ, &ip->auth_key_kp.pubkey);
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
+ /* We'll mangle the IP object to expire. */
+ ip->time_to_expire = now;
+ run_housekeeping_event(now);
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
}
- /* Client: Simulate the sending of an encrypted INTRODUCE1 cell */
- retval =
- hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
- &service_intro_enc_keypair.pubkey,
- &client_ephemeral_enc_keypair,
- subcredential,
- &client_hs_ntor_intro_cell_keys);
- tt_int_op(retval, ==, 0);
+ done:
+ hs_circuitmap_remove_circuit(TO_CIRCUIT(circ));
+ circuit_free(TO_CIRCUIT(circ));
+ hs_free_all();
+ UNMOCK(circuit_mark_for_close_);
+}
- /* Service: Simulate the decryption of the received INTRODUCE1 */
- retval =
- hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
- &service_intro_enc_keypair,
- &client_ephemeral_enc_keypair.pubkey,
- subcredential,
- &service_hs_ntor_intro_cell_keys);
- tt_int_op(retval, ==, 0);
+/** Test that we rotate descriptors correctly. */
+static void
+test_rotate_descriptors(void *arg)
+{
+ int ret;
+ time_t next_rotation_time, now = time(NULL);
+ hs_service_t *service;
+ hs_service_descriptor_t *desc_next;
- /* Test that the INTRODUCE1 encryption/mac keys match! */
- tt_mem_op(client_hs_ntor_intro_cell_keys.enc_key, OP_EQ,
- service_hs_ntor_intro_cell_keys.enc_key,
- CIPHER256_KEY_LEN);
- tt_mem_op(client_hs_ntor_intro_cell_keys.mac_key, OP_EQ,
- service_hs_ntor_intro_cell_keys.mac_key,
- DIGEST256_LEN);
+ (void) arg;
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ hs_init();
+ MOCK(get_or_state, get_or_state_replacement);
+ MOCK(circuit_mark_for_close_, mock_circuit_mark_for_close);
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ /* Descriptor rotation happens with a consensus with a new SRV. */
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), mock_ns.valid_after);
- /* Service: Simulate creation of RENDEZVOUS1 key material. */
- retval =
- hs_ntor_service_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
- &service_intro_enc_keypair,
- &service_ephemeral_rend_keypair,
- &client_ephemeral_enc_keypair.pubkey,
- &service_hs_ntor_rend_cell_keys);
- tt_int_op(retval, ==, 0);
+ /* Create a service with a default descriptor and state. It's added to the
+ * global map. */
+ service = helper_create_service();
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+ /* This triggers a build for both descriptors. The time now is only used in
+ * the descriptor certificate which is important to be now else the decoding
+ * will complain that the cert has expired if we use valid_after. */
+ build_all_descriptors(now);
+ tt_assert(service->desc_current);
+ tt_assert(service->desc_next);
- /* Client: Simulate the verification of a received RENDEZVOUS1 cell */
- retval =
- hs_ntor_client_get_rendezvous1_keys(&service_intro_auth_keypair.pubkey,
- &client_ephemeral_enc_keypair,
- &service_intro_enc_keypair.pubkey,
- &service_ephemeral_rend_keypair.pubkey,
- &client_hs_ntor_rend_cell_keys);
- tt_int_op(retval, ==, 0);
+ /* Tweak our service next rotation time so we can use a custom time. */
+ service->state.next_rotation_time = next_rotation_time =
+ mock_ns.valid_after + (11 * 60 * 60);
- /* Test that the RENDEZVOUS1 key material match! */
- tt_mem_op(client_hs_ntor_rend_cell_keys.rend_cell_auth_mac, OP_EQ,
- service_hs_ntor_rend_cell_keys.rend_cell_auth_mac,
- DIGEST256_LEN);
- tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ,
- service_hs_ntor_rend_cell_keys.ntor_key_seed,
- DIGEST256_LEN);
+ /* Nothing should happen, we are not at a new SRV. Our next rotation time
+ * should be untouched. */
+ rotate_all_descriptors(mock_ns.valid_after);
+ tt_u64_op(service->state.next_rotation_time, OP_EQ, next_rotation_time);
+ tt_assert(service->desc_current);
+ tt_assert(service->desc_next);
+ tt_u64_op(service->desc_current->time_period_num, OP_EQ,
+ hs_get_previous_time_period_num(0));
+ tt_u64_op(service->desc_next->time_period_num, OP_EQ,
+ hs_get_time_period_num(0));
+ /* Keep a reference so we can compare it after rotation to the current. */
+ desc_next = service->desc_next;
+
+ /* Going right after a new SRV. */
+ ret = parse_rfc1123_time("Sat, 27 Oct 1985 01:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 27 Oct 1985 02:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), mock_ns.valid_after);
+
+ /* Note down what to expect for the next rotation time which is 01:00 + 23h
+ * meaning 00:00:00. */
+ next_rotation_time = mock_ns.valid_after + (23 * 60 * 60);
+ /* We should have our next rotation time modified, our current descriptor
+ * cleaned up and the next descriptor becoming the current. */
+ rotate_all_descriptors(mock_ns.valid_after);
+ tt_u64_op(service->state.next_rotation_time, OP_EQ, next_rotation_time);
+ tt_mem_op(service->desc_current, OP_EQ, desc_next, sizeof(*desc_next));
+ tt_assert(service->desc_next == NULL);
+
+ /* A second time should do nothing. */
+ rotate_all_descriptors(mock_ns.valid_after);
+ tt_u64_op(service->state.next_rotation_time, OP_EQ, next_rotation_time);
+ tt_mem_op(service->desc_current, OP_EQ, desc_next, sizeof(*desc_next));
+ tt_assert(service->desc_next == NULL);
+
+ build_all_descriptors(now);
+ tt_mem_op(service->desc_current, OP_EQ, desc_next, sizeof(*desc_next));
+ tt_u64_op(service->desc_current->time_period_num, OP_EQ,
+ hs_get_time_period_num(0));
+ tt_u64_op(service->desc_next->time_period_num, OP_EQ,
+ hs_get_next_time_period_num(0));
+ tt_assert(service->desc_next);
done:
- ;
+ hs_free_all();
+ UNMOCK(get_or_state);
+ UNMOCK(circuit_mark_for_close_);
+ UNMOCK(networkstatus_get_live_consensus);
}
-/** Test that our HS time period calculation functions work properly */
+/** Test building descriptors: picking intro points, setting up their link
+ * specifiers, etc. */
static void
-test_time_period(void *arg)
+test_build_update_descriptors(void *arg)
{
+ int ret;
+ time_t now = time(NULL);
+ node_t *node;
+ hs_service_t *service;
+ hs_service_intro_point_t *ip_cur, *ip_next;
+ routerinfo_t ri;
+
(void) arg;
- uint64_t tn;
+
+ hs_init();
+
+ MOCK(get_or_state,
+ get_or_state_replacement);
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 04:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), mock_ns.valid_after);
+
+ /* Create a service without a current descriptor to trigger a build. */
+ service = helper_create_service();
+ tt_assert(service);
+ /* Unfortunately, the helper creates a dummy descriptor so get rid of it. */
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ /* We have a fresh service so this should trigger a build for both
+ * descriptors for specific time period that we'll test. */
+ build_all_descriptors(now);
+ /* Check *current* descriptor. */
+ tt_assert(service->desc_current);
+ tt_assert(service->desc_current->desc);
+ tt_assert(service->desc_current->intro_points.map);
+ /* The current time period is the one expected when starting at 03:00. */
+ tt_u64_op(service->desc_current->time_period_num, OP_EQ,
+ hs_get_time_period_num(0));
+ /* This should be untouched, the update descriptor process changes it. */
+ tt_u64_op(service->desc_current->next_upload_time, OP_EQ, 0);
+
+ /* Check *next* descriptor. */
+ tt_assert(service->desc_next);
+ tt_assert(service->desc_next->desc);
+ tt_assert(service->desc_next->intro_points.map);
+ tt_assert(service->desc_current != service->desc_next);
+ tt_u64_op(service->desc_next->time_period_num, OP_EQ,
+ hs_get_next_time_period_num(0));
+ /* This should be untouched, the update descriptor process changes it. */
+ tt_u64_op(service->desc_next->next_upload_time, OP_EQ, 0);
+
+ /* Time to test the update of those descriptors. At first, we have no node
+ * in the routerlist so this will find NO suitable node for the IPs. */
+ setup_full_capture_of_logs(LOG_INFO);
+ update_all_descriptors(now);
+ expect_log_msg_containing("Unable to find a suitable node to be an "
+ "introduction point for service");
+ teardown_capture_of_logs();
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 0);
+ tt_int_op(digest256map_size(service->desc_next->intro_points.map),
+ OP_EQ, 0);
+
+ /* Now, we'll setup a node_t. */
+ {
+ tor_addr_t ipv4_addr;
+ curve25519_secret_key_t curve25519_secret_key;
+
+ memset(&ri, 0, sizeof(routerinfo_t));
+
+ tor_addr_parse(&ipv4_addr, "127.0.0.1");
+ ri.addr = tor_addr_to_ipv4h(&ipv4_addr);
+ ri.or_port = 1337;
+ ri.purpose = ROUTER_PURPOSE_GENERAL;
+ /* Ugly yes but we never free the "ri" object so this just makes things
+ * easier. */
+ ri.protocol_list = (char *) "HSDir=1-2 LinkAuth=3";
+ ret = curve25519_secret_key_generate(&curve25519_secret_key, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ ri.onion_curve25519_pkey =
+ tor_malloc_zero(sizeof(curve25519_public_key_t));
+ ri.onion_pkey = crypto_pk_new();
+ curve25519_public_key_generate(ri.onion_curve25519_pkey,
+ &curve25519_secret_key);
+ memset(ri.cache_info.identity_digest, 'A', DIGEST_LEN);
+ /* Setup ed25519 identity */
+ ed25519_keypair_t kp1;
+ ed25519_keypair_generate(&kp1, 0);
+ ri.cache_info.signing_key_cert = tor_malloc_zero(sizeof(tor_cert_t));
+ tt_assert(ri.cache_info.signing_key_cert);
+ ed25519_pubkey_copy(&ri.cache_info.signing_key_cert->signing_key,
+ &kp1.pubkey);
+ nodelist_set_routerinfo(&ri, NULL);
+ node = node_get_mutable_by_id(ri.cache_info.identity_digest);
+ tt_assert(node);
+ node->is_running = node->is_valid = node->is_fast = node->is_stable = 1;
+ }
+
+ /* We expect to pick only one intro point from the node above. */
+ setup_full_capture_of_logs(LOG_INFO);
+ update_all_descriptors(now);
+ tor_free(node->ri->onion_curve25519_pkey); /* Avoid memleak. */
+ tor_free(node->ri->cache_info.signing_key_cert);
+ crypto_pk_free(node->ri->onion_pkey);
+ expect_log_msg_containing("just picked 1 intro points and wanted 3 for next "
+ "descriptor. It currently has 0 intro points. "
+ "Launching ESTABLISH_INTRO circuit shortly.");
+ teardown_capture_of_logs();
+ tt_int_op(digest256map_size(service->desc_current->intro_points.map),
+ OP_EQ, 1);
+ tt_int_op(digest256map_size(service->desc_next->intro_points.map),
+ OP_EQ, 1);
+ /* Get the IP object. Because we don't have the auth key of the IP, we can't
+ * query it so get the first element in the map. */
+ {
+ void *obj = NULL;
+ const uint8_t *key;
+ digest256map_iter_t *iter =
+ digest256map_iter_init(service->desc_current->intro_points.map);
+ digest256map_iter_get(iter, &key, &obj);
+ tt_assert(obj);
+ ip_cur = obj;
+ /* Get also the IP from the next descriptor. We'll make sure it's not the
+ * same object as in the current descriptor. */
+ iter = digest256map_iter_init(service->desc_next->intro_points.map);
+ digest256map_iter_get(iter, &key, &obj);
+ tt_assert(obj);
+ ip_next = obj;
+ }
+ tt_mem_op(ip_cur, OP_NE, ip_next, sizeof(hs_desc_intro_point_t));
+
+ /* We won't test the service IP object because there is a specific test
+ * already for this but we'll make sure that the state is coherent.*/
+
+ /* Three link specifiers are mandatoy so make sure we do have them. */
+ tt_int_op(smartlist_len(ip_cur->base.link_specifiers), OP_EQ, 3);
+ /* Make sure we have a valid encryption keypair generated when we pick an
+ * intro point in the update process. */
+ tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.seckey.secret_key,
+ CURVE25519_SECKEY_LEN));
+ tt_assert(!tor_mem_is_zero((char *) ip_cur->enc_key_kp.pubkey.public_key,
+ CURVE25519_PUBKEY_LEN));
+ tt_u64_op(ip_cur->time_to_expire, OP_GE, now +
+ INTRO_POINT_LIFETIME_MIN_SECONDS);
+ tt_u64_op(ip_cur->time_to_expire, OP_LE, now +
+ INTRO_POINT_LIFETIME_MAX_SECONDS);
+
+ /* Now, we will try to set up a service after a new time period has started
+ * and see if it behaves as expected. */
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Create a service without a current descriptor to trigger a build. */
+ service = helper_create_service();
+ tt_assert(service);
+ /* Unfortunately, the helper creates a dummy descriptor so get rid of it. */
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ /* We have a fresh service so this should trigger a build for both
+ * descriptors for specific time period that we'll test. */
+ build_all_descriptors(now);
+ /* Check *current* descriptor. */
+ tt_assert(service->desc_current);
+ tt_assert(service->desc_current->desc);
+ tt_assert(service->desc_current->intro_points.map);
+ /* This should be for the previous time period. */
+ tt_u64_op(service->desc_current->time_period_num, OP_EQ,
+ hs_get_previous_time_period_num(0));
+ /* This should be untouched, the update descriptor process changes it. */
+ tt_u64_op(service->desc_current->next_upload_time, OP_EQ, 0);
+
+ /* Check *next* descriptor. */
+ tt_assert(service->desc_next);
+ tt_assert(service->desc_next->desc);
+ tt_assert(service->desc_next->intro_points.map);
+ tt_assert(service->desc_current != service->desc_next);
+ tt_u64_op(service->desc_next->time_period_num, OP_EQ,
+ hs_get_time_period_num(0));
+ /* This should be untouched, the update descriptor process changes it. */
+ tt_u64_op(service->desc_next->next_upload_time, OP_EQ, 0);
+
+ /* Let's remove the next descriptor to simulate a rotation. */
+ service_descriptor_free(service->desc_next);
+ service->desc_next = NULL;
+
+ build_all_descriptors(now);
+ /* Check *next* descriptor. */
+ tt_assert(service->desc_next);
+ tt_assert(service->desc_next->desc);
+ tt_assert(service->desc_next->intro_points.map);
+ tt_assert(service->desc_current != service->desc_next);
+ tt_u64_op(service->desc_next->time_period_num, OP_EQ,
+ hs_get_next_time_period_num(0));
+ /* This should be untouched, the update descriptor process changes it. */
+ tt_u64_op(service->desc_next->next_upload_time, OP_EQ, 0);
+
+ done:
+ hs_free_all();
+ nodelist_free_all();
+}
+
+static void
+test_upload_descriptors(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+ hs_service_t *service;
+
+ (void) arg;
+
+ hs_init();
+ MOCK(get_or_state,
+ get_or_state_replacement);
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Create a service with no descriptor. It's added to the global map. */
+ service = hs_service_new(get_options());
+ tt_assert(service);
+ service->config.version = HS_VERSION_THREE;
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ /* Register service to global map. */
+ ret = register_service(get_hs_service_map(), service);
+ tt_int_op(ret, OP_EQ, 0);
+ /* But first, build our descriptor. */
+ build_all_descriptors(now);
+
+ /* Nothing should happen because we have 0 introduction circuit established
+ * and we want (by default) 3 intro points. */
+ run_upload_descriptor_event(now);
+ /* If no upload happened, this should be untouched. */
+ tt_u64_op(service->desc_current->next_upload_time, OP_EQ, 0);
+ /* We'll simulate that we've opened our intro point circuit and that we only
+ * want one intro point. */
+ service->config.num_intro_points = 1;
+
+ /* Set our next upload time after now which will skip the upload. */
+ service->desc_current->next_upload_time = now + 1000;
+ run_upload_descriptor_event(now);
+ /* If no upload happened, this should be untouched. */
+ tt_u64_op(service->desc_current->next_upload_time, OP_EQ, now + 1000);
+
+ done:
+ hs_free_all();
+ UNMOCK(get_or_state);
+}
+
+/** Test the functions that save and load HS revision counters to state. */
+static void
+test_revision_counter_state(void *arg)
+{
+ char *state_line_one = NULL;
+ char *state_line_two = NULL;
+
+ hs_service_descriptor_t *desc_one = service_descriptor_new();
+ hs_service_descriptor_t *desc_two = service_descriptor_new();
+
+ (void) arg;
+
+ /* Prepare both descriptors */
+ desc_one->desc->plaintext_data.revision_counter = 42;
+ desc_two->desc->plaintext_data.revision_counter = 240;
+ memset(&desc_one->blinded_kp.pubkey.pubkey, 66,
+ sizeof(desc_one->blinded_kp.pubkey.pubkey));
+ memset(&desc_two->blinded_kp.pubkey.pubkey, 240,
+ sizeof(desc_one->blinded_kp.pubkey.pubkey));
+
+ /* Turn the descriptor rev counters into state lines */
+ state_line_one = encode_desc_rev_counter_for_state(desc_one);
+ tt_str_op(state_line_one, OP_EQ,
+ "QkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkJCQkI 42");
+
+ state_line_two = encode_desc_rev_counter_for_state(desc_two);
+ tt_str_op(state_line_two, OP_EQ,
+ "8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PDw8PA 240");
+
+ /* Now let's test our state parsing function: */
+ int service_found;
+ uint64_t cached_rev_counter;
+
+ /* First's try with wrong pubkey and check that no service was found */
+ cached_rev_counter =check_state_line_for_service_rev_counter(state_line_one,
+ &desc_two->blinded_kp.pubkey,
+ &service_found);
+ tt_int_op(service_found, OP_EQ, 0);
+ tt_u64_op(cached_rev_counter, OP_EQ, 0);
+
+ /* Now let's try with the right pubkeys */
+ cached_rev_counter =check_state_line_for_service_rev_counter(state_line_one,
+ &desc_one->blinded_kp.pubkey,
+ &service_found);
+ tt_int_op(service_found, OP_EQ, 1);
+ tt_u64_op(cached_rev_counter, OP_EQ, 42);
+
+ cached_rev_counter =check_state_line_for_service_rev_counter(state_line_two,
+ &desc_two->blinded_kp.pubkey,
+ &service_found);
+ tt_int_op(service_found, OP_EQ, 1);
+ tt_u64_op(cached_rev_counter, OP_EQ, 240);
+
+ done:
+ tor_free(state_line_one);
+ tor_free(state_line_two);
+ service_descriptor_free(desc_one);
+ service_descriptor_free(desc_two);
+}
+
+/** Global vars used by test_rendezvous1_parsing() */
+static char rend1_payload[RELAY_PAYLOAD_SIZE];
+static size_t rend1_payload_len = 0;
+
+/** Mock for relay_send_command_from_edge() to send a RENDEZVOUS1 cell. Instead
+ * of sending it to the network, instead save it to the global `rend1_payload`
+ * variable so that we can inspect it in the test_rendezvous1_parsing()
+ * test. */
+static int
+mock_relay_send_rendezvous1(streamid_t stream_id, circuit_t *circ,
+ uint8_t relay_command, const char *payload,
+ size_t payload_len,
+ crypt_path_t *cpath_layer,
+ const char *filename, int lineno)
+{
+ (void) stream_id;
+ (void) circ;
+ (void) relay_command;
+ (void) cpath_layer;
+ (void) filename;
+ (void) lineno;
+
+ memcpy(rend1_payload, payload, payload_len);
+ rend1_payload_len = payload_len;
+
+ return 0;
+}
+
+/** Send a RENDEZVOUS1 as a service, and parse it as a client. */
+static void
+test_rendezvous1_parsing(void *arg)
+{
int retval;
- time_t fake_time;
+ static const char *test_addr =
+ "4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion";
+ hs_service_t *service = NULL;
+ origin_circuit_t *service_circ = NULL;
+ origin_circuit_t *client_circ = NULL;
+ ed25519_keypair_t ip_auth_kp;
+ curve25519_keypair_t ephemeral_kp;
+ curve25519_keypair_t client_kp;
+ curve25519_keypair_t ip_enc_kp;
+ int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
- /* Let's do the example in prop224 section [TIME-PERIODS] */
- retval = parse_rfc1123_time("Wed, 13 Apr 2016 11:00:00 UTC",
- &fake_time);
- tt_int_op(retval, ==, 0);
+ (void) arg;
- /* Check that the time period number is right */
- tn = get_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16903);
+ MOCK(relay_send_command_from_edge_, mock_relay_send_rendezvous1);
- /* Increase current time to 11:59:59 UTC and check that the time period
- number is still the same */
- fake_time += 3599;
- tn = get_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16903);
+ {
+ /* Let's start by setting up the service that will start the rend */
+ service = tor_malloc_zero(sizeof(hs_service_t));
+ ed25519_secret_key_generate(&service->keys.identity_sk, 0);
+ ed25519_public_key_generate(&service->keys.identity_pk,
+ &service->keys.identity_sk);
+ memcpy(service->onion_address, test_addr, sizeof(service->onion_address));
+ tt_assert(service);
+ }
+
+ {
+ /* Now let's set up the service rendezvous circuit and its keys. */
+ service_circ = helper_create_origin_circuit(CIRCUIT_PURPOSE_S_CONNECT_REND,
+ flags);
+ tor_free(service_circ->hs_ident);
+ hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
+ uint8_t rendezvous_cookie[HS_REND_COOKIE_LEN];
+ curve25519_keypair_generate(&ip_enc_kp, 0);
+ curve25519_keypair_generate(&ephemeral_kp, 0);
+ curve25519_keypair_generate(&client_kp, 0);
+ ed25519_keypair_generate(&ip_auth_kp, 0);
+ retval = hs_ntor_service_get_rendezvous1_keys(&ip_auth_kp.pubkey,
+ &ip_enc_kp,
+ &ephemeral_kp,
+ &client_kp.pubkey,
+ &hs_ntor_rend_cell_keys);
+ tt_int_op(retval, OP_EQ, 0);
+
+ memset(rendezvous_cookie, 2, sizeof(rendezvous_cookie));
+ service_circ->hs_ident =
+ create_rp_circuit_identifier(service, rendezvous_cookie,
+ &ephemeral_kp.pubkey,
+ &hs_ntor_rend_cell_keys);
+ }
- /* Now take time to 12:00:00 UTC and check that the time period rotated */
- fake_time += 1;
- tn = get_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16904);
+ /* Send out the RENDEZVOUS1 and make sure that our mock func worked */
+ tt_assert(tor_mem_is_zero(rend1_payload, 32));
+ hs_circ_service_rp_has_opened(service, service_circ);
+ tt_assert(!tor_mem_is_zero(rend1_payload, 32));
+ tt_int_op(rend1_payload_len, OP_EQ, HS_LEGACY_RENDEZVOUS_CELL_SIZE);
- /* Now also check our hs_get_next_time_period_num() function */
- tn = hs_get_next_time_period_num(fake_time);
- tt_u64_op(tn, ==, 16905);
+ /******************************/
+
+ /** Now let's create the client rendezvous circuit */
+ client_circ =
+ helper_create_origin_circuit(CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED,
+ flags);
+ /* fix up its circ ident */
+ ed25519_pubkey_copy(&client_circ->hs_ident->intro_auth_pk,
+ &ip_auth_kp.pubkey);
+ memcpy(&client_circ->hs_ident->rendezvous_client_kp,
+ &client_kp, sizeof(client_circ->hs_ident->rendezvous_client_kp));
+ memcpy(&client_circ->hs_ident->intro_enc_pk.public_key,
+ &ip_enc_kp.pubkey.public_key,
+ sizeof(client_circ->hs_ident->intro_enc_pk.public_key));
+
+ /* Now parse the rendezvous2 circuit and make sure it was fine. We are
+ * skipping 20 bytes off its payload, since that's the rendezvous cookie
+ * which is only present in REND1. */
+ retval = handle_rendezvous2(client_circ,
+ (uint8_t*)rend1_payload+20,
+ rend1_payload_len-20);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* TODO: We are only simulating client/service here. We could also simulate
+ * the rendezvous point by plugging in rend_mid_establish_rendezvous(). We
+ * would need an extra circuit and some more stuff but it's doable. */
done:
- ;
+ circuit_free(TO_CIRCUIT(service_circ));
+ circuit_free(TO_CIRCUIT(client_circ));
+ hs_service_free(service);
+ hs_free_all();
+ UNMOCK(relay_send_command_from_edge_);
}
struct testcase_t hs_service_tests[] = {
- { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK,
+ { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
+ NULL, NULL },
+ { "load_keys", test_load_keys, TT_FORK,
+ NULL, NULL },
+ { "access_service", test_access_service, TT_FORK,
+ NULL, NULL },
+ { "service_intro_point", test_service_intro_point, TT_FORK,
+ NULL, NULL },
+ { "helper_functions", test_helper_functions, TT_FORK,
+ NULL, NULL },
+ { "intro_circuit_opened", test_intro_circuit_opened, TT_FORK,
+ NULL, NULL },
+ { "intro_established", test_intro_established, TT_FORK,
+ NULL, NULL },
+ { "closing_intro_circs", test_closing_intro_circs, TT_FORK,
+ NULL, NULL },
+ { "rdv_circuit_opened", test_rdv_circuit_opened, TT_FORK,
+ NULL, NULL },
+ { "introduce2", test_introduce2, TT_FORK,
+ NULL, NULL },
+ { "service_event", test_service_event, TT_FORK,
+ NULL, NULL },
+ { "rotate_descriptors", test_rotate_descriptors, TT_FORK,
+ NULL, NULL },
+ { "build_update_descriptors", test_build_update_descriptors, TT_FORK,
NULL, NULL },
- { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK,
+ { "upload_descriptors", test_upload_descriptors, TT_FORK,
NULL, NULL },
- { "hs_ntor", test_hs_ntor, TT_FORK,
+ { "revision_counter_state", test_revision_counter_state, TT_FORK,
NULL, NULL },
- { "time_period", test_time_period, TT_FORK,
+ { "rendezvous1_parsing", test_rendezvous1_parsing, TT_FORK,
NULL, NULL },
END_OF_TESTCASES
diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c
index cfb8d83b1d..d502bdddb1 100644
--- a/src/test/test_introduce.c
+++ b/src/test/test_introduce.c
@@ -307,7 +307,7 @@ do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase)
/* Do early parsing */
parsed_req = rend_service_begin_parse_intro(cell, cell_len, 2, &err_msg);
tt_assert(parsed_req);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
tt_mem_op(parsed_req->pk,OP_EQ, digest, DIGEST_LEN);
tt_assert(parsed_req->ciphertext);
tt_assert(parsed_req->ciphertext_len > 0);
@@ -318,7 +318,7 @@ do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase)
/* Do decryption */
r = rend_service_decrypt_intro(parsed_req, k, &err_msg);
tt_assert(!r);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
tt_assert(parsed_req->plaintext);
tt_assert(parsed_req->plaintext_len > 0);
@@ -328,7 +328,7 @@ do_parse_test(uint8_t *plaintext, size_t plaintext_len, int phase)
/* Do late parsing */
r = rend_service_parse_intro_plaintext(parsed_req, &err_msg);
tt_assert(!r);
- tt_assert(!err_msg);
+ tt_ptr_op(err_msg, OP_EQ, NULL);
tt_assert(parsed_req->parsed);
done:
@@ -355,7 +355,7 @@ make_intro_from_plaintext(
/*
* Figure out an upper bound on how big the ciphertext will be
- * (see crypto_pk_public_hybrid_encrypt())
+ * (see crypto_pk_obsolete_public_hybrid_encrypt())
*/
ciphertext_size = PKCS1_OAEP_PADDING_OVERHEAD;
ciphertext_size += crypto_pk_keysize(key);
@@ -372,7 +372,7 @@ make_intro_from_plaintext(
tt_assert(r >= 0);
/* Do encryption */
- r = crypto_pk_public_hybrid_encrypt(
+ r = crypto_pk_obsolete_public_hybrid_encrypt(
key, cell + DIGEST_LEN, ciphertext_size,
buf, len,
PK_PKCS1_OAEP_PADDING, 0);
diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh
new file mode 100755
index 0000000000..5511dbf18c
--- /dev/null
+++ b/src/test/test_key_expiration.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+# Note: some of this code is lifted from zero_length_keys.sh and
+# test_keygen.sh, and could be unified.
+
+umask 077
+set -e
+
+if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+ if [ "$TESTING_TOR_BINARY" = "" ] ; then
+ echo "Usage: ${0} PATH_TO_TOR [case-number]"
+ exit 1
+ fi
+fi
+
+if [ $# -ge 1 ]; then
+ TOR_BINARY="${1}"
+ shift
+else
+ TOR_BINARY="${TESTING_TOR_BINARY}"
+fi
+
+if [ $# -ge 1 ]; then
+ dflt=0
+else
+ dflt=1
+fi
+
+CASE1=$dflt
+CASE2=$dflt
+CASE3=$dflt
+
+if [ $# -ge 1 ]; then
+ eval "CASE${1}"=1
+fi
+
+
+dump() { xxd -p "$1" | tr -d '\n '; }
+die() { echo "$1" >&2 ; exit 5; }
+check_dir() { [ -d "$1" ] || die "$1 did not exist"; }
+check_file() { [ -e "$1" ] || die "$1 did not exist"; }
+check_no_file() { [ -e "$1" ] && die "$1 was not supposed to exist" || true; }
+check_files_eq() { cmp "$1" "$2" || die "$1 and $2 did not match: `dump $1` vs `dump $2`"; }
+check_keys_eq() { check_files_eq "${SRC}/keys/${1}" "${ME}/keys/${1}"; }
+
+DATA_DIR=`mktemp -d -t tor_key_expiration_tests.XXXXXX`
+if [ -z "$DATA_DIR" ]; then
+ echo "Failure: mktemp invocation returned empty string" >&2
+ exit 3
+fi
+if [ ! -d "$DATA_DIR" ]; then
+ echo "Failure: mktemp invocation result doesn't point to directory" >&2
+ exit 3
+fi
+trap "rm -rf '$DATA_DIR'" 0
+
+# Use an absolute path for this or Tor will complain
+DATA_DIR=`cd "${DATA_DIR}" && pwd`
+
+touch "${DATA_DIR}/empty_torrc"
+
+QUIETLY="--hush"
+SILENTLY="--quiet"
+TOR="${TOR_BINARY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f ${DATA_DIR}/empty_torrc --DataDirectory ${DATA_DIR}"
+
+##### SETUP
+#
+# Here we create a set of keys.
+
+# Step 1: Start Tor with --list-fingerprint --quiet. Make sure everything is there.
+echo "Setup step #1"
+${TOR} --list-fingerprint ${SILENTLY} > /dev/null
+
+check_dir "${DATA_DIR}/keys"
+check_file "${DATA_DIR}/keys/ed25519_master_id_public_key"
+check_file "${DATA_DIR}/keys/ed25519_master_id_secret_key"
+check_file "${DATA_DIR}/keys/ed25519_signing_cert"
+check_file "${DATA_DIR}/keys/ed25519_signing_secret_key"
+check_file "${DATA_DIR}/keys/secret_id_key"
+check_file "${DATA_DIR}/keys/secret_onion_key"
+check_file "${DATA_DIR}/keys/secret_onion_key_ntor"
+
+##### TEST CASES
+
+echo "=== Starting key expiration tests."
+
+FN="${DATA_DIR}/stderr"
+
+if [ "$CASE1" = 1 ]; then
+ echo "==== Case 1: Test --key-expiration without argument and ensure usage"
+ echo " instructions are printed."
+
+ ${TOR} ${QUIETLY} --key-expiration 2>"$FN" || true
+ grep "No valid argument to --key-expiration found!" "$FN" >/dev/null || \
+ die "Tor didn't mention supported --key-expiration argmuents"
+
+ echo "==== Case 1: ok"
+fi
+
+if [ "$CASE2" = 1 ]; then
+ echo "==== Case 2: Start Tor with --key-expiration 'sign' and make sure it prints an expiration."
+
+ ${TOR} ${QUIETLY} --key-expiration sign 2>"$FN"
+ grep "signing-cert-expiry:" "$FN" >/dev/null || \
+ die "Tor didn't print an expiration"
+
+ echo "==== Case 2: ok"
+fi
+
+if [ "$CASE3" = 1 ]; then
+ echo "==== Case 3: Start Tor with --key-expiration 'sign', when there is no"
+ echo " signing key, and make sure that Tor generates a new key"
+ echo " and prints its certificate's expiration."
+
+ mv "${DATA_DIR}/keys/ed25519_signing_cert" \
+ "${DATA_DIR}/keys/ed25519_signing_cert.bak"
+
+ ${TOR} --key-expiration sign > "$FN" 2>&1
+ grep "It looks like I need to generate and sign a new medium-term signing key" "$FN" >/dev/null || \
+ die "Tor didn't create a new signing key"
+ check_file "${DATA_DIR}/keys/ed25519_signing_cert"
+ grep "signing-cert-expiry:" "$FN" >/dev/null || \
+ die "Tor didn't print an expiration"
+
+ mv "${DATA_DIR}/keys/ed25519_signing_cert.bak" \
+ "${DATA_DIR}/keys/ed25519_signing_cert"
+
+ echo "==== Case 3: ok"
+fi
diff --git a/src/test/test_keypin.c b/src/test/test_keypin.c
index d2ec8e9ca7..79d7bac902 100644
--- a/src/test/test_keypin.c
+++ b/src/test/test_keypin.c
@@ -20,8 +20,8 @@ test_keypin_parse_line(void *arg)
"aGVyZSBpcyBhIGdvb2Qgc2hhMSE "
"VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4");
tt_assert(ent);
- tt_mem_op(ent->rsa_id, ==, "here is a good sha1!", 20);
- tt_mem_op(ent->ed25519_key, ==, "This ed25519 scoffs at the sha1.", 32);
+ tt_mem_op(ent->rsa_id, OP_EQ, "here is a good sha1!", 20);
+ tt_mem_op(ent->ed25519_key, OP_EQ, "This ed25519 scoffs at the sha1.", 32);
tor_free(ent); ent = NULL;
/* Good line with extra stuff we will ignore. */
@@ -29,27 +29,27 @@ test_keypin_parse_line(void *arg)
"aGVyZSBpcyBhIGdvb2Qgc2hhMSE "
"VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4helloworld");
tt_assert(ent);
- tt_mem_op(ent->rsa_id, ==, "here is a good sha1!", 20);
- tt_mem_op(ent->ed25519_key, ==, "This ed25519 scoffs at the sha1.", 32);
+ tt_mem_op(ent->rsa_id, OP_EQ, "here is a good sha1!", 20);
+ tt_mem_op(ent->ed25519_key, OP_EQ, "This ed25519 scoffs at the sha1.", 32);
tor_free(ent); ent = NULL;
/* Bad line: no space in the middle. */
ent = keypin_parse_journal_line(
"aGVyZSBpcyBhIGdvb2Qgc2hhMSE?"
"VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4");
- tt_assert(! ent);
+ tt_ptr_op(ent, OP_EQ, NULL);
/* Bad line: bad base64 in RSA ID */
ent = keypin_parse_journal_line(
"aGVyZSBpcyBhIGdv!2Qgc2hhMSE "
"VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4");
- tt_assert(! ent);
+ tt_ptr_op(ent, OP_EQ, NULL);
/* Bad line: bad base64 in Ed25519 */
ent = keypin_parse_journal_line(
"aGVyZSBpcyBhIGdvb2Qgc2hhMSE "
"VGhpcyBlZDI1NTE5IHNjb2ZmcyB!dCB0aGUgc2hhMS4");
- tt_assert(! ent);
+ tt_ptr_op(ent, OP_EQ, NULL);
done:
tor_free(ent);
@@ -82,11 +82,11 @@ test_keypin_parse_file(void *arg)
"Z2dsZSBpbiBzd29tZWVzd2FucyA aW4gdm9sdXB0YXRlIGF4ZS1oYWNrZXIgZXNzZSByaXA\n"
"cHVsdXMgY3J1bW1paSBldSBtb28 ZiBudWxsYSBzbnV2di5QTFVHSFBMT1ZFUlhZWlpZLi4\n";
- tt_int_op(0, ==, keypin_load_journal_impl(data1, strlen(data1)));
- tt_int_op(8, ==, smartlist_len(mock_addent_got));
+ tt_int_op(0, OP_EQ, keypin_load_journal_impl(data1, strlen(data1)));
+ tt_int_op(8, OP_EQ, smartlist_len(mock_addent_got));
keypin_ent_t *ent = smartlist_get(mock_addent_got, 2);
- tt_mem_op(ent->rsa_id, ==, "r lerkim, sed do bar", 20);
- tt_mem_op(ent->ed25519_key, ==, "baloot tempor gluppitus ut labor", 32);
+ tt_mem_op(ent->rsa_id, OP_EQ, "r lerkim, sed do bar", 20);
+ tt_mem_op(ent->ed25519_key, OP_EQ, "baloot tempor gluppitus ut labor", 32);
/* More complex example: weird lines, bogus lines,
duplicate/conflicting lines */
@@ -107,24 +107,25 @@ test_keypin_parse_file(void *arg)
"ZHMgc3BlYWsgdHJ1dGgsIGFuZCA aXQgd2FzIHRydaUgdGhhdCBhbGwgdGhlIG1hc3Rlcgo\n"
;
- tt_int_op(0, ==, keypin_load_journal_impl(data2, strlen(data2)));
- tt_int_op(13, ==, smartlist_len(mock_addent_got));
+ tt_int_op(0, OP_EQ, keypin_load_journal_impl(data2, strlen(data2)));
+ tt_int_op(13, OP_EQ, smartlist_len(mock_addent_got));
ent = smartlist_get(mock_addent_got, 9);
- tt_mem_op(ent->rsa_id, ==, "\"You have made a goo", 20);
- tt_mem_op(ent->ed25519_key, ==, "d beginning.\" But no more. Wizar", 32);
+ tt_mem_op(ent->rsa_id, OP_EQ, "\"You have made a goo", 20);
+ tt_mem_op(ent->ed25519_key, OP_EQ, "d beginning.\" But no more. Wizar", 32);
ent = smartlist_get(mock_addent_got, 12);
- tt_mem_op(ent->rsa_id, ==, "ds speak truth, and ", 20);
- tt_mem_op(ent->ed25519_key, ==, "it was tru\xa5 that all the master\n", 32);
+ tt_mem_op(ent->rsa_id, OP_EQ, "ds speak truth, and ", 20);
+ tt_mem_op(ent->ed25519_key, OP_EQ,
+ "it was tru\xa5 that all the master\n", 32);
/* File truncated before NL */
const char data3[] =
"Tm8gZHJhZ29uIGNhbiByZXNpc3Q IHRoZSBmYXNjaW5hdGlvbiBvZiByaWRkbGluZyB0YWw";
- tt_int_op(0, ==, keypin_load_journal_impl(data3, strlen(data3)));
- tt_int_op(14, ==, smartlist_len(mock_addent_got));
+ tt_int_op(0, OP_EQ, keypin_load_journal_impl(data3, strlen(data3)));
+ tt_int_op(14, OP_EQ, smartlist_len(mock_addent_got));
ent = smartlist_get(mock_addent_got, 13);
- tt_mem_op(ent->rsa_id, ==, "No dragon can resist", 20);
- tt_mem_op(ent->ed25519_key, ==, " the fascination of riddling tal", 32);
+ tt_mem_op(ent->rsa_id, OP_EQ, "No dragon can resist", 20);
+ tt_mem_op(ent->ed25519_key, OP_EQ, " the fascination of riddling tal", 32);
done:
keypin_clear();
@@ -141,32 +142,32 @@ test_keypin_add_entry(void *arg)
(void)arg;
keypin_clear();
- tt_int_op(KEYPIN_ADDED, ==, ADD("ambassadors-at-large",
+ tt_int_op(KEYPIN_ADDED, OP_EQ, ADD("ambassadors-at-large",
"bread-and-butter thing-in-itself"));
- tt_int_op(KEYPIN_ADDED, ==, ADD("gentleman-adventurer",
+ tt_int_op(KEYPIN_ADDED, OP_EQ, ADD("gentleman-adventurer",
"cloak-and-dagger what's-his-face"));
- tt_int_op(KEYPIN_FOUND, ==, ADD("ambassadors-at-large",
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("ambassadors-at-large",
"bread-and-butter thing-in-itself"));
- tt_int_op(KEYPIN_FOUND, ==, ADD("ambassadors-at-large",
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("ambassadors-at-large",
"bread-and-butter thing-in-itself"));
- tt_int_op(KEYPIN_FOUND, ==, ADD("gentleman-adventurer",
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("gentleman-adventurer",
"cloak-and-dagger what's-his-face"));
- tt_int_op(KEYPIN_ADDED, ==, ADD("Johnnies-come-lately",
+ tt_int_op(KEYPIN_ADDED, OP_EQ, ADD("Johnnies-come-lately",
"run-of-the-mill root-mean-square"));
- tt_int_op(KEYPIN_MISMATCH, ==, ADD("gentleman-adventurer",
+ tt_int_op(KEYPIN_MISMATCH, OP_EQ, ADD("gentleman-adventurer",
"hypersentimental closefistedness"));
- tt_int_op(KEYPIN_MISMATCH, ==, ADD("disestablismentarian",
+ tt_int_op(KEYPIN_MISMATCH, OP_EQ, ADD("disestablismentarian",
"cloak-and-dagger what's-his-face"));
- tt_int_op(KEYPIN_FOUND, ==, ADD("gentleman-adventurer",
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("gentleman-adventurer",
"cloak-and-dagger what's-his-face"));
- tt_int_op(KEYPIN_NOT_FOUND, ==, LONE_RSA("Llanfairpwllgwyngyll"));
- tt_int_op(KEYPIN_MISMATCH, ==, LONE_RSA("Johnnies-come-lately"));
+ tt_int_op(KEYPIN_NOT_FOUND, OP_EQ, LONE_RSA("Llanfairpwllgwyngyll"));
+ tt_int_op(KEYPIN_MISMATCH, OP_EQ, LONE_RSA("Johnnies-come-lately"));
done:
keypin_clear();
@@ -179,51 +180,51 @@ test_keypin_journal(void *arg)
char *contents = NULL;
const char *fname = get_fname("keypin-journal");
- tt_int_op(0, ==, keypin_load_journal(fname)); /* ENOENT is okay */
+ tt_int_op(0, OP_EQ, keypin_load_journal(fname)); /* ENOENT is okay */
update_approx_time(1217709000);
- tt_int_op(0, ==, keypin_open_journal(fname));
+ tt_int_op(0, OP_EQ, keypin_open_journal(fname));
- tt_int_op(KEYPIN_ADDED, ==, ADD("king-of-the-herrings",
+ tt_int_op(KEYPIN_ADDED, OP_EQ, ADD("king-of-the-herrings",
"good-for-nothing attorney-at-law"));
- tt_int_op(KEYPIN_ADDED, ==, ADD("yellowish-red-yellow",
+ tt_int_op(KEYPIN_ADDED, OP_EQ, ADD("yellowish-red-yellow",
"salt-and-pepper high-muck-a-muck"));
- tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow",
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("yellowish-red-yellow",
"salt-and-pepper high-muck-a-muck"));
keypin_close_journal();
keypin_clear();
- tt_int_op(0, ==, keypin_load_journal(fname));
+ tt_int_op(0, OP_EQ, keypin_load_journal(fname));
update_approx_time(1231041600);
- tt_int_op(0, ==, keypin_open_journal(fname));
- tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow",
+ tt_int_op(0, OP_EQ, keypin_open_journal(fname));
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("yellowish-red-yellow",
"salt-and-pepper high-muck-a-muck"));
- tt_int_op(KEYPIN_ADDED, ==, ADD("theatre-in-the-round",
+ tt_int_op(KEYPIN_ADDED, OP_EQ, ADD("theatre-in-the-round",
"holier-than-thou jack-in-the-box"));
- tt_int_op(KEYPIN_ADDED, ==, ADD("no-deposit-no-return",
+ tt_int_op(KEYPIN_ADDED, OP_EQ, ADD("no-deposit-no-return",
"across-the-board will-o-the-wisp"));
- tt_int_op(KEYPIN_MISMATCH, ==, ADD("intellectualizations",
+ tt_int_op(KEYPIN_MISMATCH, OP_EQ, ADD("intellectualizations",
"salt-and-pepper high-muck-a-muck"));
keypin_close_journal();
keypin_clear();
- tt_int_op(0, ==, keypin_load_journal(fname));
+ tt_int_op(0, OP_EQ, keypin_load_journal(fname));
update_approx_time(1412278354);
- tt_int_op(0, ==, keypin_open_journal(fname));
- tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow",
+ tt_int_op(0, OP_EQ, keypin_open_journal(fname));
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("yellowish-red-yellow",
"salt-and-pepper high-muck-a-muck"));
- tt_int_op(KEYPIN_MISMATCH, ==, ADD("intellectualizations",
+ tt_int_op(KEYPIN_MISMATCH, OP_EQ, ADD("intellectualizations",
"salt-and-pepper high-muck-a-muck"));
- tt_int_op(KEYPIN_FOUND, ==, ADD("theatre-in-the-round",
+ tt_int_op(KEYPIN_FOUND, OP_EQ, ADD("theatre-in-the-round",
"holier-than-thou jack-in-the-box"));
- tt_int_op(KEYPIN_MISMATCH, ==, ADD("counterrevolutionary",
+ tt_int_op(KEYPIN_MISMATCH, OP_EQ, ADD("counterrevolutionary",
"holier-than-thou jack-in-the-box"));
- tt_int_op(KEYPIN_MISMATCH, ==, ADD("no-deposit-no-return",
+ tt_int_op(KEYPIN_MISMATCH, OP_EQ, ADD("no-deposit-no-return",
"floccinaucinihilipilificationism"));
keypin_close_journal();
contents = read_file_to_str(fname, RFTS_BIN, NULL);
tt_assert(contents);
- tt_str_op(contents,==,
+ tt_str_op(contents,OP_EQ,
"\n"
"@opened-at 2008-08-02 20:30:00\n"
"a2luZy1vZi10aGUtaGVycmluZ3M Z29vZC1mb3Itbm90aGluZyBhdHRvcm5leS1hdC1sYXc\n"
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index c5508b0f04..422d419078 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -136,7 +136,7 @@ test_link_handshake_certs_ok(void *arg)
* actually generate a CERTS cell.
*/
tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
- key1, key2, 86400), ==, 0);
+ key1, key2, 86400), OP_EQ, 0);
if (with_ed) {
/* If we're making a CERTS cell for an ed handshake, let's make sure we
@@ -155,63 +155,63 @@ test_link_handshake_certs_ok(void *arg)
c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
c1->link_proto = 3;
- tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0);
+ tt_int_op(connection_init_or_handshake_state(c1, 1), OP_EQ, 0);
/* c2 has started_here == 0 */
c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
c2->link_proto = 3;
- tt_int_op(connection_init_or_handshake_state(c2, 0), ==, 0);
+ tt_int_op(connection_init_or_handshake_state(c2, 0), OP_EQ, 0);
- tt_int_op(0, ==, connection_or_send_certs_cell(c1));
+ tt_int_op(0, OP_EQ, connection_or_send_certs_cell(c1));
tt_assert(mock_got_var_cell);
cell1 = mock_got_var_cell;
- tt_int_op(0, ==, connection_or_send_certs_cell(c2));
+ tt_int_op(0, OP_EQ, connection_or_send_certs_cell(c2));
tt_assert(mock_got_var_cell);
cell2 = mock_got_var_cell;
- tt_int_op(cell1->command, ==, CELL_CERTS);
- tt_int_op(cell1->payload_len, >, 1);
+ tt_int_op(cell1->command, OP_EQ, CELL_CERTS);
+ tt_int_op(cell1->payload_len, OP_GT, 1);
- tt_int_op(cell2->command, ==, CELL_CERTS);
- tt_int_op(cell2->payload_len, >, 1);
+ tt_int_op(cell2->command, OP_EQ, CELL_CERTS);
+ tt_int_op(cell2->payload_len, OP_GT, 1);
- tt_int_op(cell1->payload_len, ==,
+ tt_int_op(cell1->payload_len, OP_EQ,
certs_cell_parse(&cc1, cell1->payload, cell1->payload_len));
- tt_int_op(cell2->payload_len, ==,
+ tt_int_op(cell2->payload_len, OP_EQ,
certs_cell_parse(&cc2, cell2->payload, cell2->payload_len));
if (with_ed) {
- tt_int_op(5, ==, cc1->n_certs);
- tt_int_op(5, ==, cc2->n_certs);
+ tt_int_op(5, OP_EQ, cc1->n_certs);
+ tt_int_op(5, OP_EQ, cc2->n_certs);
} else {
- tt_int_op(2, ==, cc1->n_certs);
- tt_int_op(2, ==, cc2->n_certs);
+ tt_int_op(2, OP_EQ, cc1->n_certs);
+ tt_int_op(2, OP_EQ, cc2->n_certs);
}
- tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, OP_EQ,
CERTTYPE_RSA1024_ID_AUTH);
- tt_int_op(certs_cell_get_certs(cc1, 1)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc1, 1)->cert_type, OP_EQ,
CERTTYPE_RSA1024_ID_ID);
- tt_int_op(certs_cell_get_certs(cc2, 0)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc2, 0)->cert_type, OP_EQ,
CERTTYPE_RSA1024_ID_LINK);
- tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, OP_EQ,
CERTTYPE_RSA1024_ID_ID);
if (with_ed) {
- tt_int_op(certs_cell_get_certs(cc1, 2)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc1, 2)->cert_type, OP_EQ,
CERTTYPE_ED_ID_SIGN);
- tt_int_op(certs_cell_get_certs(cc1, 3)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc1, 3)->cert_type, OP_EQ,
CERTTYPE_ED_SIGN_AUTH);
- tt_int_op(certs_cell_get_certs(cc1, 4)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc1, 4)->cert_type, OP_EQ,
CERTTYPE_RSA1024_ID_EDID);
- tt_int_op(certs_cell_get_certs(cc2, 2)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc2, 2)->cert_type, OP_EQ,
CERTTYPE_ED_ID_SIGN);
- tt_int_op(certs_cell_get_certs(cc2, 3)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc2, 3)->cert_type, OP_EQ,
CERTTYPE_ED_SIGN_LINK);
- tt_int_op(certs_cell_get_certs(cc2, 4)->cert_type, ==,
+ tt_int_op(certs_cell_get_certs(cc2, 4)->cert_type, OP_EQ,
CERTTYPE_RSA1024_ID_EDID);
}
@@ -240,8 +240,8 @@ test_link_handshake_certs_ok(void *arg)
tor_assert(c1->handshake_state->authenticated);
tt_assert(c1->handshake_state->received_certs_cell);
- tt_assert(c1->handshake_state->certs->auth_cert == NULL);
- tt_assert(c1->handshake_state->certs->ed_sign_auth == NULL);
+ tt_ptr_op(c1->handshake_state->certs->auth_cert, OP_EQ, NULL);
+ tt_ptr_op(c1->handshake_state->certs->ed_sign_auth, OP_EQ, NULL);
tt_assert(c1->handshake_state->certs->id_cert);
if (with_ed) {
tt_assert(c1->handshake_state->certs->ed_sign_link);
@@ -250,9 +250,9 @@ test_link_handshake_certs_ok(void *arg)
tt_assert(c1->handshake_state->authenticated_rsa);
tt_assert(c1->handshake_state->authenticated_ed25519);
} else {
- tt_assert(c1->handshake_state->certs->ed_sign_link == NULL);
- tt_assert(c1->handshake_state->certs->ed_rsa_crosscert == NULL);
- tt_assert(c1->handshake_state->certs->ed_id_sign == NULL);
+ tt_ptr_op(c1->handshake_state->certs->ed_sign_link, OP_EQ, NULL);
+ tt_ptr_op(c1->handshake_state->certs->ed_rsa_crosscert, OP_EQ, NULL);
+ tt_ptr_op(c1->handshake_state->certs->ed_id_sign, OP_EQ, NULL);
tt_assert(c1->handshake_state->authenticated_rsa);
tt_assert(! c1->handshake_state->authenticated_ed25519);
}
@@ -278,9 +278,9 @@ test_link_handshake_certs_ok(void *arg)
tt_assert(c2->handshake_state->certs->ed_id_sign);
} else {
tt_assert(c2->handshake_state->certs->auth_cert);
- tt_assert(c2->handshake_state->certs->ed_sign_auth == NULL);
- tt_assert(c2->handshake_state->certs->ed_rsa_crosscert == NULL);
- tt_assert(c2->handshake_state->certs->ed_id_sign == NULL);
+ tt_ptr_op(c2->handshake_state->certs->ed_sign_auth, OP_EQ, NULL);
+ tt_ptr_op(c2->handshake_state->certs->ed_rsa_crosscert, OP_EQ, NULL);
+ tt_ptr_op(c2->handshake_state->certs->ed_id_sign, OP_EQ, NULL);
}
tt_assert(c2->handshake_state->certs->id_cert);
tt_assert(tor_mem_is_zero(
@@ -376,14 +376,14 @@ recv_certs_setup(const struct testcase_t *test)
tor_addr_from_ipv4h(&d->c->base_.addr, 0x801f0127);
d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
d->chan->conn = d->c;
- tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0);
+ tt_int_op(connection_init_or_handshake_state(d->c, 1), OP_EQ, 0);
d->c->link_proto = 4;
d->key1 = pk_generate(2);
d->key2 = pk_generate(3);
tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
- d->key1, d->key2, 86400), ==, 0);
+ d->key1, d->key2, 86400), OP_EQ, 0);
if (is_ed) {
init_mock_ed_keys(d->key2);
} else {
@@ -452,7 +452,7 @@ recv_certs_setup(const struct testcase_t *test)
d->cell->command = CELL_CERTS;
n = certs_cell_encode(d->cell->payload, 4096, d->ccell);
- tt_int_op(n, >, 0);
+ tt_int_op(n, OP_GT, 0);
d->cell->payload_len = n;
MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
@@ -465,9 +465,9 @@ recv_certs_setup(const struct testcase_t *test)
mock_peer_cert = tor_x509_cert_dup(a);
}
- tt_int_op(0, ==, d->c->handshake_state->received_certs_cell);
- tt_int_op(0, ==, mock_send_authenticate_called);
- tt_int_op(0, ==, mock_send_netinfo_called);
+ tt_int_op(0, OP_EQ, d->c->handshake_state->received_certs_cell);
+ tt_int_op(0, OP_EQ, mock_send_authenticate_called);
+ tt_int_op(0, OP_EQ, mock_send_netinfo_called);
return d;
done:
@@ -485,25 +485,25 @@ test_link_handshake_recv_certs_ok(void *arg)
{
certs_data_t *d = arg;
channel_tls_process_certs_cell(d->cell, d->chan);
- tt_int_op(0, ==, mock_close_called);
- tt_int_op(d->c->handshake_state->authenticated, ==, 1);
- tt_int_op(d->c->handshake_state->authenticated_rsa, ==, 1);
- tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1);
- tt_assert(d->c->handshake_state->certs->id_cert != NULL);
- tt_assert(d->c->handshake_state->certs->auth_cert == NULL);
+ tt_int_op(0, OP_EQ, mock_close_called);
+ tt_int_op(d->c->handshake_state->authenticated, OP_EQ, 1);
+ tt_int_op(d->c->handshake_state->authenticated_rsa, OP_EQ, 1);
+ tt_int_op(d->c->handshake_state->received_certs_cell, OP_EQ, 1);
+ tt_ptr_op(d->c->handshake_state->certs->id_cert, OP_NE, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->auth_cert, OP_EQ, NULL);
if (d->is_ed) {
- tt_assert(d->c->handshake_state->certs->ed_id_sign != NULL);
- tt_assert(d->c->handshake_state->certs->ed_sign_link != NULL);
- tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL);
- tt_assert(d->c->handshake_state->certs->ed_rsa_crosscert != NULL);
- tt_int_op(d->c->handshake_state->authenticated_ed25519, ==, 1);
+ tt_ptr_op(d->c->handshake_state->certs->ed_id_sign, OP_NE, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_sign_link, OP_NE, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_sign_auth, OP_EQ, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_rsa_crosscert, OP_NE, NULL);
+ tt_int_op(d->c->handshake_state->authenticated_ed25519, OP_EQ, 1);
} else {
- tt_assert(d->c->handshake_state->certs->ed_id_sign == NULL);
- tt_assert(d->c->handshake_state->certs->ed_sign_link == NULL);
- tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL);
- tt_assert(d->c->handshake_state->certs->ed_rsa_crosscert == NULL);
- tt_int_op(d->c->handshake_state->authenticated_ed25519, ==, 0);
+ tt_ptr_op(d->c->handshake_state->certs->ed_id_sign, OP_EQ, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_sign_link, OP_EQ, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_sign_auth, OP_EQ, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_rsa_crosscert, OP_EQ, NULL);
+ tt_int_op(d->c->handshake_state->authenticated_ed25519, OP_EQ, 0);
}
done:
@@ -517,17 +517,17 @@ test_link_handshake_recv_certs_ok_server(void *arg)
d->c->handshake_state->started_here = 0;
d->c->handshake_state->certs->started_here = 0;
channel_tls_process_certs_cell(d->cell, d->chan);
- tt_int_op(0, ==, mock_close_called);
- tt_int_op(d->c->handshake_state->authenticated, ==, 0);
- tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1);
- tt_assert(d->c->handshake_state->certs->id_cert != NULL);
- tt_assert(d->c->handshake_state->certs->link_cert == NULL);
+ tt_int_op(0, OP_EQ, mock_close_called);
+ tt_int_op(d->c->handshake_state->authenticated, OP_EQ, 0);
+ tt_int_op(d->c->handshake_state->received_certs_cell, OP_EQ, 1);
+ tt_ptr_op(d->c->handshake_state->certs->id_cert, OP_NE, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->link_cert, OP_EQ, NULL);
if (d->is_ed) {
- tt_assert(d->c->handshake_state->certs->ed_sign_auth != NULL);
- tt_assert(d->c->handshake_state->certs->auth_cert == NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_sign_auth, OP_NE, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->auth_cert, OP_EQ, NULL);
} else {
- tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL);
- tt_assert(d->c->handshake_state->certs->auth_cert != NULL);
+ tt_ptr_op(d->c->handshake_state->certs->ed_sign_auth, OP_EQ, NULL);
+ tt_ptr_op(d->c->handshake_state->certs->auth_cert, OP_NE, NULL);
}
done:
@@ -543,11 +543,11 @@ test_link_handshake_recv_certs_ok_server(void *arg)
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); \
- tt_int_op(0, ==, d->c->handshake_state->authenticated_rsa); \
- tt_int_op(0, ==, d->c->handshake_state->authenticated_ed25519); \
+ tt_int_op(1, OP_EQ, mock_close_called); \
+ tt_int_op(0, OP_EQ, mock_send_authenticate_called); \
+ tt_int_op(0, OP_EQ, mock_send_netinfo_called); \
+ tt_int_op(0, OP_EQ, d->c->handshake_state->authenticated_rsa); \
+ tt_int_op(0, OP_EQ, d->c->handshake_state->authenticated_ed25519); \
if (require_failure_message) { \
expect_log_msg_containing(require_failure_message); \
} \
@@ -603,7 +603,7 @@ CERTS_FAIL(truncated_5, /* ed25519 */
const char *msg = certs_cell_check(d->ccell); \
if (msg) puts(msg); \
ssize_t n = certs_cell_encode(d->cell->payload, 4096, d->ccell); \
- tt_int_op(n, >, 0); \
+ tt_int_op(n, OP_GT, 0); \
d->cell->payload_len = n; \
} while (0)
@@ -686,9 +686,9 @@ test_link_handshake_recv_certs_missing_id(void *arg) /* ed25519 */
/* This handshake succeeds, but since we have no ID cert, we will
* just do the RSA handshake. */
channel_tls_process_certs_cell(d->cell, d->chan);
- tt_int_op(0, ==, mock_close_called);
- tt_int_op(0, ==, d->c->handshake_state->authenticated_ed25519);
- tt_int_op(1, ==, d->c->handshake_state->authenticated_rsa);
+ tt_int_op(0, OP_EQ, mock_close_called);
+ tt_int_op(0, OP_EQ, d->c->handshake_state->authenticated_ed25519);
+ tt_int_op(1, OP_EQ, d->c->handshake_state->authenticated_rsa);
done:
;
}
@@ -697,7 +697,7 @@ CERTS_FAIL(missing_signing_key, /* ed25519 */
require_failure_message = "No Ed25519 signing key";
tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5);
certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 2);
- tt_int_op(cert->cert_type, ==, CERTTYPE_ED_ID_SIGN);
+ tt_int_op(cert->cert_type, OP_EQ, CERTTYPE_ED_ID_SIGN);
/* replace this with a valid master->signing cert, but with no
* signing key. */
const ed25519_keypair_t *mk = get_master_identity_keypair();
@@ -905,28 +905,28 @@ test_link_handshake_send_authchallenge(void *arg)
crypto_pk_t *rsa0 = pk_generate(0), *rsa1 = pk_generate(1);
tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
- rsa0, rsa1, 86400), ==, 0);
+ rsa0, rsa1, 86400), OP_EQ, 0);
init_mock_ed_keys(rsa0);
MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell);
- tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0);
+ tt_int_op(connection_init_or_handshake_state(c1, 0), OP_EQ, 0);
c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
- tt_assert(! mock_got_var_cell);
- tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1));
+ tt_ptr_op(mock_got_var_cell, OP_EQ, NULL);
+ tt_int_op(0, OP_EQ, connection_or_send_auth_challenge_cell(c1));
cell1 = mock_got_var_cell;
- tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1));
+ tt_int_op(0, OP_EQ, connection_or_send_auth_challenge_cell(c1));
cell2 = mock_got_var_cell;
- tt_int_op(38, ==, cell1->payload_len);
- tt_int_op(38, ==, cell2->payload_len);
- tt_int_op(0, ==, cell1->circ_id);
- tt_int_op(0, ==, cell2->circ_id);
- tt_int_op(CELL_AUTH_CHALLENGE, ==, cell1->command);
- tt_int_op(CELL_AUTH_CHALLENGE, ==, cell2->command);
+ tt_int_op(38, OP_EQ, cell1->payload_len);
+ tt_int_op(38, OP_EQ, cell2->payload_len);
+ tt_int_op(0, OP_EQ, cell1->circ_id);
+ tt_int_op(0, OP_EQ, cell2->circ_id);
+ tt_int_op(CELL_AUTH_CHALLENGE, OP_EQ, cell1->command);
+ tt_int_op(CELL_AUTH_CHALLENGE, OP_EQ, cell2->command);
- tt_mem_op("\x00\x02\x00\x01\x00\x03", ==, cell1->payload + 32, 6);
- tt_mem_op("\x00\x02\x00\x01\x00\x03", ==, cell2->payload + 32, 6);
- tt_mem_op(cell1->payload, !=, cell2->payload, 32);
+ tt_mem_op("\x00\x02\x00\x01\x00\x03", OP_EQ, cell1->payload + 32, 6);
+ tt_mem_op("\x00\x02\x00\x01\x00\x03", OP_EQ, cell2->payload + 32, 6);
+ tt_mem_op(cell1->payload, OP_NE, cell2->payload, 32);
done:
UNMOCK(connection_or_write_var_cell_to_buf);
@@ -974,7 +974,7 @@ recv_authchallenge_setup(const struct testcase_t *test)
d->c->base_.address = tor_strdup("HaveAnAddress");
d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
d->chan->conn = d->c;
- tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0);
+ tt_int_op(connection_init_or_handshake_state(d->c, 1), OP_EQ, 0);
d->c->link_proto = 4;
d->c->handshake_state->received_certs_cell = 1;
d->cell = var_cell_new(128);
@@ -989,9 +989,9 @@ recv_authchallenge_setup(const struct testcase_t *test)
MOCK(connection_or_close_for_error, mock_close_for_err);
MOCK(connection_or_send_netinfo, mock_send_netinfo);
MOCK(connection_or_send_authenticate_cell, mock_send_authenticate);
- tt_int_op(0, ==, d->c->handshake_state->received_auth_challenge);
- tt_int_op(0, ==, mock_send_authenticate_called);
- tt_int_op(0, ==, mock_send_netinfo_called);
+ tt_int_op(0, OP_EQ, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(0, OP_EQ, mock_send_authenticate_called);
+ tt_int_op(0, OP_EQ, mock_send_netinfo_called);
return d;
done:
@@ -1010,11 +1010,11 @@ test_link_handshake_recv_authchallenge_ok(void *arg)
authchallenge_data_t *d = arg;
channel_tls_process_auth_challenge_cell(d->cell, d->chan);
- tt_int_op(0, ==, mock_close_called);
- tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge);
- tt_int_op(1, ==, mock_send_authenticate_called);
- tt_int_op(1, ==, mock_send_netinfo_called);
- tt_int_op(1, ==, mock_send_authenticate_called_with_type); /* RSA */
+ tt_int_op(0, OP_EQ, mock_close_called);
+ tt_int_op(1, OP_EQ, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(1, OP_EQ, mock_send_authenticate_called);
+ tt_int_op(1, OP_EQ, mock_send_netinfo_called);
+ tt_int_op(1, OP_EQ, mock_send_authenticate_called_with_type); /* RSA */
done:
;
}
@@ -1029,11 +1029,11 @@ test_link_handshake_recv_authchallenge_ok_ed25519(void *arg)
d->cell->payload[39] = 3;
d->cell->payload_len += 2;
channel_tls_process_auth_challenge_cell(d->cell, d->chan);
- tt_int_op(0, ==, mock_close_called);
- tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge);
- tt_int_op(1, ==, mock_send_authenticate_called);
- tt_int_op(1, ==, mock_send_netinfo_called);
- tt_int_op(3, ==, mock_send_authenticate_called_with_type); /* Ed25519 */
+ tt_int_op(0, OP_EQ, mock_close_called);
+ tt_int_op(1, OP_EQ, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(1, OP_EQ, mock_send_authenticate_called);
+ tt_int_op(1, OP_EQ, mock_send_netinfo_called);
+ tt_int_op(3, OP_EQ, mock_send_authenticate_called_with_type); /* Ed25519 */
done:
;
}
@@ -1045,10 +1045,10 @@ test_link_handshake_recv_authchallenge_ok_noserver(void *arg)
get_options_mutable()->ORPort_set = 0;
channel_tls_process_auth_challenge_cell(d->cell, d->chan);
- tt_int_op(0, ==, mock_close_called);
- tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge);
- tt_int_op(0, ==, mock_send_authenticate_called);
- tt_int_op(0, ==, mock_send_netinfo_called);
+ tt_int_op(0, OP_EQ, mock_close_called);
+ tt_int_op(1, OP_EQ, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(0, OP_EQ, mock_send_authenticate_called);
+ tt_int_op(0, OP_EQ, mock_send_netinfo_called);
done:
;
}
@@ -1060,10 +1060,10 @@ test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg)
d->cell->payload[37] = 99;
channel_tls_process_auth_challenge_cell(d->cell, d->chan);
- tt_int_op(0, ==, mock_close_called);
- tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge);
- tt_int_op(0, ==, mock_send_authenticate_called);
- tt_int_op(1, ==, mock_send_netinfo_called);
+ tt_int_op(0, OP_EQ, mock_close_called);
+ tt_int_op(1, OP_EQ, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(0, OP_EQ, mock_send_authenticate_called);
+ tt_int_op(1, OP_EQ, mock_send_netinfo_called);
done:
;
}
@@ -1077,9 +1077,9 @@ test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg)
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); \
+ tt_int_op(1, OP_EQ, mock_close_called); \
+ tt_int_op(0, OP_EQ, mock_send_authenticate_called); \
+ tt_int_op(0, OP_EQ, mock_send_netinfo_called); \
if (require_failure_message) { \
expect_log_msg_containing(require_failure_message); \
} \
@@ -1197,17 +1197,17 @@ authenticate_data_setup(const struct testcase_t *test)
d->key1 = pk_generate(2);
d->key2 = pk_generate(3);
tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
- d->key1, d->key2, 86400), ==, 0);
+ d->key1, d->key2, 86400), OP_EQ, 0);
init_mock_ed_keys(d->key2);
d->c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
d->c1->link_proto = 3;
- tt_int_op(connection_init_or_handshake_state(d->c1, 1), ==, 0);
+ tt_int_op(connection_init_or_handshake_state(d->c1, 1), OP_EQ, 0);
d->c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
d->c2->link_proto = 3;
- tt_int_op(connection_init_or_handshake_state(d->c2, 0), ==, 0);
+ tt_int_op(connection_init_or_handshake_state(d->c2, 0), OP_EQ, 0);
var_cell_t *cell = var_cell_new(16);
cell->command = CELL_CERTS;
or_handshake_state_record_var_cell(d->c1, d->c1->handshake_state, cell, 1);
@@ -1260,7 +1260,7 @@ authenticate_data_setup(const struct testcase_t *test)
authtype = AUTHTYPE_ED25519_SHA256_RFC5705;
else
authtype = AUTHTYPE_RSA_SHA256_TLSSECRET;
- tt_int_op(0, ==, connection_or_send_authenticate_cell(d->c1, authtype));
+ tt_int_op(0, OP_EQ, connection_or_send_authenticate_cell(d->c1, authtype));
tt_assert(mock_got_var_cell);
d->cell = mock_got_var_cell;
@@ -1285,65 +1285,65 @@ test_link_handshake_auth_cell(void *arg)
crypto_pk_t *auth_pubkey = NULL;
/* Is the cell well-formed on the outer layer? */
- tt_int_op(d->cell->command, ==, CELL_AUTHENTICATE);
- tt_int_op(d->cell->payload[0], ==, 0);
+ tt_int_op(d->cell->command, OP_EQ, CELL_AUTHENTICATE);
+ tt_int_op(d->cell->payload[0], OP_EQ, 0);
if (d->is_ed)
- tt_int_op(d->cell->payload[1], ==, 3);
+ tt_int_op(d->cell->payload[1], OP_EQ, 3);
else
- tt_int_op(d->cell->payload[1], ==, 1);
- tt_int_op(ntohs(get_uint16(d->cell->payload + 2)), ==,
+ tt_int_op(d->cell->payload[1], OP_EQ, 1);
+ tt_int_op(ntohs(get_uint16(d->cell->payload + 2)), OP_EQ,
d->cell->payload_len - 4);
/* Check it out for plausibility... */
auth_ctx_t ctx;
ctx.is_ed = d->is_ed;
- tt_int_op(d->cell->payload_len-4, ==, auth1_parse(&auth1,
+ tt_int_op(d->cell->payload_len-4, OP_EQ, auth1_parse(&auth1,
d->cell->payload+4,
d->cell->payload_len - 4, &ctx));
tt_assert(auth1);
if (d->is_ed) {
- tt_mem_op(auth1->type, ==, "AUTH0003", 8);
+ tt_mem_op(auth1->type, OP_EQ, "AUTH0003", 8);
} else {
- tt_mem_op(auth1->type, ==, "AUTH0001", 8);
+ tt_mem_op(auth1->type, OP_EQ, "AUTH0001", 8);
}
- tt_mem_op(auth1->tlssecrets, ==, "int getRandomNumber(){return 4;}", 32);
+ tt_mem_op(auth1->tlssecrets, OP_EQ, "int getRandomNumber(){return 4;}", 32);
/* Is the signature okay? */
const uint8_t *start = d->cell->payload+4, *end = auth1->end_of_signed;
if (d->is_ed) {
ed25519_signature_t sig;
- tt_int_op(auth1_getlen_sig(auth1), ==, ED25519_SIG_LEN);
+ tt_int_op(auth1_getlen_sig(auth1), OP_EQ, ED25519_SIG_LEN);
memcpy(&sig.sig, auth1_getarray_sig(auth1), ED25519_SIG_LEN);
tt_assert(!ed25519_checksig(&sig, start, end-start,
&get_current_auth_keypair()->pubkey));
} else {
uint8_t sig[128];
uint8_t digest[32];
- tt_int_op(auth1_getlen_sig(auth1), >, 120);
+ tt_int_op(auth1_getlen_sig(auth1), OP_GT, 120);
auth_pubkey = tor_tls_cert_get_key(
d->c2->handshake_state->certs->auth_cert);
int n = crypto_pk_public_checksig(
auth_pubkey,
(char*)sig, sizeof(sig), (char*)auth1_getarray_sig(auth1),
auth1_getlen_sig(auth1));
- tt_int_op(n, ==, 32);
+ tt_int_op(n, OP_EQ, 32);
crypto_digest256((char*)digest,
(const char*)start, end-start, DIGEST_SHA256);
- tt_mem_op(sig, ==, digest, 32);
+ tt_mem_op(sig, OP_EQ, digest, 32);
}
/* Then feed it to c2. */
- tt_int_op(d->c2->handshake_state->authenticated, ==, 0);
+ tt_int_op(d->c2->handshake_state->authenticated, OP_EQ, 0);
channel_tls_process_authenticate_cell(d->cell, d->chan2);
- tt_int_op(mock_close_called, ==, 0);
- tt_int_op(d->c2->handshake_state->authenticated, ==, 1);
+ tt_int_op(mock_close_called, OP_EQ, 0);
+ tt_int_op(d->c2->handshake_state->authenticated, OP_EQ, 1);
if (d->is_ed) {
- tt_int_op(d->c2->handshake_state->authenticated_ed25519, ==, 1);
- tt_int_op(d->c2->handshake_state->authenticated_rsa, ==, 1);
+ tt_int_op(d->c2->handshake_state->authenticated_ed25519, OP_EQ, 1);
+ tt_int_op(d->c2->handshake_state->authenticated_rsa, OP_EQ, 1);
} else {
- tt_int_op(d->c2->handshake_state->authenticated_ed25519, ==, 0);
- tt_int_op(d->c2->handshake_state->authenticated_rsa, ==, 1);
+ tt_int_op(d->c2->handshake_state->authenticated_ed25519, OP_EQ, 0);
+ tt_int_op(d->c2->handshake_state->authenticated_rsa, OP_EQ, 1);
}
done:
@@ -1359,10 +1359,10 @@ test_link_handshake_auth_cell(void *arg)
const char *require_failure_message = NULL; \
setup_capture_of_logs(LOG_INFO); \
{ code ; } \
- tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \
+ tt_int_op(d->c2->handshake_state->authenticated, OP_EQ, 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); \
+ tt_int_op(mock_close_called, OP_EQ, 1); \
+ tt_int_op(d->c2->handshake_state->authenticated, OP_EQ, 0); \
if (require_failure_message) { \
expect_log_msg_containing(require_failure_message); \
} \
@@ -1390,8 +1390,8 @@ test_link_handshake_auth_already_authenticated(void *arg)
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);
+ tt_int_op(mock_close_called, OP_EQ, 1);
+ tt_int_op(d->c2->handshake_state->authenticated, OP_EQ, 1);
expect_log_msg_containing("The peer is already authenticated");
done:
teardown_capture_of_logs();
@@ -1425,7 +1425,7 @@ AUTHENTICATE_FAIL(truncated_2,
d->cell->payload[3]++)
AUTHENTICATE_FAIL(tooshort_1,
require_failure_message = "Authenticator was too short";
- tt_int_op(d->cell->payload_len, >=, 260);
+ tt_int_op(d->cell->payload_len, OP_GE, 260);
d->cell->payload[2] -= 1;
d->cell->payload_len -= 256;)
AUTHENTICATE_FAIL(badcontent,
diff --git a/src/test/test_logging.c b/src/test/test_logging.c
index 94b3e4ea68..e373158e34 100644
--- a/src/test/test_logging.c
+++ b/src/test/test_logging.c
@@ -107,7 +107,7 @@ test_sigsafe_err(void *arg)
close(STDERR_FILENO);
content = read_file_to_str(fn, 0, NULL);
- tt_assert(content != NULL);
+ tt_ptr_op(content, OP_NE, NULL);
tor_split_lines(lines, content, (int)strlen(content));
tt_int_op(smartlist_len(lines), OP_GE, 5);
@@ -140,7 +140,7 @@ test_ratelim(void *arg)
char *msg = NULL;
msg = rate_limit_log(&ten_min, now);
- tt_assert(msg != NULL);
+ tt_ptr_op(msg, OP_NE, NULL);
tt_str_op(msg, OP_EQ, ""); /* nothing was suppressed. */
tt_int_op(ten_min.last_allowed, OP_EQ, now);
@@ -150,14 +150,14 @@ test_ratelim(void *arg)
for (i = 0; i < 9; ++i) {
now += 60; /* one minute has passed. */
msg = rate_limit_log(&ten_min, now);
- tt_assert(msg == NULL);
+ tt_ptr_op(msg, OP_EQ, NULL);
tt_int_op(ten_min.last_allowed, OP_EQ, start);
tt_int_op(ten_min.n_calls_since_last_time, OP_EQ, i + 1);
}
now += 240; /* Okay, we can be done. */
msg = rate_limit_log(&ten_min, now);
- tt_assert(msg != NULL);
+ tt_ptr_op(msg, OP_NE, NULL);
tt_str_op(msg, OP_EQ,
" [9 similar message(s) suppressed in last 600 seconds]");
done:
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index c78fda3b69..4f0ecd778b 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -19,7 +19,7 @@
#include <direct.h>
#else
#include <dirent.h>
-#endif
+#endif /* defined(_WIN32) */
static const char test_md1[] =
"onion-key\n"
@@ -464,7 +464,7 @@ test_md_generate(void *arg)
microdesc_free(md);
md = NULL;
md = dirvote_create_microdescriptor(ri, 21);
- tt_str_op(md->body, ==, test_md_18);
+ tt_str_op(md->body, OP_EQ, test_md_18);
routerinfo_free(ri);
ri = router_parse_entry_from_string(test_ri2, NULL, 0, 0, NULL, NULL);
@@ -472,12 +472,12 @@ test_md_generate(void *arg)
microdesc_free(md);
md = NULL;
md = dirvote_create_microdescriptor(ri, 18);
- tt_str_op(md->body, ==, test_md2_18);
+ tt_str_op(md->body, OP_EQ, test_md2_18);
microdesc_free(md);
md = NULL;
md = dirvote_create_microdescriptor(ri, 21);
- tt_str_op(md->body, ==, test_md2_21);
+ tt_str_op(md->body, OP_EQ, test_md2_21);
tt_assert(ed25519_pubkey_eq(md->ed25519_identity_pkey,
&ri->cache_info.signing_key_cert->signing_key));
@@ -823,14 +823,14 @@ test_md_corrupt_desc(void *arg)
"@last-listed 2015-06-22 10:00:00\n"
"onion-k\n",
NULL, SAVED_IN_JOURNAL, 0, time(NULL), NULL);
- tt_int_op(smartlist_len(sl), ==, 0);
+ tt_int_op(smartlist_len(sl), OP_EQ, 0);
smartlist_free(sl);
sl = microdescs_add_to_cache(get_microdesc_cache(),
"@last-listed 2015-06-22 10:00:00\n"
"wiggly\n",
NULL, SAVED_IN_JOURNAL, 0, time(NULL), NULL);
- tt_int_op(smartlist_len(sl), ==, 0);
+ tt_int_op(smartlist_len(sl), OP_EQ, 0);
smartlist_free(sl);
tor_asprintf(&cp, "%s\n%s", test_md1, "@foobar\nonion-wobble\n");
@@ -838,7 +838,7 @@ test_md_corrupt_desc(void *arg)
sl = microdescs_add_to_cache(get_microdesc_cache(),
cp, cp+strlen(cp),
SAVED_IN_JOURNAL, 0, time(NULL), NULL);
- tt_int_op(smartlist_len(sl), ==, 0);
+ tt_int_op(smartlist_len(sl), OP_EQ, 0);
done:
tor_free(cp);
diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c
index 256354415c..a873003d72 100644
--- a/src/test/test_nodelist.c
+++ b/src/test/test_nodelist.c
@@ -7,7 +7,9 @@
**/
#include "or.h"
+#include "networkstatus.h"
#include "nodelist.h"
+#include "torcert.h"
#include "test.h"
/** Test the case when node_get_by_id() returns NULL,
@@ -100,6 +102,107 @@ test_nodelist_node_is_dir(void *arg)
return;
}
+static networkstatus_t *dummy_ns = NULL;
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus(void)
+{
+ return dummy_ns;
+}
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
+{
+ tor_assert(f == FLAV_MICRODESC);
+ return dummy_ns;
+}
+
+static void
+test_nodelist_ed_id(void *arg)
+{
+ routerstatus_t *rs[4];
+ microdesc_t *md[4];
+ routerinfo_t *ri[4];
+ networkstatus_t *ns;
+ int i;
+ (void)arg;
+
+ ns = tor_malloc_zero(sizeof(networkstatus_t));
+ ns->flavor = FLAV_MICRODESC;
+ ns->routerstatus_list = smartlist_new();
+ dummy_ns = ns;
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+ MOCK(networkstatus_get_latest_consensus_by_flavor,
+ mock_networkstatus_get_latest_consensus_by_flavor);
+
+ /* Make a bunch of dummy objects that we can play around with. Only set the
+ necessary fields */
+
+ for (i = 0; i < 4; ++i) {
+ rs[i] = tor_malloc_zero(sizeof(*rs[i]));
+ md[i] = tor_malloc_zero(sizeof(*md[i]));
+ ri[i] = tor_malloc_zero(sizeof(*ri[i]));
+
+ crypto_rand(md[i]->digest, sizeof(md[i]->digest));
+ md[i]->ed25519_identity_pkey = tor_malloc(sizeof(ed25519_public_key_t));
+ crypto_rand((char*)md[i]->ed25519_identity_pkey,
+ sizeof(ed25519_public_key_t));
+ crypto_rand(rs[i]->identity_digest, sizeof(rs[i]->identity_digest));
+ memcpy(ri[i]->cache_info.identity_digest, rs[i]->identity_digest,
+ DIGEST_LEN);
+ memcpy(rs[i]->descriptor_digest, md[i]->digest, DIGEST256_LEN);
+ ri[i]->cache_info.signing_key_cert = tor_malloc_zero(sizeof(tor_cert_t));
+ memcpy(&ri[i]->cache_info.signing_key_cert->signing_key,
+ md[i]->ed25519_identity_pkey, sizeof(ed25519_public_key_t));
+
+ if (i != 3)
+ smartlist_add(ns->routerstatus_list, rs[i]);
+ }
+
+ tt_int_op(0, OP_EQ, smartlist_len(nodelist_get_list()));
+
+ nodelist_set_consensus(ns);
+
+ tt_int_op(3, OP_EQ, smartlist_len(nodelist_get_list()));
+
+ /* No Ed25519 info yet, so nothing has an ED id. */
+ tt_ptr_op(NULL, OP_EQ, node_get_by_ed25519_id(md[0]->ed25519_identity_pkey));
+
+ /* Register the first one by md, then look it up. */
+ node_t *n = nodelist_add_microdesc(md[0]);
+ tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[0]->ed25519_identity_pkey));
+
+ /* Register the second by ri, then look it up. */
+ routerinfo_t *ri_old = NULL;
+ n = nodelist_set_routerinfo(ri[1], &ri_old);
+ tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[1]->ed25519_identity_pkey));
+ tt_ptr_op(ri_old, OP_EQ, NULL);
+
+ /* Register it by md too. */
+ node_t *n2 = nodelist_add_microdesc(md[1]);
+ tt_ptr_op(n2, OP_EQ, n);
+ tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[1]->ed25519_identity_pkey));
+
+ /* Register the 4th by ri only -- we never put it into the networkstatus,
+ * so it has to be independent */
+ n = nodelist_set_routerinfo(ri[3], &ri_old);
+ tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[3]->ed25519_identity_pkey));
+ tt_ptr_op(ri_old, OP_EQ, NULL);
+ tt_int_op(4, OP_EQ, smartlist_len(nodelist_get_list()));
+
+ done:
+ for (i = 0; i < 4; ++i) {
+ tor_free(rs[i]);
+ tor_free(md[i]->ed25519_identity_pkey);
+ tor_free(md[i]);
+ tor_free(ri[i]->cache_info.signing_key_cert);
+ tor_free(ri[i]);
+ }
+ smartlist_clear(ns->routerstatus_list);
+ networkstatus_vote_free(ns);
+ UNMOCK(networkstatus_get_latest_consensus);
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+}
+
#define NODE(name, flags) \
{ #name, test_nodelist_##name, (flags), NULL, NULL }
@@ -107,6 +210,7 @@ struct testcase_t nodelist_tests[] = {
NODE(node_get_verbose_nickname_by_id_null_node, TT_FORK),
NODE(node_get_verbose_nickname_not_named, TT_FORK),
NODE(node_is_dir, TT_FORK),
+ NODE(ed_id, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_oom.c b/src/test/test_oom.c
index f03a504d1d..cf28690a28 100644
--- a/src/test/test_oom.c
+++ b/src/test/test_oom.c
@@ -67,7 +67,7 @@ add_bytes_to_buf(buf_t *buf, size_t n_bytes)
while (n_bytes) {
size_t this_add = n_bytes > sizeof(b) ? sizeof(b) : n_bytes;
crypto_rand(b, this_add);
- write_to_buf(b, this_add, buf);
+ buf_add(buf, b, this_add);
n_bytes -= this_add;
}
}
diff --git a/src/test/test_oos.c b/src/test/test_oos.c
index 9fd6bce5ae..e72fcf5de9 100644
--- a/src/test/test_oos.c
+++ b/src/test/test_oos.c
@@ -52,7 +52,7 @@ kill_conn_list_mock(smartlist_t *conns)
{
++kill_conn_list_calls;
- tt_assert(conns != NULL);
+ tt_ptr_op(conns, OP_NE, NULL);
kill_conn_list_killed += smartlist_len(conns);
@@ -248,7 +248,7 @@ close_for_error_mock(or_connection_t *orconn, int flush)
{
(void)flush;
- tt_assert(orconn != NULL);
+ tt_ptr_op(orconn, OP_NE, NULL);
++cfe_calls;
done:
@@ -264,7 +264,7 @@ mark_for_close_oos_mock(connection_t *conn,
(void)line;
(void)file;
- tt_assert(conn != NULL);
+ tt_ptr_op(conn, OP_NE, NULL);
++mark_calls;
done:
@@ -298,8 +298,8 @@ test_oos_kill_conn_list(void *arg)
dir_c2->base_.purpose = DIR_PURPOSE_MIN_;
c2 = TO_CONN(dir_c2);
- tt_assert(c1 != NULL);
- tt_assert(c2 != NULL);
+ tt_ptr_op(c1, OP_NE, NULL);
+ tt_ptr_op(c2, OP_NE, NULL);
/* Make list */
l = smartlist_new();
@@ -345,7 +345,7 @@ get_num_circuits_mock(or_connection_t *conn)
{
int circs = 0;
- tt_assert(conn != NULL);
+ tt_ptr_op(conn, OP_NE, NULL);
if (conns_with_circs &&
smartlist_contains(conns_with_circs, TO_CONN(conn))) {
@@ -397,7 +397,7 @@ test_oos_pick_oos_victims(void *arg)
/* Try picking one */
picked = pick_oos_victims(1);
/* It should be the one with circuits */
- tt_assert(picked != NULL);
+ tt_ptr_op(picked, OP_NE, NULL);
tt_int_op(smartlist_len(picked), OP_EQ, 1);
tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0)));
smartlist_free(picked);
@@ -405,14 +405,14 @@ test_oos_pick_oos_victims(void *arg)
/* Try picking none */
picked = pick_oos_victims(0);
/* We should get an empty list */
- tt_assert(picked != NULL);
+ tt_ptr_op(picked, OP_NE, 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_ptr_op(picked, OP_NE, 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)));
diff --git a/src/test/test_options.c b/src/test/test_options.c
index ad735b72a6..62732cabf7 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -282,7 +282,7 @@ test_have_enough_mem_for_dircache(void *arg)
or_options_t *opt=NULL;
or_options_t *dflt=NULL;
config_line_t *cl=NULL;
- char *msg=NULL;;
+ char *msg=NULL;
int r;
const char *configuration = "ORPort 8080\nDirCache 1", *expect_errmsg;
@@ -299,7 +299,7 @@ test_have_enough_mem_for_dircache(void *arg)
/* 300 MB RAM available, DirCache enabled */
r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
tt_int_op(r, OP_EQ, 0);
- tt_assert(!msg);
+ tt_ptr_op(msg, OP_EQ, NULL);
/* 200 MB RAM available, DirCache enabled */
r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
@@ -322,7 +322,7 @@ test_have_enough_mem_for_dircache(void *arg)
/* 300 MB RAM available, DirCache enabled, Bridge */
r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
tt_int_op(r, OP_EQ, 0);
- tt_assert(!msg);
+ tt_ptr_op(msg, OP_EQ, NULL);
/* 200 MB RAM available, DirCache enabled, Bridge */
r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
@@ -345,7 +345,7 @@ test_have_enough_mem_for_dircache(void *arg)
/* 200 MB RAM available, DirCache disabled */
r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(200), &msg);
tt_int_op(r, OP_EQ, 0);
- tt_assert(!msg);
+ tt_ptr_op(msg, OP_EQ, NULL);
/* 300 MB RAM available, DirCache disabled */
r = have_enough_mem_for_dircache(opt, MEGABYTEIFY(300), &msg);
@@ -398,12 +398,12 @@ fixed_get_uname(void)
"V3AuthVoteDelay 20\n" \
"V3AuthDistDelay 20\n" \
"V3AuthNIntervalsValid 3\n" \
- "ClientUseIPv4 1\n" \
+ "ClientUseIPv4 1\n" \
"VirtualAddrNetworkIPv4 127.192.0.0/10\n" \
"VirtualAddrNetworkIPv6 [FE80::]/10\n" \
- "SchedulerHighWaterMark__ 42\n" \
- "SchedulerLowWaterMark__ 10\n" \
- "UseEntryGuards 1\n"
+ "UseEntryGuards 1\n" \
+ "Schedulers Vanilla\n" \
+ "ClientDNSRejectInternalAddresses 1\n"
typedef struct {
or_options_t *old_opt;
@@ -430,24 +430,24 @@ get_options_test_data(const char *conf)
result->opt->ConnectionPadding = -1; // default must be "auto"
rv = config_get_lines(conf, &cl, 1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
rv = config_assign(&options_format, result->opt, cl, 0, &msg);
if (msg) {
/* Display the parse error message by comparing it with an empty string */
tt_str_op(msg, OP_EQ, "");
}
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
config_free_lines(cl);
result->opt->LogTimeGranularity = 1;
result->opt->TokenBucketRefillInterval = 1;
rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1);
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
rv = config_assign(&options_format, result->def_opt, cl, 0, &msg);
if (msg) {
/* Display the parse error message by comparing it with an empty string */
tt_str_op(msg, OP_EQ, "");
}
- tt_assert(rv == 0);
+ tt_int_op(rv, OP_EQ, 0);
done:
config_free_lines(cl);
@@ -507,7 +507,7 @@ test_options_validate__uname_for_server(void *ignored)
fixed_get_uname_result = "Windows 2000";
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
- expect_log_entry();
+ expect_no_log_entry();
tor_free(msg);
done:
@@ -528,6 +528,8 @@ test_options_validate__outbound_addresses(void *ignored)
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "Multiple outbound bind addresses configured: "
+ "xxyy!!!sdfaf");
done:
free_options_test_data(tdata);
@@ -591,13 +593,14 @@ test_options_validate__nickname(void *ignored)
tdata = get_options_test_data("Nickname AMoreValidNick");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
- tt_assert(!msg);
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
+ tor_free(msg);
free_options_test_data(tdata);
tdata = get_options_test_data("DataDirectory /tmp/somewhere");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
- tt_assert(!msg);
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
done:
free_options_test_data(tdata);
@@ -657,6 +660,7 @@ test_options_validate__logs(void *ignored)
tt_str_op(tdata->opt->Logs->key, OP_EQ, "Log");
tt_str_op(tdata->opt->Logs->value, OP_EQ, "notice stdout");
tor_free(msg);
+ tt_int_op(ret, OP_EQ, -1);
free_options_test_data(tdata);
tdata = get_options_test_data("");
@@ -667,6 +671,7 @@ test_options_validate__logs(void *ignored)
tt_str_op(tdata->opt->Logs->key, OP_EQ, "Log");
tt_str_op(tdata->opt->Logs->value, OP_EQ, "warn stdout");
tor_free(msg);
+ tt_int_op(ret, OP_EQ, -1);
free_options_test_data(tdata);
tdata = get_options_test_data("");
@@ -676,6 +681,7 @@ test_options_validate__logs(void *ignored)
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_assert(!tdata->opt->Logs);
tor_free(msg);
+ tt_int_op(ret, OP_EQ, -1);
free_options_test_data(tdata);
tdata = get_options_test_data("");
@@ -684,6 +690,7 @@ test_options_validate__logs(void *ignored)
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 1, &msg);
tt_assert(!tdata->opt->Logs);
tor_free(msg);
+ tt_int_op(ret, OP_EQ, -1);
free_options_test_data(tdata);
tdata = get_options_test_data("");
@@ -692,6 +699,7 @@ test_options_validate__logs(void *ignored)
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_assert(!tdata->opt->Logs);
tor_free(msg);
+ tt_int_op(ret, OP_EQ, -1);
free_options_test_data(tdata);
tdata = get_options_test_data("");
@@ -701,6 +709,7 @@ test_options_validate__logs(void *ignored)
tdata->opt->Logs = cl;
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op((intptr_t)tdata->opt->Logs, OP_EQ, (intptr_t)cl);
+ tt_int_op(ret, OP_EQ, -1);
done:
quiet_level = orig_quiet_level;
@@ -747,13 +756,13 @@ test_options_validate__authdir(void *ignored)
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
- tt_assert(!msg);
+ tt_str_op(msg, OP_EQ, "Authoritative directory servers must set "
+ "ContactInfo");
+ tor_free(msg);
free_options_test_data(tdata);
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
- "Address 100.200.10.1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "Address 100.200.10.1\n");
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -764,9 +773,7 @@ test_options_validate__authdir(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
"Address 100.200.10.1\n"
- "TestingTorNetwork 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "TestingTorNetwork 1\n");
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -777,9 +784,7 @@ test_options_validate__authdir(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
"Address 100.200.10.1\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -791,9 +796,7 @@ test_options_validate__authdir(void *ignored)
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
"Address 100.200.10.1\n"
"RecommendedVersions 1.2, 3.14\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(tdata->opt->RecommendedClientVersions->value, OP_EQ, "1.2, 3.14");
@@ -806,9 +809,7 @@ test_options_validate__authdir(void *ignored)
"RecommendedVersions 1.2, 3.14\n"
"RecommendedClientVersions 25\n"
"RecommendedServerVersions 4.18\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(tdata->opt->RecommendedClientVersions->value, OP_EQ, "25");
@@ -822,9 +823,7 @@ test_options_validate__authdir(void *ignored)
"RecommendedVersions 1.2, 3.14\n"
"RecommendedClientVersions 25\n"
"RecommendedServerVersions 4.18\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
@@ -836,9 +835,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"VersioningAuthoritativeDirectory 1\n"
"RecommendedServerVersions 4.18\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ, "Versioning authoritative dir servers must set "
@@ -850,9 +847,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"VersioningAuthoritativeDirectory 1\n"
"RecommendedClientVersions 4.18\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ, "Versioning authoritative dir servers must set "
@@ -863,9 +858,7 @@ test_options_validate__authdir(void *ignored)
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
"Address 100.200.10.1\n"
"UseEntryGuards 1\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
expect_log_msg("Authoritative directory servers "
@@ -877,9 +870,7 @@ test_options_validate__authdir(void *ignored)
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
"Address 100.200.10.1\n"
"V3AuthoritativeDir 1\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
expect_log_msg("Authoritative directories always try"
@@ -892,9 +883,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"DownloadExtraInfo 1\n"
"V3AuthoritativeDir 1\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
expect_no_log_msg("Authoritative directories always try"
@@ -905,9 +894,7 @@ test_options_validate__authdir(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
"Address 100.200.10.1\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ, "AuthoritativeDir is set, but none of (Bridge/V3)"
@@ -919,9 +906,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "V3BandwidthsFile non-existant-file\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "V3BandwidthsFile non-existant-file\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -933,9 +918,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "V3BandwidthsFile non-existant-file\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "V3BandwidthsFile non-existant-file\n");
mock_clean_saved_logs();
options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -947,9 +930,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "GuardfractionFile non-existant-file\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "GuardfractionFile non-existant-file\n");
mock_clean_saved_logs();
options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -961,9 +942,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
"ContactInfo hello@hello.com\n"
- "GuardfractionFile non-existant-file\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "GuardfractionFile non-existant-file\n");
mock_clean_saved_logs();
options_validate(NULL, tdata->opt, tdata->def_opt, 0, &msg);
tt_str_op(msg, OP_EQ,
@@ -974,9 +953,7 @@ test_options_validate__authdir(void *ignored)
tdata = get_options_test_data("AuthoritativeDirectory 1\n"
"Address 100.200.10.1\n"
"BridgeAuthoritativeDir 1\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -989,9 +966,7 @@ test_options_validate__authdir(void *ignored)
"Address 100.200.10.1\n"
"DirPort 999\n"
"BridgeAuthoritativeDir 1\n"
- "ContactInfo hello@hello.com\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ContactInfo hello@hello.com\n");
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1008,9 +983,7 @@ test_options_validate__authdir(void *ignored)
/* "ORPort 888\n" */
/* "ClientOnly 1\n" */
/* "BridgeAuthoritativeDir 1\n" */
- /* "ContactInfo hello@hello.com\n" */
- /* "SchedulerHighWaterMark__ 42\n" */
- /* "SchedulerLowWaterMark__ 10\n"); */
+ /* "ContactInfo hello@hello.com\n" ); */
/* mock_clean_saved_logs(); */
/* ret = options_validate(tdata->old_opt, tdata->opt, */
/* tdata->def_opt, 0, &msg); */
@@ -1103,7 +1076,7 @@ test_options_validate__transproxy(void *ignored)
tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_PF_DIVERT);
tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without "
"any valid TransPort.");
-#endif
+#endif /* !defined(OpenBSD) && !defined( DARWIN ) */
tor_free(msg);
// Test tproxy trans proxy
@@ -1118,7 +1091,7 @@ test_options_validate__transproxy(void *ignored)
tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_TPROXY);
tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid "
"TransPort.");
-#endif
+#endif /* !defined(__linux__) */
tor_free(msg);
// Test ipfw trans proxy
@@ -1134,7 +1107,7 @@ test_options_validate__transproxy(void *ignored)
tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW);
tt_str_op(msg, OP_EQ, "Cannot use TransProxyType without any valid "
"TransPort.");
-#endif
+#endif /* !defined(KERNEL_MAY_SUPPORT_IPFW) */
tor_free(msg);
// Test unknown trans proxy
@@ -1154,46 +1127,41 @@ test_options_validate__transproxy(void *ignored)
"TransPort 127.0.0.1:123\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
- if (msg) {
- TT_DIE(("Expected NULL but got '%s'", msg));
- }
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
#elif defined(KERNEL_MAY_SUPPORT_IPFW)
tdata = get_options_test_data("TransProxyType ipfw\n"
"TransPort 127.0.0.1:123\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
- if (msg) {
- TT_DIE(("Expected NULL but got '%s'", msg));
- }
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
+ tor_free(msg);
#elif defined(OpenBSD)
tdata = get_options_test_data("TransProxyType pf-divert\n"
"TransPort 127.0.0.1:123\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
- if (msg) {
- TT_DIE(("Expected NULL but got '%s'", msg));
- }
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
+ tor_free(msg);
#elif defined(__NetBSD__)
tdata = get_options_test_data("TransProxyType default\n"
"TransPort 127.0.0.1:123\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
- if (msg) {
- TT_DIE(("Expected NULL but got '%s'", msg));
- }
-#endif
+ tt_str_op(msg, OP_EQ, "ConnLimit must be greater than 0, but was set to 0");
+ tor_free(msg);
+#endif /* defined(__linux__) || ... */
// Assert that a test has run for some TransProxyType
tt_assert(tdata);
-#else
+#else /* !(defined(USE_TRANSPARENT)) */
tdata = get_options_test_data("TransPort 127.0.0.1:555\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
tt_str_op(msg, OP_EQ, "TransPort is disabled in this build.");
tor_free(msg);
-#endif
+#endif /* defined(USE_TRANSPARENT) */
done:
free_options_test_data(tdata);
@@ -1258,9 +1226,7 @@ test_options_validate__exclude_nodes(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("ExcludeNodes {cn}\n"
- "StrictNodes 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "StrictNodes 1\n");
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1271,9 +1237,7 @@ test_options_validate__exclude_nodes(void *ignored)
tor_free(msg);
free_options_test_data(tdata);
- tdata = get_options_test_data("ExcludeNodes {cn}\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ tdata = get_options_test_data("ExcludeNodes {cn}\n");
mock_clean_saved_logs();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1291,49 +1255,6 @@ test_options_validate__exclude_nodes(void *ignored)
}
static void
-test_options_validate__scheduler(void *ignored)
-{
- (void)ignored;
- int ret;
- char *msg;
- setup_capture_of_logs(LOG_DEBUG);
- options_test_data_t *tdata = get_options_test_data(
- "SchedulerLowWaterMark__ 0\n");
-
- ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
- tt_int_op(ret, OP_EQ, -1);
- expect_log_msg("Bad SchedulerLowWaterMark__ option\n");
- tor_free(msg);
-
- // TODO: this test cannot run on platforms where UINT32_MAX == UINT64_MAX.
- // I suspect it's unlikely this branch can actually happen
- /* free_options_test_data(tdata); */
- /* tdata = get_options_test_data( */
- /* "SchedulerLowWaterMark 10000000000000000000\n"); */
- /* tdata->opt->SchedulerLowWaterMark__ = (uint64_t)UINT32_MAX; */
- /* tdata->opt->SchedulerLowWaterMark__++; */
- /* mock_clean_saved_logs(); */
- /* ret = options_validate(tdata->old_opt, tdata->opt, */
- /* tdata->def_opt, 0, &msg); */
- /* tt_int_op(ret, OP_EQ, -1); */
- /* expect_log_msg("Bad SchedulerLowWaterMark__ option\n"); */
-
- free_options_test_data(tdata);
- tdata = get_options_test_data("SchedulerLowWaterMark__ 42\n"
- "SchedulerHighWaterMark__ 42\n");
- mock_clean_saved_logs();
- ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
- tt_int_op(ret, OP_EQ, -1);
- expect_log_msg("Bad SchedulerHighWaterMark option\n");
- tor_free(msg);
-
- done:
- teardown_capture_of_logs();
- free_options_test_data(tdata);
- tor_free(msg);
-}
-
-static void
test_options_validate__node_families(void *ignored)
{
(void)ignored;
@@ -1341,9 +1262,7 @@ test_options_validate__node_families(void *ignored)
char *msg;
options_test_data_t *tdata = get_options_test_data(
"NodeFamily flux, flax\n"
- "NodeFamily somewhere\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "NodeFamily somewhere\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1361,8 +1280,7 @@ test_options_validate__node_families(void *ignored)
tor_free(msg);
free_options_test_data(tdata);
- tdata = get_options_test_data("SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ tdata = get_options_test_data("");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1370,9 +1288,7 @@ test_options_validate__node_families(void *ignored)
tor_free(msg);
free_options_test_data(tdata);
- tdata = get_options_test_data("NodeFamily !flux\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ tdata = get_options_test_data("NodeFamily !flux\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1421,9 +1337,7 @@ test_options_validate__recommended_packages(void *ignored)
setup_capture_of_logs(LOG_WARN);
options_test_data_t *tdata = get_options_test_data(
"RecommendedPackages foo 1.2 http://foo.com sha1=123123123123\n"
- "RecommendedPackages invalid-package-line\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "RecommendedPackages invalid-package-line\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1445,9 +1359,7 @@ test_options_validate__fetch_dir(void *ignored)
char *msg;
options_test_data_t *tdata = get_options_test_data(
"FetchDirInfoExtraEarly 1\n"
- "FetchDirInfoEarly 0\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "FetchDirInfoEarly 0\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1457,9 +1369,7 @@ test_options_validate__fetch_dir(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("FetchDirInfoExtraEarly 1\n"
- "FetchDirInfoEarly 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "FetchDirInfoEarly 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1479,9 +1389,7 @@ test_options_validate__conn_limit(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = get_options_test_data(
- "ConnLimit 0\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 0\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1489,9 +1397,7 @@ test_options_validate__conn_limit(void *ignored)
tor_free(msg);
free_options_test_data(tdata);
- tdata = get_options_test_data("ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ tdata = get_options_test_data("ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1513,9 +1419,7 @@ test_options_validate__paths_needed(void *ignored)
setup_capture_of_logs(LOG_WARN);
options_test_data_t *tdata = get_options_test_data(
"PathsNeededToBuildCircuits 0.1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1528,9 +1432,7 @@ test_options_validate__paths_needed(void *ignored)
free_options_test_data(tdata);
mock_clean_saved_logs();
tdata = get_options_test_data("PathsNeededToBuildCircuits 0.99\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1543,9 +1445,7 @@ test_options_validate__paths_needed(void *ignored)
free_options_test_data(tdata);
mock_clean_saved_logs();
tdata = get_options_test_data("PathsNeededToBuildCircuits 0.91\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1568,9 +1468,7 @@ test_options_validate__max_client_circuits(void *ignored)
char *msg;
options_test_data_t *tdata = get_options_test_data(
"MaxClientCircuitsPending 0\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1580,9 +1478,7 @@ test_options_validate__max_client_circuits(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("MaxClientCircuitsPending 1025\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1592,9 +1488,7 @@ test_options_validate__max_client_circuits(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1615,9 +1509,7 @@ test_options_validate__ports(void *ignored)
options_test_data_t *tdata = get_options_test_data(
"FirewallPorts 65537\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1628,9 +1520,7 @@ test_options_validate__ports(void *ignored)
tdata = get_options_test_data("FirewallPorts 1\n"
"LongLivedPorts 124444\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1642,9 +1532,7 @@ test_options_validate__ports(void *ignored)
"LongLivedPorts 2\n"
"RejectPlaintextPorts 112233\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1657,9 +1545,7 @@ test_options_validate__ports(void *ignored)
"RejectPlaintextPorts 3\n"
"WarnPlaintextPorts 65536\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1672,9 +1558,7 @@ test_options_validate__ports(void *ignored)
"RejectPlaintextPorts 3\n"
"WarnPlaintextPorts 4\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1696,9 +1580,7 @@ test_options_validate__reachable_addresses(void *ignored)
options_test_data_t *tdata = get_options_test_data(
"FascistFirewall 1\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1716,9 +1598,7 @@ test_options_validate__reachable_addresses(void *ignored)
"ReachableDirAddresses *:81\n"
"ReachableORAddresses *:444\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
tdata->opt->FirewallPorts = smartlist_new();
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1732,9 +1612,7 @@ test_options_validate__reachable_addresses(void *ignored)
tdata = get_options_test_data("FascistFirewall 1\n"
"FirewallPort 123\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1751,9 +1629,7 @@ test_options_validate__reachable_addresses(void *ignored)
"ReachableAddresses *:83\n"
"ReachableAddresses reject *:*\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1769,9 +1645,7 @@ test_options_validate__reachable_addresses(void *ignored)
tdata = get_options_test_data("ReachableAddresses *:82\n"
"ORPort 127.0.0.1:5555\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1782,9 +1656,7 @@ test_options_validate__reachable_addresses(void *ignored)
tdata = get_options_test_data("ReachableORAddresses *:82\n"
"ORPort 127.0.0.1:5555\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1795,9 +1667,7 @@ test_options_validate__reachable_addresses(void *ignored)
tdata = get_options_test_data("ReachableDirAddresses *:82\n"
"ORPort 127.0.0.1:5555\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1808,9 +1678,7 @@ test_options_validate__reachable_addresses(void *ignored)
tdata = get_options_test_data("ClientUseIPv4 0\n"
"ORPort 127.0.0.1:5555\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1908,9 +1776,7 @@ test_options_validate__use_bridges(void *ignored)
"ClientUseIPv4 1\n"
"ORPort 127.0.0.1:5555\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1921,9 +1787,7 @@ test_options_validate__use_bridges(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("UseBridges 1\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1936,9 +1800,7 @@ test_options_validate__use_bridges(void *ignored)
tdata = get_options_test_data("UseBridges 1\n"
"EntryNodes {cn}\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -1998,9 +1860,7 @@ test_options_validate__entry_nodes(void *ignored)
"EntryNodes {cn}\n"
"UseEntryGuards 0\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -2012,9 +1872,7 @@ test_options_validate__entry_nodes(void *ignored)
tdata = get_options_test_data("EntryNodes {cn}\n"
"UseEntryGuards 1\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -2035,9 +1893,7 @@ test_options_validate__safe_logging(void *ignored)
char *msg;
options_test_data_t *tdata = get_options_test_data(
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -2047,9 +1903,7 @@ test_options_validate__safe_logging(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("SafeLogging 0\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -2059,9 +1913,7 @@ test_options_validate__safe_logging(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("SafeLogging Relay\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -2071,9 +1923,7 @@ test_options_validate__safe_logging(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("SafeLogging 1\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -2083,9 +1933,7 @@ test_options_validate__safe_logging(void *ignored)
free_options_test_data(tdata);
tdata = get_options_test_data("SafeLogging stuffy\n"
"MaxClientCircuitsPending 1\n"
- "ConnLimit 1\n"
- "SchedulerHighWaterMark__ 42\n"
- "SchedulerLowWaterMark__ 10\n");
+ "ConnLimit 1\n");
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
tt_int_op(ret, OP_EQ, -1);
@@ -2230,6 +2078,7 @@ test_options_validate__testing(void *ignored)
ENSURE_DEFAULT(TestingServerConsensusDownloadSchedule, 3000);
ENSURE_DEFAULT(TestingClientConsensusDownloadSchedule, 3000);
ENSURE_DEFAULT(TestingBridgeDownloadSchedule, 3000);
+ ENSURE_DEFAULT(TestingBridgeBootstrapDownloadSchedule, 3000);
ENSURE_DEFAULT(TestingClientMaxIntervalWithoutRequest, 3000);
ENSURE_DEFAULT(TestingDirConnectionMaxStall, 3000);
ENSURE_DEFAULT(TestingConsensusMaxDownloadTries, 3000);
@@ -3501,7 +3350,7 @@ test_options_validate__control(void *ignored)
"can reconfigure your Tor. That's bad! You should upgrade your "
"Tor controller as soon as possible.\n");
tor_free(msg);
-#endif
+#endif /* defined(HAVE_SYS_UN_H) */
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -4413,7 +4262,6 @@ struct testcase_t options_tests[] = {
LOCAL_VALIDATE_TEST(relay_with_hidden_services),
LOCAL_VALIDATE_TEST(transproxy),
LOCAL_VALIDATE_TEST(exclude_nodes),
- LOCAL_VALIDATE_TEST(scheduler),
LOCAL_VALIDATE_TEST(node_families),
LOCAL_VALIDATE_TEST(token_bucket),
LOCAL_VALIDATE_TEST(recommended_packages),
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 1b2fac4325..83dca2d431 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -59,7 +59,7 @@ test_policy_summary_helper_family_flags(const char *policy_str,
summary = policy_summarize(policy, family);
- tt_assert(summary != NULL);
+ tt_ptr_op(summary, OP_NE, NULL);
tt_str_op(summary,OP_EQ, expected_summary);
short_policy = parse_short_policy(summary);
@@ -147,7 +147,7 @@ test_policies_general(void *arg)
p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
tt_int_op(ADDR_POLICY_REJECT,OP_EQ, p->policy_type);
tor_addr_from_ipv4h(&tar, 0xc0a80000u);
tt_int_op(0,OP_EQ, tor_addr_compare(&p->addr, &tar, CMP_EXACT));
@@ -192,75 +192,75 @@ test_policies_general(void *arg)
policy3 = smartlist_new();
p = router_parse_addr_policy_item_from_string("reject *:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy3, p);
p = router_parse_addr_policy_item_from_string("accept *:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy3, p);
policy4 = smartlist_new();
p = router_parse_addr_policy_item_from_string("accept *:443", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy4, p);
p = router_parse_addr_policy_item_from_string("accept *:443", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy4, p);
policy5 = smartlist_new();
p = router_parse_addr_policy_item_from_string("reject 0.0.0.0/8:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject 169.254.0.0/16:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject 127.0.0.0/8:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject 192.168.0.0/16:*",
-1, &malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject 10.0.0.0/8:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject 172.16.0.0/12:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject 80.190.250.90:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject *:1-65534", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("reject *:65535", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
p = router_parse_addr_policy_item_from_string("accept *:1-65535", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy5, p);
policy6 = smartlist_new();
p = router_parse_addr_policy_item_from_string("accept 43.3.0.0/9:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy6, p);
policy7 = smartlist_new();
p = router_parse_addr_policy_item_from_string("accept 0.0.0.0/8:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy7, p);
tt_int_op(0, OP_EQ, policies_parse_exit_policy(NULL, &policy8,
@@ -282,13 +282,13 @@ test_policies_general(void *arg)
policy10 = smartlist_new();
p = router_parse_addr_policy_item_from_string("accept6 *:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy10, p);
policy11 = smartlist_new();
p = router_parse_addr_policy_item_from_string("reject6 *:*", -1,
&malformed_list);
- tt_assert(p != NULL);
+ tt_ptr_op(p, OP_NE, NULL);
smartlist_add(policy11, p);
tt_assert(!exit_policy_is_general_exit(policy));
@@ -392,21 +392,21 @@ test_policies_general(void *arg)
p = router_parse_addr_policy_item_from_string("acce::abcd",
ADDR_POLICY_ACCEPT,
&malformed_list);
- tt_assert(!p);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
malformed_list = 0;
p = router_parse_addr_policy_item_from_string("7:1234",
ADDR_POLICY_ACCEPT,
&malformed_list);
- tt_assert(!p);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
malformed_list = 0;
p = router_parse_addr_policy_item_from_string("::",
ADDR_POLICY_ACCEPT,
&malformed_list);
- tt_assert(!p);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
malformed_list = 0;
@@ -968,63 +968,63 @@ test_policies_general(void *arg)
/* Make sure that IPv4 addresses are ignored in accept6/reject6 lines. */
p = router_parse_addr_policy_item_from_string("accept6 1.2.3.4:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(!malformed_list);
p = router_parse_addr_policy_item_from_string("reject6 2.4.6.0/24:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(!malformed_list);
p = router_parse_addr_policy_item_from_string("accept6 *4:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(!malformed_list);
/* Make sure malformed policies are detected as such. */
p = router_parse_addr_policy_item_from_string("bad_token *4:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("accept6 **:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("accept */15:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("reject6 */:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("accept 127.0.0.1/33:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("accept6 [::1]/129:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("reject 8.8.8.8/-1:*", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("reject 8.8.4.4:10-5", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
p = router_parse_addr_policy_item_from_string("reject 1.2.3.4:-1", -1,
&malformed_list);
- tt_assert(p == NULL);
+ tt_ptr_op(p, OP_EQ, NULL);
tt_assert(malformed_list);
/* Test a too-long policy. */
@@ -1148,7 +1148,7 @@ test_policies_reject_exit_address(void *arg)
/* test that IPv4 addresses are rejected on an IPv4-only exit */
policies_parse_exit_policy_reject_private(&policy, 0, ipv4_list, 0, 0);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 1);
+ tt_int_op(smartlist_len(policy), OP_EQ, 1);
tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
addr_policy_list_free(policy);
policy = NULL;
@@ -1158,12 +1158,12 @@ test_policies_reject_exit_address(void *arg)
* on IPv4-only exits, so policies_parse_exit_policy_reject_private doesn't
* need to do anything) */
policies_parse_exit_policy_reject_private(&policy, 0, ipv6_list, 0, 0);
- tt_assert(policy == NULL);
+ tt_ptr_op(policy, OP_EQ, NULL);
/* test that only IPv4 addresses are rejected on an IPv4-only exit */
policies_parse_exit_policy_reject_private(&policy, 0, both_list, 0, 0);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 1);
+ tt_int_op(smartlist_len(policy), OP_EQ, 1);
tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
addr_policy_list_free(policy);
policy = NULL;
@@ -1171,7 +1171,7 @@ test_policies_reject_exit_address(void *arg)
/* Test that lists with duplicate entries produce the same results */
policies_parse_exit_policy_reject_private(&policy, 0, dupl_list, 0, 0);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 1);
+ tt_int_op(smartlist_len(policy), OP_EQ, 1);
tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
addr_policy_list_free(policy);
policy = NULL;
@@ -1181,7 +1181,7 @@ test_policies_reject_exit_address(void *arg)
/* test that IPv4 addresses are rejected on an IPv4/IPv6 exit */
policies_parse_exit_policy_reject_private(&policy, 1, ipv4_list, 0, 0);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 1);
+ tt_int_op(smartlist_len(policy), OP_EQ, 1);
tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
addr_policy_list_free(policy);
policy = NULL;
@@ -1189,7 +1189,7 @@ test_policies_reject_exit_address(void *arg)
/* test that IPv6 addresses are rejected on an IPv4/IPv6 exit */
policies_parse_exit_policy_reject_private(&policy, 1, ipv6_list, 0, 0);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 1);
+ tt_int_op(smartlist_len(policy), OP_EQ, 1);
tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
addr_policy_list_free(policy);
policy = NULL;
@@ -1197,7 +1197,7 @@ test_policies_reject_exit_address(void *arg)
/* test that IPv4 and IPv6 addresses are rejected on an IPv4/IPv6 exit */
policies_parse_exit_policy_reject_private(&policy, 1, both_list, 0, 0);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 2);
+ tt_int_op(smartlist_len(policy), OP_EQ, 2);
tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
addr_policy_list_free(policy);
@@ -1206,7 +1206,7 @@ test_policies_reject_exit_address(void *arg)
/* Test that lists with duplicate entries produce the same results */
policies_parse_exit_policy_reject_private(&policy, 1, dupl_list, 0, 0);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 2);
+ tt_int_op(smartlist_len(policy), OP_EQ, 2);
tt_assert(test_policy_has_address_helper(policy, &ipv4_addr));
tt_assert(test_policy_has_address_helper(policy, &ipv6_addr));
addr_policy_list_free(policy);
@@ -1258,7 +1258,7 @@ test_policies_reject_port_address(void *arg)
* with IPv6 addresses on IPv4-only exits) */
policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 1);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 1);
+ tt_int_op(smartlist_len(policy), OP_EQ, 1);
tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
addr_policy_list_free(policy);
policy = NULL;
@@ -1266,7 +1266,7 @@ test_policies_reject_port_address(void *arg)
/* test that IPv4 and IPv6 ports are rejected on an IPv4/IPv6 exit */
policies_parse_exit_policy_reject_private(&policy, 1, NULL, 0, 1);
tt_assert(policy);
- tt_assert(smartlist_len(policy) == 2);
+ tt_int_op(smartlist_len(policy), OP_EQ, 2);
tt_assert(test_policy_has_address_helper(policy, &ipv4_port->addr));
tt_assert(test_policy_has_address_helper(policy, &ipv6_port->addr));
addr_policy_list_free(policy);
@@ -1337,7 +1337,7 @@ test_policies_reject_interface_address(void *arg)
/* test that no addresses are rejected when none are supplied/requested */
policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
- tt_assert(policy == NULL);
+ tt_ptr_op(policy, OP_EQ, NULL);
/* test that only IPv4 interface addresses are rejected on an IPv4-only exit
* (and allow for duplicates)
@@ -1372,7 +1372,7 @@ test_policies_reject_interface_address(void *arg)
/* test that no addresses are rejected when none are supplied/requested */
policies_parse_exit_policy_reject_private(&policy, 0, NULL, 0, 0);
- tt_assert(policy == NULL);
+ tt_ptr_op(policy, OP_EQ, NULL);
/* test that only IPv4 interface addresses are rejected on an IPv4-only exit
*/
@@ -1528,15 +1528,15 @@ test_policies_getinfo_helper_policies(void *arg)
memset(&mock_my_routerinfo, 0, sizeof(mock_my_routerinfo));
rv = getinfo_helper_policies(NULL, "exit-policy/default", &answer, &errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) > 0);
tor_free(answer);
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/default",
&answer, &errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) > 0);
tor_free(answer);
@@ -1550,15 +1550,15 @@ test_policies_getinfo_helper_policies(void *arg)
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
&answer, &errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) == 0);
tor_free(answer);
rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
&errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
ipv4_len = strlen(answer);
tt_assert(ipv4_len == 0 || ipv4_len == strlen(DEFAULT_POLICY_STRING));
tt_assert(ipv4_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
@@ -1566,8 +1566,8 @@ test_policies_getinfo_helper_policies(void *arg)
rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
&errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
ipv6_len = strlen(answer);
tt_assert(ipv6_len == 0 || ipv6_len == strlen(DEFAULT_POLICY_STRING));
tt_assert(ipv6_len == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
@@ -1575,8 +1575,8 @@ test_policies_getinfo_helper_policies(void *arg)
rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
&errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
/* It's either empty or it's the default */
tt_assert(strlen(answer) == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING));
tor_free(answer);
@@ -1599,8 +1599,8 @@ test_policies_getinfo_helper_policies(void *arg)
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
&answer, &errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) > 0);
tor_free(answer);
@@ -1609,8 +1609,8 @@ test_policies_getinfo_helper_policies(void *arg)
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
&answer, &errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) > 0);
tor_free(answer);
@@ -1619,8 +1619,8 @@ test_policies_getinfo_helper_policies(void *arg)
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
&answer, &errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) > 0);
tor_free(answer);
@@ -1629,31 +1629,31 @@ test_policies_getinfo_helper_policies(void *arg)
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
&answer, &errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) == 0);
tor_free(answer);
rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
&errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
ipv4_len = strlen(answer);
tt_assert(ipv4_len > 0);
tor_free(answer);
rv = getinfo_helper_policies(NULL, "exit-policy/ipv6", &answer,
&errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
ipv6_len = strlen(answer);
tt_assert(ipv6_len > 0);
tor_free(answer);
rv = getinfo_helper_policies(NULL, "exit-policy/full", &answer,
&errmsg);
- tt_assert(rv == 0);
- tt_assert(answer != NULL);
+ tt_int_op(rv, OP_EQ, 0);
+ tt_ptr_op(answer, OP_NE, NULL);
tt_assert(strlen(answer) > 0);
tt_assert(strlen(answer) == ipv4_len + ipv6_len + 1);
tor_free(answer);
@@ -1746,34 +1746,34 @@ test_policies_fascist_firewall_allows_address(void *arg)
mock_options.ClientUseIPv6 = 1;
mock_options.UseBridges = 0;
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
/* Preferring IPv4 */
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0),
+ OP_EQ, 0);
/* Preferring IPv6 */
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1)
- == 0);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1)
- == 1);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1),
+ OP_EQ, 0);
/* Test the function's address matching with UseBridges on */
memset(&mock_options, 0, sizeof(or_options_t));
@@ -1781,46 +1781,46 @@ test_policies_fascist_firewall_allows_address(void *arg)
mock_options.ClientUseIPv6 = 1;
mock_options.UseBridges = 1;
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
/* Preferring IPv4 */
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 0),
+ OP_EQ, 0);
/* Preferring IPv6 */
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1)
- == 0);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1)
- == 1);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 1, 1),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 1, 1),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 1, 1),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 1, 1),
+ OP_EQ, 0);
/* bridge clients always use IPv6, regardless of ClientUseIPv6 */
mock_options.ClientUseIPv4 = 1;
mock_options.ClientUseIPv6 = 0;
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
/* Test the function's address matching with IPv4 on */
memset(&mock_options, 0, sizeof(or_options_t));
@@ -1828,14 +1828,14 @@ test_policies_fascist_firewall_allows_address(void *arg)
mock_options.ClientUseIPv6 = 0;
mock_options.UseBridges = 0;
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
/* Test the function's address matching with IPv6 on */
memset(&mock_options, 0, sizeof(or_options_t));
@@ -1843,14 +1843,14 @@ test_policies_fascist_firewall_allows_address(void *arg)
mock_options.ClientUseIPv6 = 1;
mock_options.UseBridges = 0;
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
/* Test the function's address matching with ClientUseIPv4 0.
* This means "use IPv6" regardless of the other settings. */
@@ -1859,14 +1859,14 @@ test_policies_fascist_firewall_allows_address(void *arg)
mock_options.ClientUseIPv6 = 0;
mock_options.UseBridges = 0;
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&r_ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
/* Test the function's address matching for unusual inputs */
memset(&mock_options, 0, sizeof(or_options_t));
@@ -1875,27 +1875,28 @@ test_policies_fascist_firewall_allows_address(void *arg)
mock_options.UseBridges = 1;
/* NULL and tor_addr_is_null addresses are rejected */
- tt_assert(fascist_firewall_allows_address(NULL, port, policy, 0, 0) == 0);
- tt_assert(fascist_firewall_allows_address(&n_ipv4_addr, port, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&n_ipv6_addr, port, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(NULL, port, policy, 0, 0), OP_EQ,
+ 0);
+ tt_int_op(fascist_firewall_allows_address(&n_ipv4_addr, port, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&n_ipv6_addr, port, policy, 0, 0),
+ OP_EQ, 0);
/* zero ports are rejected */
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, 0, policy, 0, 0)
- == 0);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, 0, policy, 0, 0)
- == 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, 0, policy, 0, 0),
+ OP_EQ, 0);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, 0, policy, 0, 0),
+ OP_EQ, 0);
/* NULL and empty policies accept everything */
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, NULL, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, NULL, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv4_addr, port, e_policy, 0, 0)
- == 1);
- tt_assert(fascist_firewall_allows_address(&ipv6_addr, port, e_policy, 0, 0)
- == 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, NULL, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, NULL, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv4_addr, port, e_policy, 0, 0),
+ OP_EQ, 1);
+ tt_int_op(fascist_firewall_allows_address(&ipv6_addr, port, e_policy, 0, 0),
+ OP_EQ, 1);
done:
addr_policy_free(item);
@@ -2032,12 +2033,12 @@ test_policies_fascist_firewall_choose_address(void *arg)
== &ipv6_or_ap);
/* null both OR addresses */
- tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
- FIREWALL_OR_CONNECTION, 0, 1)
- == NULL);
- tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
- FIREWALL_OR_CONNECTION, 0, 0)
- == NULL);
+ tt_ptr_op(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
+ FIREWALL_OR_CONNECTION, 0, 1),
+ OP_EQ, NULL);
+ tt_ptr_op(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
+ FIREWALL_OR_CONNECTION, 0, 0),
+ OP_EQ, NULL);
/* null preferred Dir addresses */
tt_assert(fascist_firewall_choose_address(&ipv4_dir_ap, &n_ipv6_ap, 0,
@@ -2048,12 +2049,12 @@ test_policies_fascist_firewall_choose_address(void *arg)
== &ipv6_dir_ap);
/* null both Dir addresses */
- tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
- FIREWALL_DIR_CONNECTION, 0, 1)
- == NULL);
- tt_assert(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
- FIREWALL_DIR_CONNECTION, 0, 0)
- == NULL);
+ tt_ptr_op(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 0,
+ FIREWALL_DIR_CONNECTION, 0, 1),
+ OP_EQ, NULL);
+ tt_ptr_op(fascist_firewall_choose_address(&n_ipv4_ap, &n_ipv6_ap, 1,
+ FIREWALL_DIR_CONNECTION, 0, 0),
+ OP_EQ, NULL);
/* Prefer IPv4 but want IPv6 (contradictory) */
tt_assert(fascist_firewall_choose_address(&ipv4_or_ap, &ipv6_or_ap, 0,
diff --git a/src/test/test_proto_http.c b/src/test/test_proto_http.c
new file mode 100644
index 0000000000..2f36fbccd7
--- /dev/null
+++ b/src/test/test_proto_http.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_proto_http.c
+ * \brief Tests for our HTTP protocol parser code
+ */
+
+#include "or.h"
+#include "test.h"
+#include "buffers.h"
+#include "proto_http.h"
+#include "log_test_helpers.h"
+
+#define S(str) str, sizeof(str)-1
+
+static void
+test_proto_http_peek(void *arg)
+{
+ (void) arg;
+ const struct {
+ int is_http;
+ const char *message;
+ size_t len;
+ } cases[] = {
+ { 1, S("GET /index HTTP/1.0\r\n") },
+ { 1, S("GET /index HTTP/1.1\r\n") },
+ { 1, S("GET ") },
+ { 0, S("GIT ") },
+ { 0, S("GET") },
+ { 0, S("get ") },
+ { 0, S("GETAWAY") },
+ };
+ unsigned i;
+ buf_t *buf = buf_new();
+ for (i = 0; i < ARRAY_LENGTH(cases); ++i) {
+ TT_BLATHER(("Trying case %u", i));
+ buf_add(buf, cases[i].message, cases[i].len);
+ tt_int_op(cases[i].is_http, OP_EQ, peek_buf_has_http_command(buf));
+ buf_clear(buf);
+ }
+ done:
+ buf_free(buf);
+}
+
+static void
+test_proto_http_valid(void *arg)
+{
+ (void) arg;
+ const struct {
+ const char *message;
+ size_t len;
+ const char *headers;
+ const char *body;
+ size_t bodylen;
+ int should_detect_truncated;
+ int bytes_left_over;
+ } cases[] = {
+ { S("GET /index.html HTTP/1.0\r\n\r\n"),
+ "GET /index.html HTTP/1.0\r\n\r\n",
+ S(""),
+ 1, 0,
+ },
+ { S("PUT /tor/foo HTTP/1.1\r\n"
+ "Content-Length: 51\r\n\r\n"
+ "this is a test of the http parsing system . test te"),
+ "PUT /tor/foo HTTP/1.1\r\n" "Content-Length: 51\r\n\r\n",
+ S("this is a test of the http parsing system . test te"),
+ 1, 0,
+ },
+ { S("PUT /tor/foo HTTP/1.1\r\n"
+ "Content-Length: 5\r\n\r\n"
+ "there are more than 5 characters in this body."),
+ "PUT /tor/foo HTTP/1.1\r\n" "Content-Length: 5\r\n\r\n",
+ S("there"),
+ 0, 41,
+ },
+ { S("PUT /tor/bar HTTP/1.1\r\n\r\n"
+ "this is another \x00test"),
+ "PUT /tor/bar HTTP/1.1\r\n\r\n",
+ S("this is another \x00test"),
+ 0, 0,
+ }
+ };
+ unsigned i;
+ buf_t *buf = buf_new();
+ char *h = NULL, *b = NULL;
+
+ for (i = 0; i < ARRAY_LENGTH(cases); ++i) {
+ TT_BLATHER(("Trying case %u", i));
+ size_t bl = 0;
+ // truncate by 2 chars
+ buf_add(buf, cases[i].message, cases[i].len - 2);
+
+ if (cases[i].should_detect_truncated) {
+ tt_int_op(0, OP_EQ, fetch_from_buf_http(buf, &h, 1024*16,
+ &b, &bl, 1024*16, 0));
+ tt_ptr_op(h, OP_EQ, NULL);
+ tt_ptr_op(b, OP_EQ, NULL);
+ tt_u64_op(bl, OP_EQ, 0);
+ tt_int_op(buf_datalen(buf), OP_EQ, cases[i].len - 2);
+ }
+
+ // add the rest.
+ buf_add(buf, cases[i].message+cases[i].len-2, 2);
+ tt_int_op(1, OP_EQ, fetch_from_buf_http(buf, &h, 1024*16,
+ &b, &bl, 1024*16, 0));
+ tt_str_op(h, OP_EQ, cases[i].headers);
+ tt_u64_op(bl, OP_EQ, cases[i].bodylen);
+ tt_mem_op(b, OP_EQ, cases[i].body, bl);
+ tt_int_op(buf_datalen(buf), OP_EQ, cases[i].bytes_left_over);
+
+ buf_clear(buf);
+ tor_free(h);
+ tor_free(b);
+ }
+ done:
+ tor_free(h);
+ tor_free(b);
+ buf_free(buf);
+}
+
+static void
+test_proto_http_invalid(void *arg)
+{
+ (void) arg;
+ const struct {
+ const char *message;
+ size_t len;
+ const char *expect;
+ } cases[] = {
+ /* Overlong headers, headers not finished. */
+ { S("GET /index.xhml HTTP/1.0\r\n"
+ "X-My-headers-are-too-long: yes indeed they are. They might be\r\n"
+ "X-My-headers-are-too-long: normal under other circumstances, but\r\n"
+ "X-My-headers-are-too-long: the 128-byte limit makes them bad\r\n"),
+ "headers too long." },
+ /* Overlong finished headers. */
+ { S("GET /index.xhml HTTP/1.0\r\n"
+ "X-My-headers-are-too-long: yes indeed they are. They might be\r\n"
+ "X-My-headers-are-too-long: normal under other circumstances, but\r\n"
+ "X-My-headers-are-too-long: the 128-byte limit makes them bad\r\n"
+ "\r\n"),
+ "headers too long." },
+ /* Exactly too long finished headers. */
+ { S("GET /index.xhml HTTP/1.0\r\n"
+ "X-My-headers-are-too-long: yes indeed they are. They might be\r\n"
+ "X-My-headers-are-too-long: normal un\r\n\r\n"),
+ "headerlen 129 larger than 127. Failing." },
+ /* Body too long, with content-length */
+ { S("GET /index.html HTTP/1.0\r\n"
+ "Content-Length: 129\r\n\r\n"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxx"),
+ "bodylen 129 larger than 127" },
+ /* Body too long, with content-length lying */
+ { S("GET /index.html HTTP/1.0\r\n"
+ "Content-Length: 99999\r\n\r\n"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
+ "bodylen 138 larger than 127" },
+ /* Body too long, no content-length. */
+ { S("GET /index.html HTTP/1.0\r\n\r\n"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxz"),
+ "bodylen 139 larger than 127" },
+ /* Content-Length is junk. */
+ { S("GET /index.html HTTP/1.0\r\n"
+ "Content-Length: Cheese\r\n\r\n"
+ "foo"),
+ "Content-Length is bogus; maybe someone is trying to crash us." },
+ };
+ unsigned i;
+ buf_t *buf = buf_new();
+ char *h = NULL, *b = NULL;
+ setup_capture_of_logs(LOG_DEBUG);
+
+ for (i = 0; i < ARRAY_LENGTH(cases); ++i) {
+ TT_BLATHER(("Trying case %u", i));
+ size_t bl = 0;
+ buf_add(buf, cases[i].message, cases[i].len);
+
+ /* Use low body limits here so we can force over-sized object warnings */
+ tt_int_op(-1, OP_EQ, fetch_from_buf_http(buf, &h, 128,
+ &b, &bl, 128, 0));
+ tt_ptr_op(h, OP_EQ, NULL);
+ tt_ptr_op(b, OP_EQ, NULL);
+ tt_u64_op(bl, OP_EQ, 0);
+ expect_log_msg_containing(cases[i].expect);
+
+ buf_clear(buf);
+ tor_free(h);
+ tor_free(b);
+ mock_clean_saved_logs();
+ }
+ done:
+ tor_free(h);
+ tor_free(b);
+ buf_free(buf);
+ teardown_capture_of_logs();
+}
+
+struct testcase_t proto_http_tests[] = {
+ { "peek", test_proto_http_peek, 0, NULL, NULL },
+ { "valid", test_proto_http_valid, 0, NULL, NULL },
+ { "invalid", test_proto_http_invalid, 0, NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_proto_misc.c b/src/test/test_proto_misc.c
new file mode 100644
index 0000000000..263ca47447
--- /dev/null
+++ b/src/test/test_proto_misc.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_proto_misc.c
+ * \brief Test our smaller buffer-based protocol functions
+ */
+
+#include "or.h"
+#include "test.h"
+#include "buffers.h"
+#include "connection_or.h"
+#include "ext_orport.h"
+#include "proto_cell.h"
+#include "proto_control0.h"
+#include "proto_ext_or.h"
+
+static void
+test_proto_var_cell(void *arg)
+{
+ (void)arg;
+ char *mem_op_hex_tmp = NULL;
+ char tmp[1024];
+ buf_t *buf = NULL;
+ var_cell_t *cell = NULL;
+
+ buf = buf_new();
+ memset(tmp, 0xf0, sizeof(tmp));
+
+ /* Short little commands will make us say "no cell yet." */
+ tt_int_op(0, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 4));
+ tt_ptr_op(cell, OP_EQ, NULL);
+ buf_add(buf, "\x01\x02\x02\0x2", 4);
+ tt_int_op(0, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 4));
+ /* An incomplete fixed-length cell makes us say "no cell yet". */
+ buf_add(buf, "\x03", 1);
+ tt_int_op(0, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 4));
+ /* A complete fixed length-cell makes us say "not a variable-length cell" */
+ buf_add(buf, tmp, 509);
+ tt_int_op(0, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 4));
+ buf_clear(buf);
+
+ /* An incomplete versions cell is a variable-length cell that isn't ready
+ * yet. */
+ buf_add(buf,
+ "\x01\x02\x03\x04" /* circid */
+ "\x07" /* VERSIONS */
+ "\x00\x04" /* 4 bytes long */
+ "\x00" /* incomplete */, 8);
+ tt_int_op(1, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 4));
+ tt_ptr_op(cell, OP_EQ, NULL);
+ /* Complete it, and it's a variable-length cell. Leave a byte on the end for
+ * fun. */
+ buf_add(buf, "\x09\x00\x25\ff", 4);
+ tt_int_op(1, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 4));
+ tt_ptr_op(cell, OP_NE, NULL);
+ tt_int_op(cell->command, OP_EQ, CELL_VERSIONS);
+ tt_uint_op(cell->circ_id, OP_EQ, 0x01020304);
+ tt_int_op(cell->payload_len, OP_EQ, 4);
+ test_mem_op_hex(cell->payload, OP_EQ, "00090025");
+ var_cell_free(cell);
+ cell = NULL;
+ tt_int_op(buf_datalen(buf), OP_EQ, 1);
+ buf_clear(buf);
+
+ /* In link protocol 3 and earlier, circid fields were two bytes long. Let's
+ * ensure that gets handled correctly. */
+ buf_add(buf,
+ "\x23\x45\x81\x00\x06" /* command 81; 6 bytes long */
+ "coraje", 11);
+ tt_int_op(1, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 3));
+ tt_ptr_op(cell, OP_NE, NULL);
+ tt_int_op(cell->command, OP_EQ, 129);
+ tt_uint_op(cell->circ_id, OP_EQ, 0x2345);
+ tt_int_op(cell->payload_len, OP_EQ, 6);
+ tt_mem_op(cell->payload, OP_EQ, "coraje", 6);
+ var_cell_free(cell);
+ cell = NULL;
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* In link protocol 2, only VERSIONS cells counted as variable-length */
+ buf_add(buf,
+ "\x23\x45\x81\x00\x06"
+ "coraje", 11); /* As above */
+ tt_int_op(0, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 2));
+ buf_clear(buf);
+ buf_add(buf,
+ "\x23\x45\x07\x00\x06"
+ "futuro", 11);
+ tt_int_op(1, OP_EQ, fetch_var_cell_from_buf(buf, &cell, 2));
+ tt_ptr_op(cell, OP_NE, NULL);
+ tt_int_op(cell->command, OP_EQ, 7);
+ tt_uint_op(cell->circ_id, OP_EQ, 0x2345);
+ tt_int_op(cell->payload_len, OP_EQ, 6);
+ tt_mem_op(cell->payload, OP_EQ, "futuro", 6);
+ var_cell_free(cell);
+ cell = NULL;
+
+ done:
+ buf_free(buf);
+ var_cell_free(cell);
+ tor_free(mem_op_hex_tmp);
+}
+
+static void
+test_proto_control0(void *arg)
+{
+ (void)arg;
+ buf_t *buf = buf_new();
+
+ /* The only remaining function for the v0 control protocol is the function
+ that detects whether the user has stumbled across an old controller
+ that's using it. The format was:
+ u16 length;
+ u16 command;
+ u8 body[length];
+ */
+
+ /* Empty buffer -- nothing to do. */
+ tt_int_op(0, OP_EQ, peek_buf_has_control0_command(buf));
+ /* 3 chars in buf -- can't tell */
+ buf_add(buf, "AUT", 3);
+ tt_int_op(0, OP_EQ, peek_buf_has_control0_command(buf));
+ /* command in buf -- easy to tell */
+ buf_add(buf, "HENTICATE ", 10);
+ tt_int_op(0, OP_EQ, peek_buf_has_control0_command(buf));
+
+ /* Control0 command header in buf: make sure we detect it. */
+ buf_clear(buf);
+ buf_add(buf, "\x09\x05" "\x00\x05" "blah", 8);
+ tt_int_op(1, OP_EQ, peek_buf_has_control0_command(buf));
+
+ done:
+ buf_free(buf);
+}
+
+static void
+test_proto_ext_or_cmd(void *arg)
+{
+ ext_or_cmd_t *cmd = NULL;
+ buf_t *buf = buf_new();
+ char *tmp = NULL;
+ (void) arg;
+
+ /* Empty -- should give "not there. */
+ tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tt_ptr_op(NULL, OP_EQ, cmd);
+
+ /* Three bytes: shouldn't work. */
+ buf_add(buf, "\x00\x20\x00", 3);
+ tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tt_ptr_op(NULL, OP_EQ, cmd);
+ tt_int_op(3, OP_EQ, buf_datalen(buf));
+
+ /* 0020 0000: That's a nil command. It should work. */
+ buf_add(buf, "\x00", 1);
+ tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tt_ptr_op(NULL, OP_NE, cmd);
+ tt_int_op(0x20, OP_EQ, cmd->cmd);
+ tt_int_op(0, OP_EQ, cmd->len);
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ /* Now try a length-6 command with one byte missing. */
+ buf_add(buf, "\x10\x21\x00\x06""abcde", 9);
+ tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tt_ptr_op(NULL, OP_EQ, cmd);
+ buf_add(buf, "f", 1);
+ tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tt_ptr_op(NULL, OP_NE, cmd);
+ tt_int_op(0x1021, OP_EQ, cmd->cmd);
+ tt_int_op(6, OP_EQ, cmd->len);
+ tt_mem_op("abcdef", OP_EQ, cmd->body, 6);
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ /* Now try a length-10 command with 4 extra bytes. */
+ buf_add(buf, "\xff\xff\x00\x0aloremipsum\x10\x00\xff\xff", 18);
+ tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tt_ptr_op(NULL, OP_NE, cmd);
+ tt_int_op(0xffff, OP_EQ, cmd->cmd);
+ tt_int_op(10, OP_EQ, cmd->len);
+ tt_mem_op("loremipsum", OP_EQ, cmd->body, 10);
+ tt_int_op(4, OP_EQ, buf_datalen(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ /* Finally, let's try a maximum-length command. We already have the header
+ * waiting. */
+ tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tmp = tor_malloc_zero(65535);
+ buf_add(buf, tmp, 65535);
+ tt_int_op(1, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
+ tt_ptr_op(NULL, OP_NE, cmd);
+ tt_int_op(0x1000, OP_EQ, cmd->cmd);
+ tt_int_op(0xffff, OP_EQ, cmd->len);
+ tt_mem_op(tmp, OP_EQ, cmd->body, 65535);
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
+ ext_or_cmd_free(cmd);
+ cmd = NULL;
+
+ done:
+ ext_or_cmd_free(cmd);
+ buf_free(buf);
+ tor_free(tmp);
+}
+
+static void
+test_proto_line(void *arg)
+{
+ (void)arg;
+ char tmp[60];
+ buf_t *buf = buf_new();
+#define S(str) str, sizeof(str)-1
+ const struct {
+ const char *input;
+ size_t input_len;
+ size_t line_len;
+ const char *output;
+ int returnval;
+ } cases[] = {
+ { S("Hello world"), 0, NULL, 0 },
+ { S("Hello world\n"), 12, "Hello world\n", 1 },
+ { S("Hello world\nMore"), 12, "Hello world\n", 1 },
+ { S("\n oh hello world\nMore"), 1, "\n", 1 },
+ { S("Hello worpd\n\nMore"), 12, "Hello worpd\n", 1 },
+ { S("------------------------------------------------------------\n"), 0,
+ NULL, -1 },
+ };
+ unsigned i;
+ for (i = 0; i < ARRAY_LENGTH(cases); ++i) {
+ buf_add(buf, cases[i].input, cases[i].input_len);
+ memset(tmp, 0xfe, sizeof(tmp));
+ size_t sz = sizeof(tmp);
+ int rv = buf_get_line(buf, tmp, &sz);
+ tt_int_op(rv, OP_EQ, cases[i].returnval);
+ if (rv == 1) {
+ tt_int_op(sz, OP_LT, sizeof(tmp));
+ tt_mem_op(cases[i].output, OP_EQ, tmp, sz+1);
+ tt_int_op(buf_datalen(buf), OP_EQ, cases[i].input_len - strlen(tmp));
+ tt_int_op(sz, OP_EQ, cases[i].line_len);
+ } else {
+ tt_int_op(buf_datalen(buf), OP_EQ, cases[i].input_len);
+ // tt_int_op(sz, OP_EQ, sizeof(tmp));
+ }
+ buf_clear(buf);
+ }
+
+ done:
+ buf_free(buf);
+}
+
+struct testcase_t proto_misc_tests[] = {
+ { "var_cell", test_proto_var_cell, 0, NULL, NULL },
+ { "control0", test_proto_control0, 0, NULL, NULL },
+ { "ext_or_cmd", test_proto_ext_or_cmd, TT_FORK, NULL, NULL },
+ { "line", test_proto_line, 0, NULL, NULL },
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_protover.c b/src/test/test_protover.c
index c093e69968..9b94044b91 100644
--- a/src/test/test_protover.c
+++ b/src/test/test_protover.c
@@ -88,27 +88,27 @@ test_protover_parse_fail(void *arg)
/* random junk */
elts = parse_protocol_list("!!3@*");
- tt_assert(elts == NULL);
+ tt_ptr_op(elts, OP_EQ, NULL);
/* Missing equals sign in an entry */
elts = parse_protocol_list("Link=4 Haprauxymatyve Desc=9");
- tt_assert(elts == NULL);
+ tt_ptr_op(elts, OP_EQ, NULL);
/* Missing word. */
elts = parse_protocol_list("Link=4 =3 Desc=9");
- tt_assert(elts == NULL);
+ tt_ptr_op(elts, OP_EQ, NULL);
/* Broken numbers */
elts = parse_protocol_list("Link=fred");
- tt_assert(elts == NULL);
+ tt_ptr_op(elts, OP_EQ, NULL);
elts = parse_protocol_list("Link=1,fred");
- tt_assert(elts == NULL);
+ tt_ptr_op(elts, OP_EQ, NULL);
elts = parse_protocol_list("Link=1,fred,3");
- tt_assert(elts == NULL);
+ tt_ptr_op(elts, OP_EQ, NULL);
/* Broken range */
elts = parse_protocol_list("Link=1,9-8,3");
- tt_assert(elts == NULL);
+ tt_ptr_op(elts, OP_EQ, NULL);
done:
;
@@ -215,16 +215,16 @@ test_protover_all_supported(void *arg)
char *msg = NULL;
tt_assert(protover_all_supported(NULL, &msg));
- tt_assert(msg == NULL);
+ tt_ptr_op(msg, OP_EQ, NULL);
tt_assert(protover_all_supported("", &msg));
- tt_assert(msg == NULL);
+ tt_ptr_op(msg, OP_EQ, NULL);
// Some things that we do support
tt_assert(protover_all_supported("Link=3-4", &msg));
- tt_assert(msg == NULL);
+ tt_ptr_op(msg, OP_EQ, NULL);
tt_assert(protover_all_supported("Link=3-4 Desc=2", &msg));
- tt_assert(msg == NULL);
+ tt_ptr_op(msg, OP_EQ, NULL);
// Some things we don't support
tt_assert(! protover_all_supported("Wombat=9", &msg));
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
index 79b03171bc..07b6712ff9 100644
--- a/src/test/test_pt.c
+++ b/src/test/test_pt.c
@@ -40,34 +40,34 @@ test_pt_parsing(void *arg)
/* incomplete cmethod */
strlcpy(line,"CMETHOD trebuchet",sizeof(line));
- tt_assert(parse_cmethod_line(line, mp) < 0);
+ tt_int_op(parse_cmethod_line(line, mp), OP_LT, 0);
reset_mp(mp);
/* wrong proxy type */
strlcpy(line,"CMETHOD trebuchet dog 127.0.0.1:1999",sizeof(line));
- tt_assert(parse_cmethod_line(line, mp) < 0);
+ tt_int_op(parse_cmethod_line(line, mp), OP_LT, 0);
reset_mp(mp);
/* wrong addrport */
strlcpy(line,"CMETHOD trebuchet socks4 abcd",sizeof(line));
- tt_assert(parse_cmethod_line(line, mp) < 0);
+ tt_int_op(parse_cmethod_line(line, mp), OP_LT, 0);
reset_mp(mp);
/* correct line */
strlcpy(line,"CMETHOD trebuchet socks5 127.0.0.1:1999",sizeof(line));
- tt_assert(parse_cmethod_line(line, mp) == 0);
- tt_assert(smartlist_len(mp->transports) == 1);
+ tt_int_op(parse_cmethod_line(line, mp), OP_EQ, 0);
+ tt_int_op(smartlist_len(mp->transports), OP_EQ, 1);
transport = smartlist_get(mp->transports, 0);
/* test registered address of transport */
tor_addr_parse(&test_addr, "127.0.0.1");
tt_assert(tor_addr_eq(&test_addr, &transport->addr));
/* test registered port of transport */
- tt_assert(transport->port == 1999);
+ tt_uint_op(transport->port, OP_EQ, 1999);
/* test registered SOCKS version of transport */
- tt_assert(transport->socks_version == PROXY_SOCKS5);
+ tt_int_op(transport->socks_version, OP_EQ, PROXY_SOCKS5);
/* test registered name of transport */
tt_str_op(transport->name,OP_EQ, "trebuchet");
@@ -75,26 +75,26 @@ test_pt_parsing(void *arg)
/* incomplete smethod */
strlcpy(line,"SMETHOD trebuchet",sizeof(line));
- tt_assert(parse_smethod_line(line, mp) < 0);
+ tt_int_op(parse_smethod_line(line, mp), OP_LT, 0);
reset_mp(mp);
/* wrong addr type */
strlcpy(line,"SMETHOD trebuchet abcd",sizeof(line));
- tt_assert(parse_smethod_line(line, mp) < 0);
+ tt_int_op(parse_smethod_line(line, mp), OP_LT, 0);
reset_mp(mp);
/* cowwect */
strlcpy(line,"SMETHOD trebuchy 127.0.0.2:2999",sizeof(line));
- tt_assert(parse_smethod_line(line, mp) == 0);
- tt_assert(smartlist_len(mp->transports) == 1);
+ tt_int_op(parse_smethod_line(line, mp), OP_EQ, 0);
+ tt_int_op(smartlist_len(mp->transports), OP_EQ, 1);
transport = smartlist_get(mp->transports, 0);
/* test registered address of transport */
tor_addr_parse(&test_addr, "127.0.0.2");
tt_assert(tor_addr_eq(&test_addr, &transport->addr));
/* test registered port of transport */
- tt_assert(transport->port == 2999);
+ tt_uint_op(transport->port, OP_EQ, 2999);
/* test registered name of transport */
tt_str_op(transport->name,OP_EQ, "trebuchy");
@@ -104,7 +104,7 @@ test_pt_parsing(void *arg)
strlcpy(line,"SMETHOD trebuchet 127.0.0.1:9999 "
"ARGS:counterweight=3,sling=snappy",
sizeof(line));
- tt_assert(parse_smethod_line(line, mp) == 0);
+ tt_int_op(parse_smethod_line(line, mp), OP_EQ, 0);
tt_int_op(1, OP_EQ, smartlist_len(mp->transports));
{
const transport_t *transport_ = smartlist_get(mp->transports, 0);
@@ -119,15 +119,15 @@ test_pt_parsing(void *arg)
/* unsupported version */
strlcpy(line,"VERSION 666",sizeof(line));
- tt_assert(parse_version(line, mp) < 0);
+ tt_int_op(parse_version(line, mp), OP_LT, 0);
/* incomplete VERSION */
strlcpy(line,"VERSION ",sizeof(line));
- tt_assert(parse_version(line, mp) < 0);
+ tt_int_op(parse_version(line, mp), OP_LT, 0);
/* correct VERSION */
strlcpy(line,"VERSION 1",sizeof(line));
- tt_assert(parse_version(line, mp) == 0);
+ tt_int_op(parse_version(line, mp), OP_EQ, 0);
done:
reset_mp(mp);
@@ -461,7 +461,7 @@ test_get_pt_proxy_uri(void *arg)
/* Test with no proxy. */
uri = get_pt_proxy_uri();
- tt_assert(uri == NULL);
+ tt_ptr_op(uri, OP_EQ, NULL);
/* Test with a SOCKS4 proxy. */
options->Socks4Proxy = tor_strdup("192.0.2.1:1080");
diff --git a/src/test/test_relay.c b/src/test/test_relay.c
index 238d4c5baf..e3489627a0 100644
--- a/src/test/test_relay.c
+++ b/src/test/test_relay.c
@@ -91,14 +91,14 @@ test_relay_append_cell_to_circuit_queue(void *arg)
append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), nchan, cell,
CELL_DIRECTION_OUT, 0);
new_count = get_mock_scheduler_has_waiting_cells_count();
- tt_int_op(new_count, ==, old_count + 1);
+ tt_int_op(new_count, OP_EQ, old_count + 1);
/* Now try the reverse direction */
old_count = get_mock_scheduler_has_waiting_cells_count();
append_cell_to_circuit_queue(TO_CIRCUIT(orcirc), pchan, cell,
CELL_DIRECTION_IN, 0);
new_count = get_mock_scheduler_has_waiting_cells_count();
- tt_int_op(new_count, ==, old_count + 1);
+ tt_int_op(new_count, OP_EQ, old_count + 1);
UNMOCK(scheduler_channel_has_waiting_cells);
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
index feba8f664e..9354dd0480 100644
--- a/src/test/test_rendcache.c
+++ b/src/test/test_rendcache.c
@@ -21,22 +21,6 @@ static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \
REND_CACHE_MAX_SKEW + 60);
static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60;
-static rend_data_t *
-mock_rend_data(const char *onion_address)
-{
- rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data));
- rend_data_t *rend_query = &v2_data->base_;
- rend_query->version = 2;
-
- strlcpy(v2_data->onion_address, onion_address,
- sizeof(v2_data->onion_address));
- v2_data->auth_type = REND_NO_AUTH;
- rend_query->hsdirs_fp = smartlist_new();
- smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa",
- DIGEST_LEN));
- return rend_query;
-}
-
static void
test_rend_cache_lookup_entry(void *data)
{
@@ -74,6 +58,7 @@ test_rend_cache_lookup_entry(void *data)
tt_int_op(ret, OP_EQ, 0);
ret = rend_cache_lookup_entry(service_id, 2, &entry);
+ tt_int_op(ret, OP_EQ, 0);
tt_assert(entry);
tt_int_op(entry->len, OP_EQ, strlen(desc_holder->desc_str));
tt_str_op(entry->desc, OP_EQ, desc_holder->desc_str);
@@ -962,9 +947,9 @@ test_rend_cache_free_all(void *data)
rend_cache_free_all();
- tt_assert(!rend_cache);
- tt_assert(!rend_cache_v2_dir);
- tt_assert(!rend_cache_failure);
+ tt_ptr_op(rend_cache, OP_EQ, NULL);
+ tt_ptr_op(rend_cache_v2_dir, OP_EQ, NULL);
+ tt_ptr_op(rend_cache_failure, OP_EQ, NULL);
tt_assert(!rend_cache_total_allocation);
done:
diff --git a/src/test/test_replay.c b/src/test/test_replay.c
index 80e7203716..c379cafa7c 100644
--- a/src/test/test_replay.c
+++ b/src/test/test_replay.c
@@ -38,7 +38,7 @@ test_replaycache_alloc(void *arg)
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
done:
if (r) replaycache_free(r);
@@ -54,15 +54,15 @@ test_replaycache_badalloc(void *arg)
/* Negative horizon should fail */
(void)arg;
r = replaycache_new(-600, 300);
- tt_assert(r == NULL);
+ tt_ptr_op(r, OP_EQ, NULL);
/* Negative interval should get adjusted to zero */
r = replaycache_new(600, -300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
tt_int_op(r->scrub_interval,OP_EQ, 0);
replaycache_free(r);
/* Negative horizon and negative interval should still fail */
r = replaycache_new(-600, -300);
- tt_assert(r == NULL);
+ tt_ptr_op(r, OP_EQ, NULL);
done:
if (r) replaycache_free(r);
@@ -90,7 +90,7 @@ test_replaycache_miss(void *arg)
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
@@ -123,7 +123,7 @@ test_replaycache_hit(void *arg)
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
@@ -161,7 +161,7 @@ test_replaycache_age(void *arg)
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
@@ -193,7 +193,7 @@ test_replaycache_elapsed(void *arg)
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
@@ -220,7 +220,7 @@ test_replaycache_noexpire(void *arg)
(void)arg;
r = replaycache_new(0, 0);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
result =
replaycache_add_and_test_internal(1200, r, test_buffer,
@@ -251,7 +251,7 @@ test_replaycache_scrub(void *arg)
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
/* Set up like in test_replaycache_hit() */
result =
@@ -294,7 +294,7 @@ test_replaycache_future(void *arg)
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
/* Set up like in test_replaycache_hit() */
result =
@@ -343,7 +343,7 @@ test_replaycache_realtime(void *arg)
/* Test the realtime as well as *_internal() entry points */
(void)arg;
r = replaycache_new(600, 300);
- tt_assert(r != NULL);
+ tt_ptr_op(r, OP_NE, NULL);
/* This should miss */
result =
diff --git a/src/test/test_router.c b/src/test/test_router.c
new file mode 100644
index 0000000000..4e96e24534
--- /dev/null
+++ b/src/test/test_router.c
@@ -0,0 +1,112 @@
+/* Copyright (c) 2017, The Tor Project, Inc. */
+/* Copyright (c) 2017, isis agora lovecruft */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_router.c
+ * \brief Unittests for code in src/or/router.c
+ **/
+
+#include "or.h"
+#include "config.h"
+#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
+#include "router.h"
+#include "routerlist.h"
+
+/* Test suite stuff */
+#include "test.h"
+
+NS_DECL(const routerinfo_t *, router_get_my_routerinfo, (void));
+
+static routerinfo_t* mock_routerinfo;
+
+static const routerinfo_t*
+NS(router_get_my_routerinfo)(void)
+{
+ crypto_pk_t* ident_key;
+ crypto_pk_t* tap_key;
+ time_t now;
+
+ if (!mock_routerinfo) {
+ /* Mock the published timestamp, otherwise router_dump_router_to_string()
+ * will poop its pants. */
+ time(&now);
+
+ /* We'll need keys, or router_dump_router_to_string() would return NULL. */
+ ident_key = pk_generate(0);
+ tap_key = pk_generate(0);
+
+ tor_assert(ident_key != NULL);
+ tor_assert(tap_key != NULL);
+
+ mock_routerinfo = tor_malloc_zero(sizeof(routerinfo_t));
+ mock_routerinfo->nickname = tor_strdup("ConlonNancarrow");
+ mock_routerinfo->addr = 123456789;
+ mock_routerinfo->or_port = 443;
+ mock_routerinfo->platform = tor_strdup("unittest");
+ mock_routerinfo->cache_info.published_on = now;
+ mock_routerinfo->identity_pkey = crypto_pk_dup_key(ident_key);
+ mock_routerinfo->onion_pkey = crypto_pk_dup_key(tap_key);
+ mock_routerinfo->bandwidthrate = 9001;
+ mock_routerinfo->bandwidthburst = 9002;
+ }
+
+ return mock_routerinfo;
+}
+
+/* If no distribution option was set, then check_bridge_distribution_setting()
+ * should have set it to "any". */
+static void
+test_router_dump_router_to_string_no_bridge_distribution_method(void *arg)
+{
+ const char* needle = "bridge-distribution-request any";
+ or_options_t* options = get_options_mutable();
+ routerinfo_t* router = NULL;
+ curve25519_keypair_t ntor_keypair;
+ ed25519_keypair_t signing_keypair;
+ char* desc = NULL;
+ char* found = NULL;
+ (void)arg;
+
+ NS_MOCK(router_get_my_routerinfo);
+
+ options->ORPort_set = 1;
+ options->BridgeRelay = 1;
+
+ /* Generate keys which router_dump_router_to_string() expects to exist. */
+ tt_int_op(0, ==, curve25519_keypair_generate(&ntor_keypair, 0));
+ tt_int_op(0, ==, ed25519_keypair_generate(&signing_keypair, 0));
+
+ /* Set up part of our routerinfo_t so that we don't trigger any other
+ * assertions in router_dump_router_to_string(). */
+ router = (routerinfo_t*)router_get_my_routerinfo();
+ tt_ptr_op(router, !=, NULL);
+
+ router->onion_curve25519_pkey = &ntor_keypair.pubkey;
+
+ /* Generate our server descriptor and ensure that the substring
+ * "bridge-distribution-request any" occurs somewhere within it. */
+ desc = router_dump_router_to_string(router,
+ router->identity_pkey,
+ router->onion_pkey,
+ &ntor_keypair,
+ &signing_keypair);
+ tt_ptr_op(desc, !=, NULL);
+ found = strstr(desc, needle);
+ tt_ptr_op(found, !=, NULL);
+
+ done:
+ NS_UNMOCK(router_get_my_routerinfo);
+
+ tor_free(desc);
+}
+
+#define ROUTER_TEST(name, flags) \
+ { #name, test_router_ ## name, flags, NULL, NULL }
+
+struct testcase_t router_tests[] = {
+ ROUTER_TEST(dump_router_to_string_no_bridge_distribution_method, TT_FORK),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c
index db6b9b3872..d8b72651a0 100644
--- a/src/test/test_routerkeys.c
+++ b/src/test/test_routerkeys.c
@@ -92,8 +92,8 @@ test_routerkeys_ed_certs(void *args)
uint8_t *junk = NULL;
char *base64 = NULL;
- tt_int_op(0,==,ed25519_keypair_generate(&kp1, 0));
- tt_int_op(0,==,ed25519_keypair_generate(&kp2, 0));
+ tt_int_op(0,OP_EQ,ed25519_keypair_generate(&kp1, 0));
+ tt_int_op(0,OP_EQ,ed25519_keypair_generate(&kp2, 0));
for (int i = 0; i <= 1; ++i) {
uint32_t flags = i ? CERT_FLAG_INCLUDE_SIGNING_KEY : 0;
@@ -101,45 +101,45 @@ test_routerkeys_ed_certs(void *args)
cert[i] = tor_cert_create(&kp1, 5, &kp2.pubkey, now, 10000, flags);
tt_assert(cert[i]);
- tt_assert(cert[i]->sig_bad == 0);
- tt_assert(cert[i]->sig_ok == 1);
- tt_assert(cert[i]->cert_expired == 0);
- tt_assert(cert[i]->cert_valid == 1);
- tt_int_op(cert[i]->cert_type, ==, 5);
- tt_mem_op(cert[i]->signed_key.pubkey, ==, &kp2.pubkey.pubkey, 32);
- tt_mem_op(cert[i]->signing_key.pubkey, ==, &kp1.pubkey.pubkey, 32);
- tt_int_op(cert[i]->signing_key_included, ==, i);
+ tt_uint_op(cert[i]->sig_bad, OP_EQ, 0);
+ tt_uint_op(cert[i]->sig_ok, OP_EQ, 1);
+ tt_uint_op(cert[i]->cert_expired, OP_EQ, 0);
+ tt_uint_op(cert[i]->cert_valid, OP_EQ, 1);
+ tt_int_op(cert[i]->cert_type, OP_EQ, 5);
+ tt_mem_op(cert[i]->signed_key.pubkey, OP_EQ, &kp2.pubkey.pubkey, 32);
+ tt_mem_op(cert[i]->signing_key.pubkey, OP_EQ, &kp1.pubkey.pubkey, 32);
+ tt_int_op(cert[i]->signing_key_included, OP_EQ, i);
tt_assert(cert[i]->encoded);
- tt_int_op(cert[i]->encoded_len, ==, 104 + 36 * i);
- tt_int_op(cert[i]->encoded[0], ==, 1);
- tt_int_op(cert[i]->encoded[1], ==, 5);
+ tt_int_op(cert[i]->encoded_len, OP_EQ, 104 + 36 * i);
+ tt_int_op(cert[i]->encoded[0], OP_EQ, 1);
+ tt_int_op(cert[i]->encoded[1], OP_EQ, 5);
parsed_cert[i] = tor_cert_parse(cert[i]->encoded, cert[i]->encoded_len);
tt_assert(parsed_cert[i]);
- tt_int_op(cert[i]->encoded_len, ==, parsed_cert[i]->encoded_len);
- tt_mem_op(cert[i]->encoded, ==, parsed_cert[i]->encoded,
+ tt_int_op(cert[i]->encoded_len, OP_EQ, parsed_cert[i]->encoded_len);
+ tt_mem_op(cert[i]->encoded, OP_EQ, parsed_cert[i]->encoded,
cert[i]->encoded_len);
- tt_assert(parsed_cert[i]->sig_bad == 0);
- tt_assert(parsed_cert[i]->sig_ok == 0);
- tt_assert(parsed_cert[i]->cert_expired == 0);
- tt_assert(parsed_cert[i]->cert_valid == 0);
+ tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 0);
+ tt_uint_op(parsed_cert[i]->sig_ok, OP_EQ, 0);
+ tt_uint_op(parsed_cert[i]->cert_expired, OP_EQ, 0);
+ tt_uint_op(parsed_cert[i]->cert_valid, OP_EQ, 0);
/* Expired */
tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now + 30000),
- <, 0);
- tt_assert(parsed_cert[i]->cert_expired == 1);
+ OP_LT, 0);
+ tt_uint_op(parsed_cert[i]->cert_expired, OP_EQ, 1);
parsed_cert[i]->cert_expired = 0;
/* Wrong key */
- tt_int_op(tor_cert_checksig(parsed_cert[i], &kp2.pubkey, now), <, 0);
- tt_assert(parsed_cert[i]->sig_bad== 1);
+ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp2.pubkey, now), OP_LT, 0);
+ tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 1);
parsed_cert[i]->sig_bad = 0;
/* Missing key */
int ok = tor_cert_checksig(parsed_cert[i], NULL, now);
- tt_int_op(ok < 0, ==, i == 0);
- tt_assert(parsed_cert[i]->sig_bad == 0);
+ tt_int_op(ok < 0, OP_EQ, i == 0);
+ tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 0);
tt_assert(parsed_cert[i]->sig_ok == (i != 0));
tt_assert(parsed_cert[i]->cert_valid == (i != 0));
parsed_cert[i]->sig_bad = 0;
@@ -147,29 +147,29 @@ test_routerkeys_ed_certs(void *args)
parsed_cert[i]->cert_valid = 0;
/* Right key */
- tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now), ==, 0);
- tt_assert(parsed_cert[i]->sig_bad == 0);
- tt_assert(parsed_cert[i]->sig_ok == 1);
- tt_assert(parsed_cert[i]->cert_expired == 0);
- tt_assert(parsed_cert[i]->cert_valid == 1);
+ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now), OP_EQ, 0);
+ tt_uint_op(parsed_cert[i]->sig_bad, OP_EQ, 0);
+ tt_uint_op(parsed_cert[i]->sig_ok, OP_EQ, 1);
+ tt_uint_op(parsed_cert[i]->cert_expired, OP_EQ, 0);
+ tt_uint_op(parsed_cert[i]->cert_valid, OP_EQ, 1);
}
/* Now try some junky certs. */
/* - Truncated */
nocert = tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len-1);
- tt_ptr_op(NULL, ==, nocert);
+ tt_ptr_op(NULL, OP_EQ, nocert);
/* - First byte modified */
cert[0]->encoded[0] = 99;
nocert = tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len);
- tt_ptr_op(NULL, ==, nocert);
+ tt_ptr_op(NULL, OP_EQ, nocert);
cert[0]->encoded[0] = 1;
/* - Extra byte at the end*/
junk = tor_malloc_zero(cert[0]->encoded_len + 1);
memcpy(junk, cert[0]->encoded, cert[0]->encoded_len);
nocert = tor_cert_parse(junk, cert[0]->encoded_len+1);
- tt_ptr_op(NULL, ==, nocert);
+ tt_ptr_op(NULL, OP_EQ, nocert);
/* - Multiple signing key instances */
tor_free(junk);
@@ -183,7 +183,7 @@ test_routerkeys_ed_certs(void *args)
junk[77] = 32; /* extlen */
junk[78] = 4; /* exttype */
nocert = tor_cert_parse(junk, 104 + 36 * 2);
- tt_ptr_op(NULL, ==, nocert);
+ tt_ptr_op(NULL, OP_EQ, nocert);
done:
tor_cert_free(cert[0]);
@@ -211,11 +211,12 @@ test_routerkeys_ed_key_create(void *arg)
kp2 = ed_key_new(kp1, INIT_ED_KEY_NEEDCERT, now, 3600, 4, &cert);
tt_assert(kp2);
tt_assert(cert);
- tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, sizeof(ed25519_public_key_t));
+ tt_mem_op(&cert->signed_key, OP_EQ, &kp2->pubkey,
+ sizeof(ed25519_public_key_t));
tt_assert(! cert->signing_key_included);
- tt_int_op(cert->valid_until, >=, now);
- tt_int_op(cert->valid_until, <=, now+7200);
+ tt_int_op(cert->valid_until, OP_GE, now);
+ tt_int_op(cert->valid_until, OP_LE, now+7200);
/* Create a new key-including certificate signed by kp1 */
ed25519_keypair_free(kp2);
@@ -227,8 +228,10 @@ test_routerkeys_ed_key_create(void *arg)
tt_assert(kp2);
tt_assert(cert);
tt_assert(cert->signing_key_included);
- tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, sizeof(ed25519_public_key_t));
- tt_mem_op(&cert->signing_key, ==, &kp1->pubkey,sizeof(ed25519_public_key_t));
+ tt_mem_op(&cert->signed_key, OP_EQ, &kp2->pubkey,
+ sizeof(ed25519_public_key_t));
+ tt_mem_op(&cert->signing_key, OP_EQ, &kp1->pubkey,
+ sizeof(ed25519_public_key_t));
done:
ed25519_keypair_free(kp1);
@@ -261,8 +264,8 @@ test_routerkeys_ed_key_init_basic(void *arg)
NULL, now, 0, 7, &cert);
tt_assert(kp1 != NULL);
tt_assert(cert == NULL);
- tt_int_op(stat(get_fname("test_ed_key_1_cert"), &st), <, 0);
- tt_int_op(stat(get_fname("test_ed_key_1_secret_key"), &st), ==, 0);
+ tt_int_op(stat(get_fname("test_ed_key_1_cert"), &st), OP_LT, 0);
+ tt_int_op(stat(get_fname("test_ed_key_1_secret_key"), &st), OP_EQ, 0);
/* Fail to load if we say we need a cert */
kp2 = ed_key_init_from_file(fname1, INIT_ED_KEY_NEEDCERT, LOG_INFO,
@@ -279,14 +282,14 @@ test_routerkeys_ed_key_init_basic(void *arg)
NULL, now, 0, 7, &cert);
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
- tt_mem_op(kp1, ==, kp2, sizeof(*kp1));
+ tt_mem_op(kp1, OP_EQ, kp2, sizeof(*kp1));
ed25519_keypair_free(kp2); kp2 = NULL;
kp2 = ed_key_init_from_file(fname1, 0, LOG_INFO,
NULL, now, 0, 7, &cert);
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
- tt_mem_op(kp1, ==, kp2, sizeof(*kp1));
+ tt_mem_op(kp1, OP_EQ, kp2, sizeof(*kp1));
ed25519_keypair_free(kp2); kp2 = NULL;
/* Now create a key with a cert. */
@@ -295,34 +298,34 @@ test_routerkeys_ed_key_init_basic(void *arg)
LOG_INFO, kp1, now, 7200, 7, &cert);
tt_assert(kp2 != NULL);
tt_assert(cert != NULL);
- tt_mem_op(kp1, !=, kp2, sizeof(*kp1));
- tt_int_op(stat(get_fname("test_ed_key_2_cert"), &st), ==, 0);
- tt_int_op(stat(get_fname("test_ed_key_2_secret_key"), &st), ==, 0);
+ tt_mem_op(kp1, OP_NE, kp2, sizeof(*kp1));
+ tt_int_op(stat(get_fname("test_ed_key_2_cert"), &st), OP_EQ, 0);
+ tt_int_op(stat(get_fname("test_ed_key_2_secret_key"), &st), OP_EQ, 0);
tt_assert(cert->cert_valid == 1);
- tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, 32);
+ tt_mem_op(&cert->signed_key, OP_EQ, &kp2->pubkey, 32);
/* Now verify we can load the cert... */
kp3 = ed_key_init_from_file(fname2, (INIT_ED_KEY_CREATE|
INIT_ED_KEY_NEEDCERT),
LOG_INFO, kp1, now, 7200, 7, &cert2);
- tt_mem_op(kp2, ==, kp3, sizeof(*kp2));
- tt_mem_op(cert2->encoded, ==, cert->encoded, cert->encoded_len);
+ tt_mem_op(kp2, OP_EQ, kp3, sizeof(*kp2));
+ tt_mem_op(cert2->encoded, OP_EQ, cert->encoded, cert->encoded_len);
ed25519_keypair_free(kp3); kp3 = NULL;
tor_cert_free(cert2); cert2 = NULL;
/* ... even without create... */
kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT,
LOG_INFO, kp1, now, 7200, 7, &cert2);
- tt_mem_op(kp2, ==, kp3, sizeof(*kp2));
- tt_mem_op(cert2->encoded, ==, cert->encoded, cert->encoded_len);
+ tt_mem_op(kp2, OP_EQ, kp3, sizeof(*kp2));
+ tt_mem_op(cert2->encoded, OP_EQ, cert->encoded, cert->encoded_len);
ed25519_keypair_free(kp3); kp3 = NULL;
tor_cert_free(cert2); cert2 = NULL;
/* ... but that we don't crash or anything if we say we don't want it. */
kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT,
LOG_INFO, kp1, now, 7200, 7, NULL);
- tt_mem_op(kp2, ==, kp3, sizeof(*kp2));
+ tt_mem_op(kp2, OP_EQ, kp3, sizeof(*kp2));
ed25519_keypair_free(kp3); kp3 = NULL;
/* Fail if we're told the wrong signing key */
@@ -367,16 +370,16 @@ test_routerkeys_ed_key_init_split(void *arg)
LOG_INFO, NULL, now, 0, 7, &cert);
tt_assert(kp1 != NULL);
tt_assert(cert == NULL);
- tt_int_op(stat(get_fname("test_ed_key_3_cert"), &st), <, 0);
- tt_int_op(stat(get_fname("test_ed_key_3_secret_key"), &st), ==, 0);
- tt_int_op(stat(get_fname("test_ed_key_3_public_key"), &st), ==, 0);
+ tt_int_op(stat(get_fname("test_ed_key_3_cert"), &st), OP_LT, 0);
+ tt_int_op(stat(get_fname("test_ed_key_3_secret_key"), &st), OP_EQ, 0);
+ tt_int_op(stat(get_fname("test_ed_key_3_public_key"), &st), OP_EQ, 0);
/* Load it. */
kp2 = ed_key_init_from_file(fname1, flags|INIT_ED_KEY_CREATE,
LOG_INFO, NULL, now, 0, 7, &cert);
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
- tt_mem_op(kp1, ==, kp2, sizeof(*kp2));
+ tt_mem_op(kp1, OP_EQ, kp2, sizeof(*kp2));
ed25519_keypair_free(kp2); kp2 = NULL;
/* Okay, try killing the secret key and loading it. */
@@ -385,7 +388,7 @@ test_routerkeys_ed_key_init_split(void *arg)
LOG_INFO, NULL, now, 0, 7, &cert);
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
- tt_mem_op(&kp1->pubkey, ==, &kp2->pubkey, sizeof(kp2->pubkey));
+ tt_mem_op(&kp1->pubkey, OP_EQ, &kp2->pubkey, sizeof(kp2->pubkey));
tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
sizeof(kp2->seckey.seckey)));
ed25519_keypair_free(kp2); kp2 = NULL;
@@ -395,7 +398,7 @@ test_routerkeys_ed_key_init_split(void *arg)
LOG_INFO, NULL, now, 0, 7, &cert);
tt_assert(kp2 != NULL);
tt_assert(cert == NULL);
- tt_mem_op(&kp1->pubkey, ==, &kp2->pubkey, sizeof(kp2->pubkey));
+ tt_mem_op(&kp1->pubkey, OP_EQ, &kp2->pubkey, sizeof(kp2->pubkey));
tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
sizeof(kp2->seckey.seckey)));
ed25519_keypair_free(kp2); kp2 = NULL;
@@ -446,12 +449,12 @@ test_routerkeys_ed_keys_init_all(void *arg)
#else
mkdir(dir, 0700);
mkdir(get_fname("test_ed_keys_init_all/keys"), 0700);
-#endif
+#endif /* defined(_WIN32) */
options->DataDirectory = dir;
- tt_int_op(1, ==, load_ed_keys(options, now));
- tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
+ tt_int_op(1, OP_EQ, load_ed_keys(options, now));
+ tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0));
tt_assert(get_master_identity_key());
tt_assert(get_master_identity_key());
tt_assert(get_master_signing_keypair());
@@ -465,21 +468,21 @@ test_routerkeys_ed_keys_init_all(void *arg)
link_cert = tor_cert_dup(get_current_link_cert_cert());
/* Call load_ed_keys again, but nothing has changed. */
- tt_int_op(0, ==, load_ed_keys(options, now));
- tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
- tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
- tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
- tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth));
+ tt_int_op(0, OP_EQ, load_ed_keys(options, now));
+ tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0));
+ tt_mem_op(&id, OP_EQ, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, OP_EQ, get_master_signing_keypair(), sizeof(sign));
+ tt_mem_op(&auth, OP_EQ, get_current_auth_keypair(), sizeof(auth));
tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert()));
/* Force a reload: we make new link/auth keys. */
routerkeys_free_all();
- tt_int_op(1, ==, load_ed_keys(options, now));
- tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
- tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
- tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_int_op(1, OP_EQ, load_ed_keys(options, now));
+ tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0));
+ tt_mem_op(&id, OP_EQ, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, OP_EQ, get_master_signing_keypair(), sizeof(sign));
tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert()));
- tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_mem_op(&auth, OP_NE, get_current_auth_keypair(), sizeof(auth));
tt_assert(get_master_signing_key_cert());
tt_assert(get_current_link_cert_cert());
tt_assert(get_current_auth_key_cert());
@@ -488,12 +491,12 @@ test_routerkeys_ed_keys_init_all(void *arg)
memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
/* Force a link/auth-key regeneration by advancing time. */
- tt_int_op(0, ==, load_ed_keys(options, now+3*86400));
- tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400, 0));
- tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
- tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_int_op(0, OP_EQ, load_ed_keys(options, now+3*86400));
+ tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now+3*86400, 0));
+ tt_mem_op(&id, OP_EQ, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, OP_EQ, get_master_signing_keypair(), sizeof(sign));
tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
- tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_mem_op(&auth, OP_NE, get_current_auth_keypair(), sizeof(auth));
tt_assert(get_master_signing_key_cert());
tt_assert(get_current_link_cert_cert());
tt_assert(get_current_auth_key_cert());
@@ -502,12 +505,12 @@ test_routerkeys_ed_keys_init_all(void *arg)
memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
/* Force a signing-key regeneration by advancing time. */
- tt_int_op(1, ==, load_ed_keys(options, now+100*86400));
- tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400, 0));
- tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
- tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign));
+ tt_int_op(1, OP_EQ, load_ed_keys(options, now+100*86400));
+ tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now+100*86400, 0));
+ tt_mem_op(&id, OP_EQ, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, OP_NE, get_master_signing_keypair(), sizeof(sign));
tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
- tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_mem_op(&auth, OP_NE, get_current_auth_keypair(), sizeof(auth));
tt_assert(get_master_signing_key_cert());
tt_assert(get_current_link_cert_cert());
tt_assert(get_current_auth_key_cert());
@@ -520,12 +523,12 @@ test_routerkeys_ed_keys_init_all(void *arg)
routerkeys_free_all();
unlink(get_fname("test_ed_keys_init_all/keys/"
"ed25519_master_id_secret_key"));
- tt_int_op(1, ==, load_ed_keys(options, now));
- tt_int_op(0, ==, generate_ed_link_cert(options, now, 0));
- tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
- tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_int_op(1, OP_EQ, load_ed_keys(options, now));
+ tt_int_op(0, OP_EQ, generate_ed_link_cert(options, now, 0));
+ tt_mem_op(&id, OP_EQ, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, OP_EQ, get_master_signing_keypair(), sizeof(sign));
tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
- tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_mem_op(&auth, OP_NE, get_current_auth_keypair(), sizeof(auth));
tt_assert(get_master_signing_key_cert());
tt_assert(get_current_link_cert_cert());
tt_assert(get_current_auth_key_cert());
@@ -535,7 +538,7 @@ test_routerkeys_ed_keys_init_all(void *arg)
log_global_min_severity_ = LOG_ERR; /* Suppress warnings.
* XXX (better way to do this)? */
routerkeys_free_all();
- tt_int_op(-1, ==, load_ed_keys(options, now+200*86400));
+ tt_int_op(-1, OP_EQ, load_ed_keys(options, now+200*86400));
done:
tor_free(dir);
@@ -556,20 +559,20 @@ test_routerkeys_cross_certify_ntor(void *args)
time_t now = time(NULL);
int sign;
- tt_int_op(0, ==, ed25519_public_from_base64(&master_key,
+ tt_int_op(0, OP_EQ, ed25519_public_from_base64(&master_key,
"IamwritingthesetestsOnARainyAfternoonin2014"));
- tt_int_op(0, ==, curve25519_keypair_generate(&onion_keys, 0));
+ tt_int_op(0, OP_EQ, curve25519_keypair_generate(&onion_keys, 0));
cert = make_ntor_onion_key_crosscert(&onion_keys,
&master_key,
now, 10000,
&sign);
tt_assert(cert);
tt_assert(sign == 0 || sign == 1);
- tt_int_op(cert->cert_type, ==, CERT_TYPE_ONION_ID);
- tt_int_op(1, ==, ed25519_pubkey_eq(&cert->signed_key, &master_key));
- tt_int_op(0, ==, ed25519_public_key_from_curve25519_public_key(
+ tt_int_op(cert->cert_type, OP_EQ, CERT_TYPE_ONION_ID);
+ tt_int_op(1, OP_EQ, ed25519_pubkey_eq(&cert->signed_key, &master_key));
+ tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key(
&onion_check_key, &onion_keys.pubkey, sign));
- tt_int_op(0, ==, tor_cert_checksig(cert, &onion_check_key, now));
+ tt_int_op(0, OP_EQ, tor_cert_checksig(cert, &onion_check_key, now));
done:
tor_cert_free(cert);
@@ -587,7 +590,7 @@ test_routerkeys_cross_certify_tap(void *args)
char buf[128];
int n;
- tt_int_op(0, ==, ed25519_public_from_base64(&master_key,
+ tt_int_op(0, OP_EQ, ed25519_public_from_base64(&master_key,
"IAlreadyWroteTestsForRouterdescsUsingTheseX"));
cc = make_tap_onion_key_crosscert(onion_key,
@@ -598,14 +601,14 @@ test_routerkeys_cross_certify_tap(void *args)
n = crypto_pk_public_checksig(onion_key, buf, sizeof(buf),
(char*)cc, cc_len);
- tt_int_op(n,>,0);
- tt_int_op(n,==,52);
+ tt_int_op(n,OP_GT,0);
+ tt_int_op(n,OP_EQ,52);
crypto_pk_get_digest(id_key, digest);
- tt_mem_op(buf,==,digest,20);
- tt_mem_op(buf+20,==,master_key.pubkey,32);
+ tt_mem_op(buf,OP_EQ,digest,20);
+ tt_mem_op(buf+20,OP_EQ,master_key.pubkey,32);
- tt_int_op(0, ==, check_tap_onion_key_crosscert(cc, cc_len,
+ tt_int_op(0, OP_EQ, check_tap_onion_key_crosscert(cc, cc_len,
onion_key, &master_key, (uint8_t*)digest));
done:
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 0b4b6c5c44..3b0e943ce5 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -117,7 +117,7 @@ test_routerlist_launch_descriptor_downloads(void *arg)
MOCK(initiate_descriptor_downloads, mock_initiate_descriptor_downloads);
launch_descriptor_downloads(DIR_PURPOSE_FETCH_MICRODESC, downloadable,
NULL, now);
- tt_int_op(3, ==, count);
+ tt_int_op(3, OP_EQ, count);
UNMOCK(initiate_descriptor_downloads);
done:
@@ -148,24 +148,24 @@ construct_consensus(char **consensus_text_md)
&v1, &n_vrs, now, 1);
networkstatus_vote_free(vote);
tt_assert(v1);
- tt_int_op(n_vrs, ==, 4);
- tt_int_op(smartlist_len(v1->routerstatus_list), ==, 4);
+ tt_int_op(n_vrs, OP_EQ, 4);
+ tt_int_op(smartlist_len(v1->routerstatus_list), OP_EQ, 4);
dir_common_construct_vote_2(&vote, cert2, sign_skey_2,
&dir_common_gen_routerstatus_for_v3ns,
&v2, &n_vrs, now, 1);
networkstatus_vote_free(vote);
tt_assert(v2);
- tt_int_op(n_vrs, ==, 4);
- tt_int_op(smartlist_len(v2->routerstatus_list), ==, 4);
+ tt_int_op(n_vrs, OP_EQ, 4);
+ tt_int_op(smartlist_len(v2->routerstatus_list), OP_EQ, 4);
dir_common_construct_vote_3(&vote, cert3, sign_skey_3,
&dir_common_gen_routerstatus_for_v3ns,
&v3, &n_vrs, now, 1);
tt_assert(v3);
- tt_int_op(n_vrs, ==, 4);
- tt_int_op(smartlist_len(v3->routerstatus_list), ==, 4);
+ tt_int_op(n_vrs, OP_EQ, 4);
+ tt_int_op(smartlist_len(v3->routerstatus_list), OP_EQ, 4);
networkstatus_vote_free(vote);
votes = smartlist_new();
smartlist_add(votes, v1);
@@ -247,16 +247,16 @@ test_router_pick_directory_server_impl(void *arg)
/* No consensus available, fail early */
rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
- tt_assert(rs == NULL);
+ tt_ptr_op(rs, OP_EQ, NULL);
construct_consensus(&consensus_text_md);
tt_assert(consensus_text_md);
con_md = networkstatus_parse_vote_from_string(consensus_text_md, NULL,
NS_TYPE_CONSENSUS);
tt_assert(con_md);
- tt_int_op(con_md->flavor,==, FLAV_MICRODESC);
+ tt_int_op(con_md->flavor,OP_EQ, FLAV_MICRODESC);
tt_assert(con_md->routerstatus_list);
- tt_int_op(smartlist_len(con_md->routerstatus_list), ==, 3);
+ tt_int_op(smartlist_len(con_md->routerstatus_list), OP_EQ, 3);
tt_assert(!networkstatus_set_current_consensus_from_ns(con_md,
"microdesc"));
@@ -287,7 +287,7 @@ test_router_pick_directory_server_impl(void *arg)
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
/* We should not fail now we have a consensus and routerstatus_list
* and nodelist are populated. */
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
/* Manipulate the nodes so we get the dir server we expect */
router1_id = tor_malloc(DIGEST_LEN);
@@ -306,7 +306,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router1->is_running = 0;
node_router3->is_running = 0;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
rs = NULL;
node_router1->is_running = 1;
@@ -319,7 +319,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router1->rs->dir_port = 0;
node_router3->rs->dir_port = 0;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
rs = NULL;
node_router1->rs->is_v2_dir = 1;
@@ -330,7 +330,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router1->is_valid = 0;
node_router3->is_valid = 0;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN));
rs = NULL;
node_router1->is_valid = 1;
@@ -341,7 +341,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router2->rs->last_dir_503_at = now;
node_router3->rs->last_dir_503_at = now;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
node_router2->rs->last_dir_503_at = 0;
node_router3->rs->last_dir_503_at = 0;
@@ -358,13 +358,13 @@ test_router_pick_directory_server_impl(void *arg)
node_router2->rs->or_port = 443;
node_router3->rs->or_port = 442;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN));
node_router1->rs->or_port = 442;
node_router2->rs->or_port = 443;
node_router3->rs->or_port = 444;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
/* Fascist firewall and overloaded */
@@ -373,7 +373,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router3->rs->or_port = 442;
node_router3->rs->last_dir_503_at = now;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
node_router3->rs->last_dir_503_at = 0;
@@ -391,7 +391,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router3->rs->dir_port = 81;
node_router1->rs->last_dir_503_at = now;
rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL);
- tt_assert(rs != NULL);
+ tt_ptr_op(rs, OP_NE, NULL);
tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN));
node_router1->rs->last_dir_503_at = 0;
@@ -449,27 +449,27 @@ test_routerlist_router_is_already_dir_fetching(void *arg)
/* Test that we never get 1 from a NULL connection */
mocked_connection = NULL;
- tt_assert(router_is_already_dir_fetching(&test_ap, 1, 1) == 0);
- tt_assert(router_is_already_dir_fetching(&test_ap, 1, 0) == 0);
- tt_assert(router_is_already_dir_fetching(&test_ap, 0, 1) == 0);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 1, 1), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 1, 0), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 0, 1), OP_EQ, 0);
/* We always expect 0 in these cases */
- tt_assert(router_is_already_dir_fetching(&test_ap, 0, 0) == 0);
- tt_assert(router_is_already_dir_fetching(NULL, 1, 1) == 0);
- tt_assert(router_is_already_dir_fetching(&null_addr_ap, 1, 1) == 0);
- tt_assert(router_is_already_dir_fetching(&zero_port_ap, 1, 1) == 0);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 0, 0), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(NULL, 1, 1), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(&null_addr_ap, 1, 1), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(&zero_port_ap, 1, 1), OP_EQ, 0);
/* Test that we get 1 with a connection in the appropriate circumstances */
mocked_connection = connection_new(CONN_TYPE_DIR, AF_INET);
- tt_assert(router_is_already_dir_fetching(&test_ap, 1, 1) == 1);
- tt_assert(router_is_already_dir_fetching(&test_ap, 1, 0) == 1);
- tt_assert(router_is_already_dir_fetching(&test_ap, 0, 1) == 1);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 1, 1), OP_EQ, 1);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 1, 0), OP_EQ, 1);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 0, 1), OP_EQ, 1);
/* Test that we get 0 even with a connection in the appropriate
* circumstances */
- tt_assert(router_is_already_dir_fetching(&test_ap, 0, 0) == 0);
- tt_assert(router_is_already_dir_fetching(NULL, 1, 1) == 0);
- tt_assert(router_is_already_dir_fetching(&null_addr_ap, 1, 1) == 0);
- tt_assert(router_is_already_dir_fetching(&zero_port_ap, 1, 1) == 0);
+ tt_int_op(router_is_already_dir_fetching(&test_ap, 0, 0), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(NULL, 1, 1), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(&null_addr_ap, 1, 1), OP_EQ, 0);
+ tt_int_op(router_is_already_dir_fetching(&zero_port_ap, 1, 1), OP_EQ, 0);
done:
/* If a connection is never set up, connection_free chokes on it. */
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index 7efd042ed5..c9c69911da 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -1602,7 +1602,7 @@ NS(test_main)(void *arg)
*/
NS_DECL(const node_t *, node_get_by_nickname,
- (const char *nickname, int warn_if_unused));
+ (const char *nickname, unsigned flags));
static const char *NS(mock_nickname);
static void
@@ -1632,11 +1632,11 @@ NS(test_main)(void *arg)
}
const node_t *
-NS(node_get_by_nickname)(const char *nickname, int warn_if_unused)
+NS(node_get_by_nickname)(const char *nickname, unsigned flags)
{
CALLED(node_get_by_nickname)++;
tt_str_op(nickname, OP_EQ, NS(mock_nickname));
- tt_int_op(warn_if_unused, OP_EQ, 1);
+ tt_uint_op(flags, OP_EQ, 0);
done:
return NULL;
@@ -1651,7 +1651,7 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused)
*/
NS_DECL(const node_t *, node_get_by_nickname,
- (const char *nickname, int warn_if_unused));
+ (const char *nickname, unsigned flags));
static const char *NS(mock_nickname);
static node_t NS(mock_node);
@@ -1683,11 +1683,11 @@ NS(test_main)(void *arg)
}
const node_t *
-NS(node_get_by_nickname)(const char *nickname, int warn_if_unused)
+NS(node_get_by_nickname)(const char *nickname, unsigned flags)
{
CALLED(node_get_by_nickname)++;
tt_str_op(nickname, OP_EQ, NS(mock_nickname));
- tt_int_op(warn_if_unused, OP_EQ, 1);
+ tt_int_op(flags, OP_EQ, 0);
done:
return &NS(mock_node);
@@ -1701,7 +1701,7 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused)
*/
NS_DECL(const node_t *, node_get_by_nickname,
- (const char *nickname, int warn_if_unused));
+ (const char *nickname, unsigned flags));
static char *NS(mock_nickname);
static node_t NS(mock_node);
@@ -1735,11 +1735,11 @@ NS(test_main)(void *arg)
}
const node_t *
-NS(node_get_by_nickname)(const char *nickname, int warn_if_unused)
+NS(node_get_by_nickname)(const char *nickname, unsigned flags)
{
CALLED(node_get_by_nickname)++;
tt_str_op(nickname, OP_EQ, NS(mock_nickname));
- tt_int_op(warn_if_unused, OP_EQ, 1);
+ tt_int_op(flags, OP_EQ, 0);
done:
return &NS(mock_node);
diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c
index 4c536b0905..724a6b56b2 100644
--- a/src/test/test_scheduler.c
+++ b/src/test/test_scheduler.c
@@ -6,11 +6,16 @@
#include <math.h>
#include <event2/event.h>
+#define SCHEDULER_KIST_PRIVATE
#define TOR_CHANNEL_INTERNAL_
#define CHANNEL_PRIVATE_
#include "or.h"
+#include "config.h"
#include "compat_libevent.h"
#include "channel.h"
+#include "channeltls.h"
+#include "connection.h"
+#include "networkstatus.h"
#define SCHEDULER_PRIVATE_
#include "scheduler.h"
@@ -18,64 +23,100 @@
#include "test.h"
#include "fakechans.h"
-/* Event base for scheduelr tests */
-static struct event_base *mock_event_base = NULL;
-
-/* Statics controlling mocks */
-static circuitmux_t *mock_ccm_tgt_1 = NULL;
-static circuitmux_t *mock_ccm_tgt_2 = NULL;
+/* Shamelessly stolen from compat_libevent.c */
+#define V(major, minor, patch) \
+ (((major) << 24) | ((minor) << 16) | ((patch) << 8))
-static circuitmux_t *mock_cgp_tgt_1 = NULL;
-static circuitmux_policy_t *mock_cgp_val_1 = NULL;
-static circuitmux_t *mock_cgp_tgt_2 = NULL;
-static circuitmux_policy_t *mock_cgp_val_2 = NULL;
+/******************************************************************************
+ * Statistical info
+ *****************************************************************************/
static int scheduler_compare_channels_mock_ctr = 0;
static int scheduler_run_mock_ctr = 0;
-static void channel_flush_some_cells_mock_free_all(void);
-static void channel_flush_some_cells_mock_set(channel_t *chan,
- ssize_t num_cells);
+/******************************************************************************
+ * Utility functions and things we need to mock
+ *****************************************************************************/
+static or_options_t mocked_options;
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mocked_options;
+}
-/* Setup for mock event stuff */
-static void mock_event_free_all(void);
-static void mock_event_init(void);
+static void
+cleanup_scheduler_options(void)
+{
+ if (mocked_options.SchedulerTypes_) {
+ SMARTLIST_FOREACH(mocked_options.SchedulerTypes_, int *, i, tor_free(i));
+ smartlist_free(mocked_options.SchedulerTypes_);
+ mocked_options.SchedulerTypes_ = NULL;
+ }
+}
+
+static void
+set_scheduler_options(int val)
+{
+ int *type;
-/* Mocks used by scheduler tests */
-static ssize_t channel_flush_some_cells_mock(channel_t *chan,
- ssize_t num_cells);
-static int circuitmux_compare_muxes_mock(circuitmux_t *cmux_1,
- circuitmux_t *cmux_2);
-static const circuitmux_policy_t * circuitmux_get_policy_mock(
- circuitmux_t *cmux);
-static int scheduler_compare_channels_mock(const void *c1_v,
- const void *c2_v);
-static void scheduler_run_noop_mock(void);
-static struct event_base * tor_libevent_get_base_mock(void);
-
-/* Scheduler test cases */
-static void test_scheduler_channel_states(void *arg);
-static void test_scheduler_compare_channels(void *arg);
-static void test_scheduler_initfree(void *arg);
-static void test_scheduler_loop(void *arg);
-static void test_scheduler_queue_heuristic(void *arg);
-
-/* Mock event init/free */
+ if (mocked_options.SchedulerTypes_ == NULL) {
+ mocked_options.SchedulerTypes_ = smartlist_new();
+ }
+ type = tor_malloc_zero(sizeof(int));
+ *type = val;
+ smartlist_add(mocked_options.SchedulerTypes_, type);
+}
-/* Shamelessly stolen from compat_libevent.c */
-#define V(major, minor, patch) \
- (((major) << 24) | ((minor) << 16) | ((patch) << 8))
+static void
+clear_options(void)
+{
+ cleanup_scheduler_options();
+ memset(&mocked_options, 0, sizeof(mocked_options));
+}
+
+static int32_t
+mock_vanilla_networkstatus_get_param(
+ const networkstatus_t *ns, const char *param_name, int32_t default_val,
+ int32_t min_val, int32_t max_val)
+{
+ (void)ns;
+ (void)default_val;
+ (void)min_val;
+ (void)max_val;
+ // only support KISTSchedRunInterval right now
+ tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0);
+ return 0;
+}
+
+static int32_t
+mock_kist_networkstatus_get_param(
+ const networkstatus_t *ns, const char *param_name, int32_t default_val,
+ int32_t min_val, int32_t max_val)
+{
+ (void)ns;
+ (void)default_val;
+ (void)min_val;
+ (void)max_val;
+ // only support KISTSchedRunInterval right now
+ tor_assert(strcmp(param_name, "KISTSchedRunInterval")==0);
+ return 12;
+}
+/* Event base for scheduelr tests */
+static struct event_base *mock_event_base = NULL;
+/* Setup for mock event stuff */
+static void mock_event_free_all(void);
+static void mock_event_init(void);
static void
mock_event_free_all(void)
{
- tt_assert(mock_event_base != NULL);
+ tt_ptr_op(mock_event_base, OP_NE, NULL);
if (mock_event_base) {
event_base_free(mock_event_base);
mock_event_base = NULL;
}
- tt_ptr_op(mock_event_base, ==, NULL);
+ tt_ptr_op(mock_event_base, OP_EQ, NULL);
done:
return;
@@ -86,7 +127,7 @@ mock_event_init(void)
{
struct event_config *cfg = NULL;
- tt_ptr_op(mock_event_base, ==, NULL);
+ tt_ptr_op(mock_event_base, OP_EQ, NULL);
/*
* Really cut down from tor_libevent_initialize of
@@ -104,13 +145,90 @@ mock_event_init(void)
event_config_free(cfg);
}
- tt_assert(mock_event_base != NULL);
+ tt_ptr_op(mock_event_base, OP_NE, NULL);
done:
return;
}
-/* Mocks */
+static struct event_base *
+tor_libevent_get_base_mock(void)
+{
+ return mock_event_base;
+}
+
+static int
+scheduler_compare_channels_mock(const void *c1_v,
+ const void *c2_v)
+{
+ uintptr_t p1, p2;
+
+ p1 = (uintptr_t)(c1_v);
+ p2 = (uintptr_t)(c2_v);
+
+ ++scheduler_compare_channels_mock_ctr;
+
+ if (p1 == p2) return 0;
+ else if (p1 < p2) return 1;
+ else return -1;
+}
+
+static void
+scheduler_run_noop_mock(void)
+{
+ ++scheduler_run_mock_ctr;
+}
+
+static circuitmux_t *mock_ccm_tgt_1 = NULL;
+static circuitmux_t *mock_ccm_tgt_2 = NULL;
+static circuitmux_t *mock_cgp_tgt_1 = NULL;
+static circuitmux_policy_t *mock_cgp_val_1 = NULL;
+static circuitmux_t *mock_cgp_tgt_2 = NULL;
+static circuitmux_policy_t *mock_cgp_val_2 = NULL;
+
+static const circuitmux_policy_t *
+circuitmux_get_policy_mock(circuitmux_t *cmux)
+{
+ const circuitmux_policy_t *result = NULL;
+
+ tt_assert(cmux != NULL);
+ if (cmux) {
+ if (cmux == mock_cgp_tgt_1) result = mock_cgp_val_1;
+ else if (cmux == mock_cgp_tgt_2) result = mock_cgp_val_2;
+ else result = circuitmux_get_policy__real(cmux);
+ }
+
+ done:
+ return result;
+}
+
+static int
+circuitmux_compare_muxes_mock(circuitmux_t *cmux_1,
+ circuitmux_t *cmux_2)
+{
+ int result = 0;
+
+ tt_assert(cmux_1 != NULL);
+ tt_assert(cmux_2 != NULL);
+
+ if (cmux_1 != cmux_2) {
+ if (cmux_1 == mock_ccm_tgt_1 && cmux_2 == mock_ccm_tgt_2) result = -1;
+ else if (cmux_1 == mock_ccm_tgt_2 && cmux_2 == mock_ccm_tgt_1) {
+ result = 1;
+ } else {
+ if (cmux_1 == mock_ccm_tgt_1 || cmux_1 == mock_ccm_tgt_2) result = -1;
+ else if (cmux_2 == mock_ccm_tgt_1 || cmux_2 == mock_ccm_tgt_2) {
+ result = 1;
+ } else {
+ result = circuitmux_compare_muxes__real(cmux_1, cmux_2);
+ }
+ }
+ }
+ /* else result = 0 always */
+
+ done:
+ return result;
+}
typedef struct {
const channel_t *chan;
@@ -174,6 +292,67 @@ channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells)
}
}
+static int
+channel_more_to_flush_mock(channel_t *chan)
+{
+ tor_assert(chan);
+
+ flush_mock_channel_t *found_mock_ch = NULL;
+
+ /* Check if we have any queued */
+ if (! TOR_SIMPLEQ_EMPTY(&chan->incoming_queue))
+ return 1;
+
+ SMARTLIST_FOREACH_BEGIN(chans_for_flush_mock,
+ flush_mock_channel_t *,
+ flush_mock_ch) {
+ if (flush_mock_ch != NULL && flush_mock_ch->chan != NULL) {
+ if (flush_mock_ch->chan == chan) {
+ /* Found it */
+ found_mock_ch = flush_mock_ch;
+ break;
+ }
+ } else {
+ /* That shouldn't be there... */
+ SMARTLIST_DEL_CURRENT(chans_for_flush_mock, flush_mock_ch);
+ tor_free(flush_mock_ch);
+ }
+ } SMARTLIST_FOREACH_END(flush_mock_ch);
+
+ tor_assert(found_mock_ch);
+
+ /* Check if any circuits would like to queue some */
+ /* special for the mock: return the number of cells (instead of 1), or zero
+ * if nothing to flush */
+ return (found_mock_ch->cells > 0 ? (int)found_mock_ch->cells : 0 );
+}
+
+static void
+channel_write_to_kernel_mock(channel_t *chan)
+{
+ (void)chan;
+ //log_debug(LD_SCHED, "chan=%d writing to kernel",
+ // (int)chan->global_identifier);
+}
+
+static int
+channel_should_write_to_kernel_mock(outbuf_table_t *ot, channel_t *chan)
+{
+ (void)ot;
+ (void)chan;
+ return 1;
+ /* We could make this more complicated if we wanted. But I don't think doing
+ * so tests much of anything */
+ //static int called_counter = 0;
+ //if (++called_counter >= 3) {
+ // called_counter -= 3;
+ // log_debug(LD_SCHED, "chan=%d should write to kernel",
+ // (int)chan->global_identifier);
+ // return 1;
+ //}
+ //return 0;
+}
+
static ssize_t
channel_flush_some_cells_mock(channel_t *chan, ssize_t num_cells)
{
@@ -181,7 +360,7 @@ channel_flush_some_cells_mock(channel_t *chan, ssize_t num_cells)
char unlimited = 0;
flush_mock_channel_t *found = NULL;
- tt_assert(chan != NULL);
+ tt_ptr_op(chan, OP_NE, NULL);
if (chan) {
if (num_cells < 0) {
num_cells = 0;
@@ -215,11 +394,6 @@ channel_flush_some_cells_mock(channel_t *chan, ssize_t num_cells)
flushed += max;
found->cells -= max;
-
- if (found->cells <= 0) {
- smartlist_remove(chans_for_flush_mock, found);
- tor_free(found);
- }
}
}
}
@@ -228,90 +402,26 @@ channel_flush_some_cells_mock(channel_t *chan, ssize_t num_cells)
return flushed;
}
-static int
-circuitmux_compare_muxes_mock(circuitmux_t *cmux_1,
- circuitmux_t *cmux_2)
-{
- int result = 0;
-
- tt_assert(cmux_1 != NULL);
- tt_assert(cmux_2 != NULL);
-
- if (cmux_1 != cmux_2) {
- if (cmux_1 == mock_ccm_tgt_1 && cmux_2 == mock_ccm_tgt_2) result = -1;
- else if (cmux_1 == mock_ccm_tgt_2 && cmux_2 == mock_ccm_tgt_1) {
- result = 1;
- } else {
- if (cmux_1 == mock_ccm_tgt_1 || cmux_1 == mock_ccm_tgt_2) result = -1;
- else if (cmux_2 == mock_ccm_tgt_1 || cmux_2 == mock_ccm_tgt_2) {
- result = 1;
- } else {
- result = circuitmux_compare_muxes__real(cmux_1, cmux_2);
- }
- }
- }
- /* else result = 0 always */
-
- done:
- return result;
-}
-
-static const circuitmux_policy_t *
-circuitmux_get_policy_mock(circuitmux_t *cmux)
-{
- const circuitmux_policy_t *result = NULL;
-
- tt_assert(cmux != NULL);
- if (cmux) {
- if (cmux == mock_cgp_tgt_1) result = mock_cgp_val_1;
- else if (cmux == mock_cgp_tgt_2) result = mock_cgp_val_2;
- else result = circuitmux_get_policy__real(cmux);
- }
-
- done:
- return result;
-}
-
-static int
-scheduler_compare_channels_mock(const void *c1_v,
- const void *c2_v)
-{
- uintptr_t p1, p2;
-
- p1 = (uintptr_t)(c1_v);
- p2 = (uintptr_t)(c2_v);
-
- ++scheduler_compare_channels_mock_ctr;
-
- if (p1 == p2) return 0;
- else if (p1 < p2) return 1;
- else return -1;
-}
-
static void
-scheduler_run_noop_mock(void)
+update_socket_info_impl_mock(socket_table_ent_t *ent)
{
- ++scheduler_run_mock_ctr;
+ ent->cwnd = ent->unacked = ent->mss = ent->notsent = 0;
+ ent->limit = INT_MAX;
}
-static struct event_base *
-tor_libevent_get_base_mock(void)
-{
- return mock_event_base;
-}
-
-/* Test cases */
-
static void
-test_scheduler_channel_states(void *arg)
+perform_channel_state_tests(int KISTSchedRunInterval, int sched_type)
{
channel_t *ch1 = NULL, *ch2 = NULL;
int old_count;
- (void)arg;
+ /* setup options so we're sure about what sched we are running */
+ MOCK(get_options, mock_get_options);
+ clear_options();
+ mocked_options.KISTSchedRunInterval = KISTSchedRunInterval;
+ set_scheduler_options(sched_type);
/* Set up libevent and scheduler */
-
mock_event_init();
MOCK(tor_libevent_get_base, tor_libevent_get_base_mock);
scheduler_init();
@@ -324,9 +434,9 @@ test_scheduler_channel_states(void *arg)
* Disable scheduler_run so we can just check the state transitions
* without having to make everything it might call work too.
*/
- MOCK(scheduler_run, scheduler_run_noop_mock);
+ ((scheduler_t *) the_scheduler)->run = scheduler_run_noop_mock;
- tt_int_op(smartlist_len(channels_pending), ==, 0);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 0);
/* Set up a fake channel */
ch1 = new_fake_channel();
@@ -341,7 +451,7 @@ test_scheduler_channel_states(void *arg)
tt_assert(ch1->registered);
/* It should start off in SCHED_CHAN_IDLE */
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_IDLE);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
/* Now get another one */
ch2 = new_fake_channel();
@@ -351,57 +461,69 @@ test_scheduler_channel_states(void *arg)
channel_register(ch2);
tt_assert(ch2->registered);
- /* Send it to SCHED_CHAN_WAITING_TO_WRITE */
+ /* Send ch1 to SCHED_CHAN_WAITING_TO_WRITE */
scheduler_channel_has_waiting_cells(ch1);
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_WAITING_TO_WRITE);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
/* This should send it to SCHED_CHAN_PENDING */
scheduler_channel_wants_writes(ch1);
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_PENDING);
- tt_int_op(smartlist_len(channels_pending), ==, 1);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
/* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */
scheduler_channel_wants_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
/* Drop ch2 back to idle */
scheduler_channel_doesnt_want_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_IDLE);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
/* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */
scheduler_channel_wants_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
/* ...and this should kick ch2 into SCHED_CHAN_PENDING */
scheduler_channel_has_waiting_cells(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING);
- tt_int_op(smartlist_len(channels_pending), ==, 2);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
/* This should send ch2 to SCHED_CHAN_WAITING_TO_WRITE */
scheduler_channel_doesnt_want_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_TO_WRITE);
- tt_int_op(smartlist_len(channels_pending), ==, 1);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
/* ...and back to SCHED_CHAN_PENDING */
scheduler_channel_wants_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING);
- tt_int_op(smartlist_len(channels_pending), ==, 2);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
/* Now we exercise scheduler_touch_channel */
old_count = scheduler_compare_channels_mock_ctr;
scheduler_touch_channel(ch1);
tt_assert(scheduler_compare_channels_mock_ctr > old_count);
+ /* Release the ch2 and then do it another time to make sure it doesn't blow
+ * up and we are still in a quiescent state. */
+ scheduler_release_channel(ch2);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
+ /* Cheat a bit so make the release more confused but also will tells us if
+ * the release did put the channel in the right state. */
+ ch2->scheduler_state = SCHED_CHAN_PENDING;
+ scheduler_release_channel(ch2);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
+
/* Close */
channel_mark_for_close(ch1);
- tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSING);
channel_mark_for_close(ch2);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSING);
channel_closed(ch1);
- tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSED);
ch1 = NULL;
channel_closed(ch2);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSED);
ch2 = NULL;
/* Shut things down */
@@ -415,8 +537,9 @@ test_scheduler_channel_states(void *arg)
tor_free(ch2);
UNMOCK(scheduler_compare_channels);
- UNMOCK(scheduler_run);
UNMOCK(tor_libevent_get_base);
+ UNMOCK(get_options);
+ cleanup_scheduler_options();
return;
}
@@ -464,21 +587,21 @@ test_scheduler_compare_channels(void *arg)
/* Equal-channel case */
result = scheduler_compare_channels(&c1, &c1);
- tt_int_op(result, ==, 0);
+ tt_int_op(result, OP_EQ, 0);
/* Distinct channels, distinct policies */
result = scheduler_compare_channels(&c1, &c2);
- tt_int_op(result, ==, -1);
+ tt_int_op(result, OP_EQ, -1);
result = scheduler_compare_channels(&c2, &c1);
- tt_int_op(result, ==, 1);
+ tt_int_op(result, OP_EQ, 1);
/* Distinct channels, same policy */
tor_free(mock_cgp_val_2);
mock_cgp_val_2 = mock_cgp_val_1;
result = scheduler_compare_channels(&c1, &c2);
- tt_int_op(result, ==, -1);
+ tt_int_op(result, OP_EQ, -1);
result = scheduler_compare_channels(&c2, &c1);
- tt_int_op(result, ==, 1);
+ tt_int_op(result, OP_EQ, 1);
done:
@@ -502,40 +625,22 @@ test_scheduler_compare_channels(void *arg)
return;
}
-static void
-test_scheduler_initfree(void *arg)
-{
- (void)arg;
-
- tt_ptr_op(channels_pending, ==, NULL);
- tt_ptr_op(run_sched_ev, ==, NULL);
-
- mock_event_init();
- MOCK(tor_libevent_get_base, tor_libevent_get_base_mock);
-
- scheduler_init();
-
- tt_assert(channels_pending != NULL);
- tt_assert(run_sched_ev != NULL);
-
- scheduler_free_all();
-
- UNMOCK(tor_libevent_get_base);
- mock_event_free_all();
-
- tt_ptr_op(channels_pending, ==, NULL);
- tt_ptr_op(run_sched_ev, ==, NULL);
-
- done:
- return;
-}
+/******************************************************************************
+ * The actual tests!
+ *****************************************************************************/
static void
-test_scheduler_loop(void *arg)
+test_scheduler_loop_vanilla(void *arg)
{
+ (void)arg;
channel_t *ch1 = NULL, *ch2 = NULL;
+ void (*run_func_ptr)(void);
- (void)arg;
+ /* setup options so we're sure about what sched we are running */
+ MOCK(get_options, mock_get_options);
+ clear_options();
+ set_scheduler_options(SCHEDULER_VANILLA);
+ mocked_options.KISTSchedRunInterval = 0;
/* Set up libevent and scheduler */
@@ -551,12 +656,14 @@ test_scheduler_loop(void *arg)
* Disable scheduler_run so we can just check the state transitions
* without having to make everything it might call work too.
*/
- MOCK(scheduler_run, scheduler_run_noop_mock);
+ run_func_ptr = the_scheduler->run;
+ ((scheduler_t *) the_scheduler)->run = scheduler_run_noop_mock;
- tt_int_op(smartlist_len(channels_pending), ==, 0);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 0);
/* Set up a fake channel */
ch1 = new_fake_channel();
+ ch1->magic = TLS_CHAN_MAGIC;
tt_assert(ch1);
/* Start it off in OPENING */
@@ -567,13 +674,14 @@ test_scheduler_loop(void *arg)
channel_register(ch1);
tt_assert(ch1->registered);
/* Finish opening it */
- channel_change_state(ch1, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch1);
/* It should start off in SCHED_CHAN_IDLE */
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_IDLE);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
/* Now get another one */
ch2 = new_fake_channel();
+ ch2->magic = TLS_CHAN_MAGIC;
tt_assert(ch2);
ch2->state = CHANNEL_STATE_OPENING;
ch2->cmux = circuitmux_alloc();
@@ -584,68 +692,62 @@ test_scheduler_loop(void *arg)
* zero and we'll get coverage of that exception case in scheduler_run()
*/
- tt_int_op(ch1->state, ==, CHANNEL_STATE_OPEN);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_OPENING);
+ tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_OPEN);
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_OPENING);
/* Send it to SCHED_CHAN_WAITING_TO_WRITE */
scheduler_channel_has_waiting_cells(ch1);
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_WAITING_TO_WRITE);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_TO_WRITE);
/* This should send it to SCHED_CHAN_PENDING */
scheduler_channel_wants_writes(ch1);
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_PENDING);
- tt_int_op(smartlist_len(channels_pending), ==, 1);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 1);
/* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */
scheduler_channel_wants_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
/* Drop ch2 back to idle */
scheduler_channel_doesnt_want_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_IDLE);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
/* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */
scheduler_channel_wants_writes(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
/* ...and this should kick ch2 into SCHED_CHAN_PENDING */
scheduler_channel_has_waiting_cells(ch2);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING);
- tt_int_op(smartlist_len(channels_pending), ==, 2);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
/*
* Now we've got two pending channels and need to fire off
- * scheduler_run(); first, unmock it.
+ * the scheduler run() that we kept.
*/
-
- UNMOCK(scheduler_run);
-
- scheduler_run();
-
- /* Now re-mock it */
- MOCK(scheduler_run, scheduler_run_noop_mock);
+ run_func_ptr();
/*
* Assert that they're still in the states we left and aren't still
* pending
*/
- tt_int_op(ch1->state, ==, CHANNEL_STATE_OPEN);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_OPENING);
+ tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_OPEN);
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_OPENING);
tt_assert(ch1->scheduler_state != SCHED_CHAN_PENDING);
tt_assert(ch2->scheduler_state != SCHED_CHAN_PENDING);
- tt_int_op(smartlist_len(channels_pending), ==, 0);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 0);
/* Now, finish opening ch2, and get both back to pending */
- channel_change_state(ch2, CHANNEL_STATE_OPEN);
+ channel_change_state_open(ch2);
scheduler_channel_wants_writes(ch1);
scheduler_channel_wants_writes(ch2);
scheduler_channel_has_waiting_cells(ch1);
scheduler_channel_has_waiting_cells(ch2);
- tt_int_op(ch1->state, ==, CHANNEL_STATE_OPEN);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_OPEN);
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_PENDING);
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_PENDING);
- tt_int_op(smartlist_len(channels_pending), ==, 2);
+ tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_OPEN);
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_OPEN);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(channels_pending), OP_EQ, 2);
/* Now, set up the channel_flush_some_cells() mock */
MOCK(channel_flush_some_cells, channel_flush_some_cells_mock);
@@ -661,38 +763,33 @@ test_scheduler_loop(void *arg)
channel_flush_some_cells_mock_set(ch2, 48);
/*
- * And re-run the scheduler_run() loop with non-zero returns from
+ * And re-run the scheduler run() loop with non-zero returns from
* channel_flush_some_cells() this time.
*/
- UNMOCK(scheduler_run);
-
- scheduler_run();
-
- /* Now re-mock it */
- MOCK(scheduler_run, scheduler_run_noop_mock);
+ run_func_ptr();
/*
* ch1 should have gone to SCHED_CHAN_WAITING_FOR_CELLS, with 16 flushed
* and 32 writeable.
*/
- tt_int_op(ch1->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS);
+ tt_int_op(ch1->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
/*
* ...ch2 should also have gone to SCHED_CHAN_WAITING_FOR_CELLS, with
* channel_more_to_flush() returning false and channel_num_cells_writeable()
* > 0/
*/
- tt_int_op(ch2->scheduler_state, ==, SCHED_CHAN_WAITING_FOR_CELLS);
+ tt_int_op(ch2->scheduler_state, OP_EQ, SCHED_CHAN_WAITING_FOR_CELLS);
/* Close */
channel_mark_for_close(ch1);
- tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSING);
channel_mark_for_close(ch2);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSING);
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSING);
channel_closed(ch1);
- tt_int_op(ch1->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch1->state, OP_EQ, CHANNEL_STATE_CLOSED);
ch1 = NULL;
channel_closed(ch2);
- tt_int_op(ch2->state, ==, CHANNEL_STATE_CLOSED);
+ tt_int_op(ch2->state, OP_EQ, CHANNEL_STATE_CLOSED);
ch2 = NULL;
/* Shut things down */
@@ -704,55 +801,297 @@ test_scheduler_loop(void *arg)
done:
tor_free(ch1);
tor_free(ch2);
+ cleanup_scheduler_options();
UNMOCK(channel_flush_some_cells);
UNMOCK(scheduler_compare_channels);
- UNMOCK(scheduler_run);
UNMOCK(tor_libevent_get_base);
+ UNMOCK(get_options);
+}
+
+static void
+test_scheduler_loop_kist(void *arg)
+{
+ (void) arg;
+
+#ifndef HAVE_KIST_SUPPORT
+ return;
+#endif
+
+ channel_t *ch1 = new_fake_channel(), *ch2 = new_fake_channel();
+ channel_t *ch3 = new_fake_channel();
+
+ /* setup options so we're sure about what sched we are running */
+ MOCK(get_options, mock_get_options);
+ MOCK(channel_flush_some_cells, channel_flush_some_cells_mock);
+ MOCK(channel_more_to_flush, channel_more_to_flush_mock);
+ MOCK(channel_write_to_kernel, channel_write_to_kernel_mock);
+ MOCK(channel_should_write_to_kernel, channel_should_write_to_kernel_mock);
+ MOCK(update_socket_info_impl, update_socket_info_impl_mock);
+ clear_options();
+ mocked_options.KISTSchedRunInterval = 11;
+ set_scheduler_options(SCHEDULER_KIST);
+ scheduler_init();
+
+ tt_assert(ch1);
+ ch1->magic = TLS_CHAN_MAGIC;
+ ch1->state = CHANNEL_STATE_OPENING;
+ ch1->cmux = circuitmux_alloc();
+ channel_register(ch1);
+ tt_assert(ch1->registered);
+ channel_change_state_open(ch1);
+ scheduler_channel_has_waiting_cells(ch1);
+ scheduler_channel_wants_writes(ch1);
+ channel_flush_some_cells_mock_set(ch1, 5);
+
+ tt_assert(ch2);
+ ch2->magic = TLS_CHAN_MAGIC;
+ ch2->state = CHANNEL_STATE_OPENING;
+ ch2->cmux = circuitmux_alloc();
+ channel_register(ch2);
+ tt_assert(ch2->registered);
+ channel_change_state_open(ch2);
+ scheduler_channel_has_waiting_cells(ch2);
+ scheduler_channel_wants_writes(ch2);
+ channel_flush_some_cells_mock_set(ch2, 5);
+
+ the_scheduler->run();
+
+ scheduler_channel_has_waiting_cells(ch1);
+ channel_flush_some_cells_mock_set(ch1, 5);
+
+ the_scheduler->run();
+
+ scheduler_channel_has_waiting_cells(ch1);
+ channel_flush_some_cells_mock_set(ch1, 5);
+ scheduler_channel_has_waiting_cells(ch2);
+ channel_flush_some_cells_mock_set(ch2, 5);
+
+ the_scheduler->run();
+
+ channel_flush_some_cells_mock_free_all();
+
+ /* We'll try to run this closed channel threw the scheduler loop and make
+ * sure it ends up in the right state. */
+ tt_assert(ch3);
+ ch3->magic = TLS_CHAN_MAGIC;
+ ch3->state = CHANNEL_STATE_OPEN;
+ circuitmux_free(ch3->cmux);
+ ch3->cmux = circuitmux_alloc();
+ channel_register(ch3);
+ tt_assert(ch3->registered);
+
+ ch3->scheduler_state = SCHED_CHAN_WAITING_FOR_CELLS;
+ scheduler_channel_has_waiting_cells(ch3);
+ /* Should be in the pending list now waiting to be handled. */
+ tt_int_op(ch3->scheduler_state, OP_EQ, SCHED_CHAN_PENDING);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 1);
+ /* By running the scheduler on a closed channel, it should end up in the
+ * IDLE state and not in the pending channel list. */
+ ch3->state = CHANNEL_STATE_CLOSED;
+ the_scheduler->run();
+ tt_int_op(ch3->scheduler_state, OP_EQ, SCHED_CHAN_IDLE);
+ tt_int_op(smartlist_len(get_channels_pending()), OP_EQ, 0);
+
+ done:
+ /* Prep the channel so the free() function doesn't explode. */
+ ch1->state = ch2->state = ch3->state = CHANNEL_STATE_CLOSED;
+ ch1->registered = ch2->registered = ch3->registered = 0;
+ channel_free(ch1);
+ channel_free(ch2);
+ channel_free(ch3);
+ UNMOCK(update_socket_info_impl);
+ UNMOCK(channel_should_write_to_kernel);
+ UNMOCK(channel_write_to_kernel);
+ UNMOCK(channel_more_to_flush);
+ UNMOCK(channel_flush_some_cells);
+ UNMOCK(get_options);
+ scheduler_free_all();
+ return;
}
static void
-test_scheduler_queue_heuristic(void *arg)
+test_scheduler_channel_states(void *arg)
{
- time_t now = approx_time();
- uint64_t qh;
+ (void)arg;
+ perform_channel_state_tests(-1, SCHEDULER_VANILLA);
+ perform_channel_state_tests(11, SCHEDULER_KIST_LITE);
+#ifdef HAVE_KIST_SUPPORT
+ perform_channel_state_tests(11, SCHEDULER_KIST);
+#endif
+}
+static void
+test_scheduler_initfree(void *arg)
+{
(void)arg;
- queue_heuristic = 0;
- queue_heuristic_timestamp = 0;
+ tt_ptr_op(channels_pending, ==, NULL);
+ tt_ptr_op(run_sched_ev, ==, NULL);
- /* Not yet inited case */
- scheduler_update_queue_heuristic(now - 180);
- tt_u64_op(queue_heuristic, ==, 0);
- tt_int_op(queue_heuristic_timestamp, ==, now - 180);
+ mock_event_init();
+ MOCK(tor_libevent_get_base, tor_libevent_get_base_mock);
+ MOCK(get_options, mock_get_options);
+ set_scheduler_options(SCHEDULER_KIST);
+ set_scheduler_options(SCHEDULER_KIST_LITE);
+ set_scheduler_options(SCHEDULER_VANILLA);
+
+ scheduler_init();
- queue_heuristic = 1000000000L;
- queue_heuristic_timestamp = now - 120;
+ tt_ptr_op(channels_pending, !=, NULL);
+ tt_ptr_op(run_sched_ev, !=, NULL);
+ /* We have specified nothing in the torrc and there's no consensus so the
+ * KIST scheduler is what should be in use */
+ tt_ptr_op(the_scheduler, ==, get_kist_scheduler());
+ tt_int_op(sched_run_interval, ==, 10);
- scheduler_update_queue_heuristic(now - 119);
- tt_u64_op(queue_heuristic, ==, 500000000L);
- tt_int_op(queue_heuristic_timestamp, ==, now - 119);
+ scheduler_free_all();
- scheduler_update_queue_heuristic(now - 116);
- tt_u64_op(queue_heuristic, ==, 62500000L);
- tt_int_op(queue_heuristic_timestamp, ==, now - 116);
+ UNMOCK(tor_libevent_get_base);
+ mock_event_free_all();
- qh = scheduler_get_queue_heuristic();
- tt_u64_op(qh, ==, 0);
+ tt_ptr_op(channels_pending, ==, NULL);
+ tt_ptr_op(run_sched_ev, ==, NULL);
done:
+ UNMOCK(get_options);
+ cleanup_scheduler_options();
+ return;
+}
+
+static void
+test_scheduler_can_use_kist(void *arg)
+{
+ (void)arg;
+
+ int res_should, res_freq;
+ MOCK(get_options, mock_get_options);
+
+ /* Test force enabling of KIST */
+ clear_options();
+ mocked_options.KISTSchedRunInterval = 1234;
+ res_should = scheduler_can_use_kist();
+ res_freq = kist_scheduler_run_interval();
+#ifdef HAVE_KIST_SUPPORT
+ tt_int_op(res_should, ==, 1);
+#else /* HAVE_KIST_SUPPORT */
+ tt_int_op(res_should, ==, 0);
+#endif /* HAVE_KIST_SUPPORT */
+ tt_int_op(res_freq, ==, 1234);
+
+ /* Test defer to consensus, but no consensus available */
+ clear_options();
+ mocked_options.KISTSchedRunInterval = 0;
+ res_should = scheduler_can_use_kist();
+ res_freq = kist_scheduler_run_interval();
+#ifdef HAVE_KIST_SUPPORT
+ tt_int_op(res_should, ==, 1);
+#else /* HAVE_KIST_SUPPORT */
+ tt_int_op(res_should, ==, 0);
+#endif /* HAVE_KIST_SUPPORT */
+ tt_int_op(res_freq, ==, 10);
+
+ /* Test defer to consensus, and kist consensus available */
+ MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
+ clear_options();
+ mocked_options.KISTSchedRunInterval = 0;
+ res_should = scheduler_can_use_kist();
+ res_freq = kist_scheduler_run_interval();
+#ifdef HAVE_KIST_SUPPORT
+ tt_int_op(res_should, ==, 1);
+#else /* HAVE_KIST_SUPPORT */
+ tt_int_op(res_should, ==, 0);
+#endif /* HAVE_KIST_SUPPORT */
+ tt_int_op(res_freq, ==, 12);
+ UNMOCK(networkstatus_get_param);
+
+ /* Test defer to consensus, and vanilla consensus available */
+ MOCK(networkstatus_get_param, mock_vanilla_networkstatus_get_param);
+ clear_options();
+ mocked_options.KISTSchedRunInterval = 0;
+ res_should = scheduler_can_use_kist();
+ res_freq = kist_scheduler_run_interval();
+ tt_int_op(res_should, ==, 0);
+ tt_int_op(res_freq, ==, 0);
+ UNMOCK(networkstatus_get_param);
+
+ done:
+ UNMOCK(get_options);
+ return;
+}
+
+static void
+test_scheduler_ns_changed(void *arg)
+{
+ (void) arg;
+
+ /*
+ * Currently no scheduler implementations use the old/new consensuses passed
+ * in scheduler_notify_networkstatus_changed, so it is okay to pass NULL.
+ *
+ * "But then what does test actually exercise???" It tests that
+ * scheduler_notify_networkstatus_changed fetches the correct value from the
+ * consensus, and then switches the scheduler if necessasry.
+ */
+
+ MOCK(get_options, mock_get_options);
+ clear_options();
+ set_scheduler_options(SCHEDULER_KIST);
+ set_scheduler_options(SCHEDULER_VANILLA);
+
+ tt_ptr_op(the_scheduler, ==, NULL);
+
+ /* Change from vanilla to kist via consensus */
+ the_scheduler = get_vanilla_scheduler();
+ MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
+ scheduler_notify_networkstatus_changed();
+ UNMOCK(networkstatus_get_param);
+#ifdef HAVE_KIST_SUPPORT
+ tt_ptr_op(the_scheduler, ==, get_kist_scheduler());
+#else
+ tt_ptr_op(the_scheduler, ==, get_vanilla_scheduler());
+#endif
+
+ /* Change from kist to vanilla via consensus */
+ the_scheduler = get_kist_scheduler();
+ MOCK(networkstatus_get_param, mock_vanilla_networkstatus_get_param);
+ scheduler_notify_networkstatus_changed();
+ UNMOCK(networkstatus_get_param);
+ tt_ptr_op(the_scheduler, ==, get_vanilla_scheduler());
+
+ /* Doesn't change when using KIST */
+ the_scheduler = get_kist_scheduler();
+ MOCK(networkstatus_get_param, mock_kist_networkstatus_get_param);
+ scheduler_notify_networkstatus_changed();
+ UNMOCK(networkstatus_get_param);
+#ifdef HAVE_KIST_SUPPORT
+ tt_ptr_op(the_scheduler, ==, get_kist_scheduler());
+#else
+ tt_ptr_op(the_scheduler, ==, get_vanilla_scheduler());
+#endif
+
+ /* Doesn't change when using vanilla */
+ the_scheduler = get_vanilla_scheduler();
+ MOCK(networkstatus_get_param, mock_vanilla_networkstatus_get_param);
+ scheduler_notify_networkstatus_changed();
+ UNMOCK(networkstatus_get_param);
+ tt_ptr_op(the_scheduler, ==, get_vanilla_scheduler());
+
+ done:
+ UNMOCK(get_options);
+ cleanup_scheduler_options();
return;
}
struct testcase_t scheduler_tests[] = {
- { "channel_states", test_scheduler_channel_states, TT_FORK, NULL, NULL },
{ "compare_channels", test_scheduler_compare_channels,
TT_FORK, NULL, NULL },
+ { "channel_states", test_scheduler_channel_states, TT_FORK, NULL, NULL },
{ "initfree", test_scheduler_initfree, TT_FORK, NULL, NULL },
- { "loop", test_scheduler_loop, TT_FORK, NULL, NULL },
- { "queue_heuristic", test_scheduler_queue_heuristic,
- TT_FORK, NULL, NULL },
+ { "loop_vanilla", test_scheduler_loop_vanilla, TT_FORK, NULL, NULL },
+ { "loop_kist", test_scheduler_loop_kist, TT_FORK, NULL, NULL },
+ { "ns_changed", test_scheduler_ns_changed, TT_FORK, NULL, NULL},
+ { "should_use_kist", test_scheduler_can_use_kist, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 026a0f3825..cac78baecf 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -73,65 +73,73 @@ test_get_sr_protocol_phase(void *arg)
{
retval = parse_rfc1123_time("Wed, 20 Apr 2015 23:59:00 UTC", &the_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
phase = get_sr_protocol_phase(the_time);
- tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ tt_int_op(phase, OP_EQ, SR_PHASE_REVEAL);
}
{
retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:00 UTC", &the_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
phase = get_sr_protocol_phase(the_time);
- tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ tt_int_op(phase, OP_EQ, SR_PHASE_COMMIT);
}
{
retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:01 UTC", &the_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
phase = get_sr_protocol_phase(the_time);
- tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ tt_int_op(phase, OP_EQ, SR_PHASE_COMMIT);
}
{
retval = parse_rfc1123_time("Wed, 20 Apr 2015 11:59:00 UTC", &the_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
phase = get_sr_protocol_phase(the_time);
- tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ tt_int_op(phase, OP_EQ, SR_PHASE_COMMIT);
}
{
retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:00 UTC", &the_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
phase = get_sr_protocol_phase(the_time);
- tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ tt_int_op(phase, OP_EQ, SR_PHASE_REVEAL);
}
{
retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:01 UTC", &the_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
phase = get_sr_protocol_phase(the_time);
- tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ tt_int_op(phase, OP_EQ, SR_PHASE_REVEAL);
}
{
retval = parse_rfc1123_time("Wed, 20 Apr 2015 13:00:00 UTC", &the_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
phase = get_sr_protocol_phase(the_time);
- tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ tt_int_op(phase, OP_EQ, SR_PHASE_REVEAL);
}
done:
;
}
-static networkstatus_t *mock_consensus = NULL;
+static networkstatus_t mock_consensus;
+
+/* Mock function to immediately return our local 'mock_consensus'. */
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void) now;
+ return &mock_consensus;
+}
static void
test_get_state_valid_until_time(void *arg)
@@ -143,11 +151,23 @@ test_get_state_valid_until_time(void *arg)
(void) arg;
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 01:00:00 UTC",
+ &mock_consensus.fresh_until);
+ tt_int_op(retval, OP_EQ, 0);
+
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ &mock_consensus.valid_after);
+ tt_int_op(retval, OP_EQ, 0);
+
{
/* Get the valid until time if called at 00:00:01 */
retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
&current_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
valid_until_time = get_state_valid_until_time(current_time);
/* Compare it with the correct result */
@@ -158,7 +178,8 @@ test_get_state_valid_until_time(void *arg)
{
retval = parse_rfc1123_time("Mon, 20 Apr 2015 19:22:00 UTC",
&current_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
valid_until_time = get_state_valid_until_time(current_time);
format_iso_time(tbuf, valid_until_time);
@@ -168,7 +189,8 @@ test_get_state_valid_until_time(void *arg)
{
retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:00 UTC",
&current_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
valid_until_time = get_state_valid_until_time(current_time);
format_iso_time(tbuf, valid_until_time);
@@ -178,7 +200,8 @@ test_get_state_valid_until_time(void *arg)
{
retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
&current_time);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
valid_until_time = get_state_valid_until_time(current_time);
format_iso_time(tbuf, valid_until_time);
@@ -186,82 +209,148 @@ test_get_state_valid_until_time(void *arg)
}
done:
- ;
-}
-
-/* Mock function to immediately return our local 'mock_consensus'. */
-static networkstatus_t *
-mock_networkstatus_get_live_consensus(time_t now)
-{
- (void) now;
- return mock_consensus;
+ UNMOCK(networkstatus_get_live_consensus);
}
-/** Test the get_next_valid_after_time() function. */
+/** Test the function that calculates the start time of the current SRV
+ * protocol run. */
static void
-test_get_next_valid_after_time(void *arg)
+test_get_start_time_of_current_run(void *arg)
{
- time_t current_time;
- time_t valid_after_time;
- char tbuf[ISO_TIME_LEN + 1];
int retval;
+ char tbuf[ISO_TIME_LEN + 1];
+ time_t current_time, run_start_time;
(void) arg;
- {
- /* Setup a fake consensus just to get the times out of it, since
- get_next_valid_after_time() needs them. */
- mock_consensus = tor_malloc_zero(sizeof(networkstatus_t));
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
- retval = parse_rfc1123_time("Mon, 13 Jan 2016 16:00:00 UTC",
- &mock_consensus->fresh_until);
- tt_int_op(retval, ==, 0);
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 01:00:00 UTC",
+ &mock_consensus.fresh_until);
+ tt_int_op(retval, OP_EQ, 0);
- retval = parse_rfc1123_time("Mon, 13 Jan 2016 15:00:00 UTC",
- &mock_consensus->valid_after);
- tt_int_op(retval, ==, 0);
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ &mock_consensus.valid_after);
+ tt_int_op(retval, OP_EQ, 0);
+
+ {
+ /* Get start time if called at 00:00:01 */
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
+ &current_time);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
- MOCK(networkstatus_get_live_consensus,
- mock_networkstatus_get_live_consensus);
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:00:00", OP_EQ, tbuf);
}
{
- /* Get the valid after time if called at 00:00:00 */
- retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:59 UTC",
&current_time);
- tt_int_op(retval, ==, 0);
- valid_after_time = get_next_valid_after_time(current_time);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
/* Compare it with the correct result */
- format_iso_time(tbuf, valid_after_time);
- tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf);
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:00:00", OP_EQ, tbuf);
}
{
- /* Get the valid until time if called at 00:00:01 */
- retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
&current_time);
- tt_int_op(retval, ==, 0);
- valid_after_time = get_next_valid_after_time(current_time);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
/* Compare it with the correct result */
- format_iso_time(tbuf, valid_after_time);
- tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf);
- }
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:00:00", OP_EQ, tbuf);
+ }
+
+ /* Next test is testing it without a consensus to use the testing voting
+ * interval . */
+ UNMOCK(networkstatus_get_live_consensus);
+ /* Now let's alter the voting schedule and check the correctness of the
+ * function. Voting interval of 10 seconds, means that an SRV protocol run
+ * takes 10 seconds * 24 rounds = 4 mins */
{
- retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:30:01 UTC",
+ or_options_t *options = get_options_mutable();
+ options->V3AuthVotingInterval = 10;
+ options->TestingV3AuthInitialVotingInterval = 10;
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:15:32 UTC",
&current_time);
- tt_int_op(retval, ==, 0);
- valid_after_time = get_next_valid_after_time(current_time);
+ tt_int_op(retval, OP_EQ, 0);
+ dirvote_recalculate_timing(get_options(), current_time);
+ run_start_time =
+ sr_state_get_start_time_of_current_protocol_run(current_time);
/* Compare it with the correct result */
- format_iso_time(tbuf, valid_after_time);
- tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
- }
+ format_iso_time(tbuf, run_start_time);
+ tt_str_op("2015-04-20 00:12:00", OP_EQ, tbuf);
+ }
+
+ done:
+ ;
+}
+
+/** Do some rudimentary consistency checks between the functions that
+ * understand the shared random protocol schedule */
+static void
+test_get_start_time_functions(void *arg)
+{
+ (void) arg;
+ int retval;
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 01:00:00 UTC",
+ &mock_consensus.fresh_until);
+ tt_int_op(retval, OP_EQ, 0);
+
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ &mock_consensus.valid_after);
+ tt_int_op(retval, OP_EQ, 0);
+ time_t now = mock_consensus.valid_after;
+
+ dirvote_recalculate_timing(get_options(), now);
+ time_t start_time_of_protocol_run =
+ sr_state_get_start_time_of_current_protocol_run(now);
+ tt_assert(start_time_of_protocol_run);
+
+ /* Check that the round start time of the beginning of the run, is itself */
+ tt_int_op(get_start_time_of_current_round(), OP_EQ,
+ start_time_of_protocol_run);
done:
- networkstatus_vote_free(mock_consensus);
+ UNMOCK(networkstatus_get_live_consensus);
+}
+
+static void
+test_get_sr_protocol_duration(void *arg)
+{
+ (void) arg;
+
+ /* Check that by default an SR phase is 12 hours */
+ tt_int_op(sr_state_get_phase_duration(), OP_EQ, 12*60*60);
+ tt_int_op(sr_state_get_protocol_run_duration(), OP_EQ, 24*60*60);
+
+ /* Now alter the voting interval and check that the SR phase is 2 mins long
+ * if voting happens every 10 seconds (10*12 seconds = 2 mins) */
+ or_options_t *options = get_options_mutable();
+ options->V3AuthVotingInterval = 10;
+ tt_int_op(sr_state_get_phase_duration(), OP_EQ, 2*60);
+ tt_int_op(sr_state_get_protocol_run_duration(), OP_EQ, 4*60);
+
+ done: ;
}
/* In this test we are going to generate a sr_commit_t object and validate
@@ -304,18 +393,18 @@ test_sr_commit(void *arg)
tt_assert(!tor_mem_is_zero((char *) our_commit->random_number,
sizeof(our_commit->random_number)));
/* Commit and reveal timestamp should be the same. */
- tt_u64_op(our_commit->commit_ts, ==, our_commit->reveal_ts);
+ tt_u64_op(our_commit->commit_ts, OP_EQ, our_commit->reveal_ts);
/* We should have a hashed reveal. */
tt_assert(!tor_mem_is_zero(our_commit->hashed_reveal,
sizeof(our_commit->hashed_reveal)));
/* Do we have a valid encoded commit and reveal. Note the following only
* tests if the generated values are correct. Their could be a bug in
* the decode function but we test them seperately. */
- tt_int_op(0, ==, reveal_decode(our_commit->encoded_reveal,
+ tt_int_op(0, OP_EQ, reveal_decode(our_commit->encoded_reveal,
&test_commit));
- tt_int_op(0, ==, commit_decode(our_commit->encoded_commit,
+ tt_int_op(0, OP_EQ, commit_decode(our_commit->encoded_commit,
&test_commit));
- tt_int_op(0, ==, verify_commit_and_reveal(our_commit));
+ tt_int_op(0, OP_EQ, verify_commit_and_reveal(our_commit));
}
/* Let's make sure our verify commit and reveal function works. We'll
@@ -328,21 +417,21 @@ test_sr_commit(void *arg)
/* Timestamp MUST match. */
test_commit.commit_ts = test_commit.reveal_ts - 42;
setup_full_capture_of_logs(LOG_WARN);
- tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit));
+ tt_int_op(-1, OP_EQ, verify_commit_and_reveal(&test_commit));
expect_log_msg_containing("doesn't match reveal timestamp");
teardown_capture_of_logs();
memcpy(&test_commit, our_commit, sizeof(test_commit));
- tt_int_op(0, ==, verify_commit_and_reveal(&test_commit));
+ tt_int_op(0, OP_EQ, verify_commit_and_reveal(&test_commit));
/* Hashed reveal must match the H(encoded_reveal). */
memset(test_commit.hashed_reveal, 'X',
sizeof(test_commit.hashed_reveal));
setup_full_capture_of_logs(LOG_WARN);
- tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit));
+ tt_int_op(-1, OP_EQ, verify_commit_and_reveal(&test_commit));
expect_single_log_msg_containing("doesn't match the commit value");
teardown_capture_of_logs();
memcpy(&test_commit, our_commit, sizeof(test_commit));
- tt_int_op(0, ==, verify_commit_and_reveal(&test_commit));
+ tt_int_op(0, OP_EQ, verify_commit_and_reveal(&test_commit));
}
/* We'll build a list of values from our commit that our parsing function
@@ -396,26 +485,26 @@ test_encoding(void *arg)
/* 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);
+ tt_int_op(0, OP_EQ, ret);
/* Hash reveal value. */
- tt_int_op(SR_REVEAL_BASE64_LEN, ==, strlen(encoded_reveal));
+ tt_int_op(SR_REVEAL_BASE64_LEN, OP_EQ, strlen(encoded_reveal));
ret = crypto_digest256(hashed_reveal, encoded_reveal,
strlen(encoded_reveal), SR_DIGEST_ALG);
- tt_int_op(0, ==, ret);
- tt_int_op(SR_COMMIT_BASE64_LEN, ==, strlen(encoded_commit));
+ tt_int_op(0, OP_EQ, ret);
+ tt_int_op(SR_COMMIT_BASE64_LEN, OP_EQ, strlen(encoded_commit));
/* Test our commit/reveal decode functions. */
{
/* Test the reveal encoded value. */
- tt_int_op(0, ==, reveal_decode(encoded_reveal, &parsed_commit));
- tt_u64_op(ts, ==, parsed_commit.reveal_ts);
+ tt_int_op(0, OP_EQ, reveal_decode(encoded_reveal, &parsed_commit));
+ tt_u64_op(ts, OP_EQ, parsed_commit.reveal_ts);
tt_mem_op(hashed_rand, OP_EQ, parsed_commit.random_number,
sizeof(hashed_rand));
/* Test the commit encoded value. */
memset(&parsed_commit, 0, sizeof(parsed_commit));
- tt_int_op(0, ==, commit_decode(encoded_commit, &parsed_commit));
- tt_u64_op(ts, ==, parsed_commit.commit_ts);
+ tt_int_op(0, OP_EQ, commit_decode(encoded_commit, &parsed_commit));
+ tt_u64_op(ts, OP_EQ, parsed_commit.commit_ts);
tt_mem_op(encoded_commit, OP_EQ, parsed_commit.encoded_commit,
sizeof(parsed_commit.encoded_commit));
tt_mem_op(hashed_reveal, OP_EQ, parsed_commit.hashed_reveal,
@@ -430,7 +519,7 @@ test_encoding(void *arg)
memcpy(parsed_commit.random_number, hashed_rand,
sizeof(parsed_commit.random_number));
ret = reveal_encode(&parsed_commit, encoded, sizeof(encoded));
- tt_int_op(SR_REVEAL_BASE64_LEN, ==, ret);
+ tt_int_op(SR_REVEAL_BASE64_LEN, OP_EQ, ret);
tt_mem_op(encoded_reveal, OP_EQ, encoded, strlen(encoded_reveal));
}
@@ -441,7 +530,7 @@ test_encoding(void *arg)
memcpy(parsed_commit.hashed_reveal, hashed_reveal,
sizeof(parsed_commit.hashed_reveal));
ret = commit_encode(&parsed_commit, encoded, sizeof(encoded));
- tt_int_op(SR_COMMIT_BASE64_LEN, ==, ret);
+ tt_int_op(SR_COMMIT_BASE64_LEN, OP_EQ, ret);
tt_mem_op(encoded_commit, OP_EQ, encoded, strlen(encoded_commit));
}
@@ -518,7 +607,7 @@ test_vote(void *arg)
tt_assert(lines);
/* Split the lines. We expect 2 here. */
ret = smartlist_split_string(chunks, lines, "\n", SPLIT_IGNORE_BLANK, 0);
- tt_int_op(ret, ==, 4);
+ tt_int_op(ret, OP_EQ, 4);
tt_str_op(smartlist_get(chunks, 0), OP_EQ, "shared-rand-participate");
/* Get our commitment line and will validate it agains our commit. The
* format is as follow:
@@ -528,7 +617,7 @@ test_vote(void *arg)
char *commit_line = smartlist_get(chunks, 1);
tt_assert(commit_line);
ret = smartlist_split_string(tokens, commit_line, " ", 0, 0);
- tt_int_op(ret, ==, 6);
+ tt_int_op(ret, OP_EQ, 6);
tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-commit");
tt_str_op(smartlist_get(tokens, 1), OP_EQ, "1");
tt_str_op(smartlist_get(tokens, 2), OP_EQ,
@@ -536,7 +625,7 @@ test_vote(void *arg)
char digest[DIGEST_LEN];
base16_decode(digest, sizeof(digest), smartlist_get(tokens, 3),
HEX_DIGEST_LEN);
- tt_mem_op(digest, ==, our_commit->rsa_identity, sizeof(digest));
+ tt_mem_op(digest, OP_EQ, our_commit->rsa_identity, sizeof(digest));
tt_str_op(smartlist_get(tokens, 4), OP_EQ, our_commit->encoded_commit);
tt_str_op(smartlist_get(tokens, 5), OP_EQ, our_commit->encoded_reveal)
;
@@ -552,7 +641,7 @@ test_vote(void *arg)
/* Set valid flag explicitly here to compare since it's not set by
* simply parsing the commit. */
parsed_commit->valid = 1;
- tt_mem_op(parsed_commit, ==, our_commit, sizeof(*our_commit));
+ tt_mem_op(parsed_commit, OP_EQ, our_commit, sizeof(*our_commit));
/* minor cleanup */
SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
@@ -562,7 +651,7 @@ test_vote(void *arg)
char *prev_srv_line = smartlist_get(chunks, 2);
tt_assert(prev_srv_line);
ret = smartlist_split_string(tokens, prev_srv_line, " ", 0, 0);
- tt_int_op(ret, ==, 3);
+ tt_int_op(ret, OP_EQ, 3);
tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-previous-value");
tt_str_op(smartlist_get(tokens, 1), OP_EQ, "42");
tt_str_op(smartlist_get(tokens, 2), OP_EQ,
@@ -576,7 +665,7 @@ test_vote(void *arg)
char *current_srv_line = smartlist_get(chunks, 3);
tt_assert(current_srv_line);
ret = smartlist_split_string(tokens, current_srv_line, " ", 0, 0);
- tt_int_op(ret, ==, 3);
+ tt_int_op(ret, OP_EQ, 3);
tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-current-value");
tt_str_op(smartlist_get(tokens, 1), OP_EQ, "128");
tt_str_op(smartlist_get(tokens, 2), OP_EQ,
@@ -629,7 +718,7 @@ test_state_load_from_disk(void *arg)
/* First try with a nonexistent path. */
ret = disk_state_load_from_disk_impl("NONEXISTENTNONEXISTENT");
- tt_assert(ret == -ENOENT);
+ tt_int_op(ret, OP_EQ, -ENOENT);
/* Now create a mock state directory and state file */
#ifdef _WIN32
@@ -637,9 +726,9 @@ test_state_load_from_disk(void *arg)
#else
ret = mkdir(dir, 0700);
#endif
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
ret = write_str_to_file(sr_state_path, sr_state_str, 0);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Try to load the directory itself. Should fail. */
ret = disk_state_load_from_disk_impl(dir);
@@ -647,11 +736,11 @@ test_state_load_from_disk(void *arg)
/* State should be non-existent at this point. */
the_sr_state = get_sr_state();
- tt_assert(!the_sr_state);
+ tt_ptr_op(the_sr_state, OP_EQ, NULL);
/* Now try to load the correct file! */
ret = disk_state_load_from_disk_impl(sr_state_path);
- tt_assert(ret == 0);
+ tt_int_op(ret, OP_EQ, 0);
/* Check the content of the state */
/* XXX check more deeply!!! */
@@ -689,7 +778,7 @@ test_sr_setup_commits(void)
tt_assert(auth_cert);
options->AuthoritativeDir = 1;
- tt_int_op(0, ==, load_ed_keys(options, now));
+ tt_int_op(0, OP_EQ, load_ed_keys(options, now));
}
/* Generate three dummy commits according to sr_srv_calc_ref.py . Then
@@ -775,7 +864,7 @@ test_sr_setup_commits(void)
save_commit_to_state(commit_b);
save_commit_to_state(commit_c);
save_commit_to_state(commit_d);
- tt_int_op(digestmap_size(get_sr_state()->commits), ==, 4);
+ tt_int_op(digestmap_size(get_sr_state()->commits), OP_EQ, 4);
/* Now during REVEAL phase save commit D by restoring its reveal. */
set_sr_phase(SR_PHASE_REVEAL);
@@ -820,9 +909,9 @@ test_sr_compute_srv(void *arg)
/* Check the result against the test vector */
current_srv = sr_state_get_current_srv();
tt_assert(current_srv);
- tt_u64_op(current_srv->num_reveals, ==, 3);
+ tt_u64_op(current_srv->num_reveals, OP_EQ, 3);
tt_str_op(hex_str((char*)current_srv->value, 32),
- ==,
+ OP_EQ,
SRV_TEST_VECTOR);
done:
@@ -878,7 +967,7 @@ test_sr_get_majority_srv_from_votes(void *arg)
/* Since it's only one vote with an SRV, it should not achieve majority and
hence no SRV will be returned. */
chosen_srv = get_majority_srv_from_votes(votes, 1);
- tt_assert(!chosen_srv);
+ tt_ptr_op(chosen_srv, OP_EQ, NULL);
{ /* Now put in 8 more votes. Let SRV_1 have majority. */
int i;
@@ -897,21 +986,21 @@ test_sr_get_majority_srv_from_votes(void *arg)
smartlist_add(votes, vote);
}
- tt_int_op(smartlist_len(votes), ==, 9);
+ tt_int_op(smartlist_len(votes), OP_EQ, 9);
}
/* Now we achieve majority for SRV_1, but not the AuthDirNumSRVAgreements
requirement. So still not picking an SRV. */
set_num_srv_agreements(8);
chosen_srv = get_majority_srv_from_votes(votes, 1);
- tt_assert(!chosen_srv);
+ tt_ptr_op(chosen_srv, OP_EQ, NULL);
/* We will now lower the AuthDirNumSRVAgreements requirement by tweaking the
* consensus parameter and we will try again. This time it should work. */
set_num_srv_agreements(7);
chosen_srv = get_majority_srv_from_votes(votes, 1);
tt_assert(chosen_srv);
- tt_u64_op(chosen_srv->num_reveals, ==, 42);
+ tt_u64_op(chosen_srv->num_reveals, OP_EQ, 42);
tt_mem_op(chosen_srv->value, OP_EQ, SRV_1, sizeof(chosen_srv->value));
done:
@@ -935,7 +1024,7 @@ test_utils(void *arg)
memcpy(srv->value, srv_value, sizeof(srv->value));
dup_srv = srv_dup(srv);
tt_assert(dup_srv);
- tt_u64_op(dup_srv->num_reveals, ==, srv->num_reveals);
+ tt_u64_op(dup_srv->num_reveals, OP_EQ, srv->num_reveals);
tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
tor_free(srv);
tor_free(dup_srv);
@@ -955,10 +1044,10 @@ test_utils(void *arg)
sr_commit_t commit1, commit2;
memcpy(commit1.encoded_commit, payload, sizeof(commit1.encoded_commit));
memcpy(commit2.encoded_commit, payload, sizeof(commit2.encoded_commit));
- tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 1);
+ tt_int_op(commitments_are_the_same(&commit1, &commit2), OP_EQ, 1);
/* Let's corrupt one of them. */
memset(commit1.encoded_commit, 'A', sizeof(commit1.encoded_commit));
- tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 0);
+ tt_int_op(commitments_are_the_same(&commit1, &commit2), OP_EQ, 0);
}
/* Testing commit_is_authoritative(). */
@@ -969,32 +1058,32 @@ test_utils(void *arg)
tt_assert(!crypto_pk_generate_key(k));
- tt_int_op(0, ==, crypto_pk_get_digest(k, digest));
+ tt_int_op(0, OP_EQ, crypto_pk_get_digest(k, digest));
memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
- tt_int_op(commit_is_authoritative(&commit, digest), ==, 1);
+ tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 1);
/* Change the pubkey. */
memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
- tt_int_op(commit_is_authoritative(&commit, digest), ==, 0);
+ tt_int_op(commit_is_authoritative(&commit, digest), OP_EQ, 0);
crypto_pk_free(k);
}
/* Testing get_phase_str(). */
{
- tt_str_op(get_phase_str(SR_PHASE_REVEAL), ==, "reveal");
- tt_str_op(get_phase_str(SR_PHASE_COMMIT), ==, "commit");
+ tt_str_op(get_phase_str(SR_PHASE_REVEAL), OP_EQ, "reveal");
+ tt_str_op(get_phase_str(SR_PHASE_COMMIT), OP_EQ, "commit");
}
/* Testing phase transition */
{
init_authority_state();
set_sr_phase(SR_PHASE_COMMIT);
- tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 1);
- tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 0);
+ tt_int_op(is_phase_transition(SR_PHASE_REVEAL), OP_EQ, 1);
+ tt_int_op(is_phase_transition(SR_PHASE_COMMIT), OP_EQ, 0);
set_sr_phase(SR_PHASE_REVEAL);
- tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 0);
- tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 1);
+ tt_int_op(is_phase_transition(SR_PHASE_REVEAL), OP_EQ, 0);
+ tt_int_op(is_phase_transition(SR_PHASE_COMMIT), OP_EQ, 1);
/* Junk. */
- tt_int_op(is_phase_transition(42), ==, 1);
+ tt_int_op(is_phase_transition(42), OP_EQ, 1);
}
done:
@@ -1022,24 +1111,24 @@ test_state_transition(void *arg)
sr_commit_t *commit = sr_generate_our_commit(now, mock_cert);
tt_assert(commit);
sr_state_add_commit(commit);
- tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
/* Let's test our delete feature. */
sr_state_delete_commits();
- tt_int_op(digestmap_size(state->commits), ==, 0);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 0);
/* Add it back so we can continue the rest of the test because after
* deletiong our commit will be freed so generate a new one. */
commit = sr_generate_our_commit(now, mock_cert);
tt_assert(commit);
sr_state_add_commit(commit);
- tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
state->n_reveal_rounds = 42;
state->n_commit_rounds = 43;
state->n_protocol_runs = 44;
reset_state_for_new_protocol_run(now);
- tt_int_op(state->n_reveal_rounds, ==, 0);
- tt_int_op(state->n_commit_rounds, ==, 0);
- tt_u64_op(state->n_protocol_runs, ==, 45);
- tt_int_op(digestmap_size(state->commits), ==, 0);
+ tt_int_op(state->n_reveal_rounds, OP_EQ, 0);
+ tt_int_op(state->n_commit_rounds, OP_EQ, 0);
+ tt_u64_op(state->n_protocol_runs, OP_EQ, 45);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 0);
}
/* Test SRV rotation in our state. */
@@ -1052,7 +1141,7 @@ test_state_transition(void *arg)
state_rotate_srv();
prev = sr_state_get_previous_srv();
tt_assert(prev == cur);
- tt_assert(!sr_state_get_current_srv());
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
sr_state_clean_srvs();
}
@@ -1077,18 +1166,18 @@ test_state_transition(void *arg)
/* Also, make sure we did change the current. */
tt_assert(sr_state_get_current_srv() != cur);
/* We should have our commitment alone. */
- tt_int_op(digestmap_size(state->commits), ==, 1);
- tt_int_op(state->n_reveal_rounds, ==, 0);
- tt_int_op(state->n_commit_rounds, ==, 0);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
+ tt_int_op(state->n_reveal_rounds, OP_EQ, 0);
+ tt_int_op(state->n_commit_rounds, OP_EQ, 0);
/* 46 here since we were at 45 just before. */
- tt_u64_op(state->n_protocol_runs, ==, 46);
+ tt_u64_op(state->n_protocol_runs, OP_EQ, 46);
}
/* Cleanup of SRVs. */
{
sr_state_clean_srvs();
- tt_assert(!sr_state_get_current_srv());
- tt_assert(!sr_state_get_previous_srv());
+ tt_ptr_op(sr_state_get_current_srv(), OP_EQ, NULL);
+ tt_ptr_op(sr_state_get_previous_srv(), OP_EQ, NULL);
}
done:
@@ -1117,6 +1206,8 @@ test_keep_commit(void *arg)
state = get_sr_state();
}
+ crypto_rand((char*)fp, sizeof(fp));
+
/* Test this very important function that tells us if we should keep a
* commit or not in our state. Most of it depends on the phase and what's
* in the commit so we'll change the commit as we go. */
@@ -1125,21 +1216,21 @@ test_keep_commit(void *arg)
/* Set us in COMMIT phase for starter. */
set_sr_phase(SR_PHASE_COMMIT);
/* We should never keep a commit from a non authoritative authority. */
- tt_int_op(should_keep_commit(commit, fp, SR_PHASE_COMMIT), ==, 0);
+ tt_int_op(should_keep_commit(commit, fp, SR_PHASE_COMMIT), OP_EQ, 0);
/* This should NOT be kept because it has a reveal value in it. */
tt_assert(commit_has_reveal_value(commit));
tt_int_op(should_keep_commit(commit, commit->rsa_identity,
- SR_PHASE_COMMIT), ==, 0);
+ SR_PHASE_COMMIT), OP_EQ, 0);
/* Add it to the state which should return to not keep it. */
sr_state_add_commit(commit);
tt_int_op(should_keep_commit(commit, commit->rsa_identity,
- SR_PHASE_COMMIT), ==, 0);
+ SR_PHASE_COMMIT), OP_EQ, 0);
/* Remove it from state so we can continue our testing. */
digestmap_remove(state->commits, commit->rsa_identity);
/* Let's remove our reveal value which should make it OK to keep it. */
memset(commit->encoded_reveal, 0, sizeof(commit->encoded_reveal));
tt_int_op(should_keep_commit(commit, commit->rsa_identity,
- SR_PHASE_COMMIT), ==, 1);
+ SR_PHASE_COMMIT), OP_EQ, 1);
/* Let's reset our commit and go into REVEAL phase. */
sr_commit_free(commit);
@@ -1151,17 +1242,17 @@ test_keep_commit(void *arg)
memset(dup_commit->encoded_reveal, 0, sizeof(dup_commit->encoded_reveal));
set_sr_phase(SR_PHASE_REVEAL);
/* We should never keep a commit from a non authoritative authority. */
- tt_int_op(should_keep_commit(commit, fp, SR_PHASE_REVEAL), ==, 0);
+ tt_int_op(should_keep_commit(commit, fp, SR_PHASE_REVEAL), OP_EQ, 0);
/* We shouldn't accept a commit that is not in our state. */
tt_int_op(should_keep_commit(commit, commit->rsa_identity,
- SR_PHASE_REVEAL), ==, 0);
+ SR_PHASE_REVEAL), OP_EQ, 0);
/* Important to add the commit _without_ the reveal here. */
sr_state_add_commit(dup_commit);
- tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
/* Our commit should be valid that is authoritative, contains a reveal, be
* in the state and commitment and reveal values match. */
tt_int_op(should_keep_commit(commit, commit->rsa_identity,
- SR_PHASE_REVEAL), ==, 1);
+ SR_PHASE_REVEAL), OP_EQ, 1);
/* The commit shouldn't be kept if it's not verified that is no matchin
* hashed reveal. */
{
@@ -1172,7 +1263,7 @@ test_keep_commit(void *arg)
memset(commit->hashed_reveal, 0, sizeof(commit->hashed_reveal));
setup_full_capture_of_logs(LOG_WARN);
tt_int_op(should_keep_commit(commit, commit->rsa_identity,
- SR_PHASE_REVEAL), ==, 0);
+ SR_PHASE_REVEAL), OP_EQ, 0);
expect_log_msg_containing("doesn't match the commit value.");
expect_log_msg_containing("has an invalid reveal value.");
assert_log_predicate(mock_saved_log_n_entries() == 2,
@@ -1183,11 +1274,11 @@ test_keep_commit(void *arg)
}
/* We shouldn't keep a commit that has no reveal. */
tt_int_op(should_keep_commit(dup_commit, dup_commit->rsa_identity,
- SR_PHASE_REVEAL), ==, 0);
+ SR_PHASE_REVEAL), OP_EQ, 0);
/* We must not keep a commit that is not the same from the commit phase. */
memset(commit->encoded_commit, 0, sizeof(commit->encoded_commit));
tt_int_op(should_keep_commit(commit, commit->rsa_identity,
- SR_PHASE_REVEAL), ==, 0);
+ SR_PHASE_REVEAL), OP_EQ, 0);
done:
teardown_capture_of_logs();
@@ -1225,35 +1316,35 @@ test_state_update(void *arg)
/* We are in COMMIT phase here and we'll trigger a state update but no
* transition. */
sr_state_update(commit_phase_time);
- tt_int_op(state->valid_after, ==, commit_phase_time);
- tt_int_op(state->n_commit_rounds, ==, 1);
- tt_int_op(state->phase, ==, SR_PHASE_COMMIT);
- tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(state->valid_after, OP_EQ, commit_phase_time);
+ tt_int_op(state->n_commit_rounds, OP_EQ, 1);
+ tt_int_op(state->phase, OP_EQ, SR_PHASE_COMMIT);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
/* We are still in the COMMIT phase here but we'll trigger a state
* transition to the REVEAL phase. */
sr_state_update(reveal_phase_time);
- tt_int_op(state->phase, ==, SR_PHASE_REVEAL);
- tt_int_op(state->valid_after, ==, reveal_phase_time);
+ tt_int_op(state->phase, OP_EQ, SR_PHASE_REVEAL);
+ tt_int_op(state->valid_after, OP_EQ, reveal_phase_time);
/* Only our commit should be in there. */
- tt_int_op(digestmap_size(state->commits), ==, 1);
- tt_int_op(state->n_reveal_rounds, ==, 1);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
+ tt_int_op(state->n_reveal_rounds, OP_EQ, 1);
/* We can't update a state with a valid after _lower_ than the creation
* time so here it is. */
sr_state_update(commit_phase_time);
- tt_int_op(state->valid_after, ==, reveal_phase_time);
+ tt_int_op(state->valid_after, OP_EQ, reveal_phase_time);
/* Finally, let's go back in COMMIT phase so we can test the state update
* of a new protocol run. */
state->valid_after = 0;
sr_state_update(commit_phase_time);
- tt_int_op(state->valid_after, ==, commit_phase_time);
- tt_int_op(state->n_commit_rounds, ==, 1);
- tt_int_op(state->n_reveal_rounds, ==, 0);
- tt_u64_op(state->n_protocol_runs, ==, 1);
- tt_int_op(state->phase, ==, SR_PHASE_COMMIT);
- tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(state->valid_after, OP_EQ, commit_phase_time);
+ tt_int_op(state->n_commit_rounds, OP_EQ, 1);
+ tt_int_op(state->n_reveal_rounds, OP_EQ, 0);
+ tt_u64_op(state->n_protocol_runs, OP_EQ, 1);
+ tt_int_op(state->phase, OP_EQ, SR_PHASE_COMMIT);
+ tt_int_op(digestmap_size(state->commits), OP_EQ, 1);
tt_assert(state->current_srv);
done:
@@ -1270,7 +1361,11 @@ struct testcase_t sr_tests[] = {
NULL, NULL },
{ "encoding", test_encoding, TT_FORK,
NULL, NULL },
- { "get_next_valid_after_time", test_get_next_valid_after_time, TT_FORK,
+ { "get_start_time_of_current_run", test_get_start_time_of_current_run,
+ TT_FORK, NULL, NULL },
+ { "get_start_time_functions", test_get_start_time_functions,
+ TT_FORK, NULL, NULL },
+ { "get_sr_protocol_duration", test_get_sr_protocol_duration, TT_FORK,
NULL, NULL },
{ "get_state_valid_until_time", test_get_state_valid_until_time, TT_FORK,
NULL, NULL },
diff --git a/src/test/test_socks.c b/src/test/test_socks.c
index bb1be11f2b..9ae7530e22 100644
--- a/src/test/test_socks.c
+++ b/src/test/test_socks.c
@@ -6,7 +6,9 @@
#include "or.h"
#include "buffers.h"
#include "config.h"
+#include "proto_socks.h"
#include "test.h"
+#include "log_test_helpers.h"
typedef struct socks_test_data_t {
socks_request_t *req;
@@ -43,7 +45,7 @@ static const struct testcase_setup_t socks_setup = {
buf_t *buf = testdata->buf; \
socks_request_t *socks = testdata->req;
#define ADD_DATA(buf, s) \
- write_to_buf(s, sizeof(s)-1, buf)
+ buf_add(buf, s, sizeof(s)-1)
static void
socks_request_clear(socks_request_t *socks)
@@ -61,8 +63,9 @@ test_socks_4_unsupported_commands(void *ptr)
/* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
- tt_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == -1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, -1);
tt_int_op(4,OP_EQ, socks->socks_version);
tt_int_op(0,OP_EQ, socks->replylen); /* XXX: shouldn't tor reply? */
@@ -80,8 +83,9 @@ test_socks_4_supported_commands(void *ptr)
/* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
- tt_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, 1);
tt_int_op(4,OP_EQ, socks->socks_version);
tt_int_op(0,OP_EQ, socks->replylen); /* XXX: shouldn't tor reply? */
tt_int_op(SOCKS_COMMAND_CONNECT,OP_EQ, socks->command);
@@ -95,8 +99,8 @@ test_socks_4_supported_commands(void *ptr)
/* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
- tt_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0),
+ OP_EQ, 1);
tt_int_op(4,OP_EQ, socks->socks_version);
tt_int_op(0,OP_EQ, socks->replylen); /* XXX: shouldn't tor reply? */
tt_int_op(SOCKS_COMMAND_CONNECT,OP_EQ, socks->command);
@@ -112,8 +116,9 @@ test_socks_4_supported_commands(void *ptr)
/* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
- tt_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1,
+ get_options()->SafeSocks),
+ OP_EQ, 1);
tt_int_op(4,OP_EQ, socks->socks_version);
tt_int_op(0,OP_EQ, socks->replylen); /* XXX: shouldn't tor reply? */
tt_str_op("torproject.org",OP_EQ, socks->address);
@@ -124,6 +129,83 @@ test_socks_4_supported_commands(void *ptr)
;
}
+static void
+test_socks_4_bad_arguments(void *ptr)
+{
+ SOCKS_TEST_INIT();
+ setup_capture_of_logs(LOG_DEBUG);
+
+ /* Try with 0 IPv4 address */
+ ADD_DATA(buf, "\x04\x01\x00\x50\x00\x00\x00\x00\x00");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("Port or DestIP is zero.");
+ mock_clean_saved_logs();
+
+ /* Try with 0 port */
+ ADD_DATA(buf, "\x04\x01\x00\x00\x01\x02\x03\x04\x00");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("Port or DestIP is zero.");
+ mock_clean_saved_logs();
+
+ /* Try with 2000-byte username (!) */
+ ADD_DATA(buf, "\x04\x01\x00\x50\x01\x02\x03\x04");
+ int i;
+ for (i = 0; i < 200; ++i) {
+ ADD_DATA(buf, "1234567890");
+ }
+ ADD_DATA(buf, "\x00");
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0),
+ OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("user name too long; rejecting.");
+ mock_clean_saved_logs();
+
+ /* Try with 2000-byte hostname */
+ ADD_DATA(buf, "\x04\x01\x00\x50\x00\x00\x00\x01\x00");
+ for (i = 0; i < 200; ++i) {
+ ADD_DATA(buf, "1234567890");
+ }
+ ADD_DATA(buf, "\x00");
+ {
+ const char *p;
+ size_t s;
+ buf_pullup(buf, 9999, &p, &s);
+ }
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0),
+ OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("Destaddr too long. Rejecting.");
+ mock_clean_saved_logs();
+
+ /* Try with 2000-byte hostname, not terminated. */
+ ADD_DATA(buf, "\x04\x01\x00\x50\x00\x00\x00\x01\x00");
+ for (i = 0; i < 200; ++i) {
+ ADD_DATA(buf, "1234567890");
+ }
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0),
+ OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("Destaddr too long.");
+ mock_clean_saved_logs();
+
+ /* Socks4, bogus hostname */
+ ADD_DATA(buf, "\x04\x01\x00\x50\x00\x00\x00\x01\x00" "---\x00" );
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0), OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("Your application (using socks4 to port 80) "
+ "gave Tor a malformed hostname: ");
+ mock_clean_saved_logs();
+
+ done:
+ teardown_capture_of_logs();
+}
+
/** Perform unsupported SOCKS 5 commands */
static void
test_socks_5_unsupported_commands(void *ptr)
@@ -199,10 +281,28 @@ test_socks_5_supported_commands(void *ptr)
tt_int_op(0,OP_EQ, buf_datalen(buf));
socks_request_clear(socks);
+ /* SOCKS 5 Send CONNECT [01] to one of the ipv6 addresses for
+ torproject.org:80 */
+ ADD_DATA(buf, "\x05\x01\x00");
+ ADD_DATA(buf, "\x05\x01\x00\x04"
+ "\x20\x02\x41\xb8\x02\x02\x0d\xeb\x02\x13\x21\xff\xfe\x20\x14\x26"
+ "\x00\x50");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),OP_EQ, 1);
+ tt_int_op(5,OP_EQ, socks->socks_version);
+ tt_int_op(2,OP_EQ, socks->replylen);
+ tt_int_op(5,OP_EQ, socks->reply[0]);
+ tt_int_op(0,OP_EQ, socks->reply[1]);
+ tt_str_op("[2002:41b8:202:deb:213:21ff:fe20:1426]",OP_EQ, socks->address);
+ tt_int_op(80,OP_EQ, socks->port);
+
+ tt_int_op(0,OP_EQ, buf_datalen(buf));
+ socks_request_clear(socks);
+
/* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
- tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1,
get_options()->SafeSocks),OP_EQ, 1);
tt_int_op(5,OP_EQ, socks->socks_version);
@@ -218,8 +318,9 @@ test_socks_5_supported_commands(void *ptr)
/* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
- tt_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, 1);
tt_int_op(5,OP_EQ, socks->socks_version);
tt_int_op(2,OP_EQ, socks->replylen);
tt_int_op(5,OP_EQ, socks->reply[0]);
@@ -229,47 +330,46 @@ test_socks_5_supported_commands(void *ptr)
tt_int_op(0,OP_EQ, buf_datalen(buf));
socks_request_clear(socks);
- /* SOCKS 5 Should reject RESOLVE [F0] request for IPv4 address
+ /* SOCKS 5 Should NOT reject RESOLVE [F0] request for IPv4 address
* string if SafeSocks is enabled. */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x07");
ADD_DATA(buf, "8.8.8.8");
- ADD_DATA(buf, "\x01\x02");
- tt_assert(fetch_from_buf_socks(buf,socks,get_options()->TestSocks,1)
- == -1);
+ ADD_DATA(buf, "\x11\x11");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, 1),
+ OP_EQ, 1);
- tt_int_op(5,OP_EQ,socks->socks_version);
- tt_int_op(10,OP_EQ,socks->replylen);
- tt_int_op(5,OP_EQ,socks->reply[0]);
- tt_int_op(SOCKS5_NOT_ALLOWED,OP_EQ,socks->reply[1]);
- tt_int_op(1,OP_EQ,socks->reply[3]);
+ tt_str_op("8.8.8.8", OP_EQ, socks->address);
+ tt_int_op(4369, OP_EQ, socks->port);
+
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
socks_request_clear(socks);
- /* SOCKS 5 should reject RESOLVE [F0] reject for IPv6 address
+ /* SOCKS 5 should NOT reject RESOLVE [F0] reject for IPv6 address
* string if SafeSocks is enabled. */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF0\x00\x03\x27");
ADD_DATA(buf, "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
ADD_DATA(buf, "\x01\x02");
- tt_assert(fetch_from_buf_socks(buf,socks,get_options()->TestSocks,1)
- == -1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks, 1),
+ OP_EQ, -1);
- tt_int_op(5,OP_EQ,socks->socks_version);
- tt_int_op(10,OP_EQ,socks->replylen);
- tt_int_op(5,OP_EQ,socks->reply[0]);
- tt_int_op(SOCKS5_NOT_ALLOWED,OP_EQ,socks->reply[1]);
- tt_int_op(1,OP_EQ,socks->reply[3]);
+ tt_str_op("2001:0db8:85a3:0000:0000:8a2e:0370:7334", OP_EQ, socks->address);
+ tt_int_op(258, OP_EQ, socks->port);
+
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
socks_request_clear(socks);
/* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
ADD_DATA(buf, "\x05\x01\x00");
ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
- tt_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, 1);
tt_int_op(5,OP_EQ, socks->socks_version);
tt_int_op(2,OP_EQ, socks->replylen);
tt_int_op(5,OP_EQ, socks->reply[0]);
@@ -380,9 +480,9 @@ test_socks_5_authenticate_with_data(void *ptr)
/* SOCKS 5 Send username/password */
/* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
- tt_assert(fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks) == 1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, 1);
tt_int_op(5,OP_EQ, socks->socks_version);
tt_int_op(2,OP_EQ, socks->replylen);
tt_int_op(1,OP_EQ, socks->reply[0]);
@@ -400,6 +500,48 @@ test_socks_5_authenticate_with_data(void *ptr)
;
}
+/** Try to negotiate an unsupported authentication type */
+static void
+test_socks_5_auth_unsupported_type(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* None of these authentication types are recognized. */
+ ADD_DATA(buf, "\x05\x03\x99\x21\x10");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, -1);
+ tt_int_op(0,OP_EQ, socks->socks_version);
+ tt_int_op(2,OP_EQ, socks->replylen);
+ tt_int_op(5,OP_EQ, socks->reply[0]);
+ tt_int_op(0xff,OP_EQ, socks->reply[1]);
+
+ done:
+ ;
+}
+
+/** Try to negotiate an unsupported version of username/password auth. */
+static void
+test_socks_5_auth_unsupported_version(void *ptr)
+{
+ SOCKS_TEST_INIT();
+
+ /* Negotiate username/password */
+ ADD_DATA(buf, "\x05\x01\x02");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, 0);
+ tt_int_op(0,OP_EQ, buf_datalen(buf)); /* buf should be drained */
+ /* Now, suggest an unrecognized username/password version */
+ ADD_DATA(buf, "\x02\x05" "hello" "\x05" "world");
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, -1);
+
+ done:
+ ;
+}
+
/** Perform SOCKS 5 authentication before method negotiated */
static void
test_socks_5_auth_before_negotiation(void *ptr)
@@ -408,9 +550,9 @@ test_socks_5_auth_before_negotiation(void *ptr)
/* SOCKS 5 Send username/password */
ADD_DATA(buf, "\x01\x02me\x02me");
- tt_assert(fetch_from_buf_socks(buf, socks,
- get_options()->TestSocks,
- get_options()->SafeSocks) == -1);
+ tt_int_op(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+ get_options()->SafeSocks),
+ OP_EQ, -1);
tt_int_op(0,OP_EQ, socks->socks_version);
tt_int_op(0,OP_EQ, socks->replylen);
tt_int_op(0,OP_EQ, socks->reply[0]);
@@ -492,20 +634,397 @@ test_socks_5_malformed_commands(void *ptr)
;
}
+static void
+test_socks_5_bad_arguments(void *ptr)
+{
+ SOCKS_TEST_INIT();
+ setup_capture_of_logs(LOG_DEBUG);
+
+ /* Socks5, bogus hostname */
+ ADD_DATA(buf, "\x05\x01\x00" "\x05\x01\x00\x03\x03" "---" "\x00\x50" );
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0), OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("Your application (using socks5 to port 80) "
+ "gave Tor a malformed hostname: ");
+ mock_clean_saved_logs();
+ socks_request_clear(socks);
+
+ done:
+ teardown_capture_of_logs();
+}
+
+/** check for correct behavior when the socks command has not arrived. */
+static void
+test_socks_truncated(void *ptr)
+{
+ const struct {
+ enum { NONE, AUTH, ALL } setup;
+ const char *body;
+ size_t len;
+ } commands[] = {
+ /* SOCKS4 */
+ /* Connect, to an IP. */
+ { NONE, "\x04\x01\x05\x05\x01\x02\x03\x04\x00", 9},
+ /* Connect, to an IP, with authentication. */
+ { NONE, "\x04\x01\x05\x05\x01\x02\x03\x04hello\x00", 14},
+ /* SOCKS4A */
+ /* Connect, to a hostname */
+ { NONE, "\x04\x01\x09\x09\x00\x00\x00\x01\x00www.example.com\x00", 25},
+ /* Connect, to a hostname, with authentication */
+ { NONE, "\x04\x01\x09\x09\x00\x00\x00\x01hi\x00www.example.com\x00", 27},
+ /* SOCKS5 */
+ /* initial handshake */
+ { NONE, "\x05\x00", 2 },
+ /* no-auth handshake */
+ { NONE, "\x05\x03\x99\x21\x10", 5 },
+ /* SOCSK5, username-password, all empty. */
+ { AUTH, "\x01\x00\x00", 3 },
+ /* SOCSK5, username-password, 1 char each. */
+ { AUTH, "\x01\x01x\x01y", 5 },
+ /* SOCSK5, username-password, max length. */
+ { AUTH, "\x01\xff"
+ "Ogni tempo ha il suo fascismo: se ne notano i segni premonitori "
+ "dovunque la concentrazione di potere nega al cittadino la "
+ "possibilit\xc3\xa0 e la capacit\xc3\xa0 di esprimere ed attuare la "
+ "sua volont\xc3\xa0. A questo si arriva in molti modi, non "
+ "necessariamente col terror"
+ "\xff"
+ "e dell'intimidazione poliziesca, ma anche negando o distorcendo "
+ "l'informazione, inquinando la giustizia, paralizzando la scuola, "
+ "diffondendo in molti modi sottili la nostalgia per un mondo in cui "
+ "regnava sovrano l'ordine, ed in cui la sicurezza dei pochi "
+ /* privilegiati riposava sul lavoro forzato e sul silenzio forzato dei
+ molti. -- Primo Levi */ , 513 },
+ /* Socks5, IPv4 address */
+ { ALL, "\x05\x01\x00\x01\x01\x02\x03\x04\x20\x20", 10 },
+ /* Socks5, IPv6 address */
+ { ALL, "\x05\x01\x00\x04"
+ "\x49\x20\x48\x41\x5a\x20\x45\x41\x53\x54\x45\x52\x20\x45\x47\x47"
+ "\x20\x20", 22 },
+ /* Socks5, hostname, empty. */
+ { ALL, "\x05\x01\x00\x03" "\x00" "\x00\x50", 7 },
+ /* Socks5, hostname, moderate. */
+ { ALL, "\x05\x01\x00\x03" "\x11" "onion.example.com" "\x00\x50", 24 },
+ /* Socks5, hostname, maximum. */
+ { ALL, "\x05\x01\x00\x03" "\xff"
+ "whatsoever.I.shall.see.or.hear.in.the.course.of.my.profession.as.well."
+ "as.outside.my.profession.in.my.intercourse.with.men.if.it.be.what."
+ "should.not.be.published.abroad.I.will.never.divulge.holding.such."
+ "things.to.be.holy.secrets.x.hippocratic.oath.wikipedia"
+ "\x00\x50", 262 },
+ };
+ unsigned i, j;
+ SOCKS_TEST_INIT();
+ for (i = 0; i < ARRAY_LENGTH(commands); ++i) {
+ for (j = 0; j < commands[i].len; ++j) {
+ switch (commands[i].setup) {
+ default: /* Falls through */
+ case NONE:
+ /* This test calls for no setup on the socks state. */
+ break;
+ case AUTH:
+ /* This test calls for the socks state to be waiting for
+ * username/password authentication */
+ ADD_DATA(buf, "\x05\x01\x02");
+ tt_int_op(0, OP_EQ, fetch_from_buf_socks(buf, socks, 0, 0));
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
+ break;
+ case ALL:
+ /* This test calls for the socks state to be waiting for
+ * the connection request */
+ ADD_DATA(buf, "\x05\x01\x00");
+ tt_int_op(0, OP_EQ, fetch_from_buf_socks(buf, socks, 0, 0));
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
+ }
+
+ TT_BLATHER(("Checking command %u, length %u, omitting char %u", i, j,
+ (unsigned)commands[i].body[j]));
+ buf_add(buf, commands[i].body, j);
+ /* This should return 0 meaning "not done yet" */
+ tt_int_op(0, OP_EQ, fetch_from_buf_socks(buf, socks, 0, 0));
+ tt_uint_op(j, OP_EQ, buf_datalen(buf)); /* Nothing was drained */
+ buf_clear(buf);
+ socks_request_free(testdata->req);
+ socks = testdata->req = socks_request_new();
+ }
+ }
+ done:
+ ;
+}
+
+static void
+test_socks_wrong_protocol(void *ptr)
+{
+ SOCKS_TEST_INIT();
+ setup_capture_of_logs(LOG_DEBUG);
+
+ /* HTTP request. */
+ ADD_DATA(buf, "GET /index.html HTTP/1.0" );
+ tt_int_op(fetch_from_buf_socks(buf, socks, 1, 0), OP_EQ, -1);
+ buf_clear(buf);
+ expect_log_msg_containing("Socks version 71 not recognized. "
+ "(This port is not an HTTP proxy;");
+ mock_clean_saved_logs();
+ socks_request_clear(socks);
+
+ done:
+ teardown_capture_of_logs();
+}
+
+/* Check our client-side socks4 parsing (that is to say, our parsing of
+ * server responses).
+ */
+static void
+test_socks_client_v4(void *arg)
+{
+ (void)arg;
+ buf_t *buf = buf_new();
+ char *reason = NULL;
+
+ /* Legit socks4 response, success */
+ ADD_DATA(buf, "\x04\x5a\x20\x25\x01\x02\x03\x04");
+ tt_int_op(1, OP_EQ,
+ fetch_from_buf_socks_client(buf, PROXY_SOCKS4_WANT_CONNECT_OK,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* Legit socks4 response, failure. */
+ ADD_DATA(buf, "\x04\x5b\x20\x25\x01\x02\x03\x04");
+ tt_int_op(-1, OP_EQ,
+ fetch_from_buf_socks_client(buf, PROXY_SOCKS4_WANT_CONNECT_OK,
+ &reason));
+ tt_ptr_op(reason, OP_NE, NULL);
+ tt_str_op(reason, OP_EQ, "server rejected connection");
+
+ done:
+ buf_free(buf);
+ tor_free(reason);
+}
+
+/* Check our client-side socks5 authentication-negotiation parsing (that is to
+ * say, our parsing of server responses).
+ */
+static void
+test_socks_client_v5_auth(void *arg)
+{
+ (void)arg;
+ buf_t *buf = buf_new();
+ char *reason = NULL;
+
+ /* Legit socks5 responses, got a method we like. */
+ ADD_DATA(buf, "\x05\x00");
+ tt_int_op(1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_AUTH_METHOD_NONE,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* Same, but we wanted something else. */
+ ADD_DATA(buf, "\x05\x00");
+ tt_int_op(1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* Same, and they offered a password. */
+ ADD_DATA(buf, "\x05\x02");
+ tt_int_op(2, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* They rejected our method, or selected something we don't know. */
+ ADD_DATA(buf, "\x05\xff");
+ tt_int_op(-1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_AUTH_METHOD_NONE,
+ &reason));
+ tt_str_op(reason, OP_EQ, "server doesn't support any of our available "
+ "authentication methods");
+ buf_clear(buf);
+ tor_free(reason);
+ ADD_DATA(buf, "\x05\xff");
+ tt_int_op(-1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929,
+ &reason));
+ tt_str_op(reason, OP_EQ, "server doesn't support any of our available "
+ "authentication methods");
+ tor_free(reason);
+ buf_clear(buf);
+
+ /* Now check for authentication responses: check success and failure. */
+ ADD_DATA(buf, "\x01\x00");
+ tt_int_op(1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_AUTH_RFC1929_OK,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ ADD_DATA(buf, "\x01\xf0");
+ tt_int_op(-1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_AUTH_RFC1929_OK,
+ &reason));
+ tt_ptr_op(reason, OP_NE, NULL);
+ tt_str_op(reason, OP_EQ, "authentication failed");
+
+ done:
+ buf_free(buf);
+ tor_free(reason);
+}
+
+/* Check our client-side socks5 connect parsing (that is to say, our parsing
+ * of server responses).
+ */
+static void
+test_socks_client_v5_connect(void *arg)
+{
+ (void)arg;
+ buf_t *buf = buf_new();
+ char *reason = NULL;
+
+ /* Legit socks5 responses, success, ipv4. */
+ ADD_DATA(buf, "\x05\x00\x00\x01\x01\x02\x03\x04\x00\x05");
+ tt_int_op(1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_CONNECT_OK,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* Legit socks5 responses, success, ipv6. */
+ ADD_DATA(buf, "\x05\x00\x00\x04"
+ "abcdefghijklmnop"
+ "\x00\x05");
+ tt_int_op(1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_CONNECT_OK,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* Legit socks5 responses, success, hostname. */
+ ADD_DATA(buf, "\x05\x00\x00\x03\x12"
+ "gopher.example.com"
+ "\x00\x05");
+ tt_int_op(1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_CONNECT_OK,
+ &reason));
+ tt_ptr_op(reason, OP_EQ, NULL);
+ tt_int_op(buf_datalen(buf), OP_EQ, 0);
+
+ /* Legit socks5 responses, failure, hostname. */
+ ADD_DATA(buf, "\x05\x03\x00\x03\x12"
+ "gopher.example.com"
+ "\x00\x05");
+ tt_int_op(-1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_CONNECT_OK,
+ &reason));
+ tt_ptr_op(reason, OP_NE, NULL);
+ tt_str_op(reason, OP_EQ, "Network unreachable");
+ tor_free(reason);
+ buf_clear(buf);
+
+ /* Bogus socks5 responses: what is address type 0x17? */
+ ADD_DATA(buf, "\x05\x03\x00\x17\x12 blah blah");
+ tt_int_op(-1, OP_EQ,
+ fetch_from_buf_socks_client(buf,
+ PROXY_SOCKS5_WANT_CONNECT_OK,
+ &reason));
+ tt_ptr_op(reason, OP_NE, NULL);
+ tt_str_op(reason, OP_EQ, "invalid response to connect request");
+ buf_clear(buf);
+
+ done:
+ buf_free(buf);
+ tor_free(reason);
+}
+
+static void
+test_socks_client_truncated(void *arg)
+{
+ (void)arg;
+ buf_t *buf = buf_new();
+ char *reason = NULL;
+
+#define S(str) str, (sizeof(str)-1)
+ const struct {
+ int state;
+ const char *body;
+ size_t len;
+ } replies[] = {
+ { PROXY_SOCKS4_WANT_CONNECT_OK, S("\x04\x5a\x20\x25\x01\x02\x03\x04") },
+ { PROXY_SOCKS4_WANT_CONNECT_OK, S("\x04\x5b\x20\x25\x01\x02\x03\x04") },
+ { PROXY_SOCKS5_WANT_AUTH_METHOD_NONE, S("\x05\x00") },
+ { PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929, S("\x05\x00") },
+ { PROXY_SOCKS5_WANT_AUTH_RFC1929_OK, S("\x01\x00") },
+ { PROXY_SOCKS5_WANT_CONNECT_OK,
+ S("\x05\x00\x00\x01\x01\x02\x03\x04\x00\x05") },
+ { PROXY_SOCKS5_WANT_CONNECT_OK,
+ S("\x05\x00\x00\x04" "abcdefghijklmnop" "\x00\x05") },
+ { PROXY_SOCKS5_WANT_CONNECT_OK,
+ S("\x05\x00\x00\x03\x12" "gopher.example.com" "\x00\x05") },
+ { PROXY_SOCKS5_WANT_CONNECT_OK,
+ S("\x05\x03\x00\x03\x12" "gopher.example.com""\x00\x05") },
+ { PROXY_SOCKS5_WANT_CONNECT_OK,
+ S("\x05\x03\x00\x17") },
+ };
+ unsigned i, j;
+ for (i = 0; i < ARRAY_LENGTH(replies); ++i) {
+ for (j = 0; j < replies[i].len; ++j) {
+ TT_BLATHER(("Checking command %u, length %u", i, j));
+ buf_add(buf, replies[i].body, j);
+ /* This should return 0 meaning "not done yet" */
+ tt_int_op(0, OP_EQ,
+ fetch_from_buf_socks_client(buf, replies[i].state, &reason));
+ tt_uint_op(j, OP_EQ, buf_datalen(buf)); /* Nothing was drained */
+ buf_clear(buf);
+ tt_ptr_op(reason, OP_EQ, NULL);
+ }
+ }
+
+ done:
+ tor_free(reason);
+ buf_free(buf);
+}
+
#define SOCKSENT(name) \
{ #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
struct testcase_t socks_tests[] = {
SOCKSENT(4_unsupported_commands),
SOCKSENT(4_supported_commands),
+ SOCKSENT(4_bad_arguments),
SOCKSENT(5_unsupported_commands),
SOCKSENT(5_supported_commands),
SOCKSENT(5_no_authenticate),
+ SOCKSENT(5_auth_unsupported_type),
+ SOCKSENT(5_auth_unsupported_version),
SOCKSENT(5_auth_before_negotiation),
SOCKSENT(5_authenticate),
SOCKSENT(5_authenticate_with_data),
SOCKSENT(5_malformed_commands),
+ SOCKSENT(5_bad_arguments),
+
+ SOCKSENT(truncated),
+
+ SOCKSENT(wrong_protocol),
+
+ { "client/v4", test_socks_client_v4, TT_FORK, NULL, NULL },
+ { "client/v5_auth", test_socks_client_v5_auth, TT_FORK, NULL, NULL },
+ { "client/v5_connect", test_socks_client_v5_connect, TT_FORK, NULL, NULL },
+ { "client/truncated", test_socks_client_truncated, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_status.c b/src/test/test_status.c
index a3b1a2af87..f86f8e3b9e 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -889,8 +889,8 @@ NS(logv)(int severity, log_domain_mask_t domain, const char *funcname,
tt_str_op(format, OP_EQ,
"Average packaged cell fullness: %2.3f%%. "
"TLS write overhead: %.f%%");
- tt_double_op(fabs(va_arg(ap, double) - 50.0), <=, DBL_EPSILON);
- tt_double_op(fabs(va_arg(ap, double) - 0.0), <=, DBL_EPSILON);
+ tt_double_op(fabs(va_arg(ap, double) - 50.0), OP_LE, DBL_EPSILON);
+ tt_double_op(fabs(va_arg(ap, double) - 0.0), OP_LE, DBL_EPSILON);
break;
default:
tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args
@@ -1039,7 +1039,7 @@ NS(logv)(int severity, log_domain_mask_t domain,
"Average packaged cell fullness: %2.3f%%. "
"TLS write overhead: %.f%%");
tt_int_op(fabs(va_arg(ap, double) - 100.0) <= DBL_EPSILON, OP_EQ, 1);
- tt_double_op(fabs(va_arg(ap, double) - 100.0), <=, DBL_EPSILON);
+ tt_double_op(fabs(va_arg(ap, double) - 100.0), OP_LE, DBL_EPSILON);
break;
default:
tt_abort_msg("unexpected call to logv()"); // TODO: prettyprint args
diff --git a/src/test/test_storagedir.c b/src/test/test_storagedir.c
index 19e5de4ea3..a27074c21f 100644
--- a/src/test/test_storagedir.c
+++ b/src/test/test_storagedir.c
@@ -338,7 +338,7 @@ test_storagedir_read_labeled(void *arg)
tt_assert(labels->next->next);
tt_str_op(labels->next->next->key, OP_EQ, "Yadda");
tt_str_op(labels->next->next->value, OP_EQ, "yadda.");
- tt_assert(labels->next->next->next == NULL);
+ tt_ptr_op(labels->next->next->next, OP_EQ, NULL);
/* Try reading this time. */
sz = 0;
diff --git a/src/test/test_switch_id.c b/src/test/test_switch_id.c
index 53de793fe8..fe36d8c6e6 100644
--- a/src/test/test_switch_id.c
+++ b/src/test/test_switch_id.c
@@ -71,7 +71,7 @@ check_can_bind_low_ports(void)
return -1;
}
-#endif
+#endif /* !defined(_WIN32) */
int
main(int argc, char **argv)
@@ -83,7 +83,7 @@ main(int argc, char **argv)
fprintf(stderr, "This test is not supported on your OS.\n");
return 77;
-#else
+#else /* !(defined(_WIN32)) */
const char *username;
const char *testname;
if (argc != 3) {
@@ -174,7 +174,7 @@ main(int argc, char **argv)
}
cap_free(caps);
}
-#endif
+#endif /* defined(HAVE_LINUX_CAPABILITIES) */
break;
default:
fprintf(stderr, "Unsupported test '%s'\n", testname);
@@ -187,6 +187,6 @@ main(int argc, char **argv)
}
return (okay ? 0 : 1);
-#endif
+#endif /* defined(_WIN32) */
}
diff --git a/src/test/test_threads.c b/src/test/test_threads.c
index 18a9407ff7..ed6d8f04aa 100644
--- a/src/test/test_threads.c
+++ b/src/test/test_threads.c
@@ -139,8 +139,8 @@ test_threads_basic(void *arg)
!strcmp(strmap_get(thread_test_strmap_, "thread 2"),
strmap_get(thread_test_strmap_, "last to run")));
- tt_int_op(thread_fns_failed, ==, 0);
- tt_int_op(thread_fn_tid1, !=, thread_fn_tid2);
+ tt_int_op(thread_fns_failed, OP_EQ, 0);
+ tt_int_op(thread_fn_tid1, OP_NE, thread_fn_tid2);
done:
tor_free(s1);
@@ -275,14 +275,14 @@ test_threads_conditionvar(void *arg)
SPIN();
tor_mutex_release(ti->mutex);
- tt_int_op(ti->value, ==, 1337);
+ tt_int_op(ti->value, OP_EQ, 1337);
if (!timeout) {
- tt_int_op(ti->n_shutdown, ==, 4);
+ tt_int_op(ti->n_shutdown, OP_EQ, 4);
} else {
tor_sleep_msec(200);
tor_mutex_acquire(ti->mutex);
- tt_int_op(ti->n_shutdown, ==, 2);
- tt_int_op(ti->n_timeouts, ==, 2);
+ tt_int_op(ti->n_shutdown, OP_EQ, 2);
+ tt_int_op(ti->n_timeouts, OP_EQ, 2);
tor_mutex_release(ti->mutex);
}
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index 7aa3051464..29f7cc9c37 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -63,7 +63,7 @@ fake_num_ciphers(void)
{
return 0;
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_errno_to_tls_error(void *data)
@@ -136,7 +136,7 @@ test_tortls_tor_tls_new(void *data)
SSL_CTX_free(client_tls_context->ctx);
client_tls_context->ctx = NULL;
tls = tor_tls_new(-1, 0);
- tt_assert(!tls);
+ tt_ptr_op(tls, OP_EQ, NULL);
#ifndef OPENSSL_OPAQUE
method = give_me_a_test_method();
@@ -144,8 +144,8 @@ test_tortls_tor_tls_new(void *data)
method->num_ciphers = fake_num_ciphers;
client_tls_context->ctx = ctx;
tls = tor_tls_new(-1, 0);
- tt_assert(!tls);
-#endif
+ tt_ptr_op(tls, OP_EQ, NULL);
+#endif /* !defined(OPENSSL_OPAQUE) */
done:
UNMOCK(tor_tls_cert_matches_key);
@@ -376,7 +376,7 @@ test_tortls_log_one_error(void *ignored)
tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_RECORD_TOO_LARGE),
LOG_WARN, 0, NULL);
expect_log_severity(LOG_INFO);
-#endif
+#endif /* !defined(OPENSSL_1_1_API) */
mock_clean_saved_logs();
tor_tls_log_one_error(tls, ERR_PACK(1, 2, SSL_R_UNKNOWN_PROTOCOL),
@@ -490,7 +490,7 @@ test_tortls_get_error(void *ignored)
tor_free(tls);
SSL_CTX_free(ctx);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_always_accept_verify_cb(void *ignored)
@@ -520,7 +520,7 @@ test_tortls_x509_cert_free(void *ignored)
cert->encoded = tor_malloc_zero(1);
tor_x509_cert_free(cert);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_x509_cert_get_id_digests(void *ignored)
@@ -662,7 +662,7 @@ test_tortls_cert_get_key(void *ignored)
tor_free(cert);
crypto_pk_free(res);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_get_my_client_auth_key(void *ignored)
@@ -744,7 +744,7 @@ get_cipher_by_name(const char *name)
return NULL;
}
-#endif
+#endif /* !defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
#ifndef OPENSSL_OPAQUE
static void
@@ -879,7 +879,7 @@ test_tortls_classify_client_ciphers(void *ignored)
tor_free(tls);
SSL_CTX_free(ctx);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_client_is_using_v2_ciphers(void *ignored)
@@ -921,7 +921,7 @@ test_tortls_client_is_using_v2_ciphers(void *ignored)
done:
SSL_free(ssl);
SSL_CTX_free(ctx);
-#endif
+#endif /* defined(HAVE_SSL_GET_CLIENT_CIPHERS) */
}
#ifndef OPENSSL_OPAQUE
@@ -937,7 +937,7 @@ fixed_try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
*cert_out = fixed_try_to_extract_certs_from_tls_cert_out_result;
*id_cert_out = fixed_try_to_extract_certs_from_tls_id_cert_out_result;
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static const char* notCompletelyValidCertString =
@@ -956,7 +956,7 @@ static const char* notCompletelyValidCertString =
"jC9UeuErhaA/zzWi8ewMTFZW/WshOrm3fNvcMrMLKtH534JKvcdMg6qIdjTFINIr\n"
"evnAhf0cwULaebn+lMs8Pdl7y37+sfluVok=\n"
"-----END CERTIFICATE-----\n";
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static const char* validCertString = "-----BEGIN CERTIFICATE-----\n"
"MIIDpTCCAY0CAg3+MA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNVBAYTAlVTMREwDwYD\n"
@@ -1079,7 +1079,7 @@ test_tortls_verify(void *ignored)
tor_free(tls);
tor_free(k);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1118,7 +1118,7 @@ test_tortls_check_lifetime(void *ignored)
tor_free(tls);
X509_free(validCert);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static int fixed_ssl_pending_result = 0;
@@ -1153,7 +1153,7 @@ test_tortls_get_pending_bytes(void *ignored)
tor_free(tls->ssl);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_get_forced_write_size(void *ignored)
@@ -1276,13 +1276,13 @@ test_tortls_SSL_SESSION_get_master_key(void *ignored)
tt_int_op(out[0], OP_EQ, 43);
done:
-#endif
+#endif /* !defined(HAVE_SSL_SESSION_GET_MASTER_KEY) */
tor_free(tls->ssl->session);
tor_free(tls->ssl);
tor_free(tls);
tor_free(out);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1290,7 +1290,7 @@ test_tortls_get_tlssecrets(void *ignored)
{
(void)ignored;
int ret;
- uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN);;
+ uint8_t *secret_out = tor_malloc_zero(DIGEST256_LEN);
tor_tls_t *tls;
tls = tor_malloc_zero(sizeof(tor_tls_t));
tls->ssl = tor_malloc_zero(sizeof(SSL));
@@ -1308,7 +1308,7 @@ test_tortls_get_tlssecrets(void *ignored)
tor_free(tls->ssl);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1350,7 +1350,7 @@ test_tortls_get_buffer_sizes(void *ignored)
tt_int_op(rbuf_c, OP_EQ, 1);
tt_int_op(wbuf_c, OP_EQ, 2);
-#endif
+#endif /* OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) */
done:
tor_free(tls->ssl->s3->rbuf.buf);
@@ -1359,7 +1359,7 @@ test_tortls_get_buffer_sizes(void *ignored)
tor_free(tls->ssl);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_evaluate_ecgroup_for_tls(void *ignored)
@@ -1378,6 +1378,7 @@ test_tortls_evaluate_ecgroup_for_tls(void *ignored)
ret = evaluate_ecgroup_for_tls("P224");
// tt_int_op(ret, OP_EQ, 1); This varies between machines
+ tt_assert(ret == 0 || ret == 1);
done:
(void)0;
@@ -1450,7 +1451,7 @@ test_tortls_try_to_extract_certs_from_tls(void *ignored)
X509_free(c1);
X509_free(c2);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1482,7 +1483,7 @@ test_tortls_get_peer_cert(void *ignored)
tor_free(tls);
X509_free(cert);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1512,7 +1513,7 @@ test_tortls_peer_has_cert(void *ignored)
tor_free(tls);
X509_free(cert);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_is_server(void *ignored)
@@ -1572,7 +1573,7 @@ test_tortls_session_secret_cb(void *ignored)
SSL_CTX_free(ctx);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
/* TODO: It seems block_renegotiation and unblock_renegotiation and
@@ -1623,7 +1624,7 @@ test_tortls_unblock_renegotiation(void *ignored)
tor_free(tls->ssl);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1642,7 +1643,7 @@ test_tortls_assert_renegotiation_unblocked(void *ignored)
tor_free(tls->ssl);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static void
test_tortls_set_logged_address(void *ignored)
@@ -1697,7 +1698,7 @@ test_tortls_set_renegotiate_callback(void *ignored)
tor_free(tls->ssl);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static SSL_CIPHER *fixed_cipher1 = NULL;
@@ -1715,7 +1716,7 @@ fake_get_cipher(unsigned ncipher)
return NULL;
}
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1785,7 +1786,7 @@ test_tortls_find_cipher_by_id(void *ignored)
SSL_CTX_free(ctx);
tor_free(fixed_cipher1);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1813,7 +1814,7 @@ test_tortls_debug_state_callback(void *ignored)
tor_free(buf);
tor_free(ssl);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -1877,7 +1878,7 @@ test_tortls_server_info_callback(void *ignored)
SSL_CTX_free(ctx);
tor_free(tls);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static int fixed_ssl_read_result_index;
@@ -1918,7 +1919,7 @@ setting_version_and_state_ssl_shutdown(SSL *s)
s->version = SSL2_VERSION;
return fixed_ssl_shutdown_result;
}
-#endif
+#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
static int
dummy_handshake_func(SSL *s)
@@ -2014,7 +2015,7 @@ test_tortls_shutdown(void *ignored)
method->ssl_shutdown = setting_version_and_state_ssl_shutdown;
ret = tor_tls_shutdown(tls);
tt_int_op(ret, OP_EQ, TOR_TLS_ERROR_MISC);
-#endif
+#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
done:
teardown_capture_of_logs();
@@ -2085,7 +2086,7 @@ test_tortls_read(void *ignored)
ret = tor_tls_read(tls, buf, 10);
tt_int_op(ret, OP_EQ, TOR_TLS_CLOSE);
tt_int_op(tls->state, OP_EQ, TOR_TLS_ST_CLOSED);
-#endif
+#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
// TODO: fill up
done:
@@ -2160,7 +2161,7 @@ test_tortls_write(void *ignored)
tor_free(tls);
tor_free(method);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static int fixed_ssl_accept_result;
@@ -2240,7 +2241,7 @@ test_tortls_handshake(void *ignored)
"(null):SSLv3 write client hello B)\n");
expect_log_msg("TLS error while handshaking: (null) (in system library:"
"connect:SSLv3 write client hello B)\n");
-#endif
+#endif /* 0 */
expect_log_severity(LOG_INFO);
tls->isServer = 0;
@@ -2258,7 +2259,7 @@ test_tortls_handshake(void *ignored)
"(null) (in bignum routines:(null):SSLv3 write client hello B)\n");
expect_log_msg("TLS error while handshaking: "
"(null) (in system library:connect:SSLv3 write client hello B)\n");
-#endif
+#endif /* 0 */
expect_log_severity(LOG_WARN);
done:
@@ -2268,7 +2269,7 @@ test_tortls_handshake(void *ignored)
tor_free(tls);
tor_free(method);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#ifndef OPENSSL_OPAQUE
static void
@@ -2343,7 +2344,7 @@ test_tortls_finish_handshake(void *ignored)
tor_free(method);
teardown_capture_of_logs();
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static int fixed_crypto_pk_new_result_index;
static crypto_pk_t *fixed_crypto_pk_new_result[5];
@@ -2568,7 +2569,7 @@ test_tortls_context_new(void *ignored)
UNMOCK(crypto_pk_generate_key_with_bits);
UNMOCK(crypto_pk_new);
}
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
static int fixed_crypto_pk_get_evp_pkey_result_index = 0;
static EVP_PKEY *fixed_crypto_pk_get_evp_pkey_result[5];
@@ -2637,7 +2638,7 @@ test_tortls_cert_new(void *ignored)
X509_get_pubkey(cert)->type = EVP_PKEY_DSA;
ret = tor_x509_cert_new(cert);
tt_assert(ret);
-#endif
+#endif /* 0 */
#ifndef OPENSSL_OPAQUE
cert = read_cert_from(validCertString);
@@ -2645,7 +2646,7 @@ test_tortls_cert_new(void *ignored)
cert->cert_info = NULL;
ret = tor_x509_cert_new(cert);
tt_assert(ret);
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
done:
tor_x509_cert_free(ret);
@@ -2692,7 +2693,7 @@ test_tortls_cert_is_valid(void *ignored)
cert->cert->cert_info->key = NULL;
ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1);
tt_int_op(ret, OP_EQ, 0);
-#endif
+#endif /* !defined(OPENSSL_OPAQUE) */
#if 0
tor_x509_cert_free(cert);
@@ -2731,7 +2732,7 @@ test_tortls_cert_is_valid(void *ignored)
X509_get_pubkey(cert->cert)->ameth = NULL;
ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0);
tt_int_op(ret, OP_EQ, 0);
-#endif
+#endif /* 0 */
done:
tor_x509_cert_free(cert);
@@ -2764,7 +2765,7 @@ test_tortls_context_init_one(void *ignored)
{ #name, NULL, TT_SKIP, NULL, NULL }
#else
#define INTRUSIVE_TEST_CASE(name, flags) LOCAL_TEST_CASE(name, flags)
-#endif
+#endif /* defined(OPENSSL_OPAQUE) */
struct testcase_t tortls_tests[] = {
LOCAL_TEST_CASE(errno_to_tls_error, 0),
diff --git a/src/test/test_util.c b/src/test/test_util.c
index fa6ce1dc85..0519a4758f 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -70,7 +70,7 @@ test_util_read_until_eof_impl(const char *fname, size_t file_len,
fd = open(fifo_name, O_RDONLY|O_BINARY);
tt_int_op(fd, OP_GE, 0);
str = read_file_to_str_until_eof(fd, read_limit, &sz);
- tt_assert(str != NULL);
+ tt_ptr_op(str, OP_NE, NULL);
if (read_limit < file_len)
tt_int_op(sz, OP_EQ, read_limit);
@@ -367,7 +367,7 @@ test_util_time(void *arg)
* calculations internally, then catches the overflow. */
#define TV_SEC_MAX TIME_MAX
#define TV_SEC_MIN TIME_MIN
-#endif
+#endif /* defined(_WIN32) */
/* Assume tv_usec is an unsigned integer until proven otherwise */
#define TV_USEC_MAX UINT_MAX
@@ -635,13 +635,16 @@ test_util_time(void *arg)
* time_t */
a_time.tm_year = 2039-1900;
#if SIZEOF_TIME_T == 4
+ setup_full_capture_of_logs(LOG_WARN);
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ expect_single_log_msg_containing("Result does not fit in tor_timegm");
+ teardown_capture_of_logs();
#elif SIZEOF_TIME_T == 8
t_res = 2178252895UL;
tt_int_op(t_res, OP_EQ, tor_timegm(&a_time));
tor_gmtime_r(&t_res, &b_time);
TM_EQUAL(a_time, b_time);
-#endif
+#endif /* SIZEOF_TIME_T == 4 || ... */
/* Test tor_timegm out of range */
@@ -651,8 +654,7 @@ test_util_time(void *arg)
setup_full_capture_of_logs(LOG_WARN); \
} while (0)
#define CHECK_TIMEGM_WARNING(msg) do { \
- expect_log_msg_containing(msg); \
- tt_int_op(1, OP_EQ, smartlist_len(mock_saved_logs())); \
+ expect_single_log_msg_containing(msg); \
teardown_capture_of_logs(); \
} while (0)
@@ -689,7 +691,7 @@ test_util_time(void *arg)
CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
CHECK_TIMEGM_ARG_OUT_OF_RANGE();
-#endif
+#endif /* SIZEOF_INT == 4 || SIZEOF_INT == 8 */
#if SIZEOF_INT == 8
a_time.tm_year = -1*(1 << 48);
@@ -711,7 +713,7 @@ test_util_time(void *arg)
CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
CHECK_TIMEGM_ARG_OUT_OF_RANGE();
-#endif
+#endif /* SIZEOF_INT == 8 */
/* Wrong year >= INT32_MAX - 1900 */
#if SIZEOF_INT == 4 || SIZEOF_INT == 8
@@ -724,7 +726,7 @@ test_util_time(void *arg)
CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
CHECK_TIMEGM_ARG_OUT_OF_RANGE();
-#endif
+#endif /* SIZEOF_INT == 4 || SIZEOF_INT == 8 */
#if SIZEOF_INT == 8
/* one of the largest tm_year values my 64 bit system supports */
@@ -752,7 +754,7 @@ test_util_time(void *arg)
CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
CHECK_TIMEGM_ARG_OUT_OF_RANGE();
-#endif
+#endif /* SIZEOF_INT == 8 */
/* month */
a_time.tm_year = 2007-1900; /* restore valid year */
@@ -888,7 +890,7 @@ test_util_time(void *arg)
teardown_capture_of_logs();
}
}
-#endif
+#endif /* SIZEOF_TIME_T == 8 */
/* time_t >= INT_MAX yields a year clamped to 2037 or 9999,
* depending on whether the implementation of the system gmtime(_r)
@@ -904,7 +906,7 @@ test_util_time(void *arg)
tt_assert(b_time.tm_year == (2037-1900) ||
b_time.tm_year == (2038-1900));
}
-#endif
+#endif /* SIZEOF_TIME_T == 4 || SIZEOF_TIME_T == 8 */
#if SIZEOF_TIME_T == 8
{
@@ -929,7 +931,7 @@ test_util_time(void *arg)
tt_assert(b_time.tm_year == (2037-1900) ||
b_time.tm_year == (9999-1900));
}
-#endif
+#endif /* SIZEOF_TIME_T == 8 */
/* Test {format,parse}_rfc1123_time */
@@ -965,7 +967,9 @@ test_util_time(void *arg)
strlcpy(timestr, "Wed, 17 Feb 2038 06:13:20 GMT", sizeof(timestr));
t_res = 0;
+ CAPTURE();
i = parse_rfc1123_time(timestr, &t_res);
+ CHECK_TIMEGM_WARNING("does not fit in tor_timegm");
tt_int_op(-1,OP_EQ, i);
#elif SIZEOF_TIME_T == 8
tt_str_op("Wed, 17 Feb 2038 06:13:20 GMT",OP_EQ, timestr);
@@ -974,7 +978,7 @@ test_util_time(void *arg)
i = parse_rfc1123_time(timestr, &t_res);
tt_int_op(0,OP_EQ, i);
tt_int_op(t_res,OP_EQ, (time_t)2150000000UL);
-#endif
+#endif /* SIZEOF_TIME_T == 4 || ... */
/* The timezone doesn't matter */
t_res = 0;
@@ -1040,13 +1044,16 @@ test_util_time(void *arg)
/* This value is out of range with 32 bit time_t, but in range for 64 bit
* time_t */
t_res = 0;
- i = parse_iso_time("2038-02-17 06:13:20", &t_res);
#if SIZEOF_TIME_T == 4
+ CAPTURE();
+ i = parse_iso_time("2038-02-17 06:13:20", &t_res);
tt_int_op(-1,OP_EQ, i);
+ CHECK_TIMEGM_WARNING("does not fit in tor_timegm");
#elif SIZEOF_TIME_T == 8
+ i = parse_iso_time("2038-02-17 06:13:20", &t_res);
tt_int_op(0,OP_EQ, i);
tt_int_op(t_res,OP_EQ, (time_t)2150000000UL);
-#endif
+#endif /* SIZEOF_TIME_T == 4 || ... */
tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-zz 99-99x99", &t_res));
tt_int_op(-1,OP_EQ, parse_iso_time("2011-03-32 00:00:00", &t_res));
@@ -1129,7 +1136,7 @@ test_util_time(void *arg)
/* This SHOULD work on windows too; see bug #18665 */
tt_str_op("2038-02-17 06:13:20",OP_EQ, timestr);
#endif
-#endif
+#endif /* SIZEOF_TIME_T == 4 || ... */
#undef CAPTURE
#undef CHECK_TIMEGM_ARG_OUT_OF_RANGE
@@ -1218,13 +1225,16 @@ test_util_parse_http_time(void *arg)
#if SIZEOF_TIME_T == 4
/* parse_http_time should indicate failure on overflow, but it doesn't yet.
* Hopefully #18480 will improve the failure semantics in this case. */
+ setup_full_capture_of_logs(LOG_WARN);
tt_int_op(0,OP_EQ,parse_http_time("Wed, 17 Feb 2038 06:13:20 GMT", &a_time));
tt_int_op((time_t)-1,OP_EQ, tor_timegm(&a_time));
+ expect_single_log_msg_containing("does not fit in tor_timegm");
+ teardown_capture_of_logs();
#elif SIZEOF_TIME_T == 8
tt_int_op(0,OP_EQ,parse_http_time("Wed, 17 Feb 2038 06:13:20 GMT", &a_time));
tt_int_op((time_t)2150000000UL,OP_EQ, tor_timegm(&a_time));
T("2038-02-17 06:13:20");
-#endif
+#endif /* SIZEOF_TIME_T == 4 || ... */
tt_int_op(-1,OP_EQ, parse_http_time("2004-08-zz 99-99x99 GMT", &a_time));
tt_int_op(-1,OP_EQ, parse_http_time("2011-03-32 00:00:00 GMT", &a_time));
@@ -1237,7 +1247,7 @@ test_util_parse_http_time(void *arg)
#undef T
done:
- ;
+ teardown_capture_of_logs();
}
static void
@@ -1471,7 +1481,7 @@ test_util_config_line_comment_character(void *arg)
tor_free(k); tor_free(v);
test_streq(str, "");
-#endif
+#endif /* 0 */
done:
tor_free(k);
@@ -1602,7 +1612,7 @@ test_util_config_line_escaped_content(void *arg)
str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_ptr_op(str, OP_EQ, NULL);
tor_free(k); tor_free(v);
-#endif
+#endif /* 0 */
str = buf6;
@@ -1674,14 +1684,14 @@ test_util_config_line_crlf(void *arg)
tt_assert(str);
tt_str_op(k,OP_EQ,"Hello");
tt_str_op(v,OP_EQ,"world");
- tt_assert(!err);
+ tt_ptr_op(err, OP_EQ, NULL);
tor_free(k); tor_free(v);
str = parse_config_line_from_str_verbose(str, &k, &v, &err);
tt_assert(str);
tt_str_op(k,OP_EQ,"Hello");
tt_str_op(v,OP_EQ,"nice big world");
- tt_assert(!err);
+ tt_ptr_op(err, OP_EQ, NULL);
tor_free(k); tor_free(v);
tt_str_op(str,OP_EQ, "");
@@ -1786,7 +1796,7 @@ test_util_expand_filename(void *arg)
done:
tor_free(str);
}
-#endif
+#endif /* !defined(_WIN32) */
/** Test tor_escape_str_for_pt_args(). */
static void
@@ -1941,7 +1951,7 @@ test_util_strmisc(void *arg)
tt_assert(!tor_mem_is_zero(buf, 10));
/* Test 'escaped' */
- tt_assert(NULL == escaped(NULL));
+ tt_ptr_op(escaped(NULL), OP_EQ, NULL);
tt_str_op("\"\"",OP_EQ, escaped(""));
tt_str_op("\"abcd\"",OP_EQ, escaped("abcd"));
tt_str_op("\"\\\\ \\n\\r\\t\\\"\\'\"",OP_EQ, escaped("\\ \n\r\t\"'"));
@@ -1999,23 +2009,23 @@ test_util_strmisc(void *arg)
/* Test memmem and memstr */
{
const char *haystack = "abcde";
- tt_assert(!tor_memmem(haystack, 5, "ef", 2));
+ tt_ptr_op(tor_memmem(haystack, 5, "ef", 2), OP_EQ, NULL);
tt_ptr_op(tor_memmem(haystack, 5, "cd", 2),OP_EQ, haystack + 2);
tt_ptr_op(tor_memmem(haystack, 5, "cde", 3),OP_EQ, haystack + 2);
- tt_assert(!tor_memmem(haystack, 4, "cde", 3));
+ tt_ptr_op(tor_memmem(haystack, 4, "cde", 3), OP_EQ, NULL);
haystack = "ababcad";
tt_ptr_op(tor_memmem(haystack, 7, "abc", 3),OP_EQ, haystack + 2);
tt_ptr_op(tor_memmem(haystack, 7, "ad", 2),OP_EQ, haystack + 5);
tt_ptr_op(tor_memmem(haystack, 7, "cad", 3),OP_EQ, haystack + 4);
- tt_assert(!tor_memmem(haystack, 7, "dadad", 5));
- tt_assert(!tor_memmem(haystack, 7, "abcdefghij", 10));
+ tt_ptr_op(tor_memmem(haystack, 7, "dadad", 5), OP_EQ, NULL);
+ tt_ptr_op(tor_memmem(haystack, 7, "abcdefghij", 10), OP_EQ, NULL);
/* memstr */
tt_ptr_op(tor_memstr(haystack, 7, "abc"),OP_EQ, haystack + 2);
tt_ptr_op(tor_memstr(haystack, 7, "cad"),OP_EQ, haystack + 4);
- tt_assert(!tor_memstr(haystack, 6, "cad"));
- tt_assert(!tor_memstr(haystack, 7, "cadd"));
- tt_assert(!tor_memstr(haystack, 7, "fe"));
- tt_assert(!tor_memstr(haystack, 7, "ababcade"));
+ tt_ptr_op(tor_memstr(haystack, 6, "cad"), OP_EQ, NULL);
+ tt_ptr_op(tor_memstr(haystack, 7, "cadd"), OP_EQ, NULL);
+ tt_ptr_op(tor_memstr(haystack, 7, "fe"), OP_EQ, NULL);
+ tt_ptr_op(tor_memstr(haystack, 7, "ababcade"), OP_EQ, NULL);
}
/* Test hex_str */
@@ -2125,10 +2135,13 @@ test_util_parse_integer(void *arg)
/* 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));
+ tor_capture_bugs_(2);
+ tt_int_op(0L,OP_EQ, tor_parse_long("10",-2,0,100,NULL,NULL));
tt_int_op(0,OP_EQ, tor_parse_long("10ABC",-1,0,70000,&i,NULL));
+ tt_int_op(2, OP_EQ, smartlist_len(tor_get_captured_bug_log_()));
+ tor_end_capture_bugs_();
tt_int_op(i,OP_EQ, 0);
/* Test parse_ulong */
@@ -2141,7 +2154,10 @@ test_util_parse_integer(void *arg)
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));
+ tor_capture_bugs_(1);
tt_int_op(0UL,OP_EQ, tor_parse_ulong("50",-1,50,100,&i,NULL));
+ tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_()));
+ tor_end_capture_bugs_();
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);
@@ -2157,8 +2173,11 @@ test_util_parse_integer(void *arg)
tt_assert(U64_LITERAL(0) ==
tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp));
tt_int_op(0,OP_EQ, i);
+ tor_capture_bugs_(1);
tt_assert(U64_LITERAL(0) ==
tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp));
+ tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_()));
+ tor_end_capture_bugs_();
tt_int_op(0,OP_EQ, i);
{
@@ -2170,10 +2189,13 @@ test_util_parse_integer(void *arg)
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_double_op(fabs(d), OP_LT, 1e-10);
tt_int_op(0,OP_EQ, i);
d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,NULL);
+ tt_double_op(fabs(d), OP_LT, 1e-10);
tt_int_op(0,OP_EQ, i);
d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,&cp);
+ tt_double_op(fabs(d), OP_LT, 1e-10);
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);
@@ -2251,15 +2273,15 @@ test_util_compress_impl(compress_method_t method)
tt_assert(tor_compress_supports_method(method));
if (method != NO_METHOD) {
- tt_assert(tor_compress_version_str(method) != NULL);
- tt_assert(tor_compress_header_version_str(method) != NULL);
+ tt_ptr_op(tor_compress_version_str(method), OP_NE, NULL);
+ tt_ptr_op(tor_compress_header_version_str(method), OP_NE, NULL);
}
buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ");
tt_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD);
tt_assert(!tor_compress(&buf2, &len1, buf1, strlen(buf1)+1, method));
- tt_assert(buf2 != NULL);
+ tt_ptr_op(buf2, OP_NE, NULL);
if (method == NO_METHOD) {
// The identity transform doesn't actually compress, and it isn't
// detectable as "the identity transform."
@@ -2271,7 +2293,7 @@ test_util_compress_impl(compress_method_t method)
}
tt_assert(!tor_uncompress(&buf3, &len2, buf2, len1, method, 1, LOG_INFO));
- tt_assert(buf3 != NULL);
+ tt_ptr_op(buf3, OP_NE, NULL);
tt_int_op(strlen(buf1) + 1, OP_EQ, len2);
tt_str_op(buf1, OP_EQ, buf3);
tt_int_op(buf3[len2], OP_EQ, 0);
@@ -2318,7 +2340,7 @@ test_util_compress_impl(compress_method_t method)
if (method != NO_METHOD) {
tt_assert(tor_uncompress(&buf3, &len2, buf2, len1-16,
method, 1, LOG_INFO));
- tt_assert(buf3 == NULL);
+ tt_ptr_op(buf3, OP_EQ, NULL);
}
done:
@@ -2459,6 +2481,126 @@ test_util_decompress_concatenated(void *arg)
}
static void
+test_util_decompress_junk_impl(compress_method_t method)
+{
+ char input[4096];
+ char *result = NULL, *result2 = NULL;
+ size_t szr, szr2, sz;
+ int r;
+
+ /* This shouldn't be a compressed string according to any method. */
+ strlcpy(input, "This shouldn't be a compressed string by any means.",
+ sizeof(input));
+ sz = strlen(input);
+ setup_capture_of_logs(LOG_WARN);
+ r = tor_uncompress(&result, &szr, input, sz, method, 0, LOG_WARN);
+ tt_int_op(r, OP_EQ, -1);
+ tt_ptr_op(result, OP_EQ, NULL);
+ expect_log_msg_containing("Error while uncompressing data: bad input?");
+ mock_clean_saved_logs();
+
+ /* Now try again, with a compressed object that starts out good and turns to
+ junk. */
+ crypto_rand(input, sizeof(input));
+ r = tor_compress(&result, &szr, input, sizeof(input), method);
+ tt_int_op(r, OP_EQ, 0);
+ crypto_rand(result+szr/2, szr-(szr/2)); // trash the 2nd half of the result
+ r = tor_uncompress(&result2, &szr2, result, szr, method, 0, LOG_WARN);
+ tt_int_op(r, OP_EQ, -1);
+ expect_log_msg_containing("Error while uncompressing data: bad input?");
+
+ done:
+ teardown_capture_of_logs();
+ tor_free(result);
+ tor_free(result2);
+}
+
+static void
+test_util_decompress_junk(void *arg)
+{
+ const char *methodname = arg;
+ tt_assert(methodname);
+
+ compress_method_t method = compression_method_get_by_name(methodname);
+ tt_int_op(method, OP_NE, UNKNOWN_METHOD);
+ if (! tor_compress_supports_method(method)) {
+ tt_skip();
+ }
+
+ test_util_decompress_junk_impl(method);
+ done:
+ ;
+}
+
+/* mock replacement for tor_compress_is_compression_bomb that doesn't
+ * believe in compression bombs. */
+static int
+mock_is_never_compression_bomb(size_t in, size_t out)
+{
+ (void)in;
+ (void) out;
+ return 0;
+}
+
+static void
+test_util_decompress_dos_impl(compress_method_t method)
+{
+ char *input;
+ char *result = NULL, *result2 = NULL;
+ size_t szr, szr2;
+ int r;
+
+ const size_t big = 1024*1024;
+ /* one megabyte of 0s. */
+ input = tor_malloc_zero(big);
+
+ /* Compress it into "result": it should fail. */
+ setup_full_capture_of_logs(LOG_WARN);
+ r = tor_compress(&result, &szr, input, big, method);
+ tt_int_op(r, OP_EQ, -1);
+ expect_log_msg_containing(
+ "other Tors would think this was a compression bomb");
+ teardown_capture_of_logs();
+
+ /* Try again, but this time suppress compression-bomb detection */
+ MOCK(tor_compress_is_compression_bomb, mock_is_never_compression_bomb);
+ r = tor_compress(&result, &szr, input, big, method);
+ UNMOCK(tor_compress_is_compression_bomb);
+ tt_int_op(r, OP_EQ, 0);
+ tt_ptr_op(result, OP_NE, NULL);
+
+ /* We should refuse to uncomrpess it again, since it looks like a
+ * compression bomb. */
+ setup_capture_of_logs(LOG_WARN);
+ r = tor_uncompress(&result2, &szr2, result, szr, method, 0, LOG_WARN);
+ tt_int_op(r, OP_EQ, -1);
+ expect_log_msg_containing("bomb; abandoning stream");
+
+ done:
+ teardown_capture_of_logs();
+ tor_free(input);
+ tor_free(result);
+ tor_free(result2);
+}
+
+static void
+test_util_decompress_dos(void *arg)
+{
+ const char *methodname = arg;
+ tt_assert(methodname);
+
+ compress_method_t method = compression_method_get_by_name(methodname);
+ tt_int_op(method, OP_NE, UNKNOWN_METHOD);
+ if (! tor_compress_supports_method(method)) {
+ tt_skip();
+ }
+
+ test_util_decompress_dos_impl(method);
+ done:
+ ;
+}
+
+static void
test_util_gzip_compression_bomb(void *arg)
{
/* A 'compression bomb' is a very small object that uncompresses to a huge
@@ -2525,7 +2667,7 @@ test_util_mmap(void *arg)
crypto_rand(buf, buflen);
mapping = tor_mmap_file(fname1);
- tt_assert(! mapping);
+ tt_ptr_op(mapping, OP_EQ, NULL);
write_str_to_file(fname1, "Short file.", 1);
@@ -2543,7 +2685,7 @@ test_util_mmap(void *arg)
tt_str_op(mapping->data,OP_EQ, "Short file.");
tt_int_op(0, OP_EQ, tor_munmap_file(mapping));
mapping = NULL;
-#endif
+#endif /* defined(_WIN32) */
/* Now a zero-length file. */
write_str_to_file(fname1, "", 1);
@@ -2554,7 +2696,7 @@ test_util_mmap(void *arg)
/* Make sure that we fail to map a no-longer-existent file. */
mapping = tor_mmap_file(fname1);
- tt_assert(! mapping);
+ tt_ptr_op(mapping, OP_EQ, NULL);
/* Now try a big file that stretches across a few pages and isn't aligned */
write_bytes_to_file(fname2, buf, buflen, 1);
@@ -2872,7 +3014,7 @@ test_util_sscanf(void *arg)
r = tor_sscanf("9223372036854775808. -9223372036854775809.",
"%d. %d.", &int1, &int2);
tt_int_op(r,OP_EQ, 0);
-#endif
+#endif /* SIZEOF_INT == 4 || ... */
#if SIZEOF_LONG == 4
/* %lu */
@@ -2967,7 +3109,7 @@ test_util_sscanf(void *arg)
r = tor_sscanf("9223372036854775808. -9223372036854775809.",
"%ld. %ld.", &lng1, &lng2);
tt_int_op(r,OP_EQ, 0);
-#endif
+#endif /* SIZEOF_LONG == 4 || ... */
r = tor_sscanf("123.456 .000007 -900123123.2000787 00003.2",
"%lf %lf %lf %lf", &d1,&d2,&d3,&d4);
@@ -2994,7 +3136,7 @@ strnlen(const char *s, size_t len)
return len;
return p - s;
}
-#endif
+#endif /* !defined(HAVE_STRNLEN) */
static void
test_util_format_time_interval(void *arg)
@@ -3361,7 +3503,7 @@ test_util_format_time_interval(void *arg)
tt_ci_char_op(label_m[0],OP_EQ, 'm');
/* and 7 or 8 seconds - ignored */
-#endif
+#endif /* SIZEOF_LONG == 4 || SIZEOF_LONG == 8 */
#if SIZEOF_LONG == 8
@@ -3399,7 +3541,7 @@ test_util_format_time_interval(void *arg)
tt_ci_char_op(label_m[0],OP_EQ, 'm');
/* and 7 or 8 seconds - ignored */
-#endif
+#endif /* SIZEOF_LONG == 8 */
done:
;
@@ -3442,7 +3584,7 @@ test_util_path_is_relative(void *arg)
tt_int_op(0,OP_EQ, path_is_relative("\\dir"));
tt_int_op(0,OP_EQ, path_is_relative("a:\\dir"));
tt_int_op(0,OP_EQ, path_is_relative("z:\\dir"));
-#endif
+#endif /* defined(_WIN32) */
done:
;
@@ -3462,7 +3604,7 @@ test_util_memarea(void *arg)
malloc(), which is free to lay out memory most any way it wants. */
if (1)
tt_skip();
-#endif
+#endif /* defined(DISABLE_MEMORY_SENTINELS) */
(void)arg;
tt_assert(area);
@@ -3633,8 +3775,8 @@ test_util_strtok(void *arg)
}
tor_snprintf(buf, sizeof(buf), "%s", pad1);
tor_snprintf(buf2, sizeof(buf2), "%s", pad2);
- tt_assert(NULL == tor_strtok_r_impl(buf, " ", &cp1));
- tt_assert(NULL == tor_strtok_r_impl(buf2, ".!..;!", &cp2));
+ tt_ptr_op(tor_strtok_r_impl(buf, " ", &cp1), OP_EQ, NULL);
+ tt_ptr_op(tor_strtok_r_impl(buf2, ".!..;!", &cp2), OP_EQ, NULL);
tor_snprintf(buf, sizeof(buf),
"%sGraved on the dark in gestures of descent%s", pad1, pad1);
@@ -3984,7 +4126,7 @@ test_util_load_win_lib(void *ptr)
if (h)
FreeLibrary(h);
}
-#endif
+#endif /* defined(_WIN32) */
#ifndef _WIN32
static void
@@ -4036,7 +4178,7 @@ test_util_exit_status(void *ptr)
tt_int_op(n,OP_EQ, strlen(hex_errno));
tt_int_op(n,OP_EQ, HEX_ERRNO_SIZE);
-#endif
+#endif /* SIZEOF_INT == 4 || ... */
clear_hex_errno(hex_errno);
n = format_helper_exit_status(0x7F, 0, hex_errno);
@@ -4054,7 +4196,7 @@ test_util_exit_status(void *ptr)
done:
;
}
-#endif
+#endif /* !defined(_WIN32) */
#ifndef _WIN32
static void
@@ -4182,7 +4324,7 @@ test_util_string_from_pipe(void *ptr)
close(test_pipe[1]);
}
-#endif // _WIN32
+#endif /* !defined(_WIN32) */
/**
* Test for format_hex_number_sigsafe()
@@ -4378,7 +4520,7 @@ test_util_split_lines(void *ptr)
/* Check we have not got too many lines */
tt_int_op(MAX_SPLIT_LINE_COUNT, OP_GT, j);
/* Check that there actually should be a line here */
- tt_assert(tests[i].split_line[j] != NULL);
+ tt_ptr_op(tests[i].split_line[j], OP_NE, NULL);
log_info(LD_GENERAL, "Line %d of test %d, should be <%s>",
j, i, tests[i].split_line[j]);
/* Check that the line is as expected */
@@ -4494,11 +4636,11 @@ test_util_di_map(void *arg)
char dflt_entry[] = "'You have made a good beginning', but no more";
- tt_int_op(32, ==, sizeof(key1));
- tt_int_op(32, ==, sizeof(key2));
- tt_int_op(32, ==, sizeof(key3));
+ tt_int_op(32, OP_EQ, sizeof(key1));
+ tt_int_op(32, OP_EQ, sizeof(key2));
+ tt_int_op(32, OP_EQ, sizeof(key3));
- tt_ptr_op(dflt_entry, ==, dimap_search(dimap, key1, dflt_entry));
+ tt_ptr_op(dflt_entry, OP_EQ, dimap_search(dimap, key1, dflt_entry));
char *str1 = tor_strdup("You are precisely as big as what you love"
" and precisely as small as what you allow"
@@ -4516,10 +4658,10 @@ test_util_di_map(void *arg)
dimap_add_entry(&dimap, key2, str2);
dimap_add_entry(&dimap, key3, str3);
- tt_ptr_op(str1, ==, dimap_search(dimap, key1, dflt_entry));
- tt_ptr_op(str3, ==, dimap_search(dimap, key3, dflt_entry));
- tt_ptr_op(str2, ==, dimap_search(dimap, key2, dflt_entry));
- tt_ptr_op(dflt_entry, ==, dimap_search(dimap, key4, dflt_entry));
+ tt_ptr_op(str1, OP_EQ, dimap_search(dimap, key1, dflt_entry));
+ tt_ptr_op(str3, OP_EQ, dimap_search(dimap, key3, dflt_entry));
+ tt_ptr_op(str2, OP_EQ, dimap_search(dimap, key2, dflt_entry));
+ tt_ptr_op(dflt_entry, OP_EQ, dimap_search(dimap, key4, dflt_entry));
done:
dimap_free(dimap, tor_free_);
@@ -4986,34 +5128,34 @@ test_util_round_to_next_multiple_of(void *arg)
{
(void)arg;
- tt_u64_op(round_uint64_to_next_multiple_of(0,1), ==, 0);
- tt_u64_op(round_uint64_to_next_multiple_of(0,7), ==, 0);
+ tt_u64_op(round_uint64_to_next_multiple_of(0,1), OP_EQ, 0);
+ tt_u64_op(round_uint64_to_next_multiple_of(0,7), OP_EQ, 0);
- tt_u64_op(round_uint64_to_next_multiple_of(99,1), ==, 99);
- tt_u64_op(round_uint64_to_next_multiple_of(99,7), ==, 105);
- tt_u64_op(round_uint64_to_next_multiple_of(99,9), ==, 99);
+ tt_u64_op(round_uint64_to_next_multiple_of(99,1), OP_EQ, 99);
+ tt_u64_op(round_uint64_to_next_multiple_of(99,7), OP_EQ, 105);
+ tt_u64_op(round_uint64_to_next_multiple_of(99,9), OP_EQ, 99);
- tt_u64_op(round_uint64_to_next_multiple_of(UINT64_MAX,2), ==,
+ tt_u64_op(round_uint64_to_next_multiple_of(UINT64_MAX,2), OP_EQ,
UINT64_MAX);
- tt_int_op(round_uint32_to_next_multiple_of(0,1), ==, 0);
- tt_int_op(round_uint32_to_next_multiple_of(0,7), ==, 0);
+ tt_int_op(round_uint32_to_next_multiple_of(0,1), OP_EQ, 0);
+ tt_int_op(round_uint32_to_next_multiple_of(0,7), OP_EQ, 0);
- tt_int_op(round_uint32_to_next_multiple_of(99,1), ==, 99);
- tt_int_op(round_uint32_to_next_multiple_of(99,7), ==, 105);
- tt_int_op(round_uint32_to_next_multiple_of(99,9), ==, 99);
+ tt_int_op(round_uint32_to_next_multiple_of(99,1), OP_EQ, 99);
+ tt_int_op(round_uint32_to_next_multiple_of(99,7), OP_EQ, 105);
+ tt_int_op(round_uint32_to_next_multiple_of(99,9), OP_EQ, 99);
- tt_int_op(round_uint32_to_next_multiple_of(UINT32_MAX,2), ==,
+ tt_int_op(round_uint32_to_next_multiple_of(UINT32_MAX,2), OP_EQ,
UINT32_MAX);
- tt_uint_op(round_to_next_multiple_of(0,1), ==, 0);
- tt_uint_op(round_to_next_multiple_of(0,7), ==, 0);
+ tt_uint_op(round_to_next_multiple_of(0,1), OP_EQ, 0);
+ tt_uint_op(round_to_next_multiple_of(0,7), OP_EQ, 0);
- tt_uint_op(round_to_next_multiple_of(99,1), ==, 99);
- tt_uint_op(round_to_next_multiple_of(99,7), ==, 105);
- tt_uint_op(round_to_next_multiple_of(99,9), ==, 99);
+ tt_uint_op(round_to_next_multiple_of(99,1), OP_EQ, 99);
+ tt_uint_op(round_to_next_multiple_of(99,7), OP_EQ, 105);
+ tt_uint_op(round_to_next_multiple_of(99,9), OP_EQ, 99);
- tt_uint_op(round_to_next_multiple_of(UINT_MAX,2), ==,
+ tt_uint_op(round_to_next_multiple_of(UINT_MAX,2), OP_EQ,
UINT_MAX);
done:
;
@@ -5034,26 +5176,26 @@ test_util_laplace(void *arg)
const double delta_f = 15.0, epsilon = 0.3; /* b = 15.0 / 0.3 = 50.0 */
(void)arg;
- tt_i64_op(INT64_MIN, ==, sample_laplace_distribution(mu, b, 0.0));
- tt_i64_op(-69, ==, sample_laplace_distribution(mu, b, 0.01));
- tt_i64_op(24, ==, sample_laplace_distribution(mu, b, 0.5));
- tt_i64_op(24, ==, sample_laplace_distribution(mu, b, 0.51));
- tt_i64_op(117, ==, sample_laplace_distribution(mu, b, 0.99));
+ tt_i64_op(INT64_MIN, OP_EQ, sample_laplace_distribution(mu, b, 0.0));
+ tt_i64_op(-69, OP_EQ, sample_laplace_distribution(mu, b, 0.01));
+ tt_i64_op(24, OP_EQ, sample_laplace_distribution(mu, b, 0.5));
+ tt_i64_op(24, OP_EQ, sample_laplace_distribution(mu, b, 0.51));
+ tt_i64_op(117, OP_EQ, sample_laplace_distribution(mu, b, 0.99));
/* >>> laplace.ppf([0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 0.99],
* ... loc = 0, scale = 50)
* array([ -inf, -80.47189562, -34.65735903, 0. ,
* 34.65735903, 80.47189562, 195.60115027])
*/
- tt_i64_op(INT64_MIN + 20, ==,
+ tt_i64_op(INT64_MIN + 20, OP_EQ,
add_laplace_noise(20, 0.0, delta_f, epsilon));
- tt_i64_op(-60, ==, add_laplace_noise(20, 0.1, delta_f, epsilon));
- tt_i64_op(-14, ==, add_laplace_noise(20, 0.25, delta_f, epsilon));
- tt_i64_op(20, ==, add_laplace_noise(20, 0.5, delta_f, epsilon));
- tt_i64_op(54, ==, add_laplace_noise(20, 0.75, delta_f, epsilon));
- tt_i64_op(100, ==, add_laplace_noise(20, 0.9, delta_f, epsilon));
- tt_i64_op(215, ==, add_laplace_noise(20, 0.99, delta_f, epsilon));
+ tt_i64_op(-60, OP_EQ, add_laplace_noise(20, 0.1, delta_f, epsilon));
+ tt_i64_op(-14, OP_EQ, add_laplace_noise(20, 0.25, delta_f, epsilon));
+ tt_i64_op(20, OP_EQ, add_laplace_noise(20, 0.5, delta_f, epsilon));
+ tt_i64_op(54, OP_EQ, add_laplace_noise(20, 0.75, delta_f, epsilon));
+ tt_i64_op(100, OP_EQ, add_laplace_noise(20, 0.9, delta_f, epsilon));
+ tt_i64_op(215, OP_EQ, add_laplace_noise(20, 0.99, delta_f, epsilon));
/* Test extreme values of signal with maximally negative values of noise
* 1.0000000000000002 is the smallest number > 1
@@ -5066,54 +5208,54 @@ test_util_laplace(void *arg)
*/
const double noscale_df = 1.0, noscale_eps = 1.0;
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(0, 0.0, noscale_df, noscale_eps));
/* is it clipped to INT64_MIN? */
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(-1, 0.0, noscale_df, noscale_eps));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(INT64_MIN, 0.0,
noscale_df, noscale_eps));
/* ... even when scaled? */
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(0, 0.0, delta_f, epsilon));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(0, 0.0,
DBL_MAX, 1));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(INT64_MIN, 0.0,
DBL_MAX, 1));
/* does it play nice with INT64_MAX? */
- tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ tt_i64_op((INT64_MIN + INT64_MAX), OP_EQ,
add_laplace_noise(INT64_MAX, 0.0,
noscale_df, noscale_eps));
/* do near-zero fractional values work? */
const double min_dbl_error = 0.0000000000000002;
- tt_i64_op(-35, ==,
+ tt_i64_op(-35, OP_EQ,
add_laplace_noise(0, min_dbl_error,
noscale_df, noscale_eps));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(INT64_MIN, min_dbl_error,
noscale_df, noscale_eps));
- tt_i64_op((-35 + INT64_MAX), ==,
+ tt_i64_op((-35 + INT64_MAX), OP_EQ,
add_laplace_noise(INT64_MAX, min_dbl_error,
noscale_df, noscale_eps));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(0, min_dbl_error,
DBL_MAX, 1));
- tt_i64_op((INT64_MAX + INT64_MIN), ==,
+ tt_i64_op((INT64_MAX + INT64_MIN), OP_EQ,
add_laplace_noise(INT64_MAX, min_dbl_error,
DBL_MAX, 1));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
add_laplace_noise(INT64_MIN, min_dbl_error,
DBL_MAX, 1));
/* does it play nice with INT64_MAX? */
- tt_i64_op((INT64_MAX - 35), ==,
+ tt_i64_op((INT64_MAX - 35), OP_EQ,
add_laplace_noise(INT64_MAX, min_dbl_error,
noscale_df, noscale_eps));
@@ -5128,31 +5270,31 @@ test_util_laplace(void *arg)
const double max_dbl_lt_one = 0.9999999999999998;
/* do near-one fractional values work? */
- tt_i64_op(35, ==,
+ tt_i64_op(35, OP_EQ,
add_laplace_noise(0, max_dbl_lt_one, noscale_df, noscale_eps));
/* is it clipped to INT64_MAX? */
- tt_i64_op(INT64_MAX, ==,
+ tt_i64_op(INT64_MAX, OP_EQ,
add_laplace_noise(INT64_MAX - 35, max_dbl_lt_one,
noscale_df, noscale_eps));
- tt_i64_op(INT64_MAX, ==,
+ tt_i64_op(INT64_MAX, OP_EQ,
add_laplace_noise(INT64_MAX - 34, max_dbl_lt_one,
noscale_df, noscale_eps));
- tt_i64_op(INT64_MAX, ==,
+ tt_i64_op(INT64_MAX, OP_EQ,
add_laplace_noise(INT64_MAX, max_dbl_lt_one,
noscale_df, noscale_eps));
/* ... even when scaled? */
- tt_i64_op(INT64_MAX, ==,
+ tt_i64_op(INT64_MAX, OP_EQ,
add_laplace_noise(INT64_MAX, max_dbl_lt_one,
delta_f, epsilon));
- tt_i64_op((INT64_MIN + INT64_MAX), ==,
+ tt_i64_op((INT64_MIN + INT64_MAX), OP_EQ,
add_laplace_noise(INT64_MIN, max_dbl_lt_one,
DBL_MAX, 1));
- tt_i64_op(INT64_MAX, ==,
+ tt_i64_op(INT64_MAX, OP_EQ,
add_laplace_noise(INT64_MAX, max_dbl_lt_one,
DBL_MAX, 1));
/* does it play nice with INT64_MIN? */
- tt_i64_op((INT64_MIN + 35), ==,
+ tt_i64_op((INT64_MIN + 35), OP_EQ,
add_laplace_noise(INT64_MIN, max_dbl_lt_one,
noscale_df, noscale_eps));
@@ -5165,32 +5307,32 @@ test_util_clamp_double_to_int64(void *arg)
{
(void)arg;
- tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY_DBL));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ, clamp_double_to_int64(-INFINITY_DBL));
+ tt_i64_op(INT64_MIN, OP_EQ,
clamp_double_to_int64(-1.0 * pow(2.0, 64.0) - 1.0));
- tt_i64_op(INT64_MIN, ==,
+ tt_i64_op(INT64_MIN, OP_EQ,
clamp_double_to_int64(-1.0 * pow(2.0, 63.0) - 1.0));
- tt_i64_op(((uint64_t) -1) << 53, ==,
+ tt_i64_op(((uint64_t) -1) << 53, OP_EQ,
clamp_double_to_int64(-1.0 * pow(2.0, 53.0)));
- tt_i64_op((((uint64_t) -1) << 53) + 1, ==,
+ tt_i64_op((((uint64_t) -1) << 53) + 1, OP_EQ,
clamp_double_to_int64(-1.0 * pow(2.0, 53.0) + 1.0));
- tt_i64_op(-1, ==, clamp_double_to_int64(-1.0));
- tt_i64_op(0, ==, clamp_double_to_int64(-0.9));
- tt_i64_op(0, ==, clamp_double_to_int64(-0.1));
- tt_i64_op(0, ==, clamp_double_to_int64(0.0));
- tt_i64_op(0, ==, clamp_double_to_int64(NAN_DBL));
- tt_i64_op(0, ==, clamp_double_to_int64(0.1));
- tt_i64_op(0, ==, clamp_double_to_int64(0.9));
- tt_i64_op(1, ==, clamp_double_to_int64(1.0));
- tt_i64_op((((int64_t) 1) << 53) - 1, ==,
+ tt_i64_op(-1, OP_EQ, clamp_double_to_int64(-1.0));
+ tt_i64_op(0, OP_EQ, clamp_double_to_int64(-0.9));
+ tt_i64_op(0, OP_EQ, clamp_double_to_int64(-0.1));
+ tt_i64_op(0, OP_EQ, clamp_double_to_int64(0.0));
+ tt_i64_op(0, OP_EQ, clamp_double_to_int64(NAN_DBL));
+ tt_i64_op(0, OP_EQ, clamp_double_to_int64(0.1));
+ tt_i64_op(0, OP_EQ, clamp_double_to_int64(0.9));
+ tt_i64_op(1, OP_EQ, clamp_double_to_int64(1.0));
+ tt_i64_op((((int64_t) 1) << 53) - 1, OP_EQ,
clamp_double_to_int64(pow(2.0, 53.0) - 1.0));
- tt_i64_op(((int64_t) 1) << 53, ==,
+ tt_i64_op(((int64_t) 1) << 53, OP_EQ,
clamp_double_to_int64(pow(2.0, 53.0)));
- tt_i64_op(INT64_MAX, ==,
+ tt_i64_op(INT64_MAX, OP_EQ,
clamp_double_to_int64(pow(2.0, 63.0)));
- tt_i64_op(INT64_MAX, ==,
+ tt_i64_op(INT64_MAX, OP_EQ,
clamp_double_to_int64(pow(2.0, 64.0)));
- tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY_DBL));
+ tt_i64_op(INT64_MAX, OP_EQ, clamp_double_to_int64(INFINITY_DBL));
done:
;
@@ -5204,7 +5346,7 @@ fd_is_cloexec(tor_socket_t fd)
int flags = fcntl(fd, F_GETFD, 0);
return (flags & FD_CLOEXEC) == FD_CLOEXEC;
}
-#endif
+#endif /* defined(FD_CLOEXEC) */
#ifndef _WIN32
#define CAN_CHECK_NONBLOCK
@@ -5214,7 +5356,7 @@ fd_is_nonblocking(tor_socket_t fd)
int flags = fcntl(fd, F_GETFL, 0);
return (flags & O_NONBLOCK) == O_NONBLOCK;
}
-#endif
+#endif /* !defined(_WIN32) */
#define ERRNO_IS_EPROTO(e) (e == SOCK_ERRNO(EPROTONOSUPPORT))
#define SOCK_ERR_IS_EPROTO(s) ERRNO_IS_EPROTO(tor_socket_errno(s))
@@ -5258,13 +5400,13 @@ test_util_socket(void *arg)
tt_int_op(fd_is_cloexec(fd2), OP_EQ, 0);
tt_int_op(fd_is_cloexec(fd3), OP_EQ, 1);
tt_int_op(fd_is_cloexec(fd4), OP_EQ, 1);
-#endif
+#endif /* defined(CAN_CHECK_CLOEXEC) */
#ifdef CAN_CHECK_NONBLOCK
tt_int_op(fd_is_nonblocking(fd1), OP_EQ, 0);
tt_int_op(fd_is_nonblocking(fd2), OP_EQ, 1);
tt_int_op(fd_is_nonblocking(fd3), OP_EQ, 0);
tt_int_op(fd_is_nonblocking(fd4), OP_EQ, 1);
-#endif
+#endif /* defined(CAN_CHECK_NONBLOCK) */
tor_assert(tor_close_socket == tor_close_socket__real);
@@ -5320,7 +5462,7 @@ is_there_a_localhost(int family)
return result;
}
-#endif
+#endif /* 0 */
/* Test for socketpair and ersatz_socketpair(). We test them both, since
* the latter is a tolerably good way to exersize tor_accept_socket(). */
@@ -5347,7 +5489,7 @@ test_util_socketpair(void *arg)
* Assume we're on a machine without 127.0.0.1 or ::1 and give up now. */
tt_skip();
}
-#endif
+#endif /* defined(__FreeBSD__) */
tt_int_op(0, OP_EQ, socketpair_result);
tt_assert(SOCKET_OK(fds[0]));
@@ -5508,7 +5650,7 @@ test_util_get_avail_disk_space(void *arg)
#else
tt_i64_op(val, OP_GT, 0); /* You have some space. */
tt_i64_op(val, OP_LT, ((int64_t)1)<<56); /* You don't have a zebibyte */
-#endif
+#endif /* !defined(HAVE_STATVFS) && !defined(_WIN32) */
done:
;
@@ -5558,25 +5700,25 @@ test_util_pwdb(void *arg)
/* Uncached case. */
/* Let's assume that we exist. */
me = tor_getpwuid(getuid());
- tt_assert(me != NULL);
+ tt_ptr_op(me, OP_NE, NULL);
name = tor_strdup(me->pw_name);
/* Uncached case */
me2 = tor_getpwnam(name);
- tt_assert(me2 != NULL);
+ tt_ptr_op(me2, OP_NE, NULL);
tt_int_op(me2->pw_uid, OP_EQ, getuid());
/* Cached case */
me3 = tor_getpwuid(getuid());
- tt_assert(me3 != NULL);
+ tt_ptr_op(me3, OP_NE, NULL);
tt_str_op(me3->pw_name, OP_EQ, name);
me3 = tor_getpwnam(name);
- tt_assert(me3 != NULL);
+ tt_ptr_op(me3, OP_NE, NULL);
tt_int_op(me3->pw_uid, OP_EQ, getuid());
dir = get_user_homedir(name);
- tt_assert(dir != NULL);
+ tt_ptr_op(dir, OP_NE, NULL);
/* Try failing cases. First find a user that doesn't exist by name */
char randbytes[4];
@@ -5596,7 +5738,7 @@ test_util_pwdb(void *arg)
/* We should do a LOG_ERR */
setup_full_capture_of_logs(LOG_ERR);
dir = get_user_homedir(badname);
- tt_assert(dir == NULL);
+ tt_ptr_op(dir, OP_EQ, NULL);
expect_log_msg_containing("not found");
tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
teardown_capture_of_logs();
@@ -5618,7 +5760,7 @@ test_util_pwdb(void *arg)
tor_free(dir);
teardown_capture_of_logs();
}
-#endif
+#endif /* !defined(_WIN32) */
static void
test_util_calloc_check(void *arg)
@@ -5796,7 +5938,7 @@ test_util_htonll(void *arg)
#else
tt_u64_op(res_le, OP_EQ, tor_htonll(0x8877665544332211));
tt_u64_op(res_le, OP_EQ, tor_ntohll(0x8877665544332211));
-#endif
+#endif /* defined(WORDS_BIGENDIAN) */
done:
;
@@ -5895,6 +6037,16 @@ test_util_get_unquoted_path(void *arg)
&passthrough_setup, \
(char*)(identifier) }
+#define COMPRESS_JUNK(name, identifier) \
+ { "compress_junk/" #name, test_util_decompress_junk, 0, \
+ &passthrough_setup, \
+ (char*)(identifier) }
+
+#define COMPRESS_DOS(name, identifier) \
+ { "compress_dos/" #name, test_util_decompress_dos, 0, \
+ &passthrough_setup, \
+ (char*)(identifier) }
+
#ifdef _WIN32
#define UTIL_TEST_NO_WIN(n, f) { #n, NULL, TT_SKIP, NULL, NULL }
#define UTIL_TEST_WIN_ONLY(n, f) UTIL_TEST(n, (f))
@@ -5903,7 +6055,7 @@ test_util_get_unquoted_path(void *arg)
#define UTIL_TEST_NO_WIN(n, f) UTIL_TEST(n, (f))
#define UTIL_TEST_WIN_ONLY(n, f) { #n, NULL, TT_SKIP, NULL, NULL }
#define UTIL_LEGACY_NO_WIN(n) UTIL_LEGACY(n)
-#endif
+#endif /* defined(_WIN32) */
struct testcase_t util_tests[] = {
UTIL_LEGACY(time),
@@ -5929,6 +6081,13 @@ struct testcase_t util_tests[] = {
COMPRESS_CONCAT(lzma, "x-tor-lzma"),
COMPRESS_CONCAT(zstd, "x-zstd"),
COMPRESS_CONCAT(none, "identity"),
+ COMPRESS_JUNK(zlib, "deflate"),
+ COMPRESS_JUNK(gzip, "gzip"),
+ COMPRESS_JUNK(lzma, "x-tor-lzma"),
+ COMPRESS_DOS(zlib, "deflate"),
+ COMPRESS_DOS(gzip, "gzip"),
+ COMPRESS_DOS(lzma, "x-tor-lzma"),
+ COMPRESS_DOS(zstd, "x-zstd"),
UTIL_TEST(gzip_compression_bomb, TT_FORK),
UTIL_LEGACY(datadir),
UTIL_LEGACY(memarea),
diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c
index ea0a86499f..683d5fdac1 100644
--- a/src/test/test_util_format.c
+++ b/src/test/test_util_format.c
@@ -345,7 +345,7 @@ test_util_format_base32_decode(void *arg)
const char *src = "mjwgc2dcnrswqmjs";
ret = base32_decode(dst, strlen(expected), src, strlen(src));
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
tt_str_op(expected, OP_EQ, dst);
}
@@ -356,7 +356,7 @@ test_util_format_base32_decode(void *arg)
const char *src = "mjwgc2dcnrswq";
ret = base32_decode(dst, strlen(expected), src, strlen(src));
- tt_int_op(ret, ==, 0);
+ tt_int_op(ret, OP_EQ, 0);
tt_mem_op(expected, OP_EQ, dst, strlen(expected));
}
@@ -364,9 +364,9 @@ test_util_format_base32_decode(void *arg)
{
/* Invalid character '#'. */
ret = base32_decode(dst, real_dstlen, "#abcde", 6);
- tt_int_op(ret, ==, -1);
+ tt_int_op(ret, OP_EQ, -1);
/* Make sure the destination buffer has been zeroed even on error. */
- tt_int_op(tor_mem_is_zero(dst, real_dstlen), ==, 1);
+ tt_int_op(tor_mem_is_zero(dst, real_dstlen), OP_EQ, 1);
}
done:
diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c
index 70292f2287..68ce6cfd40 100644
--- a/src/test/test_util_process.c
+++ b/src/test/test_util_process.c
@@ -67,7 +67,7 @@ test_util_process_clear_waitpid_callback(void *ignored)
done:
teardown_capture_of_logs();
}
-#endif /* _WIN32 */
+#endif /* !defined(_WIN32) */
#ifndef _WIN32
#define TEST(name) { #name, test_util_process_##name, 0, NULL, NULL }
diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c
index 3e5d78948d..2cd68cf118 100644
--- a/src/test/test_util_slow.c
+++ b/src/test/test_util_slow.c
@@ -22,14 +22,14 @@
#else
#define TEST_CHILD (BUILDDIR "/src/test/test-child")
#define EOL "\n"
-#endif
+#endif /* defined(_WIN32) */
#ifdef _WIN32
/* I've assumed Windows doesn't have the gap between fork and exec
* that causes the race condition on unix-like platforms */
#define MATCH_PROCESS_STATUS(s1,s2) ((s1) == (s2))
-#else
+#else /* !(defined(_WIN32)) */
/* work around a race condition of the timing of SIGCHLD handler updates
* to the process_handle's fields, and checks of those fields
*
@@ -46,7 +46,7 @@
||((s2) == PROCESS_STATUS_RUNNING_OR_NOTRUNNING \
&& IS_RUNNING_OR_NOTRUNNING(s1)))
-#endif // _WIN32
+#endif /* defined(_WIN32) */
/** Helper function for testing tor_spawn_background */
static void
@@ -78,7 +78,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
return;
}
- tt_assert(process_handle != NULL);
+ tt_ptr_op(process_handle, OP_NE, NULL);
/* When a spawned process forks, fails, then exits very quickly,
* (this typically occurs when exec fails)
@@ -102,7 +102,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
* that is, PROCESS_STATUS_RUNNING_OR_NOTRUNNING */
tt_assert(process_handle->waitpid_cb != NULL
|| expected_status == PROCESS_STATUS_RUNNING_OR_NOTRUNNING);
-#endif
+#endif /* !defined(_WIN32) */
#ifdef _WIN32
tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
@@ -112,7 +112,7 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
tt_assert(process_handle->stdout_pipe >= 0);
tt_assert(process_handle->stderr_pipe >= 0);
tt_assert(process_handle->stdin_pipe >= 0);
-#endif
+#endif /* defined(_WIN32) */
/* Check stdout */
pos = tor_read_all_from_process_stdout(process_handle, stdout_buf,
@@ -178,7 +178,7 @@ test_util_spawn_background_fail(void *ptr)
/* TODO: Once we can signal failure to exec, set this to be
* PROCESS_STATUS_RUNNING_OR_ERROR */
const int expected_status = PROCESS_STATUS_RUNNING_OR_NOTRUNNING;
-#endif
+#endif /* defined(_WIN32) */
memset(expected_out, 0xf0, sizeof(expected_out));
memset(code, 0xf0, sizeof(code));
@@ -244,7 +244,7 @@ test_util_spawn_background_partial_read_impl(int exit_early)
tt_assert(!eof);
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
sizeof(stdout_buf) - 1, NULL, &eof);
-#endif
+#endif /* defined(_WIN32) */
log_info(LD_GENERAL, "tor_read_all_handle() returned %d", (int)pos);
/* We would have blocked, keep on trying */
@@ -270,7 +270,7 @@ test_util_spawn_background_partial_read_impl(int exit_early)
sizeof(stdout_buf) - 1,
process_handle);
tt_int_op(0,OP_EQ, pos);
-#else
+#else /* !(defined(_WIN32)) */
if (!eof) {
/* We should have got all the data, but maybe not the EOF flag */
pos = tor_read_all_handle(process_handle->stdout_pipe, stdout_buf,
@@ -280,7 +280,7 @@ test_util_spawn_background_partial_read_impl(int exit_early)
tt_assert(eof);
}
/* Otherwise, we got the EOF on the last read */
-#endif
+#endif /* defined(_WIN32) */
/* Check it terminated correctly */
retval = tor_get_exit_code(process_handle, 1, &exit_code);
@@ -351,7 +351,7 @@ test_util_spawn_background_waitpid_notify(void *arg)
}
tt_int_op(ms_timer, OP_GT, 0);
tt_ptr_op(process_handle->waitpid_cb, OP_EQ, NULL);
-#endif
+#endif /* !defined(_WIN32) */
ms_timer = 30*1000;
while (((retval = tor_get_exit_code(process_handle, 0, &exit_code))
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index 6fa46f90d4..2b03173717 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -61,9 +61,9 @@ mark_handled(int serial)
tor_assert(! bitarray_is_set(handled, serial));
bitarray_set(handled, serial);
tor_mutex_release(&bitmap_mutex);
-#else
+#else /* !(defined(TRACK_RESPONSES)) */
(void)serial;
-#endif
+#endif /* defined(TRACK_RESPONSES) */
}
static workqueue_reply_t
@@ -288,7 +288,7 @@ replysock_readable_cb(tor_socket_t sock, short what, void *arg)
}
puts("");
tor_mutex_release(&bitmap_mutex);
-#endif
+#endif /* defined(TRACK_RESPONSES) */
if (n_sent - (n_received+n_successful_cancel) < opt_n_lowwater) {
int n_to_send = n_received + opt_n_inflight - n_sent;
@@ -422,7 +422,7 @@ main(int argc, char **argv)
received = bitarray_init_zero(opt_n_items);
tor_mutex_init(&bitmap_mutex);
handled_len = opt_n_items;
-#endif
+#endif /* defined(TRACK_RESPONSES) */
for (i = 0; i < opt_n_inflight; ++i) {
if (! add_work(tp)) {
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index d7e36edbc0..7e9c47b48d 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -33,7 +33,7 @@ const char tor_git_revision[] = "";
#include <direct.h>
#else
#include <dirent.h>
-#endif
+#endif /* defined(_WIN32) */
#include "or.h"
@@ -84,7 +84,7 @@ setup_directory(void)
(int)getpid(), rnd32);
r = mkdir(temp_dir);
}
-#else
+#else /* !(defined(_WIN32)) */
tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s",
(int) getpid(), rnd32);
r = mkdir(temp_dir, 0700);
@@ -92,7 +92,7 @@ setup_directory(void)
/* undo sticky bit so tests don't get confused. */
r = chown(temp_dir, getuid(), getgid());
}
-#endif
+#endif /* defined(_WIN32) */
if (r) {
fprintf(stderr, "Can't create directory %s:", temp_dir);
perror("");
@@ -241,7 +241,7 @@ main(int c, const char **v)
int r = crypto_use_tor_alloc_functions();
tor_assert(r == 0);
}
-#endif
+#endif /* defined(USE_DMALLOC) */
update_approx_time(time(NULL));
options = options_new();
diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c
index 5dff233a69..7a24c0ed14 100644
--- a/src/test/testing_rsakeys.c
+++ b/src/test/testing_rsakeys.c
@@ -436,7 +436,7 @@ static int next_key_idx_1024;
#define N_PREGEN_KEYS_2048 ARRAY_LENGTH(PREGEN_KEYS_2048)
static crypto_pk_t *pregen_keys_2048[N_PREGEN_KEYS_2048];
static int next_key_idx_2048;
-#endif
+#endif /* defined(USE_PREGENERATED_RSA_KEYS) */
/** Generate and return a new keypair for use in unit tests. If we're using
* the key cache optimization, we might reuse keys. "idx" is ignored.
@@ -466,14 +466,14 @@ pk_generate_internal(int bits)
*idxp += crypto_rand_int_range(1,3);
*idxp %= n_pregen;
return crypto_pk_dup_key(pregen_array[*idxp]);
-#else
+#else /* !(defined(USE_PREGENERATED_RSA_KEYS)) */
crypto_pk_t *result;
int res;
result = crypto_pk_new();
res = crypto_pk_generate_key_with_bits__real(result, bits);
tor_assert(!res);
return result;
-#endif
+#endif /* defined(USE_PREGENERATED_RSA_KEYS) */
}
crypto_pk_t *
@@ -496,7 +496,7 @@ crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits)
}
return 0;
}
-#endif
+#endif /* defined(USE_PREGENERATED_RSA_KEYS) */
/** Free all storage used for the cached key optimization. */
void
@@ -516,7 +516,7 @@ free_pregenerated_keys(void)
pregen_keys_2048[idx] = NULL;
}
}
-#endif
+#endif /* defined(USE_PREGENERATED_RSA_KEYS) */
}
void
@@ -541,6 +541,6 @@ init_pregenerated_keys(void)
MOCK(crypto_pk_generate_key_with_bits,
crypto_pk_generate_key_with_bits__get_cached);
-#endif
+#endif /* defined(USE_PREGENERATED_RSA_KEYS) */
}
diff --git a/src/tools/include.am b/src/tools/include.am
index 717af9e2ae..37936c742f 100644
--- a/src/tools/include.am
+++ b/src/tools/include.am
@@ -8,8 +8,8 @@ src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c
src_tools_tor_resolve_LDFLAGS =
src_tools_tor_resolve_LDADD = src/common/libor.a \
src/common/libor-ctime.a \
- @TOR_LIB_MATH@ @TOR_LIB_WS32@ \
- $(rust_ldadd)
+ $(rust_ldadd) \
+ @TOR_LIB_MATH@ @TOR_LIB_WS32@ @TOR_LIB_USERENV@
if COVERAGE_ENABLED
src_tools_tor_cov_resolve_SOURCES = src/tools/tor-resolve.c
@@ -26,9 +26,9 @@ src_tools_tor_gencert_LDADD = src/common/libor.a src/common/libor-crypto.a \
src/common/libor-ctime.a \
$(LIBKECCAK_TINY) \
$(LIBDONNA) \
+ $(rust_ldadd) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
- @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
- $(rust_ldadd)
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@
if COVERAGE_ENABLED
src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index 395535697f..fb7465c0eb 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -430,7 +430,7 @@ key_to_string(EVP_PKEY *key)
static int
get_fingerprint(EVP_PKEY *pkey, char *out)
{
- int r = 1;
+ int r = -1;
crypto_pk_t *pk = crypto_new_pk_from_rsa_(EVP_PKEY_get1_RSA(pkey));
if (pk) {
r = crypto_pk_get_fingerprint(pk, out, 0);
@@ -443,7 +443,7 @@ get_fingerprint(EVP_PKEY *pkey, char *out)
static int
get_digest(EVP_PKEY *pkey, char *out)
{
- int r = 1;
+ int r = -1;
crypto_pk_t *pk = crypto_new_pk_from_rsa_(EVP_PKEY_get1_RSA(pkey));
if (pk) {
r = crypto_pk_get_digest(pk, out);
@@ -464,16 +464,20 @@ generate_certificate(void)
char expires[ISO_TIME_LEN+1];
char id_digest[DIGEST_LEN];
char fingerprint[FINGERPRINT_LEN+1];
- char *ident = key_to_string(identity_key);
- char *signing = key_to_string(signing_key);
FILE *f;
size_t signed_len;
char digest[DIGEST_LEN];
char signature[1024]; /* handles up to 8192-bit keys. */
int r;
- get_fingerprint(identity_key, fingerprint);
- get_digest(identity_key, id_digest);
+ if (get_fingerprint(identity_key, fingerprint) < 0) {
+ return -1;
+ }
+ if (get_digest(identity_key, id_digest)) {
+ return -1;
+ }
+ char *ident = key_to_string(identity_key);
+ char *signing = key_to_string(signing_key);
tor_localtime_r(&now, &tm);
tm.tm_mon += months_lifetime;
diff --git a/src/trunnel/channelpadding_negotiation.c b/src/trunnel/channelpadding_negotiation.c
index 172d6f8a03..59e6b38384 100644
--- a/src/trunnel/channelpadding_negotiation.c
+++ b/src/trunnel/channelpadding_negotiation.c
@@ -1,4 +1,4 @@
-/* channelpadding_negotiation.c -- generated by Trunnel v1.4.3.
+/* channelpadding_negotiation.c -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -13,7 +13,7 @@
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
-/* If we're runnning a static analysis tool, we don't want it to complain
+/* If we're running a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int channelpaddingnegotiation_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || channelpaddingnegotiation_deadcode_dummy__
@@ -57,7 +57,7 @@ channelpadding_negotiate_free(channelpadding_negotiate_t *obj)
}
uint8_t
-channelpadding_negotiate_get_version(channelpadding_negotiate_t *inp)
+channelpadding_negotiate_get_version(const channelpadding_negotiate_t *inp)
{
return inp->version;
}
@@ -72,7 +72,7 @@ channelpadding_negotiate_set_version(channelpadding_negotiate_t *inp, uint8_t va
return 0;
}
uint8_t
-channelpadding_negotiate_get_command(channelpadding_negotiate_t *inp)
+channelpadding_negotiate_get_command(const channelpadding_negotiate_t *inp)
{
return inp->command;
}
@@ -87,7 +87,7 @@ channelpadding_negotiate_set_command(channelpadding_negotiate_t *inp, uint8_t va
return 0;
}
uint16_t
-channelpadding_negotiate_get_ito_low_ms(channelpadding_negotiate_t *inp)
+channelpadding_negotiate_get_ito_low_ms(const channelpadding_negotiate_t *inp)
{
return inp->ito_low_ms;
}
@@ -98,7 +98,7 @@ channelpadding_negotiate_set_ito_low_ms(channelpadding_negotiate_t *inp, uint16_
return 0;
}
uint16_t
-channelpadding_negotiate_get_ito_high_ms(channelpadding_negotiate_t *inp)
+channelpadding_negotiate_get_ito_high_ms(const channelpadding_negotiate_t *inp)
{
return inp->ito_high_ms;
}
diff --git a/src/trunnel/channelpadding_negotiation.h b/src/trunnel/channelpadding_negotiation.h
index e58bda3be1..fcfc232fea 100644
--- a/src/trunnel/channelpadding_negotiation.h
+++ b/src/trunnel/channelpadding_negotiation.h
@@ -1,4 +1,4 @@
-/* channelpadding_negotiation.h -- generated by by Trunnel v1.4.3.
+/* channelpadding_negotiation.h -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -60,7 +60,7 @@ int channelpadding_negotiate_clear_errors(channelpadding_negotiate_t *obj);
/** Return the value of the version field of the
* channelpadding_negotiate_t in 'inp'
*/
-uint8_t channelpadding_negotiate_get_version(channelpadding_negotiate_t *inp);
+uint8_t channelpadding_negotiate_get_version(const channelpadding_negotiate_t *inp);
/** Set the value of the version field of the
* channelpadding_negotiate_t in 'inp' to 'val'. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
@@ -69,7 +69,7 @@ int channelpadding_negotiate_set_version(channelpadding_negotiate_t *inp, uint8_
/** Return the value of the command field of the
* channelpadding_negotiate_t in 'inp'
*/
-uint8_t channelpadding_negotiate_get_command(channelpadding_negotiate_t *inp);
+uint8_t channelpadding_negotiate_get_command(const channelpadding_negotiate_t *inp);
/** Set the value of the command field of the
* channelpadding_negotiate_t in 'inp' to 'val'. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
@@ -78,7 +78,7 @@ int channelpadding_negotiate_set_command(channelpadding_negotiate_t *inp, uint8_
/** Return the value of the ito_low_ms field of the
* channelpadding_negotiate_t in 'inp'
*/
-uint16_t channelpadding_negotiate_get_ito_low_ms(channelpadding_negotiate_t *inp);
+uint16_t channelpadding_negotiate_get_ito_low_ms(const channelpadding_negotiate_t *inp);
/** Set the value of the ito_low_ms field of the
* channelpadding_negotiate_t in 'inp' to 'val'. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
@@ -87,7 +87,7 @@ int channelpadding_negotiate_set_ito_low_ms(channelpadding_negotiate_t *inp, uin
/** Return the value of the ito_high_ms field of the
* channelpadding_negotiate_t in 'inp'
*/
-uint16_t channelpadding_negotiate_get_ito_high_ms(channelpadding_negotiate_t *inp);
+uint16_t channelpadding_negotiate_get_ito_high_ms(const channelpadding_negotiate_t *inp);
/** Set the value of the ito_high_ms field of the
* channelpadding_negotiate_t in 'inp' to 'val'. Return 0 on success;
* return -1 and set the error code on 'inp' on failure.
diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c
index ee02fda646..1276c7a505 100644
--- a/src/trunnel/ed25519_cert.c
+++ b/src/trunnel/ed25519_cert.c
@@ -1,4 +1,4 @@
-/* ed25519_cert.c -- generated by Trunnel v1.5.1.
+/* ed25519_cert.c -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -13,7 +13,7 @@
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
-/* If we're runnning a static analysis tool, we don't want it to complain
+/* If we're running a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int edcert_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || edcert_deadcode_dummy__
diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h
index 782bd59585..e086c6fced 100644
--- a/src/trunnel/ed25519_cert.h
+++ b/src/trunnel/ed25519_cert.h
@@ -1,4 +1,4 @@
-/* ed25519_cert.h -- generated by by Trunnel v1.5.1.
+/* ed25519_cert.h -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/ed25519_cert.trunnel b/src/trunnel/ed25519_cert.trunnel
index e424ce5464..8d6483d558 100644
--- a/src/trunnel/ed25519_cert.trunnel
+++ b/src/trunnel/ed25519_cert.trunnel
@@ -28,6 +28,12 @@ const LS_IPV6 = 0x01;
const LS_LEGACY_ID = 0x02;
const LS_ED25519_ID = 0x03;
+// XXX hs_link_specifier_dup() violates the opaqueness of link_specifier_t by
+// taking its sizeof(). If we ever want to turn on TRUNNEL_OPAQUE, or
+// if we ever make link_specifier contain other types, we will
+// need to refactor that function to do the copy by encoding and decoding the
+// object.
+
// amended from tor.trunnel
struct link_specifier {
u8 ls_type;
diff --git a/src/trunnel/hs/cell_common.c b/src/trunnel/hs/cell_common.c
index b7f19ffc60..af223560c1 100644
--- a/src/trunnel/hs/cell_common.c
+++ b/src/trunnel/hs/cell_common.c
@@ -1,4 +1,4 @@
-/* cell_common.c -- generated by Trunnel v1.5.1.
+/* cell_common.c -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -13,7 +13,7 @@
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
-/* If we're runnning a static analysis tool, we don't want it to complain
+/* If we're running a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int cellcommon_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || cellcommon_deadcode_dummy__
diff --git a/src/trunnel/hs/cell_common.h b/src/trunnel/hs/cell_common.h
index 4d98a54cf4..e08eedfdb3 100644
--- a/src/trunnel/hs/cell_common.h
+++ b/src/trunnel/hs/cell_common.h
@@ -1,4 +1,4 @@
-/* cell_common.h -- generated by by Trunnel v1.5.1.
+/* cell_common.h -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/hs/cell_establish_intro.c b/src/trunnel/hs/cell_establish_intro.c
index 22e198c369..ae3b7b1bc8 100644
--- a/src/trunnel/hs/cell_establish_intro.c
+++ b/src/trunnel/hs/cell_establish_intro.c
@@ -1,4 +1,4 @@
-/* cell_establish_intro.c -- generated by Trunnel v1.5.1.
+/* cell_establish_intro.c -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -13,7 +13,7 @@
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
-/* If we're runnning a static analysis tool, we don't want it to complain
+/* If we're running a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int cellestablishintro_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || cellestablishintro_deadcode_dummy__
diff --git a/src/trunnel/hs/cell_establish_intro.h b/src/trunnel/hs/cell_establish_intro.h
index 2f0d893659..ccaef5488c 100644
--- a/src/trunnel/hs/cell_establish_intro.h
+++ b/src/trunnel/hs/cell_establish_intro.h
@@ -1,4 +1,4 @@
-/* cell_establish_intro.h -- generated by by Trunnel v1.5.1.
+/* cell_establish_intro.h -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/hs/cell_introduce1.c b/src/trunnel/hs/cell_introduce1.c
index 7501f6f196..358b355cda 100644
--- a/src/trunnel/hs/cell_introduce1.c
+++ b/src/trunnel/hs/cell_introduce1.c
@@ -1,4 +1,4 @@
-/* cell_introduce1.c -- generated by Trunnel v1.5.1.
+/* cell_introduce1.c -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -13,7 +13,7 @@
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
-/* If we're runnning a static analysis tool, we don't want it to complain
+/* If we're running a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int cellintroduce_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || cellintroduce_deadcode_dummy__
diff --git a/src/trunnel/hs/cell_introduce1.h b/src/trunnel/hs/cell_introduce1.h
index cca825a431..fa218adc6d 100644
--- a/src/trunnel/hs/cell_introduce1.h
+++ b/src/trunnel/hs/cell_introduce1.h
@@ -1,4 +1,4 @@
-/* cell_introduce1.h -- generated by by Trunnel v1.5.1.
+/* cell_introduce1.h -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/hs/cell_rendezvous.c b/src/trunnel/hs/cell_rendezvous.c
new file mode 100644
index 0000000000..53cb609138
--- /dev/null
+++ b/src/trunnel/hs/cell_rendezvous.c
@@ -0,0 +1,470 @@
+/* cell_rendezvous.c -- generated by Trunnel v1.5.2.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#include <stdlib.h>
+#include "trunnel-impl.h"
+
+#include "cell_rendezvous.h"
+
+#define TRUNNEL_SET_ERROR_CODE(obj) \
+ do { \
+ (obj)->trunnel_error_code_ = 1; \
+ } while (0)
+
+#if defined(__COVERITY__) || defined(__clang_analyzer__)
+/* If we're running a static analysis tool, we don't want it to complain
+ * that some of our remaining-bytes checks are dead-code. */
+int cellrendezvous_deadcode_dummy__ = 0;
+#define OR_DEADCODE_DUMMY || cellrendezvous_deadcode_dummy__
+#else
+#define OR_DEADCODE_DUMMY
+#endif
+
+#define CHECK_REMAINING(nbytes, label) \
+ do { \
+ if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \
+ goto label; \
+ } \
+ } while (0)
+
+trn_cell_rendezvous1_t *
+trn_cell_rendezvous1_new(void)
+{
+ trn_cell_rendezvous1_t *val = trunnel_calloc(1, sizeof(trn_cell_rendezvous1_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+trn_cell_rendezvous1_clear(trn_cell_rendezvous1_t *obj)
+{
+ (void) obj;
+ TRUNNEL_DYNARRAY_WIPE(&obj->handshake_info);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->handshake_info);
+}
+
+void
+trn_cell_rendezvous1_free(trn_cell_rendezvous1_t *obj)
+{
+ if (obj == NULL)
+ return;
+ trn_cell_rendezvous1_clear(obj);
+ trunnel_memwipe(obj, sizeof(trn_cell_rendezvous1_t));
+ trunnel_free_(obj);
+}
+
+size_t
+trn_cell_rendezvous1_getlen_rendezvous_cookie(const trn_cell_rendezvous1_t *inp)
+{
+ (void)inp; return TRUNNEL_REND_COOKIE_LEN;
+}
+
+uint8_t
+trn_cell_rendezvous1_get_rendezvous_cookie(trn_cell_rendezvous1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < TRUNNEL_REND_COOKIE_LEN);
+ return inp->rendezvous_cookie[idx];
+}
+
+uint8_t
+trn_cell_rendezvous1_getconst_rendezvous_cookie(const trn_cell_rendezvous1_t *inp, size_t idx)
+{
+ return trn_cell_rendezvous1_get_rendezvous_cookie((trn_cell_rendezvous1_t*)inp, idx);
+}
+int
+trn_cell_rendezvous1_set_rendezvous_cookie(trn_cell_rendezvous1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < TRUNNEL_REND_COOKIE_LEN);
+ inp->rendezvous_cookie[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+trn_cell_rendezvous1_getarray_rendezvous_cookie(trn_cell_rendezvous1_t *inp)
+{
+ return inp->rendezvous_cookie;
+}
+const uint8_t *
+trn_cell_rendezvous1_getconstarray_rendezvous_cookie(const trn_cell_rendezvous1_t *inp)
+{
+ return (const uint8_t *)trn_cell_rendezvous1_getarray_rendezvous_cookie((trn_cell_rendezvous1_t*)inp);
+}
+size_t
+trn_cell_rendezvous1_getlen_handshake_info(const trn_cell_rendezvous1_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->handshake_info);
+}
+
+uint8_t
+trn_cell_rendezvous1_get_handshake_info(trn_cell_rendezvous1_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->handshake_info, idx);
+}
+
+uint8_t
+trn_cell_rendezvous1_getconst_handshake_info(const trn_cell_rendezvous1_t *inp, size_t idx)
+{
+ return trn_cell_rendezvous1_get_handshake_info((trn_cell_rendezvous1_t*)inp, idx);
+}
+int
+trn_cell_rendezvous1_set_handshake_info(trn_cell_rendezvous1_t *inp, size_t idx, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->handshake_info, idx, elt);
+ return 0;
+}
+int
+trn_cell_rendezvous1_add_handshake_info(trn_cell_rendezvous1_t *inp, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->handshake_info, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+uint8_t *
+trn_cell_rendezvous1_getarray_handshake_info(trn_cell_rendezvous1_t *inp)
+{
+ return inp->handshake_info.elts_;
+}
+const uint8_t *
+trn_cell_rendezvous1_getconstarray_handshake_info(const trn_cell_rendezvous1_t *inp)
+{
+ return (const uint8_t *)trn_cell_rendezvous1_getarray_handshake_info((trn_cell_rendezvous1_t*)inp);
+}
+int
+trn_cell_rendezvous1_setlen_handshake_info(trn_cell_rendezvous1_t *inp, size_t newlen)
+{
+ uint8_t *newptr;
+ newptr = trunnel_dynarray_setlen(&inp->handshake_info.allocated_,
+ &inp->handshake_info.n_, inp->handshake_info.elts_, newlen,
+ sizeof(inp->handshake_info.elts_[0]), (trunnel_free_fn_t) NULL,
+ &inp->trunnel_error_code_);
+ if (newlen != 0 && newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->handshake_info.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+const char *
+trn_cell_rendezvous1_check(const trn_cell_rendezvous1_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ return NULL;
+}
+
+ssize_t
+trn_cell_rendezvous1_encoded_len(const trn_cell_rendezvous1_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != trn_cell_rendezvous1_check(obj))
+ return -1;
+
+
+ /* Length of u8 rendezvous_cookie[TRUNNEL_REND_COOKIE_LEN] */
+ result += TRUNNEL_REND_COOKIE_LEN;
+
+ /* Length of u8 handshake_info[] */
+ result += TRUNNEL_DYNARRAY_LEN(&obj->handshake_info);
+ return result;
+}
+int
+trn_cell_rendezvous1_clear_errors(trn_cell_rendezvous1_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+trn_cell_rendezvous1_encode(uint8_t *output, const size_t avail, const trn_cell_rendezvous1_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = trn_cell_rendezvous1_encoded_len(obj);
+#endif
+
+ if (NULL != (msg = trn_cell_rendezvous1_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 rendezvous_cookie[TRUNNEL_REND_COOKIE_LEN] */
+ trunnel_assert(written <= avail);
+ if (avail - written < TRUNNEL_REND_COOKIE_LEN)
+ goto truncated;
+ memcpy(ptr, obj->rendezvous_cookie, TRUNNEL_REND_COOKIE_LEN);
+ written += TRUNNEL_REND_COOKIE_LEN; ptr += TRUNNEL_REND_COOKIE_LEN;
+
+ /* Encode u8 handshake_info[] */
+ {
+ size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->handshake_info);
+ trunnel_assert(written <= avail);
+ if (avail - written < elt_len)
+ goto truncated;
+ if (elt_len)
+ memcpy(ptr, obj->handshake_info.elts_, elt_len);
+ written += elt_len; ptr += elt_len;
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As trn_cell_rendezvous1_parse(), but do not allocate the output
+ * object.
+ */
+static ssize_t
+trn_cell_rendezvous1_parse_into(trn_cell_rendezvous1_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 rendezvous_cookie[TRUNNEL_REND_COOKIE_LEN] */
+ CHECK_REMAINING(TRUNNEL_REND_COOKIE_LEN, truncated);
+ memcpy(obj->rendezvous_cookie, ptr, TRUNNEL_REND_COOKIE_LEN);
+ remaining -= TRUNNEL_REND_COOKIE_LEN; ptr += TRUNNEL_REND_COOKIE_LEN;
+
+ /* Parse u8 handshake_info[] */
+ TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->handshake_info, remaining, {});
+ obj->handshake_info.n_ = remaining;
+ if (remaining)
+ memcpy(obj->handshake_info.elts_, ptr, remaining);
+ ptr += remaining; remaining -= remaining;
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ trunnel_alloc_failed:
+ return -1;
+}
+
+ssize_t
+trn_cell_rendezvous1_parse(trn_cell_rendezvous1_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = trn_cell_rendezvous1_new();
+ if (NULL == *output)
+ return -1;
+ result = trn_cell_rendezvous1_parse_into(*output, input, len_in);
+ if (result < 0) {
+ trn_cell_rendezvous1_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
+trn_cell_rendezvous2_t *
+trn_cell_rendezvous2_new(void)
+{
+ trn_cell_rendezvous2_t *val = trunnel_calloc(1, sizeof(trn_cell_rendezvous2_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+trn_cell_rendezvous2_clear(trn_cell_rendezvous2_t *obj)
+{
+ (void) obj;
+}
+
+void
+trn_cell_rendezvous2_free(trn_cell_rendezvous2_t *obj)
+{
+ if (obj == NULL)
+ return;
+ trn_cell_rendezvous2_clear(obj);
+ trunnel_memwipe(obj, sizeof(trn_cell_rendezvous2_t));
+ trunnel_free_(obj);
+}
+
+size_t
+trn_cell_rendezvous2_getlen_handshake_info(const trn_cell_rendezvous2_t *inp)
+{
+ (void)inp; return TRUNNEL_HANDSHAKE_INFO_LEN;
+}
+
+uint8_t
+trn_cell_rendezvous2_get_handshake_info(trn_cell_rendezvous2_t *inp, size_t idx)
+{
+ trunnel_assert(idx < TRUNNEL_HANDSHAKE_INFO_LEN);
+ return inp->handshake_info[idx];
+}
+
+uint8_t
+trn_cell_rendezvous2_getconst_handshake_info(const trn_cell_rendezvous2_t *inp, size_t idx)
+{
+ return trn_cell_rendezvous2_get_handshake_info((trn_cell_rendezvous2_t*)inp, idx);
+}
+int
+trn_cell_rendezvous2_set_handshake_info(trn_cell_rendezvous2_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < TRUNNEL_HANDSHAKE_INFO_LEN);
+ inp->handshake_info[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+trn_cell_rendezvous2_getarray_handshake_info(trn_cell_rendezvous2_t *inp)
+{
+ return inp->handshake_info;
+}
+const uint8_t *
+trn_cell_rendezvous2_getconstarray_handshake_info(const trn_cell_rendezvous2_t *inp)
+{
+ return (const uint8_t *)trn_cell_rendezvous2_getarray_handshake_info((trn_cell_rendezvous2_t*)inp);
+}
+const char *
+trn_cell_rendezvous2_check(const trn_cell_rendezvous2_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ return NULL;
+}
+
+ssize_t
+trn_cell_rendezvous2_encoded_len(const trn_cell_rendezvous2_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != trn_cell_rendezvous2_check(obj))
+ return -1;
+
+
+ /* Length of u8 handshake_info[TRUNNEL_HANDSHAKE_INFO_LEN] */
+ result += TRUNNEL_HANDSHAKE_INFO_LEN;
+ return result;
+}
+int
+trn_cell_rendezvous2_clear_errors(trn_cell_rendezvous2_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+trn_cell_rendezvous2_encode(uint8_t *output, const size_t avail, const trn_cell_rendezvous2_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = trn_cell_rendezvous2_encoded_len(obj);
+#endif
+
+ if (NULL != (msg = trn_cell_rendezvous2_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 handshake_info[TRUNNEL_HANDSHAKE_INFO_LEN] */
+ trunnel_assert(written <= avail);
+ if (avail - written < TRUNNEL_HANDSHAKE_INFO_LEN)
+ goto truncated;
+ memcpy(ptr, obj->handshake_info, TRUNNEL_HANDSHAKE_INFO_LEN);
+ written += TRUNNEL_HANDSHAKE_INFO_LEN; ptr += TRUNNEL_HANDSHAKE_INFO_LEN;
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As trn_cell_rendezvous2_parse(), but do not allocate the output
+ * object.
+ */
+static ssize_t
+trn_cell_rendezvous2_parse_into(trn_cell_rendezvous2_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 handshake_info[TRUNNEL_HANDSHAKE_INFO_LEN] */
+ CHECK_REMAINING(TRUNNEL_HANDSHAKE_INFO_LEN, truncated);
+ memcpy(obj->handshake_info, ptr, TRUNNEL_HANDSHAKE_INFO_LEN);
+ remaining -= TRUNNEL_HANDSHAKE_INFO_LEN; ptr += TRUNNEL_HANDSHAKE_INFO_LEN;
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+}
+
+ssize_t
+trn_cell_rendezvous2_parse(trn_cell_rendezvous2_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = trn_cell_rendezvous2_new();
+ if (NULL == *output)
+ return -1;
+ result = trn_cell_rendezvous2_parse_into(*output, input, len_in);
+ if (result < 0) {
+ trn_cell_rendezvous2_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
diff --git a/src/trunnel/hs/cell_rendezvous.h b/src/trunnel/hs/cell_rendezvous.h
new file mode 100644
index 0000000000..39e14da25b
--- /dev/null
+++ b/src/trunnel/hs/cell_rendezvous.h
@@ -0,0 +1,187 @@
+/* cell_rendezvous.h -- generated by Trunnel v1.5.2.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#ifndef TRUNNEL_CELL_RENDEZVOUS_H
+#define TRUNNEL_CELL_RENDEZVOUS_H
+
+#include <stdint.h>
+#include "trunnel.h"
+
+#define TRUNNEL_REND_COOKIE_LEN 20
+#define TRUNNEL_HANDSHAKE_INFO_LEN 64
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_RENDEZVOUS1)
+struct trn_cell_rendezvous1_st {
+ uint8_t rendezvous_cookie[TRUNNEL_REND_COOKIE_LEN];
+ TRUNNEL_DYNARRAY_HEAD(, uint8_t) handshake_info;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct trn_cell_rendezvous1_st trn_cell_rendezvous1_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_RENDEZVOUS2)
+struct trn_cell_rendezvous2_st {
+ uint8_t handshake_info[TRUNNEL_HANDSHAKE_INFO_LEN];
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct trn_cell_rendezvous2_st trn_cell_rendezvous2_t;
+/** Return a newly allocated trn_cell_rendezvous1 with all elements
+ * set to zero.
+ */
+trn_cell_rendezvous1_t *trn_cell_rendezvous1_new(void);
+/** Release all storage held by the trn_cell_rendezvous1 in 'victim'.
+ * (Do nothing if 'victim' is NULL.)
+ */
+void trn_cell_rendezvous1_free(trn_cell_rendezvous1_t *victim);
+/** Try to parse a trn_cell_rendezvous1 from the buffer in 'input',
+ * using up to 'len_in' bytes from the input buffer. On success,
+ * return the number of bytes consumed and set *output to the newly
+ * allocated trn_cell_rendezvous1_t. On failure, return -2 if the
+ * input appears truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t trn_cell_rendezvous1_parse(trn_cell_rendezvous1_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * trn_cell_rendezvous1 in 'obj'. On failure, return a negative value.
+ * Note that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t trn_cell_rendezvous1_encoded_len(const trn_cell_rendezvous1_t *obj);
+/** Try to encode the trn_cell_rendezvous1 from 'input' into the
+ * buffer at 'output', using up to 'avail' bytes of the output buffer.
+ * On success, return the number of bytes used. On failure, return -2
+ * if the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t trn_cell_rendezvous1_encode(uint8_t *output, size_t avail, const trn_cell_rendezvous1_t *input);
+/** Check whether the internal state of the trn_cell_rendezvous1 in
+ * 'obj' is consistent. Return NULL if it is, and a short message if
+ * it is not.
+ */
+const char *trn_cell_rendezvous1_check(const trn_cell_rendezvous1_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int trn_cell_rendezvous1_clear_errors(trn_cell_rendezvous1_t *obj);
+/** Return the (constant) length of the array holding the
+ * rendezvous_cookie field of the trn_cell_rendezvous1_t in 'inp'.
+ */
+size_t trn_cell_rendezvous1_getlen_rendezvous_cookie(const trn_cell_rendezvous1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * rendezvous_cookie of the trn_cell_rendezvous1_t in 'inp'.
+ */
+uint8_t trn_cell_rendezvous1_get_rendezvous_cookie(trn_cell_rendezvous1_t *inp, size_t idx);
+/** As trn_cell_rendezvous1_get_rendezvous_cookie, but take and return
+ * a const pointer
+ */
+uint8_t trn_cell_rendezvous1_getconst_rendezvous_cookie(const trn_cell_rendezvous1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * rendezvous_cookie of the trn_cell_rendezvous1_t in 'inp', so that
+ * it will hold the value 'elt'.
+ */
+int trn_cell_rendezvous1_set_rendezvous_cookie(trn_cell_rendezvous1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the TRUNNEL_REND_COOKIE_LEN-element array
+ * field rendezvous_cookie of 'inp'.
+ */
+uint8_t * trn_cell_rendezvous1_getarray_rendezvous_cookie(trn_cell_rendezvous1_t *inp);
+/** As trn_cell_rendezvous1_get_rendezvous_cookie, but take and return
+ * a const pointer
+ */
+const uint8_t * trn_cell_rendezvous1_getconstarray_rendezvous_cookie(const trn_cell_rendezvous1_t *inp);
+/** Return the length of the dynamic array holding the handshake_info
+ * field of the trn_cell_rendezvous1_t in 'inp'.
+ */
+size_t trn_cell_rendezvous1_getlen_handshake_info(const trn_cell_rendezvous1_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * handshake_info of the trn_cell_rendezvous1_t in 'inp'.
+ */
+uint8_t trn_cell_rendezvous1_get_handshake_info(trn_cell_rendezvous1_t *inp, size_t idx);
+/** As trn_cell_rendezvous1_get_handshake_info, but take and return a
+ * const pointer
+ */
+uint8_t trn_cell_rendezvous1_getconst_handshake_info(const trn_cell_rendezvous1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * handshake_info of the trn_cell_rendezvous1_t in 'inp', so that it
+ * will hold the value 'elt'.
+ */
+int trn_cell_rendezvous1_set_handshake_info(trn_cell_rendezvous1_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field
+ * handshake_info of the trn_cell_rendezvous1_t in 'inp'.
+ */
+int trn_cell_rendezvous1_add_handshake_info(trn_cell_rendezvous1_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field handshake_info
+ * of 'inp'.
+ */
+uint8_t * trn_cell_rendezvous1_getarray_handshake_info(trn_cell_rendezvous1_t *inp);
+/** As trn_cell_rendezvous1_get_handshake_info, but take and return a
+ * const pointer
+ */
+const uint8_t * trn_cell_rendezvous1_getconstarray_handshake_info(const trn_cell_rendezvous1_t *inp);
+/** Change the length of the variable-length array field
+ * handshake_info of 'inp' to 'newlen'.Fill extra elements with 0.
+ * Return 0 on success; return -1 and set the error code on 'inp' on
+ * failure.
+ */
+int trn_cell_rendezvous1_setlen_handshake_info(trn_cell_rendezvous1_t *inp, size_t newlen);
+/** Return a newly allocated trn_cell_rendezvous2 with all elements
+ * set to zero.
+ */
+trn_cell_rendezvous2_t *trn_cell_rendezvous2_new(void);
+/** Release all storage held by the trn_cell_rendezvous2 in 'victim'.
+ * (Do nothing if 'victim' is NULL.)
+ */
+void trn_cell_rendezvous2_free(trn_cell_rendezvous2_t *victim);
+/** Try to parse a trn_cell_rendezvous2 from the buffer in 'input',
+ * using up to 'len_in' bytes from the input buffer. On success,
+ * return the number of bytes consumed and set *output to the newly
+ * allocated trn_cell_rendezvous2_t. On failure, return -2 if the
+ * input appears truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t trn_cell_rendezvous2_parse(trn_cell_rendezvous2_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * trn_cell_rendezvous2 in 'obj'. On failure, return a negative value.
+ * Note that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t trn_cell_rendezvous2_encoded_len(const trn_cell_rendezvous2_t *obj);
+/** Try to encode the trn_cell_rendezvous2 from 'input' into the
+ * buffer at 'output', using up to 'avail' bytes of the output buffer.
+ * On success, return the number of bytes used. On failure, return -2
+ * if the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t trn_cell_rendezvous2_encode(uint8_t *output, size_t avail, const trn_cell_rendezvous2_t *input);
+/** Check whether the internal state of the trn_cell_rendezvous2 in
+ * 'obj' is consistent. Return NULL if it is, and a short message if
+ * it is not.
+ */
+const char *trn_cell_rendezvous2_check(const trn_cell_rendezvous2_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int trn_cell_rendezvous2_clear_errors(trn_cell_rendezvous2_t *obj);
+/** Return the (constant) length of the array holding the
+ * handshake_info field of the trn_cell_rendezvous2_t in 'inp'.
+ */
+size_t trn_cell_rendezvous2_getlen_handshake_info(const trn_cell_rendezvous2_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * handshake_info of the trn_cell_rendezvous2_t in 'inp'.
+ */
+uint8_t trn_cell_rendezvous2_get_handshake_info(trn_cell_rendezvous2_t *inp, size_t idx);
+/** As trn_cell_rendezvous2_get_handshake_info, but take and return a
+ * const pointer
+ */
+uint8_t trn_cell_rendezvous2_getconst_handshake_info(const trn_cell_rendezvous2_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * handshake_info of the trn_cell_rendezvous2_t in 'inp', so that it
+ * will hold the value 'elt'.
+ */
+int trn_cell_rendezvous2_set_handshake_info(trn_cell_rendezvous2_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the TRUNNEL_HANDSHAKE_INFO_LEN-element array
+ * field handshake_info of 'inp'.
+ */
+uint8_t * trn_cell_rendezvous2_getarray_handshake_info(trn_cell_rendezvous2_t *inp);
+/** As trn_cell_rendezvous2_get_handshake_info, but take and return a
+ * const pointer
+ */
+const uint8_t * trn_cell_rendezvous2_getconstarray_handshake_info(const trn_cell_rendezvous2_t *inp);
+
+
+#endif
diff --git a/src/trunnel/hs/cell_rendezvous.trunnel b/src/trunnel/hs/cell_rendezvous.trunnel
new file mode 100644
index 0000000000..2128b4d126
--- /dev/null
+++ b/src/trunnel/hs/cell_rendezvous.trunnel
@@ -0,0 +1,29 @@
+/*
+ * This contains the definition of the RENDEZVOUS1/2 cell for onion service
+ * version 3 and onward. The following format is specified in proposal 224
+ * section 4.2.
+ */
+
+/* Rendezvous cookie length. */
+const TRUNNEL_REND_COOKIE_LEN = 20;
+/* The HANDSHAKE_INFO field layout is as follow:
+ * SERVER_PK [PK_PUBKEY_LEN bytes]
+ * AUTH [MAC_LEN bytes]
+ * This means, the size is 32 bytes + 32 bytes. */
+const TRUNNEL_HANDSHAKE_INFO_LEN = 64;
+
+/* RENDEZVOUS1 payload. See details in section 4.2. */
+struct trn_cell_rendezvous1 {
+ /* The RENDEZVOUS_COOKIE field. */
+ u8 rendezvous_cookie[TRUNNEL_REND_COOKIE_LEN];
+
+ /* The HANDSHAKE_INFO field which has a variable length depending on the
+ * handshake type used. */
+ u8 handshake_info[];
+};
+
+/* RENDEZVOUS2 payload. See details in section 4.2. */
+struct trn_cell_rendezvous2 {
+ /* The HANDSHAKE_INFO field. */
+ u8 handshake_info[TRUNNEL_HANDSHAKE_INFO_LEN];
+};
diff --git a/src/trunnel/include.am b/src/trunnel/include.am
index de6cf4781f..ca79ff3a39 100644
--- a/src/trunnel/include.am
+++ b/src/trunnel/include.am
@@ -22,6 +22,7 @@ TRUNNELSOURCES = \
src/trunnel/hs/cell_common.c \
src/trunnel/hs/cell_establish_intro.c \
src/trunnel/hs/cell_introduce1.c \
+ src/trunnel/hs/cell_rendezvous.c \
src/trunnel/channelpadding_negotiation.c
TRUNNELHEADERS = \
@@ -34,6 +35,7 @@ TRUNNELHEADERS = \
src/trunnel/hs/cell_common.h \
src/trunnel/hs/cell_establish_intro.h \
src/trunnel/hs/cell_introduce1.h \
+ src/trunnel/hs/cell_rendezvous.h \
src/trunnel/channelpadding_negotiation.h
src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES)
diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c
index 887f710d9c..03ead31c62 100644
--- a/src/trunnel/link_handshake.c
+++ b/src/trunnel/link_handshake.c
@@ -1,4 +1,4 @@
-/* link_handshake.c -- generated by Trunnel v1.5.1.
+/* link_handshake.c -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -13,7 +13,7 @@
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
-/* If we're runnning a static analysis tool, we don't want it to complain
+/* If we're running a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int linkhandshake_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || linkhandshake_deadcode_dummy__
diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h
index 418662631a..6a23483adc 100644
--- a/src/trunnel/link_handshake.h
+++ b/src/trunnel/link_handshake.h
@@ -1,4 +1,4 @@
-/* link_handshake.h -- generated by by Trunnel v1.5.1.
+/* link_handshake.h -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/pwbox.c b/src/trunnel/pwbox.c
index f4b910bdab..c356515d36 100644
--- a/src/trunnel/pwbox.c
+++ b/src/trunnel/pwbox.c
@@ -1,4 +1,4 @@
-/* pwbox.c -- generated by Trunnel v1.5.1.
+/* pwbox.c -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -13,7 +13,7 @@
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
-/* If we're runnning a static analysis tool, we don't want it to complain
+/* If we're running a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int pwbox_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || pwbox_deadcode_dummy__
diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h
index 939b3c41a7..a9a421408a 100644
--- a/src/trunnel/pwbox.h
+++ b/src/trunnel/pwbox.h
@@ -1,4 +1,4 @@
-/* pwbox.h -- generated by by Trunnel v1.5.1.
+/* pwbox.h -- generated by Trunnel v1.5.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index 271b018c68..f5186fd242 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.3.1.10-dev"
+#define VERSION "0.3.2.10-dev"