aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.travis.yml2
-rw-r--r--ChangeLog1149
-rw-r--r--LICENSE104
-rw-r--r--Makefile.am31
-rw-r--r--ReleaseNotes806
-rw-r--r--acinclude.m420
-rw-r--r--changes/192712
-rw-r--r--changes/199745
-rw-r--r--changes/204604
-rw-r--r--changes/204924
-rw-r--r--changes/213598
-rw-r--r--changes/asciidoc-UTC4
-rw-r--r--changes/broken-028-fallbacks3
-rw-r--r--changes/buf-sentinel11
-rw-r--r--changes/bug155824
-rw-r--r--changes/bug171507
-rw-r--r--changes/bug17744_redux5
-rw-r--r--changes/bug181005
-rw-r--r--changes/bug181334
-rw-r--r--changes/bug182865
-rw-r--r--changes/bug183124
-rw-r--r--changes/bug183977
-rw-r--r--changes/bug184604
-rw-r--r--changes/bug184815
-rw-r--r--changes/bug1861614
-rw-r--r--changes/bug186683
-rw-r--r--changes/bug186734
-rw-r--r--changes/bug186865
-rw-r--r--changes/bug187106
-rw-r--r--changes/bug187164
-rw-r--r--changes/bug187284
-rw-r--r--changes/bug187293
-rw-r--r--changes/bug187613
-rw-r--r--changes/bug1880916
-rw-r--r--changes/bug188124
-rw-r--r--changes/bug188164
-rw-r--r--changes/bug18841.17
-rw-r--r--changes/bug188494
-rw-r--r--changes/bug189205
-rw-r--r--changes/bug189214
-rw-r--r--changes/bug189295
-rw-r--r--changes/bug189436
-rw-r--r--changes/bug189774
-rw-r--r--changes/bug190035
-rw-r--r--changes/bug190083
-rw-r--r--changes/bug190254
-rw-r--r--changes/bug190324
-rw-r--r--changes/bug191613
-rw-r--r--changes/bug191915
-rw-r--r--changes/bug192034
-rw-r--r--changes/bug192133
-rw-r--r--changes/bug194064
-rw-r--r--changes/bug194543
-rw-r--r--changes/bug194646
-rw-r--r--changes/bug194994
-rw-r--r--changes/bug195567
-rw-r--r--changes/bug195574
-rw-r--r--changes/bug196086
-rw-r--r--changes/bug196608
-rw-r--r--changes/bug196823
-rw-r--r--changes/bug197283
-rw-r--r--changes/bug197823
-rw-r--r--changes/bug198694
-rw-r--r--changes/bug199034
-rw-r--r--changes/bug19926_029_info3
-rw-r--r--changes/bug199474
-rw-r--r--changes/bug199604
-rw-r--r--changes/bug1996811
-rw-r--r--changes/bug199694
-rw-r--r--changes/bug199736
-rw-r--r--changes/bug200593
-rw-r--r--changes/bug200854
-rw-r--r--changes/bug201037
-rw-r--r--changes/bug202036
-rw-r--r--changes/bug202474
-rw-r--r--changes/bug20306_0294
-rw-r--r--changes/bug203077
-rw-r--r--changes/bug204014
-rw-r--r--changes/bug204236
-rw-r--r--changes/bug204725
-rw-r--r--changes/bug204845
-rw-r--r--changes/bug204874
-rw-r--r--changes/bug205095
-rw-r--r--changes/bug205294
-rw-r--r--changes/bug205337
-rw-r--r--changes/bug205348
-rw-r--r--changes/bug205366
-rw-r--r--changes/bug205512
-rw-r--r--changes/bug205604
-rw-r--r--changes/bug205875
-rw-r--r--changes/bug205882
-rw-r--r--changes/bug205913
-rw-r--r--changes/bug205936
-rw-r--r--changes/bug205975
-rw-r--r--changes/bug206136
-rw-r--r--changes/bug206343
-rw-r--r--changes/bug206385
-rw-r--r--changes/bug20710_0254
-rw-r--r--changes/bug207154
-rw-r--r--changes/bug207163
-rw-r--r--changes/bug208104
-rw-r--r--changes/bug208644
-rw-r--r--changes/bug208657
-rw-r--r--changes/bug208754
-rw-r--r--changes/bug209353
-rw-r--r--changes/bug210356
-rw-r--r--changes/bug210513
-rw-r--r--changes/bug21108_0296
-rw-r--r--changes/bug21278_extras3
-rw-r--r--changes/bug21278_prevention4
-rw-r--r--changes/bug212805
-rw-r--r--changes/bug213577
-rw-r--r--changes/bug213949
-rw-r--r--changes/bug214504
-rw-r--r--changes/bug215075
-rw-r--r--changes/bug215764
-rw-r--r--changes/bug219436
-rw-r--r--changes/bug220344
-rw-r--r--changes/bug222455
-rw-r--r--changes/bug223499
-rw-r--r--changes/bug223704
-rw-r--r--changes/bug224464
-rw-r--r--changes/bug22460_case28
-rw-r--r--changes/bug225165
-rw-r--r--changes/bug226445
-rw-r--r--changes/bug227974
-rw-r--r--changes/bug228015
-rw-r--r--changes/bug229153
-rw-r--r--changes/bug22916_0273
-rw-r--r--changes/bug23030_0297
-rw-r--r--changes/bug230818
-rw-r--r--changes/bug234706
-rw-r--r--changes/bug236936
-rw-r--r--changes/bug238743
-rw-r--r--changes/bug239859
-rw-r--r--changes/bug8185_0256
-rw-r--r--changes/doc176213
-rw-r--r--changes/fallbacks-2016049
-rw-r--r--changes/feature184834
-rw-r--r--changes/geoip-april20164
-rw-r--r--changes/geoip-august20164
-rw-r--r--changes/geoip-january20172
-rw-r--r--changes/geoip-july20164
-rw-r--r--changes/geoip-jun20164
-rw-r--r--changes/geoip-may20164
-rw-r--r--changes/geoip-november20162
-rw-r--r--changes/geoip-october20164
-rw-r--r--changes/geoip-september20164
-rw-r--r--changes/memarea_overflow7
-rw-r--r--changes/more_module_docs4
-rw-r--r--changes/prop275-minimal9
-rw-r--r--changes/rsa_init_bug7
-rw-r--r--changes/ticket19071-1948013
-rw-r--r--changes/ticket197697
-rw-r--r--changes/ticket219536
-rw-r--r--changes/ticket228953
-rw-r--r--changes/trove-2017-0018
-rw-r--r--changes/trove-2017-0106
-rw-r--r--configure.ac686
-rw-r--r--contrib/clang/sanitize_blacklist.txt11
-rw-r--r--contrib/win32build/tor-mingw.nsi.in2
-rw-r--r--doc/HACKING/CodingStandards.md12
-rw-r--r--doc/HACKING/HowToReview.md2
-rw-r--r--doc/HACKING/ReleasingTor.md78
-rw-r--r--doc/HACKING/WritingTests.md68
-rw-r--r--doc/tor-gencert.1.txt2
-rw-r--r--doc/tor.1.txt194
-rw-r--r--m4/pc_from_ucontext.m49
-rwxr-xr-xscripts/maint/checkSpace.pl19
-rwxr-xr-xscripts/maint/format_changelog.py12
-rwxr-xr-xscripts/maint/lintChanges.py5
-rwxr-xr-xscripts/maint/redox.py2
-rwxr-xr-xscripts/test/cov-diff4
-rwxr-xr-xscripts/test/cov-exclude28
-rw-r--r--src/common/address.c45
-rw-r--r--src/common/address.h20
-rw-r--r--src/common/aes.c57
-rw-r--r--src/common/aes.h3
-rw-r--r--src/common/backtrace.c11
-rw-r--r--src/common/compat.c209
-rw-r--r--src/common/compat.h61
-rw-r--r--src/common/compat_libevent.c371
-rw-r--r--src/common/compat_libevent.h71
-rw-r--r--src/common/compat_openssl.h5
-rw-r--r--src/common/compat_pthreads.c56
-rw-r--r--src/common/compat_threads.c15
-rw-r--r--src/common/compat_time.c657
-rw-r--r--src/common/compat_time.h162
-rw-r--r--src/common/container.c2
-rw-r--r--src/common/container.h2
-rw-r--r--src/common/crypto.c325
-rw-r--r--src/common/crypto.h19
-rw-r--r--src/common/crypto_curve25519.c60
-rw-r--r--src/common/crypto_curve25519.h12
-rw-r--r--src/common/crypto_ed25519.c143
-rw-r--r--src/common/crypto_ed25519.h11
-rw-r--r--src/common/crypto_format.c7
-rw-r--r--src/common/crypto_pwbox.c28
-rw-r--r--src/common/crypto_s2k.c8
-rw-r--r--src/common/di_ops.c46
-rw-r--r--src/common/di_ops.h3
-rw-r--r--src/common/handles.h153
-rw-r--r--src/common/include.am56
-rw-r--r--src/common/log.c27
-rw-r--r--src/common/memarea.c6
-rw-r--r--src/common/procmon.c27
-rw-r--r--src/common/pubsub.c129
-rw-r--r--src/common/pubsub.h179
-rw-r--r--src/common/sandbox.c33
-rw-r--r--src/common/sandbox.h6
-rw-r--r--src/common/testsupport.h10
-rw-r--r--src/common/timers.c293
-rw-r--r--src/common/timers.h24
-rw-r--r--src/common/torgzip.c75
-rw-r--r--src/common/torlog.h7
-rw-r--r--src/common/tortls.c145
-rw-r--r--src/common/tortls.h20
-rw-r--r--src/common/util.c468
-rw-r--r--src/common/util.h80
-rw-r--r--src/common/util_bug.c115
-rw-r--r--src/common/util_bug.h173
-rw-r--r--src/common/util_format.c69
-rw-r--r--src/common/util_format.h1
-rw-r--r--src/common/util_process.c4
-rw-r--r--src/common/workqueue.c31
-rw-r--r--src/common/workqueue.h2
-rw-r--r--src/config/mmdb-convert.py6
-rw-r--r--src/config/torrc.minimal.in-staging24
-rw-r--r--src/config/torrc.sample.in4
-rw-r--r--src/ext/README18
-rw-r--r--src/ext/csiphash.c14
-rw-r--r--src/ext/curve25519_donna/curve25519-donna.c3
-rw-r--r--src/ext/ed25519/donna/curve25519-donna-64bit.h4
-rw-r--r--src/ext/ed25519/donna/curve25519-donna-sse2.h7
-rw-r--r--src/ext/ed25519/donna/ed25519-donna-64bit-x86.h9
-rw-r--r--src/ext/ed25519/donna/ed25519-donna-batchverify.h2
-rw-r--r--src/ext/ed25519/donna/ed25519-donna.h10
-rw-r--r--src/ext/ed25519/donna/ed25519_tor.c11
-rw-r--r--src/ext/ed25519/donna/modm-donna-32bit.h48
-rw-r--r--src/ext/ed25519/donna/modm-donna-64bit.h24
-rw-r--r--src/ext/ed25519/ref10/blinding.c6
-rw-r--r--src/ext/ed25519/ref10/keypair.c3
-rw-r--r--src/ext/ed25519/ref10/open.c3
-rw-r--r--src/ext/eventdns.c3518
-rw-r--r--src/ext/eventdns.h337
-rw-r--r--src/ext/ht.h92
-rw-r--r--src/ext/include.am34
-rw-r--r--src/ext/mulodi/LICENSE.TXT91
-rw-r--r--src/ext/mulodi/mulodi4.c67
-rw-r--r--src/ext/timeouts/Makefile68
-rw-r--r--src/ext/timeouts/Rules.shrc40
-rw-r--r--src/ext/timeouts/bench/Rules.mk49
-rwxr-xr-xsrc/ext/timeouts/bench/bench-add.lua30
-rw-r--r--src/ext/timeouts/bench/bench-aux.lua30
-rwxr-xr-xsrc/ext/timeouts/bench/bench-del.lua25
-rwxr-xr-xsrc/ext/timeouts/bench/bench-expire.lua29
-rw-r--r--src/ext/timeouts/bench/bench-heap.c236
-rw-r--r--src/ext/timeouts/bench/bench-llrb.c425
-rw-r--r--src/ext/timeouts/bench/bench-wheel.c81
-rw-r--r--src/ext/timeouts/bench/bench.c293
-rw-r--r--src/ext/timeouts/bench/bench.h11
-rw-r--r--src/ext/timeouts/bench/bench.plt19
-rw-r--r--src/ext/timeouts/lua/Rules.mk20
-rw-r--r--src/ext/timeouts/lua/timeout-lua.c396
-rw-r--r--src/ext/timeouts/test-timeout.c530
-rw-r--r--src/ext/timeouts/timeout-bitops.c254
-rw-r--r--src/ext/timeouts/timeout-debug.h77
-rw-r--r--src/ext/timeouts/timeout.c754
-rw-r--r--src/ext/timeouts/timeout.h256
-rw-r--r--src/ext/tinytest.c9
-rw-r--r--src/ext/trunnel/trunnel-impl.h8
-rw-r--r--src/ext/trunnel/trunnel.c4
-rw-r--r--src/ext/trunnel/trunnel.h2
-rw-r--r--src/or/addressmap.c30
-rw-r--r--src/or/buffers.c468
-rw-r--r--src/or/buffers.h43
-rw-r--r--src/or/channel.c46
-rw-r--r--src/or/channel.h10
-rw-r--r--src/or/channeltls.c43
-rw-r--r--src/or/channeltls.h8
-rw-r--r--src/or/circpathbias.c5
-rw-r--r--src/or/circuitbuild.c355
-rw-r--r--src/or/circuitbuild.h5
-rw-r--r--src/or/circuitlist.c14
-rw-r--r--src/or/circuitmux.c91
-rw-r--r--src/or/circuitmux_ewma.c29
-rw-r--r--src/or/circuitmux_ewma.h5
-rw-r--r--src/or/circuitstats.c53
-rw-r--r--src/or/circuituse.c42
-rw-r--r--src/or/command.c20
-rw-r--r--src/or/config.c794
-rw-r--r--src/or/config.h30
-rw-r--r--src/or/confparse.c59
-rw-r--r--src/or/confparse.h22
-rw-r--r--src/or/connection.c805
-rw-r--r--src/or/connection.h57
-rw-r--r--src/or/connection_edge.c252
-rw-r--r--src/or/connection_or.c217
-rw-r--r--src/or/connection_or.h2
-rw-r--r--src/or/control.c767
-rw-r--r--src/or/control.h27
-rw-r--r--src/or/cpuworker.c11
-rw-r--r--src/or/dircollate.c40
-rw-r--r--src/or/directory.c712
-rw-r--r--src/or/directory.h25
-rw-r--r--src/or/dirserv.c279
-rw-r--r--src/or/dirserv.h6
-rw-r--r--src/or/dirvote.c499
-rw-r--r--src/or/dirvote.h59
-rw-r--r--src/or/dns.c182
-rw-r--r--src/or/dns.h14
-rw-r--r--src/or/dns_structs.h12
-rw-r--r--src/or/dnsserv.c18
-rw-r--r--src/or/entrynodes.c65
-rw-r--r--src/or/entrynodes.h4
-rw-r--r--src/or/eventdns_tor.h22
-rw-r--r--src/or/ext_orport.c25
-rw-r--r--src/or/fp_pair.c8
-rw-r--r--src/or/geoip.c47
-rw-r--r--src/or/hibernate.c81
-rw-r--r--src/or/include.am19
-rw-r--r--src/or/keypin.c22
-rw-r--r--src/or/main.c303
-rw-r--r--src/or/main.h12
-rw-r--r--src/or/microdesc.c5
-rw-r--r--src/or/networkstatus.c383
-rw-r--r--src/or/networkstatus.h20
-rw-r--r--src/or/nodelist.c44
-rw-r--r--src/or/ntmain.c15
-rw-r--r--src/or/onion.c85
-rw-r--r--src/or/onion_fast.c26
-rw-r--r--src/or/onion_ntor.c18
-rw-r--r--src/or/onion_tap.c24
-rw-r--r--src/or/or.h283
-rw-r--r--src/or/periodic.c8
-rw-r--r--src/or/policies.c292
-rw-r--r--src/or/policies.h15
-rw-r--r--src/or/protover.c742
-rw-r--r--src/or/protover.h74
-rw-r--r--src/or/reasons.c6
-rw-r--r--src/or/relay.c47
-rw-r--r--src/or/rendcache.c12
-rw-r--r--src/or/rendcache.h7
-rw-r--r--src/or/rendclient.c176
-rw-r--r--src/or/rendclient.h3
-rw-r--r--src/or/rendcommon.c177
-rw-r--r--src/or/rendcommon.h15
-rw-r--r--src/or/rendmid.c2
-rw-r--r--src/or/rendservice.c938
-rw-r--r--src/or/rendservice.h77
-rw-r--r--src/or/rephist.c148
-rw-r--r--src/or/rephist.h7
-rw-r--r--src/or/replaycache.c14
-rw-r--r--src/or/router.c196
-rw-r--r--src/or/router.h3
-rw-r--r--src/or/routerkeys.c26
-rw-r--r--src/or/routerlist.c559
-rw-r--r--src/or/routerlist.h34
-rw-r--r--src/or/routerparse.c1098
-rw-r--r--src/or/routerparse.h33
-rw-r--r--src/or/routerset.c14
-rw-r--r--src/or/scheduler.c12
-rw-r--r--src/or/scheduler.h7
-rw-r--r--src/or/shared_random.c1363
-rw-r--r--src/or/shared_random.h168
-rw-r--r--src/or/shared_random_state.c1359
-rw-r--r--src/or/shared_random_state.h147
-rw-r--r--src/or/statefile.c20
-rw-r--r--src/or/status.c8
-rw-r--r--src/or/tor_main.c8
-rw-r--r--src/or/transports.c18
-rw-r--r--src/test/bench.c30
-rw-r--r--src/test/example_extrainfo.inc34
-rw-r--r--src/test/include.am62
-rw-r--r--src/test/log_test_helpers.c158
-rw-r--r--src/test/log_test_helpers.h77
-rw-r--r--src/test/sr_commit_calc_ref.py51
-rw-r--r--src/test/sr_srv_calc_ref.py71
-rw-r--r--src/test/test-child.c2
-rw-r--r--src/test/test-memwipe.c24
-rwxr-xr-xsrc/test/test-network.sh152
-rw-r--r--src/test/test-timers.c144
-rw-r--r--src/test/test.c118
-rw-r--r--src/test/test.h82
-rw-r--r--src/test/test_addr.c16
-rw-r--r--src/test/test_address.c50
-rwxr-xr-xsrc/test/test_bt.sh7
-rw-r--r--src/test/test_bt_cl.c6
-rw-r--r--src/test/test_buffers.c141
-rw-r--r--src/test/test_cell_formats.c10
-rw-r--r--src/test/test_channel.c21
-rw-r--r--src/test/test_channeltls.c21
-rw-r--r--src/test/test_circuitlist.c22
-rw-r--r--src/test/test_compat_libevent.c119
-rw-r--r--src/test/test_config.c319
-rw-r--r--src/test/test_connection.c36
-rw-r--r--src/test/test_containers.c24
-rw-r--r--src/test/test_controller.c1180
-rw-r--r--src/test/test_crypto.c719
-rw-r--r--src/test/test_crypto_slow.c87
-rw-r--r--src/test/test_data.c2
-rw-r--r--src/test/test_dir.c1369
-rw-r--r--src/test/test_dir_common.c8
-rw-r--r--src/test/test_dir_handle_get.c208
-rw-r--r--src/test/test_dns.c36
-rw-r--r--src/test/test_guardfraction.c9
-rw-r--r--src/test/test_handles.c95
-rw-r--r--src/test/test_helpers.c8
-rw-r--r--src/test/test_hs.c353
-rw-r--r--src/test/test_introduce.c2
-rw-r--r--src/test/test_link_handshake.c132
-rw-r--r--src/test/test_logging.c38
-rw-r--r--src/test/test_microdesc.c36
-rw-r--r--src/test/test_ntor_cl.c7
-rw-r--r--src/test/test_oom.c98
-rw-r--r--src/test/test_oos.c456
-rw-r--r--src/test/test_options.c313
-rw-r--r--src/test/test_policy.c611
-rw-r--r--src/test/test_protover.c195
-rw-r--r--src/test/test_pt.c12
-rw-r--r--src/test/test_pubsub.c85
-rw-r--r--src/test/test_relay.c12
-rw-r--r--src/test/test_relaycell.c2
-rw-r--r--src/test/test_rendcache.c24
-rw-r--r--src/test/test_routerlist.c26
-rw-r--r--src/test/test_routerset.c26
-rw-r--r--src/test/test_scheduler.c26
-rw-r--r--src/test/test_shared_random.c1290
-rw-r--r--src/test/test_slow.c3
-rw-r--r--src/test/test_socks.c2
-rw-r--r--src/test/test_status.c4
-rwxr-xr-xsrc/test/test_switch_id.sh7
-rw-r--r--src/test/test_threads.c20
-rw-r--r--src/test/test_tortls.c135
-rw-r--r--src/test/test_util.c1277
-rw-r--r--src/test/test_util_format.c99
-rw-r--r--src/test/test_util_process.c11
-rw-r--r--src/test/test_workqueue.c7
-rwxr-xr-xsrc/test/test_workqueue_cancel.sh4
-rwxr-xr-xsrc/test/test_workqueue_efd.sh4
-rwxr-xr-xsrc/test/test_workqueue_efd2.sh4
-rwxr-xr-xsrc/test/test_workqueue_pipe.sh4
-rwxr-xr-xsrc/test/test_workqueue_pipe2.sh4
-rwxr-xr-xsrc/test/test_workqueue_socketpair.sh4
-rw-r--r--src/test/testing_common.c121
-rw-r--r--src/test/vote_descriptors.inc2
-rw-r--r--src/tools/include.am25
-rw-r--r--src/tools/tor-gencert.c49
-rw-r--r--src/tools/tor-resolve.c4
-rw-r--r--src/trunnel/ed25519_cert.c6
-rw-r--r--src/trunnel/ed25519_cert.h2
-rw-r--r--src/trunnel/link_handshake.c12
-rw-r--r--src/trunnel/link_handshake.h2
-rw-r--r--src/trunnel/pwbox.c6
-rw-r--r--src/trunnel/pwbox.h2
-rw-r--r--src/win32/orconfig.h16
457 files changed, 32057 insertions, 11081 deletions
diff --git a/.gitignore b/.gitignore
index f304a32ecf..dc6738c079 100644
--- a/.gitignore
+++ b/.gitignore
@@ -56,6 +56,7 @@ uptime-*.json
/stamp-h
/stamp-h.in
/stamp-h1
+/TAGS
/test-driver
/tor.sh
/tor.spec
@@ -132,6 +133,9 @@ uptime-*.json
/src/common/libor.a
/src/common/libor-testing.a
/src/common/libor.lib
+/src/common/libor-ctime.a
+/src/common/libor-ctime-testing.a
+/src/common/libor-ctime.lib
/src/common/libor-crypto.a
/src/common/libor-crypto-testing.a
/src/common/libor-crypto.lib
@@ -180,6 +184,7 @@ uptime-*.json
/src/test/test-memwipe
/src/test/test-ntor-cl
/src/test/test-switch-id
+/src/test/test-timers
/src/test/test_workqueue
/src/test/test.exe
/src/test/test-slow.exe
@@ -188,6 +193,7 @@ uptime-*.json
/src/test/test-ntor-cl.exe
/src/test/test-memwipe.exe
/src/test/test-switch-id.exe
+/src/test/test-timers.exe
/src/test/test_workqueue.exe
# /src/tools/
diff --git a/.travis.yml b/.travis.yml
index 59b32dbe42..092bf54f82 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -79,7 +79,7 @@ install:
script:
- ./autogen.sh
- - ./configure $RUST_OPTIONS --disable-asciidoc --enable-gcc-warnings --disable-silent-rules --enable-fragile-hardening
+ - ./configure $RUST_OPTIONS --disable-asciidoc --enable-fatal-warnings --disable-silent-rules --enable-fragile-hardening
## We run `make check` because that's what https://jenkins.torproject.org does.
- make check
diff --git a/ChangeLog b/ChangeLog
index c93348ac01..7a10a7ce96 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,1152 @@
+Changes in version 0.2.9.5-rc - 2016-1?-??
+
+
+Changes in version 0.2.8.9 - 2016-10-17
+ Tor 0.2.8.9 backports a fix for a security hole in previous versions
+ of Tor that would allow a remote attacker to crash a Tor client,
+ hidden service, relay, or authority. All Tor users should upgrade to
+ this version, or to 0.2.9.4-alpha. Patches will be released for older
+ versions of Tor.
+
+ o Major features (security fixes, also in 0.2.9.4-alpha):
+ - Prevent a class of security bugs caused by treating the contents
+ of a buffer chunk as if they were a NUL-terminated string. At
+ least one such bug seems to be present in all currently used
+ versions of Tor, and would allow an attacker to remotely crash
+ most Tor instances, especially those compiled with extra compiler
+ hardening. With this defense in place, such bugs can't crash Tor,
+ though we should still fix them as they occur. Closes ticket
+ 20384 (TROVE-2016-10-001).
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the October 4 2016 Maxmind GeoLite2
+ Country database.
+
+
+Changes in version 0.2.9.4-alpha - 2016-10-17
+ Tor 0.2.9.4-alpha fixes a security hole in previous versions of Tor
+ that would allow a remote attacker to crash a Tor client, hidden
+ service, relay, or authority. All Tor users should upgrade to this
+ version, or to 0.2.8.9. Patches will be released for older versions
+ of Tor.
+
+ Tor 0.2.9.4-alpha also adds numerous small features and fix-ups to
+ previous versions of Tor, including the implementation of a feature to
+ future- proof the Tor ecosystem against protocol changes, some bug
+ fixes necessary for Tor Browser to use unix domain sockets correctly,
+ and several portability improvements. We anticipate that this will be
+ the last alpha in the Tor 0.2.9 series, and that the next release will
+ be a release candidate.
+
+ o Major features (security fixes):
+ - Prevent a class of security bugs caused by treating the contents
+ of a buffer chunk as if they were a NUL-terminated string. At
+ least one such bug seems to be present in all currently used
+ versions of Tor, and would allow an attacker to remotely crash
+ most Tor instances, especially those compiled with extra compiler
+ hardening. With this defense in place, such bugs can't crash Tor,
+ though we should still fix them as they occur. Closes ticket
+ 20384 (TROVE-2016-10-001).
+
+ o Major features (subprotocol versions):
+ - Tor directory authorities now vote on a set of recommended
+ subprotocol versions, and on a set of required subprotocol
+ versions. Clients and relays that lack support for a _required_
+ subprotocol version will not start; those that lack support for a
+ _recommended_ subprotocol version will warn the user to upgrade.
+ Closes ticket 19958; implements part of proposal 264.
+ - Tor now uses "subprotocol versions" to indicate compatibility.
+ Previously, versions of Tor looked at the declared Tor version of
+ a relay to tell whether they could use a given feature. Now, they
+ should be able to rely on its declared subprotocol versions. This
+ change allows compatible implementations of the Tor protocol(s) to
+ exist without pretending to be 100% bug-compatible with particular
+ releases of Tor itself. Closes ticket 19958; implements part of
+ proposal 264.
+
+ o Minor feature (fallback directories):
+ - Remove broken fallbacks from the hard-coded fallback directory
+ list. Closes ticket 20190; patch by teor.
+
+ o Minor features (client, directory):
+ - Since authorities now omit all routers that lack the Running and
+ Valid flags, we assume that any relay listed in the consensus must
+ have those flags. Closes ticket 20001; implements part of
+ proposal 272.
+
+ o Minor features (compilation, portability):
+ - Compile correctly on MacOS 10.12 (aka "Sierra"). Closes
+ ticket 20241.
+
+ o Minor features (development tools, etags):
+ - Teach the "make tags" Makefile target how to correctly find
+ "MOCK_IMPL" function definitions. Patch from nherring; closes
+ ticket 16869.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the October 4 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (unix domain sockets):
+ - When configuring a unix domain socket for a SocksPort,
+ ControlPort, or Hidden service, you can now wrap the address in
+ quotes, using C-style escapes inside the quotes. This allows unix
+ domain socket paths to contain spaces.
+
+ o Minor features (virtual addresses):
+ - Increase the maximum number of bits for the IPv6 virtual network
+ prefix from 16 to 104. In this way, the condition for address
+ allocation is less restrictive. Closes ticket 20151; feature
+ on 0.2.4.7-alpha.
+
+ o Minor bugfixes (address discovery):
+ - Stop reordering IP addresses returned by the OS. This makes it
+ more likely that Tor will guess the same relay IP address every
+ time. Fixes issue 20163; bugfix on 0.2.7.1-alpha, ticket 17027.
+ Reported by René Mayrhofer, patch by "cypherpunks".
+
+ o Minor bugfixes (client, unix domain sockets):
+ - Disable IsolateClientAddr when using AF_UNIX backed SocksPorts as
+ the client address is meaningless. Fixes bug 20261; bugfix
+ on 0.2.6.3-alpha.
+
+ o Minor bugfixes (compilation, OpenBSD):
+ - Detect Libevent2 functions correctly on systems that provide
+ libevent2, but where libevent1 is linked with -levent. Fixes bug
+ 19904; bugfix on 0.2.2.24-alpha. Patch from Rubiate.
+
+ o Minor bugfixes (configuration):
+ - When parsing quoted configuration values from the torrc file,
+ handle windows line endings correctly. Fixes bug 19167; bugfix on
+ 0.2.0.16-alpha. Patch from "Pingl".
+
+ o Minor bugfixes (getpass):
+ - Defensively fix a non-triggerable heap corruption at do_getpass()
+ to protect ourselves from mistakes in the future. Fixes bug
+ #19223; bugfix on 0.2.7.3-rc. Bug found by Guido Vranken, patch
+ by nherring.
+
+ o Minor bugfixes (hidden service):
+ - Allow hidden services to run on IPv6 addresses even when the
+ IPv6Exit option is not set. Fixes bug 18357; bugfix
+ on 0.2.4.7-alpha.
+
+ o Documentation:
+ - Add module-level internal documentation for 36 C files that
+ previously didn't have a high-level overview. Closes ticket #20385.
+
+ o Required libraries:
+ - When building with OpenSSL, Tor now requires version 1.0.1 or
+ later. OpenSSL 1.0.0 and earlier are no longer supported by the
+ OpenSSL team, and should not be used. Closes ticket 20303.
+
+
+Changes in version 0.2.9.3-alpha - 2016-09-23
+ Tor 0.2.9.3-alpha adds improved support for entities that want to make
+ high-performance services available through the Tor .onion mechanism
+ without themselves receiving anonymity as they host those services. It
+ also tries harder to ensure that all steps on a circuit are using the
+ strongest crypto possible, strengthens some TLS properties, and
+ resolves several bugs -- including a pair of crash bugs from the 0.2.8
+ series. Anybody running an earlier version of 0.2.9.x should upgrade.
+
+ o Major bugfixes (crash, also in 0.2.8.8):
+ - Fix a complicated crash bug that could affect Tor clients
+ configured to use bridges when replacing a networkstatus consensus
+ in which one of their bridges was mentioned. OpenBSD users saw
+ more crashes here, but all platforms were potentially affected.
+ Fixes bug 20103; bugfix on 0.2.8.2-alpha.
+
+ o Major bugfixes (relay, OOM handler, also in 0.2.8.8):
+ - Fix a timing-dependent assertion failure that could occur when we
+ tried to flush from a circuit after having freed its cells because
+ of an out-of-memory condition. Fixes bug 20203; bugfix on
+ 0.2.8.1-alpha. Thanks to "cypherpunks" for help diagnosing
+ this one.
+
+ o Major features (circuit building, security):
+ - Authorities, relays and clients now require ntor keys in all
+ descriptors, for all hops (except for rare hidden service protocol
+ cases), for all circuits, and for all other roles. Part of
+ ticket 19163.
+ - Tor authorities, relays, and clients only use ntor, except for
+ rare cases in the hidden service protocol. Part of ticket 19163.
+
+ o Major features (single-hop "hidden" services):
+ - Add experimental HiddenServiceSingleHopMode and
+ HiddenServiceNonAnonymousMode options. When both are set to 1,
+ every hidden service on a Tor instance becomes a non-anonymous
+ Single Onion Service. Single Onions make one-hop (direct)
+ connections to their introduction and renzedvous points. One-hop
+ circuits make Single Onion servers easily locatable, but clients
+ remain location-anonymous. This is compatible with the existing
+ hidden service implementation, and works on the current tor
+ network without any changes to older relays or clients. Implements
+ proposal 260, completes ticket 17178. Patch by teor and asn.
+
+ o Major features (resource management):
+ - Tor can now notice it is about to run out of sockets, and
+ preemptively close connections of lower priority. (This feature is
+ off by default for now, since the current prioritizing method is
+ yet not mature enough. You can enable it by setting
+ "DisableOOSCheck 0", but watch out: it might close some sockets
+ you would rather have it keep.) Closes ticket 18640.
+
+ o Major bugfixes (circuit building):
+ - Hidden service client-to-intro-point and service-to-rendezvous-
+ point cicruits use the TAP key supplied by the protocol, to avoid
+ epistemic attacks. Fixes bug 19163; bugfix on 0.2.4.18-rc.
+
+ o Major bugfixes (compilation, OpenBSD):
+ - Fix a Libevent-detection bug in our autoconf script that would
+ prevent Tor from linking successfully on OpenBSD. Patch from
+ rubiate. Fixes bug 19902; bugfix on 0.2.9.1-alpha.
+
+ o Major bugfixes (hidden services):
+ - Clients now require hidden services to include the TAP keys for
+ their intro points in the hidden service descriptor. This prevents
+ an inadvertent upgrade to ntor, which a malicious hidden service
+ could use to distinguish clients by consensus version. Fixes bug
+ 20012; bugfix on 0.2.4.8-alpha. Patch by teor.
+
+ o Minor features (security, TLS):
+ - Servers no longer support clients that without AES ciphersuites.
+ (3DES is no longer considered an acceptable cipher.) We believe
+ that no such Tor clients currently exist, since Tor has required
+ OpenSSL 0.9.7 or later since 2009. Closes ticket 19998.
+
+ o Minor feature (fallback directories):
+ - Remove broken entries from the hard-coded fallback directory list.
+ Closes ticket 20190; patch by teor.
+
+ o Minor features (geoip, also in 0.2.8.8):
+ - Update geoip and geoip6 to the September 6 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor feature (port flags):
+ - Add new flags to the *Port options to finer control over which
+ requests are allowed. The flags are NoDNSRequest, NoOnionTraffic,
+ and the synthetic flag OnionTrafficOnly, which is equivalent to
+ NoDNSRequest, NoIPv4Traffic, and NoIPv6Traffic. Closes enhancement
+ 18693; patch by "teor".
+
+ o Minor features (directory authority):
+ - After voting, if the authorities decide that a relay is not
+ "Valid", they no longer include it in the consensus at all. Closes
+ ticket 20002; implements part of proposal 272.
+
+ o Minor features (testing):
+ - Disable memory protections on OpenBSD when performing our unit
+ tests for memwipe(). The test deliberately invokes undefined
+ behavior, and the OpenBSD protections interfere with this. Patch
+ from "rubiate". Closes ticket 20066.
+
+ o Minor features (testing, ipv6):
+ - Add the single-onion and single-onion-ipv6 chutney targets to
+ "make test-network-all". This requires a recent chutney version
+ with the single onion network flavours (git c72a652 or later).
+ Closes ticket 20072; patch by teor.
+ - Add the hs-ipv6 chutney target to make test-network-all's IPv6
+ tests. Remove bridges+hs, as it's somewhat redundant. This
+ requires a recent chutney version that supports IPv6 clients,
+ relays, and authorities. Closes ticket 20069; patch by teor.
+
+ o Minor features (Tor2web):
+ - Make Tor2web clients respect ReachableAddresses. This feature was
+ inadvertently enabled in 0.2.8.6, then removed by bugfix 19973 on
+ 0.2.8.7. Implements feature 20034. Patch by teor.
+
+ o Minor features (unit tests):
+ - We've done significant work to make the unit tests run faster.
+ - Our link-handshake unit tests now check that when invalid
+ handshakes fail, they fail with the error messages we expected.
+ - Our unit testing code that captures log messages no longer
+ prevents them from being written out if the user asked for them
+ (by passing --debug or --info or or --notice --warn to the "test"
+ binary). This change prevents us from missing unexpected log
+ messages simply because we were looking for others. Related to
+ ticket 19999.
+ - The unit tests now log all warning messages with the "BUG" flag.
+ Previously, they only logged errors by default. This change will
+ help us make our testing code more correct, and make sure that we
+ only hit this code when we mean to. In the meantime, however,
+ there will be more warnings in the unit test logs than before.
+ This is preparatory work for ticket 19999.
+ - The unit tests now treat any failure of a "tor_assert_nonfatal()"
+ assertion as a test failure.
+
+ o Minor bug fixes (circuits):
+ - Use the CircuitBuildTimeout option whenever
+ LearnCircuitBuildTimeout is disabled. Previously, we would respect
+ the option when a user disabled it, but not when it was disabled
+ because some other option was set. Fixes bug 20073; bugfix on
+ 0.2.4.12-alpha. Patch by teor.
+
+ o Minor bugfixes (allocation):
+ - Change how we allocate memory for large chunks on buffers, to
+ avoid a (currently impossible) integer overflow, and to waste less
+ space when allocating unusually large chunks. Fixes bug 20081;
+ bugfix on 0.2.0.16-alpha. Issue identified by Guido Vranken.
+ - Always include orconfig.h before including any other C headers.
+ Sometimes, it includes macros that affect the behavior of the
+ standard headers. Fixes bug 19767; bugfix on 0.2.9.1-alpha (the
+ first version to use AC_USE_SYSTEM_EXTENSIONS).
+ - Fix a syntax error in the IF_BUG_ONCE__() macro in non-GCC-
+ compatible compilers. Fixes bug 20141; bugfix on 0.2.9.1-alpha.
+ Patch from Gisle Vanem.
+ - Stop trying to build with Clang 4.0's -Wthread-safety warnings.
+ They apparently require a set of annotations that we aren't
+ currently using, and they create false positives in our pthreads
+ wrappers. Fixes bug 20110; bugfix on 0.2.9.1-alpha.
+
+ o Minor bugfixes (directory authority):
+ - Die with a more useful error when the operator forgets to place
+ the authority_signing_key file into the keys directory. This
+ avoids an uninformative assert & traceback about having an invalid
+ key. Fixes bug 20065; bugfix on 0.2.0.1-alpha.
+ - When allowing private addresses, mark Exits that only exit to
+ private locations as such. Fixes bug 20064; bugfix
+ on 0.2.2.9-alpha.
+
+ o Minor bugfixes (documentation):
+ - Document the default PathsNeededToBuildCircuits value that's used
+ by clients when the directory authorities don't set
+ min_paths_for_circs_pct. Fixes bug 20117; bugfix on 02c320916e02
+ in tor-0.2.4.10-alpha. Patch by teor, reported by Jesse V.
+ - Fix manual for the User option: it takes a username, not a UID.
+ Fixes bug 19122; bugfix on 0.0.2pre16 (the first version to have
+ a manpage!).
+
+ o Minor bugfixes (hidden services):
+ - Stop logging intro point details to the client log on certain
+ error conditions. Fixed as part of bug 20012; bugfix on
+ 0.2.4.8-alpha. Patch by teor.
+
+ o Minor bugfixes (IPv6, testing):
+ - Check for IPv6 correctly on Linux when running test networks.
+ Fixes bug 19905; bugfix on 0.2.7.3-rc; patch by teor.
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Add permission to run the sched_yield() and sigaltstack() system
+ calls, in order to support versions of Tor compiled with asan or
+ ubsan code that use these calls. Now "sandbox 1" and
+ "--enable-expensive-hardening" should be compatible on more
+ systems. Fixes bug 20063; bugfix on 0.2.5.1-alpha.
+
+ o Minor bugfixes (logging):
+ - When logging a message from the BUG() macro, be explicit about
+ what we were asserting. Previously we were confusing what we were
+ asserting with what the bug was. Fixes bug 20093; bugfix
+ on 0.2.9.1-alpha.
+ - When we are unable to remove the bw_accounting file, do not warn
+ if the reason we couldn't remove it was that it didn't exist.
+ Fixes bug 19964; bugfix on 0.2.5.4-alpha. Patch from 'pastly'.
+
+ o Minor bugfixes (option parsing):
+ - Count unix sockets when counting client listeners (SOCKS, Trans,
+ NATD, and DNS). This has no user-visible behaviour changes: these
+ options are set once, and never read. Required for correct
+ behaviour in ticket 17178. Fixes bug 19677; bugfix on
+ 0.2.6.3-alpha. Patch by teor.
+
+ o Minor bugfixes (options):
+ - Check the consistency of UseEntryGuards and EntryNodes more
+ reliably. Fixes bug 20074; bugfix on tor- 0.2.4.12-alpha. Patch
+ by teor.
+ - Stop changing the configured value of UseEntryGuards on
+ authorities and Tor2web clients. Fixes bug 20074; bugfix on
+ commits 51fc6799 in tor-0.1.1.16-rc and acda1735 in tor-0.2.4.3-
+ alpha. Patch by teor.
+
+ o Minor bugfixes (Tor2web):
+ - Prevent Tor2web clients running hidden services, these services
+ are not anonymous due to the one-hop client paths. Fixes bug
+ 19678. Patch by teor.
+
+ o Minor bugfixes (unit tests):
+ - Fix a shared-random unit test that was failing on big endian
+ architectures due to internal representation of a integer copied
+ to a buffer. The test is changed to take a full 32 bytes of data
+ and use the output of a python script that make the COMMIT and
+ REVEAL calculation according to the spec. Fixes bug 19977; bugfix
+ on 0.2.9.1-alpha.
+ - The tor_tls_server_info_callback unit test no longer crashes when
+ debug-level logging is turned on. Fixes bug 20041; bugfix
+ on 0.2.8.1-alpha.
+
+
+Changes in version 0.2.8.8 - 2016-09-23
+ Tor 0.2.8.8 fixes two crash bugs present in previous versions of the
+ 0.2.8.x series. Relays running 0.2.8.x should upgrade, as should users
+ who select public relays as their bridges.
+
+ o Major bugfixes (crash):
+ - Fix a complicated crash bug that could affect Tor clients
+ configured to use bridges when replacing a networkstatus consensus
+ in which one of their bridges was mentioned. OpenBSD users saw
+ more crashes here, but all platforms were potentially affected.
+ Fixes bug 20103; bugfix on 0.2.8.2-alpha.
+
+ o Major bugfixes (relay, OOM handler):
+ - Fix a timing-dependent assertion failure that could occur when we
+ tried to flush from a circuit after having freed its cells because
+ of an out-of-memory condition. Fixes bug 20203; bugfix on
+ 0.2.8.1-alpha. Thanks to "cypherpunks" for help diagnosing
+ this one.
+
+ o Minor feature (fallback directories):
+ - Remove broken fallbacks from the hard-coded fallback directory
+ list. Closes ticket 20190; patch by teor.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the September 6 2016 Maxmind GeoLite2
+ Country database.
+
+
+Changes in version 0.2.9.2-alpha - 2016-08-24
+ Tor 0.2.9.2-alpha continues development of the 0.2.9 series with
+ several new features and bugfixes. It also includes an important
+ authority update and an important bugfix from 0.2.8.7. Everyone who
+ sets the ReachableAddresses option, and all bridges, are strongly
+ encouraged to upgrade to 0.2.8.7, or to 0.2.9.2-alpha.
+
+ o Directory authority changes (also in 0.2.8.7):
+ - The "Tonga" bridge authority has been retired; the new bridge
+ authority is "Bifroest". Closes tickets 19728 and 19690.
+
+ o Major bugfixes (client, security, also in 0.2.8.7):
+ - Only use the ReachableAddresses option to restrict the first hop
+ in a path. In earlier versions of 0.2.8.x, it would apply to
+ every hop in the path, with a possible degradation in anonymity
+ for anyone using an uncommon ReachableAddress setting. Fixes bug
+ 19973; bugfix on 0.2.8.2-alpha.
+
+ o Major features (user interface):
+ - Tor now supports the ability to declare options deprecated, so
+ that we can recommend that people stop using them. Previously,
+ this was done in an ad-hoc way. Closes ticket 19820.
+
+ o Major bugfixes (directory downloads):
+ - Avoid resetting download status for consensuses hourly, since we
+ already have another, smarter retry mechanism. Fixes bug 8625;
+ bugfix on 0.2.0.9-alpha.
+
+ o Minor features (config):
+ - Warn users when descriptor and port addresses are inconsistent.
+ Mitigates bug 13953; patch by teor.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the August 2 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (user interface):
+ - There is a new --list-deprecated-options command-line option to
+ list all of the deprecated options. Implemented as part of
+ ticket 19820.
+
+ o Minor bugfixes (code style):
+ - Fix an integer signedness conversion issue in the case conversion
+ tables. Fixes bug 19168; bugfix on 0.2.1.11-alpha.
+
+ o Minor bugfixes (compilation):
+ - Build correctly on versions of libevent2 without support for
+ evutil_secure_rng_add_bytes(). Fixes bug 19904; bugfix
+ on 0.2.5.4-alpha.
+ - Fix a compilation warning on GCC versions before 4.6. Our
+ ENABLE_GCC_WARNING macro used the word "warning" as an argument,
+ when it is also required as an argument to the compiler pragma.
+ Fixes bug 19901; bugfix on 0.2.9.1-alpha.
+
+ o Minor bugfixes (compilation, also in 0.2.8.7):
+ - Remove an inappropriate "inline" in tortls.c that was causing
+ warnings on older versions of GCC. Fixes bug 19903; bugfix
+ on 0.2.8.1-alpha.
+
+ o Minor bugfixes (fallback directories, also in 0.2.8.7):
+ - Avoid logging a NULL string pointer when loading fallback
+ directory information. Fixes bug 19947; bugfix on 0.2.4.7-alpha
+ and 0.2.8.1-alpha. Report and patch by "rubiate".
+
+ o Minor bugfixes (logging):
+ - Log a more accurate message when we fail to dump a microdescriptor.
+ Fixes bug 17758; bugfix on 0.2.2.8-alpha. Patch from Daniel Pinto.
+
+ o Minor bugfixes (memory leak):
+ - Fix a series of slow memory leaks related to parsing torrc files
+ and options. Fixes bug 19466; bugfix on 0.2.1.6-alpha.
+
+ o Deprecated features:
+ - A number of DNS-cache-related sub-options for client ports are now
+ deprecated for security reasons, and may be removed in a future
+ version of Tor. (We believe that client-side DNS cacheing is a bad
+ idea for anonymity, and you should not turn it on.) The options
+ are: CacheDNS, CacheIPv4DNS, CacheIPv6DNS, UseDNSCache,
+ UseIPv4Cache, and UseIPv6Cache.
+ - A number of options are deprecated for security reasons, and may
+ be removed in a future version of Tor. The options are:
+ AllowDotExit, AllowInvalidNodes, AllowSingleHopCircuits,
+ AllowSingleHopExits, ClientDNSRejectInternalAddresses,
+ CloseHSClientCircuitsImmediatelyOnTimeout,
+ CloseHSServiceRendCircuitsImmediatelyOnTimeout,
+ ExcludeSingleHopRelays, FastFirstHopPK, TLSECGroup,
+ UseNTorHandshake, and WarnUnsafeSocks.
+ - The *ListenAddress options are now deprecated as unnecessary: the
+ corresponding *Port options should be used instead. These options
+ may someday be removed. The affected options are:
+ ControlListenAddress, DNSListenAddress, DirListenAddress,
+ NATDListenAddress, ORListenAddress, SocksListenAddress,
+ and TransListenAddress.
+
+ o Documentation:
+ - Correct the IPv6 syntax in our documentation for the
+ VirtualAddrNetworkIPv6 torrc option. Closes ticket 19743.
+
+ o Removed code:
+ - We no longer include the (dead, deprecated) bufferevent code in
+ Tor. Closes ticket 19450. Based on a patch from U+039b.
+
+
+Changes in version 0.2.8.7 - 2016-08-24
+ Tor 0.2.8.7 fixes an important bug related to the ReachableAddresses
+ option in 0.2.8.6, and replaces a retiring bridge authority. Everyone
+ who sets the ReachableAddresses option, and all bridges, are strongly
+ encouraged to upgrade.
+
+ o Directory authority changes:
+ - The "Tonga" bridge authority has been retired; the new bridge
+ authority is "Bifroest". Closes tickets 19728 and 19690.
+
+ o Major bugfixes (client, security):
+ - Only use the ReachableAddresses option to restrict the first hop
+ in a path. In earlier versions of 0.2.8.x, it would apply to
+ every hop in the path, with a possible degradation in anonymity
+ for anyone using an uncommon ReachableAddress setting. Fixes bug
+ 19973; bugfix on 0.2.8.2-alpha.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the August 2 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (compilation):
+ - Remove an inappropriate "inline" in tortls.c that was causing
+ warnings on older versions of GCC. Fixes bug 19903; bugfix
+ on 0.2.8.1-alpha.
+
+ o Minor bugfixes (fallback directories):
+ - Avoid logging a NULL string pointer when loading fallback
+ directory information. Fixes bug 19947; bugfix on 0.2.4.7-alpha
+ and 0.2.8.1-alpha. Report and patch by "rubiate".
+
+
+Changes in version 0.2.9.1-alpha - 2016-08-08
+ Tor 0.2.9.1-alpha is the first alpha release in the 0.2.9 development
+ series. It improves our support for hardened builds and compiler
+ warnings, deploys some critical infrastructure for improvements to
+ hidden services, includes a new timing backend that we hope to use for
+ better support for traffic padding, makes it easier for programmers to
+ log unexpected events, and contains other small improvements to
+ security, correctness, and performance.
+
+ Below are the changes since 0.2.8.6.
+
+ o New system requirements:
+ - Tor now requires Libevent version 2.0.10-stable or later. Older
+ versions of Libevent have less efficient backends for several
+ platforms, and lack the DNS code that we use for our server-side
+ DNS support. This implements ticket 19554.
+ - Tor now requires zlib version 1.2 or later, for security,
+ efficiency, and (eventually) gzip support. (Back when we started,
+ zlib 1.1 and zlib 1.0 were still found in the wild. 1.2 was
+ released in 2003. We recommend the latest version.)
+
+ o Major features (build, hardening):
+ - Tor now builds with -ftrapv by default on compilers that support
+ it. This option detects signed integer overflow (which C forbids),
+ and turns it into a hard-failure. We do not apply this option to
+ code that needs to run in constant time to avoid side-channels;
+ instead, we use -fwrapv in that code. Closes ticket 17983.
+ - When --enable-expensive-hardening is selected, stop applying the
+ clang/gcc sanitizers to code that needs to run in constant time.
+ Although we are aware of no introduced side-channels, we are not
+ able to prove that there are none. Related to ticket 17983.
+
+ o Major features (compilation):
+ - Our big list of extra GCC warnings is now enabled by default when
+ building with GCC (or with anything like Clang that claims to be
+ GCC-compatible). To make all warnings into fatal compilation
+ errors, pass --enable-fatal-warnings to configure. Closes
+ ticket 19044.
+ - Use the Autoconf macro AC_USE_SYSTEM_EXTENSIONS to automatically
+ turn on C and POSIX extensions. (Previously, we attempted to do
+ this on an ad hoc basis.) Closes ticket 19139.
+
+ o Major features (directory authorities, hidden services):
+ - Directory authorities can now perform the shared randomness
+ protocol specified by proposal 250. Using this protocol, directory
+ authorities generate a global fresh random value every day. In the
+ future, this value will be used by hidden services to select
+ HSDirs. This release implements the directory authority feature;
+ the hidden service side will be implemented in the future as part
+ of proposal 224. Resolves ticket 16943; implements proposal 250.
+
+ o Major features (downloading, random exponential backoff):
+ - When we fail to download an object from a directory service, wait
+ for an (exponentially increasing) randomized amount of time before
+ retrying, rather than a fixed interval as we did before. This
+ prevents a group of Tor instances from becoming too synchronized,
+ or a single Tor instance from becoming too predictable, in its
+ download schedule. Closes ticket 15942.
+
+ o Major bugfixes (exit policies):
+ - Avoid disclosing exit outbound bind addresses, configured port
+ bind addresses, and local interface addresses in relay descriptors
+ by default under ExitPolicyRejectPrivate. Instead, only reject
+ these (otherwise unlisted) addresses if
+ ExitPolicyRejectLocalInterfaces is set. Fixes bug 18456; bugfix on
+ 0.2.7.2-alpha. Patch by teor.
+
+ o Major bugfixes (hidden service client):
+ - Allow Tor clients with appropriate controllers to work with
+ FetchHidServDescriptors set to 0. Previously, this option also
+ disabled descriptor cache lookup, thus breaking hidden services
+ entirely. Fixes bug 18704; bugfix on 0.2.0.20-rc. Patch by "twim".
+
+ o Minor features (build, hardening):
+ - Detect and work around a libclang_rt problem that would prevent
+ clang from finding __mulodi4() on some 32-bit platforms, and thus
+ keep -ftrapv from linking on those systems. Closes ticket 19079.
+ - When building on a system without runtime support for the runtime
+ hardening options, try to log a useful warning at configuration
+ time, rather than an incomprehensible warning at link time. If
+ expensive hardening was requested, this warning becomes an error.
+ Closes ticket 18895.
+
+ o Minor features (code safety):
+ - In our integer-parsing functions, ensure that maxiumum value we
+ give is no smaller than the minimum value. Closes ticket 19063;
+ patch from U+039b.
+
+ o Minor features (controller):
+ - Implement new GETINFO queries for all downloads that use
+ download_status_t to schedule retries. This allows controllers to
+ examine the schedule for pending downloads. Closes ticket 19323.
+ - Allow controllers to configure basic client authorization on
+ hidden services when they create them with the ADD_ONION control
+ command. Implements ticket 15588. Patch by "special".
+ - Fire a STATUS_SERVER controller event whenever the hibernation
+ status changes between "awake"/"soft"/"hard". Closes ticket 18685.
+
+ o Minor features (directory authority):
+ - Directory authorities now only give the Guard flag to a relay if
+ they are also giving it the Stable flag. This change allows us to
+ simplify path selection for clients. It should have minimal effect
+ in practice, since >99% of Guards already have the Stable flag.
+ Implements ticket 18624.
+ - Directory authorities now write their v3-status-votes file out to
+ disk earlier in the consensus process, so we have a record of the
+ votes even if we abort the consensus process. Resolves
+ ticket 19036.
+
+ o Minor features (hidden service):
+ - Stop being so strict about the payload length of "rendezvous1"
+ cells. We used to be locked in to the "TAP" handshake length, and
+ now we can handle better handshakes like "ntor". Resolves
+ ticket 18998.
+
+ o Minor features (infrastructure, time):
+ - Tor now uses the operating system's monotonic timers (where
+ available) for internal fine-grained timing. Previously we would
+ look at the system clock, and then attempt to compensate for the
+ clock running backwards. Closes ticket 18908.
+ - Tor now includes an improved timer backend, so that we can
+ efficiently support tens or hundreds of thousands of concurrent
+ timers, as will be needed for some of our planned anti-traffic-
+ analysis work. This code is based on William Ahern's "timeout.c"
+ project, which implements a "tickless hierarchical timing wheel".
+ Closes ticket 18365.
+
+ o Minor features (logging):
+ - Provide a more useful warning message when configured with an
+ invalid Nickname. Closes ticket 18300; patch from "icanhasaccount".
+ - When dumping unparseable router descriptors, optionally store them
+ in separate files, named by digest, up to a configurable size
+ limit. You can change the size limit by setting the
+ MaxUnparseableDescSizeToLog option, and disable this feature by
+ setting that option to 0. Closes ticket 18322.
+ - Add a set of macros to check nonfatal assertions, for internal
+ use. Migrating more of our checks to these should help us avoid
+ needless crash bugs. Closes ticket 18613.
+
+ o Minor features (performance):
+ - Changer the "optimistic data" extension from "off by default" to
+ "on by default". The default was ordinarily overridden by a
+ consensus option, but when clients were bootstrapping for the
+ first time, they would not have a consensus to get the option
+ from. Changing this default When fetching a consensus for the
+ first time, use optimistic data. This saves a round-trip during
+ startup. Closes ticket 18815.
+
+ o Minor features (relay, usability):
+ - When the directory authorities refuse a bad relay's descriptor,
+ encourage the relay operator to contact us. Many relay operators
+ won't notice this line in their logs, but it's a win if even a few
+ learn why we don't like what their relay was doing. Resolves
+ ticket 18760.
+
+ o Minor features (testing):
+ - Let backtrace tests work correctly under AddressSanitizer. Fixes
+ part of bug 18934; bugfix on 0.2.5.2-alpha.
+ - Move the test-network.sh script to chutney, and modify tor's test-
+ network.sh to call the (newer) chutney version when available.
+ Resolves ticket 19116. Patch by teor.
+ - Use the lcov convention for marking lines as unreachable, so that
+ we don't count them when we're generating test coverage data.
+ Update our coverage tools to understand this convention. Closes
+ ticket 16792.
+
+ o Minor bugfixes (bootstrap):
+ - Remember the directory we fetched the consensus or previous
+ certificates from, and use it to fetch future authority
+ certificates. This change improves bootstrapping performance.
+ Fixes bug 18963; bugfix on 0.2.8.1-alpha.
+
+ o Minor bugfixes (build):
+ - The test-stem and test-network makefile targets now depend only on
+ the tor binary that they are testing. Previously, they depended on
+ "make all". Fixes bug 18240; bugfix on 0.2.8.2-alpha. Based on a
+ patch from "cypherpunks".
+
+ o Minor bugfixes (circuits):
+ - Make sure extend_info_from_router() is only called on servers.
+ Fixes bug 19639; bugfix on 0.2.8.1-alpha.
+
+ o Minor bugfixes (compilation):
+ - When building with Clang, use a full set of GCC warnings.
+ (Previously, we included only a subset, because of the way we
+ detected them.) Fixes bug 19216; bugfix on 0.2.0.1-alpha.
+
+ o Minor bugfixes (directory authority):
+ - Authorities now sort the "package" lines in their votes, for ease
+ of debugging. (They are already sorted in consensus documents.)
+ Fixes bug 18840; bugfix on 0.2.6.3-alpha.
+ - When parsing a detached signature, make sure we use the length of
+ the digest algorithm instead of an hardcoded DIGEST256_LEN in
+ order to avoid comparing bytes out-of-bounds with a smaller digest
+ length such as SHA1. Fixes bug 19066; bugfix on 0.2.2.6-alpha.
+
+ o Minor bugfixes (documentation):
+ - Document the --passphrase-fd option in the tor manpage. Fixes bug
+ 19504; bugfix on 0.2.7.3-rc.
+ - Fix the description of the --passphrase-fd option in the
+ tor-gencert manpage. The option is used to pass the number of a
+ file descriptor to read the passphrase from, not to read the file
+ descriptor from. Fixes bug 19505; bugfix on 0.2.0.20-alpha.
+
+ o Minor bugfixes (ephemeral hidden service):
+ - When deleting an ephemeral hidden service, close its intro points
+ even if they are not completely open. Fixes bug 18604; bugfix
+ on 0.2.7.1-alpha.
+
+ o Minor bugfixes (guard selection):
+ - Use a single entry guard even if the NumEntryGuards consensus
+ parameter is not provided. Fixes bug 17688; bugfix
+ on 0.2.5.6-alpha.
+ - Don't mark guards as unreachable if connection_connect() fails.
+ That function fails for local reasons, so it shouldn't reveal
+ anything about the status of the guard. Fixes bug 14334; bugfix
+ on 0.2.3.10-alpha.
+
+ o Minor bugfixes (hidden service client):
+ - Increase the minimum number of internal circuits we preemptively
+ build from 2 to 3, so a circuit is available when a client
+ connects to another onion service. Fixes bug 13239; bugfix
+ on 0.1.0.1-rc.
+
+ o Minor bugfixes (logging):
+ - When logging a directory ownership mismatch, log the owning
+ username correctly. Fixes bug 19578; bugfix on 0.2.2.29-beta.
+
+ o Minor bugfixes (memory leaks):
+ - Fix a small, uncommon memory leak that could occur when reading a
+ truncated ed25519 key file. Fixes bug 18956; bugfix
+ on 0.2.6.1-alpha.
+
+ o Minor bugfixes (testing):
+ - Allow clients to retry HSDirs much faster in test networks. Fixes
+ bug 19702; bugfix on 0.2.7.1-alpha. Patch by teor.
+ - Disable ASAN's detection of segmentation faults while running
+ test_bt.sh, so that we can make sure that our own backtrace
+ generation code works. Fixes another aspect of bug 18934; bugfix
+ on 0.2.5.2-alpha. Patch from "cypherpunks".
+ - Fix the test-network-all target on out-of-tree builds by using the
+ correct path to the test driver script. Fixes bug 19421; bugfix
+ on 0.2.7.3-rc.
+
+ o Minor bugfixes (time):
+ - Improve overflow checks in tv_udiff and tv_mdiff. Fixes bug 19483;
+ bugfix on all released tor versions.
+ - When computing the difference between two times in milliseconds,
+ we now round to the nearest millisecond correctly. Previously, we
+ could sometimes round in the wrong direction. Fixes bug 19428;
+ bugfix on 0.2.2.2-alpha.
+
+ o Minor bugfixes (user interface):
+ - Display a more accurate number of suppressed messages in the log
+ rate-limiter. Previously, there was a potential integer overflow
+ in the counter. Now, if the number of messages hits a maximum, the
+ rate-limiter doesn't count any further. Fixes bug 19435; bugfix
+ on 0.2.4.11-alpha.
+ - Fix a typo in the passphrase prompt for the ed25519 identity key.
+ Fixes bug 19503; bugfix on 0.2.7.2-alpha.
+
+ o Code simplification and refactoring:
+ - Remove redundant declarations of the MIN macro. Closes
+ ticket 18889.
+ - Rename tor_dup_addr() to tor_addr_to_str_dup() to avoid confusion.
+ Closes ticket 18462; patch from "icanhasaccount".
+ - Split the 600-line directory_handle_command_get function into
+ separate functions for different URL types. Closes ticket 16698.
+
+ o Documentation:
+ - Fix spelling of "--enable-tor2web-mode" in the manpage. Closes
+ ticket 19153. Patch from "U+039b".
+
+ o Removed features:
+ - Remove support for "GET /tor/bytes.txt" DirPort request, and
+ "GETINFO dir-usage" controller request, which were only available
+ via a compile-time option in Tor anyway. Feature was added in
+ 0.2.2.1-alpha. Resolves ticket 19035.
+ - There is no longer a compile-time option to disable support for
+ TransPort. (If you don't want TransPort; just don't use it.) Patch
+ from "U+039b". Closes ticket 19449.
+
+ o Testing:
+ - Run more workqueue tests as part of "make check". These had
+ previously been implemented, but you needed to know special
+ command-line options to enable them.
+ - We now have unit tests for our code to reject zlib "compression
+ bombs". (Fortunately, the code works fine.)
+
+
+Changes in version 0.2.8.6 - 2016-08-02
+
+ Tor 0.2.8.6 is the first stable version of the Tor 0.2.8 series.
+
+ The Tor 0.2.8 series improves client bootstrapping performance,
+ completes the authority-side implementation of improved identity
+ keys for relays, and includes numerous bugfixes and performance
+ improvements throughout the program. This release continues to
+ improve the coverage of Tor's test suite. For a full list of
+ changes since Tor 0.2.7, see the ReleaseNotes file.
+
+ Changes since 0.2.8.5-rc:
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the July 6 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (compilation):
+ - Fix a compilation warning in the unit tests on systems where char
+ is signed. Fixes bug 19682; bugfix on 0.2.8.1-alpha.
+
+ o Minor bugfixes (fallback directories):
+ - Remove a fallback that was on the hardcoded list, then opted-out.
+ Fixes bug 19782; update to fallback list from 0.2.8.2-alpha.
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Allow more syscalls when running with "Sandbox 1" enabled:
+ sysinfo, getsockopt(SO_SNDBUF), and setsockopt(SO_SNDBUFFORCE). On
+ some systems, these are required for Tor to start. Fixes bug
+ 18397; bugfix on 0.2.5.1-alpha. Patch from Daniel Pinto.
+ - Allow IPPROTO_UDP datagram sockets when running with "Sandbox 1",
+ so that get_interface_address6_via_udp_socket_hack() can work.
+ Fixes bug 19660; bugfix on 0.2.5.1-alpha.
+
+
+Changes in version 0.2.8.5-rc - 2016-07-07
+ Tor 0.2.8.5-rc is the second release candidate in the Tor 0.2.8
+ series. If we find no new bugs or regressions here, the first stable
+ 0.2.8 release will be identical to it. It has a few small bugfixes
+ against previous versions.
+
+ o Directory authority changes:
+ - Urras is no longer a directory authority. Closes ticket 19271.
+
+ o Major bugfixes (heartbeat):
+ - Fix a regression that would crash Tor when the periodic
+ "heartbeat" log messages were disabled. Fixes bug 19454; bugfix on
+ tor-0.2.8.1-alpha. Reported by "kubaku".
+
+ o Minor features (build):
+ - Tor now again builds with the recent OpenSSL 1.1 development
+ branch (tested against 1.1.0-pre6-dev). Closes ticket 19499.
+ - When building manual pages, set the timezone to "UTC", so that the
+ output is reproducible. Fixes bug 19558; bugfix on 0.2.2.9-alpha.
+ Patch from intrigeri.
+
+ o Minor bugfixes (fallback directory selection):
+ - Avoid errors during fallback selection if there are no eligible
+ fallbacks. Fixes bug 19480; bugfix on 0.2.8.3-alpha. Patch
+ by teor.
+
+ o Minor bugfixes (IPv6, microdescriptors):
+ - Don't check node addresses when we only have a routerstatus. This
+ allows IPv6-only clients to bootstrap by fetching microdescriptors
+ from fallback directory mirrors. (The microdescriptor consensus
+ has no IPv6 addresses in it.) Fixes bug 19608; bugfix
+ on 0.2.8.2-alpha.
+
+ o Minor bugfixes (logging):
+ - Reduce pointlessly verbose log messages when directory servers
+ can't be found. Fixes bug 18849; bugfix on 0.2.8.3-alpha and
+ 0.2.8.1-alpha. Patch by teor.
+ - When a fallback directory changes its fingerprint from the hard-
+ coded fingerprint, log a less severe, more explanatory log
+ message. Fixes bug 18812; bugfix on 0.2.8.1-alpha. Patch by teor.
+
+ o Minor bugfixes (Linux seccomp2 sandboxing):
+ - Allow statistics to be written to disk when "Sandbox 1" is
+ enabled. Fixes bugs 19556 and 19957; bugfix on 0.2.5.1-alpha and
+ 0.2.6.1-alpha respectively.
+
+ o Minor bugfixes (user interface):
+ - Remove a warning message "Service [scrubbed] not found after
+ descriptor upload". This message appears when one uses HSPOST
+ control command to upload a service descriptor. Since there is
+ only a descriptor and no service, showing this message is
+ pointless and confusing. Fixes bug 19464; bugfix on 0.2.7.2-alpha.
+
+ o Fallback directory list:
+ - Add a comment to the generated fallback directory list that
+ explains how to comment out unsuitable fallbacks in a way that's
+ compatible with the stem fallback parser.
+ - Update fallback whitelist and blacklist based on relay operator
+ emails. Blacklist unsuitable (non-working, over-volatile)
+ fallbacks. Resolves ticket 19071. Patch by teor.
+ - Update hard-coded fallback list to remove unsuitable fallbacks.
+ Resolves ticket 19071. Patch by teor.
+
+
+Changes in version 0.2.8.4-rc - 2016-06-15
+ Tor 0.2.8.4-rc is the first release candidate in the Tor 0.2.8 series.
+ If we find no new bugs or regressions here, the first stable 0.2.8
+ release will be identical to it. It has a few small bugfixes against
+ previous versions.
+
+ o Major bugfixes (user interface):
+ - Correctly give a warning in the cases where a relay is specified
+ by nickname, and one such relay is found, but it is not officially
+ Named. Fixes bug 19203; bugfix on 0.2.3.1-alpha.
+
+ o Minor features (build):
+ - Tor now builds once again with the recent OpenSSL 1.1 development
+ branch (tested against 1.1.0-pre5 and 1.1.0-pre6-dev).
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the June 7 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (compilation):
+ - Cause the unit tests to compile correctly on mingw64 versions that
+ lack sscanf. Fixes bug 19213; bugfix on 0.2.7.1-alpha.
+
+ o Minor bugfixes (downloading):
+ - Predict more correctly whether we'll be downloading over HTTP when
+ we determine the maximum length of a URL. This should avoid a
+ "BUG" warning about the Squid HTTP proxy and its URL limits. Fixes
+ bug 19191.
+
+
+Changes in version 0.2.8.3-alpha - 2016-05-26
+ Tor 0.2.8.3-alpha resolves several bugs, most of them introduced over
+ the course of the 0.2.8 development cycle. It improves the behavior of
+ directory clients, fixes several crash bugs, fixes a gap in compiler
+ hardening, and allows the full integration test suite to run on
+ more platforms.
+
+ o Major bugfixes (security, client, DNS proxy):
+ - Stop a crash that could occur when a client running with DNSPort
+ received a query with multiple address types, and the first
+ address type was not supported. Found and fixed by Scott Dial.
+ Fixes bug 18710; bugfix on 0.2.5.4-alpha.
+
+ o Major bugfixes (security, compilation):
+ - Correctly detect compiler flags on systems where _FORTIFY_SOURCE
+ is predefined. Previously, our use of -D_FORTIFY_SOURCE would
+ cause a compiler warning, thereby making other checks fail, and
+ needlessly disabling compiler-hardening support. Fixes one case of
+ bug 18841; bugfix on 0.2.3.17-beta. Patch from "trudokal".
+
+ o Major bugfixes (security, directory authorities):
+ - Fix a crash and out-of-bounds write during authority voting, when
+ the list of relays includes duplicate ed25519 identity keys. Fixes
+ bug 19032; bugfix on 0.2.8.2-alpha.
+
+ o Major bugfixes (client, bootstrapping):
+ - Check if bootstrap consensus downloads are still needed when the
+ linked connection attaches. This prevents tor making unnecessary
+ begindir-style connections, which are the only directory
+ connections tor clients make since the fix for 18483 was merged.
+ - Fix some edge cases where consensus download connections may not
+ have been closed, even though they were not needed. Related to fix
+ for 18809.
+ - Make relays retry consensus downloads the correct number of times,
+ rather than the more aggressive client retry count. Fixes part of
+ ticket 18809.
+ - Stop downloading consensuses when we have a consensus, even if we
+ don't have all the certificates for it yet. Fixes bug 18809;
+ bugfix on 0.2.8.1-alpha. Patches by arma and teor.
+
+ o Major bugfixes (directory mirrors):
+ - Decide whether to advertise begindir support in the the same way
+ we decide whether to advertise our DirPort. Allowing these
+ decisions to become out-of-sync led to surprising behavior like
+ advertising begindir support when hibernation made us not
+ advertise a DirPort. Resolves bug 18616; bugfix on 0.2.8.1-alpha.
+ Patch by teor.
+
+ o Major bugfixes (IPv6 bridges, client):
+ - Actually use IPv6 addresses when selecting directory addresses for
+ IPv6 bridges. Fixes bug 18921; bugfix on 0.2.8.1-alpha. Patch
+ by "teor".
+
+ o Major bugfixes (key management):
+ - If OpenSSL fails to generate an RSA key, do not retain a dangling
+ pointer to the previous (uninitialized) key value. The impact here
+ should be limited to a difficult-to-trigger crash, if OpenSSL is
+ running an engine that makes key generation failures possible, or
+ if OpenSSL runs out of memory. Fixes bug 19152; bugfix on
+ 0.2.1.10-alpha. Found by Yuan Jochen Kang, Suman Jana, and
+ Baishakhi Ray.
+
+ o Major bugfixes (testing):
+ - Fix a bug that would block 'make test-network-all' on systems where
+ IPv6 packets were lost. Fixes bug 19008; bugfix on tor-0.2.7.3-rc.
+ - Avoid "WSANOTINITIALISED" warnings in the unit tests. Fixes bug 18668;
+ bugfix on 0.2.8.1-alpha.
+
+ o Minor features (clients):
+ - Make clients, onion services, and bridge relays always use an
+ encrypted begindir connection for directory requests. Resolves
+ ticket 18483. Patch by "teor".
+
+ o Minor features (fallback directory mirrors):
+ - Give each fallback the same weight for client selection; restrict
+ fallbacks to one per operator; report fallback directory detail
+ changes when rebuilding list; add new fallback directory mirrors
+ to the whitelist; update fallback directories based on the latest
+ OnionOO data; and any other minor simplifications and fixes.
+ Closes tasks 17158, 17905, 18749, bug 18689, and fixes part of bug
+ 18812 on 0.2.8.1-alpha; patch by "teor".
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the May 4 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (assert, portability):
+ - Fix an assertion failure in memarea.c on systems where "long" is
+ shorter than the size of a pointer. Fixes bug 18716; bugfix
+ on 0.2.1.1-alpha.
+
+ o Minor bugfixes (bootstrap):
+ - Consistently use the consensus download schedule for authority
+ certificates. Fixes bug 18816; bugfix on 0.2.4.13-alpha.
+
+ o Minor bugfixes (build):
+ - Remove a pair of redundant AM_CONDITIONAL declarations from
+ configure.ac. Fixes one final case of bug 17744; bugfix
+ on 0.2.8.2-alpha.
+ - Resolve warnings when building on systems that are concerned with
+ signed char. Fixes bug 18728; bugfix on 0.2.7.2-alpha
+ and 0.2.6.1-alpha.
+ - When libscrypt.h is found, but no libscrypt library can be linked,
+ treat libscrypt as absent. Fixes bug 19161; bugfix
+ on 0.2.6.1-alpha.
+
+ o Minor bugfixes (client):
+ - Turn all TestingClientBootstrap* into non-testing torrc options.
+ This changes simply renames them by removing "Testing" in front of
+ them and they do not require TestingTorNetwork to be enabled
+ anymore. Fixes bug 18481; bugfix on 0.2.8.1-alpha.
+ - Make directory node selection more reliable, mainly for IPv6-only
+ clients and clients with few reachable addresses. Fixes bug 18929;
+ bugfix on 0.2.8.1-alpha. Patch by "teor".
+
+ o Minor bugfixes (controller, microdescriptors):
+ - Make GETINFO dir/status-vote/current/consensus conform to the
+ control specification by returning "551 Could not open cached
+ consensus..." when not caching consensuses. Fixes bug 18920;
+ bugfix on 0.2.2.6-alpha.
+
+ o Minor bugfixes (crypto, portability):
+ - The SHA3 and SHAKE routines now produce the correct output on Big
+ Endian systems. No code calls either algorithm yet, so this is
+ primarily a build fix. Fixes bug 18943; bugfix on 0.2.8.1-alpha.
+ - Tor now builds again with the recent OpenSSL 1.1 development
+ branch (tested against 1.1.0-pre4 and 1.1.0-pre5-dev). Closes
+ ticket 18286.
+
+ o Minor bugfixes (directories):
+ - When fetching extrainfo documents, compare their SHA256 digests
+ and Ed25519 signing key certificates with the routerinfo that led
+ us to fetch them, rather than with the most recent routerinfo.
+ Otherwise we generate many spurious warnings about mismatches.
+ Fixes bug 17150; bugfix on 0.2.7.2-alpha.
+
+ o Minor bugfixes (logging):
+ - When we can't generate a signing key because OfflineMasterKey is
+ set, do not imply that we should have been able to load it. Fixes
+ bug 18133; bugfix on 0.2.7.2-alpha.
+ - Stop periodic_event_dispatch() from blasting twelve lines per
+ second at loglevel debug. Fixes bug 18729; fix on 0.2.8.1-alpha.
+ - When rejecting a misformed INTRODUCE2 cell, only log at
+ PROTOCOL_WARN severity. Fixes bug 18761; bugfix on 0.2.8.2-alpha.
+
+ o Minor bugfixes (pluggable transports):
+ - Avoid reporting a spurious error when we decide that we don't need
+ to terminate a pluggable transport because it has already exited.
+ Fixes bug 18686; bugfix on 0.2.5.5-alpha.
+
+ o Minor bugfixes (pointer arithmetic):
+ - Fix a bug in memarea_alloc() that could have resulted in remote
+ heap write access, if Tor had ever passed an unchecked size to
+ memarea_alloc(). Fortunately, all the sizes we pass to
+ memarea_alloc() are pre-checked to be less than 128 kilobytes.
+ Fixes bug 19150; bugfix on 0.2.1.1-alpha. Bug found by
+ Guido Vranken.
+
+ o Minor bugfixes (relays):
+ - Consider more config options when relays decide whether to
+ regenerate their descriptor. Fixes more of bug 12538; bugfix
+ on 0.2.8.1-alpha.
+ - Resolve some edge cases where we might launch an ORPort
+ reachability check even when DisableNetwork is set. Noticed while
+ fixing bug 18616; bugfix on 0.2.3.9-alpha.
+
+ o Minor bugfixes (statistics):
+ - We now include consensus downloads via IPv6 in our directory-
+ request statistics. Fixes bug 18460; bugfix on 0.2.3.14-alpha.
+
+ o Minor bugfixes (testing):
+ - Allow directories in small networks to bootstrap by skipping
+ DirPort checks when the consensus has no exits. Fixes bug 19003;
+ bugfix on 0.2.8.1-alpha. Patch by teor.
+ - Fix a small memory leak that would occur when the
+ TestingEnableCellStatsEvent option was turned on. Fixes bug 18673;
+ bugfix on 0.2.5.2-alpha.
+
+ o Minor bugfixes (time handling):
+ - When correcting a corrupt 'struct tm' value, fill in the tm_wday
+ field. Otherwise, our unit tests crash on Windows. Fixes bug
+ 18977; bugfix on 0.2.2.25-alpha.
+
+ o Documentation:
+ - Document the contents of the 'datadir/keys' subdirectory in the
+ manual page. Closes ticket 17621.
+ - Stop recommending use of nicknames to identify relays in our
+ MapAddress documentation. Closes ticket 18312.
+
+
Changes in version 0.2.8.2-alpha - 2016-03-28
Tor 0.2.8.2-alpha is the second alpha in its series. It fixes numerous
bugs in earlier versions of Tor, including some that prevented
diff --git a/LICENSE b/LICENSE
index bc0ac4195b..56d1002251 100644
--- a/LICENSE
+++ b/LICENSE
@@ -271,6 +271,110 @@ src/ext/readpassphrase.[ch] are distributed under this license:
Materiel Command, USAF, under agreement number F39502-99-1-0512.
===============================================================================
+src/ext/mulodi4.c is distributed under this license:
+
+ =========================================================================
+ compiler_rt License
+ =========================================================================
+
+ The compiler_rt library is dual licensed under both the
+ University of Illinois "BSD-Like" license and the MIT license.
+ As a user of this code you may choose to use it under either
+ license. As a contributor, you agree to allow your code to be
+ used under both.
+
+ Full text of the relevant licenses is included below.
+
+ =========================================================================
+
+ University of Illinois/NCSA
+ Open Source License
+
+ Copyright (c) 2009-2016 by the contributors listed in CREDITS.TXT
+
+ All rights reserved.
+
+ Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal with the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the following
+ disclaimers.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimers in the documentation and/or other materials
+ provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois
+ at Urbana-Champaign, nor the names of its contributors may
+ be used to endorse or promote products derived from this
+ Software without specific prior written permission.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS WITH THE SOFTWARE.
+
+ =========================================================================
+
+ Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+
+ =========================================================================
+ Copyrights and Licenses for Third Party Software Distributed with LLVM:
+ =========================================================================
+
+ The LLVM software contains code written by third parties. Such
+ software will have its own individual LICENSE.TXT file in the
+ directory in which it appears. This file will describe the
+ copyrights, license, and restrictions which apply to that code.
+
+ The disclaimer of warranty in the University of Illinois Open
+ Source License applies to all code in the LLVM Distribution, and
+ nothing in any of the other licenses gives permission to use the
+ names of the LLVM Team or the University of Illinois to endorse
+ or promote products derived from this Software.
+
+===============================================================================
If you got Tor as a static binary with OpenSSL included, then you should know:
"This product includes software developed by the OpenSSL Project
for use in the OpenSSL Toolkit (http://www.openssl.org/)"
diff --git a/Makefile.am b/Makefile.am
index cd88264264..f400728bac 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -15,13 +15,13 @@ noinst_PROGRAMS=
DISTCLEANFILES=
bin_SCRIPTS=
AM_CPPFLAGS=
-AM_CFLAGS = @TOR_SYSTEMD_CFLAGS@
-SHELL = @SHELL@
+AM_CFLAGS=@TOR_SYSTEMD_CFLAGS@ @CFLAGS_BUGTRAP@
+SHELL=@SHELL@
if COVERAGE_ENABLED
-TESTING_TOR_BINARY="$(top_builddir)/src/or/tor-cov"
+TESTING_TOR_BINARY=$(top_builddir)/src/or/tor-cov$(EXEEXT)
else
-TESTING_TOR_BINARY="$(top_builddir)/src/or/tor"
+TESTING_TOR_BINARY=$(top_builddir)/src/or/tor$(EXEEXT)
endif
include src/include.am
@@ -36,6 +36,9 @@ EXTRA_DIST+= \
README \
ReleaseNotes
+## This tells etags how to find mockable function definitions.
+AM_ETAGSFLAGS=--regex='{c}/MOCK_IMPL([^,]+,\W*\([a-zA-Z0-9_]+\)\W*,/\1/s'
+
if COVERAGE_ENABLED
TEST_CFLAGS=-fno-inline -fprofile-arcs -ftest-coverage
if DISABLE_ASSERTS_IN_UNIT_TESTS
@@ -93,18 +96,18 @@ need-chutney-path:
# Note that test-network requires a copy of Chutney in $CHUTNEY_PATH.
# Chutney can be cloned from https://git.torproject.org/chutney.git .
-test-network: need-chutney-path all
+test-network: need-chutney-path $(TESTING_TOR_BINARY) src/tools/tor-gencert
$(top_srcdir)/src/test/test-network.sh $(TEST_NETWORK_FLAGS)
# Run all available tests using automake's test-driver
# only run IPv6 tests if we can ping6 ::1 (localhost)
# some IPv6 tests will fail without an IPv6 DNS server (see #16971 and #17011)
# only run mixed tests if we have a tor-stable binary
-# see #17015 for autodetection of different tor versions
-test-network-all: need-chutney-path all test-driver
+# Try both the BSD and the Linux ping6 syntax, because they're incompatible
+test-network-all: need-chutney-path test-driver $(TESTING_TOR_BINARY) src/tools/tor-gencert
mkdir -p $(TEST_NETWORK_ALL_LOG_DIR)
@flavors="$(TEST_CHUTNEY_FLAVORS)"; \
- if ping6 -q -c 1 -o ::1 >/dev/null 2>&1; then \
+ if ping6 -q -c 1 -o ::1 >/dev/null 2>&1 || ping6 -q -c 1 -W 1 ::1 >/dev/null 2>&1; then \
echo "ping6 ::1 succeeded, running IPv6 flavors: $(TEST_CHUTNEY_FLAVORS_IPV6)."; \
flavors="$$flavors $(TEST_CHUTNEY_FLAVORS_IPV6)"; \
else \
@@ -122,7 +125,7 @@ test-network-all: need-chutney-path all test-driver
echo "SKIP: $$f"; \
done; \
for f in $$flavors; do \
- ./test-driver --test-name $$f --log-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.log --trs-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.trs $(TEST_NETWORK_ALL_DRIVER_FLAGS) $(top_srcdir)/src/test/test-network.sh --flavor $$f $(TEST_NETWORK_FLAGS); \
+ $(SHELL) $(top_srcdir)/test-driver --test-name $$f --log-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.log --trs-file $(TEST_NETWORK_ALL_LOG_DIR)/$$f.trs $(TEST_NETWORK_ALL_DRIVER_FLAGS) $(top_srcdir)/src/test/test-network.sh --flavor $$f $(TEST_NETWORK_FLAGS); \
done; \
echo "Log and result files are available in $(TEST_NETWORK_ALL_LOG_DIR)."; \
! grep -q FAIL test_network_log/*.trs
@@ -134,11 +137,11 @@ need-stem-path:
exit 1; \
fi
-test-stem: need-stem-path all
- @$(PYTHON) "$$STEM_SOURCE_DIR"/run_tests.py --tor $(TESTING_TOR_BINARY) --all --log notice --target RUN_ALL;
+test-stem: need-stem-path $(TESTING_TOR_BINARY)
+ @$(PYTHON) "$$STEM_SOURCE_DIR"/run_tests.py --tor "$(TESTING_TOR_BINARY)" --all --log notice --target RUN_ALL;
-test-stem-full: need-stem-path all
- @$(PYTHON) "$$STEM_SOURCE_DIR"/run_tests.py --tor $(TESTING_TOR_BINARY) --all --log notice --target RUN_ALL,ONLINE -v;
+test-stem-full: need-stem-path $(TESTING_TOR_BINARY)
+ @$(PYTHON) "$$STEM_SOURCE_DIR"/run_tests.py --tor "$(TESTING_TOR_BINARY)" --all --log notice --target RUN_ALL,ONLINE -v;
test-full: need-stem-path need-chutney-path check test-network test-stem
@@ -176,7 +179,7 @@ coverage-html-full: all
genhtml --branch-coverage -o "$(HTML_COVER_DIR)" "$(HTML_COVER_DIR)/lcov.info"
# Avoid strlcpy.c, strlcat.c, aes.c, OpenBSD_malloc_Linux.c, sha256.c,
-# eventdns.[hc], tinytest*.[ch]
+# tinytest*.[ch]
check-spaces:
$(top_srcdir)/scripts/maint/checkSpace.pl -C \
$(top_srcdir)/src/common/*.[ch] \
diff --git a/ReleaseNotes b/ReleaseNotes
index ba5de30c01..af61a4d739 100644
--- a/ReleaseNotes
+++ b/ReleaseNotes
@@ -2,6 +2,812 @@ This document summarizes new features and bugfixes in each stable release
of Tor. If you want to see more detailed descriptions of the changes in
each development snapshot, see the ChangeLog file.
+Changes in version 0.2.8.9 - 2016-10-17
+ Tor 0.2.8.9 backports a fix for a security hole in previous versions
+ of Tor that would allow a remote attacker to crash a Tor client,
+ hidden service, relay, or authority. All Tor users should upgrade to
+ this version, or to 0.2.9.4-alpha. Patches will be released for older
+ versions of Tor.
+
+ o Major features (security fixes, also in 0.2.9.4-alpha):
+ - Prevent a class of security bugs caused by treating the contents
+ of a buffer chunk as if they were a NUL-terminated string. At
+ least one such bug seems to be present in all currently used
+ versions of Tor, and would allow an attacker to remotely crash
+ most Tor instances, especially those compiled with extra compiler
+ hardening. With this defense in place, such bugs can't crash Tor,
+ though we should still fix them as they occur. Closes ticket
+ 20384 (TROVE-2016-10-001).
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the October 4 2016 Maxmind GeoLite2
+ Country database.
+
+
+Changes in version 0.2.8.8 - 2016-09-23
+ Tor 0.2.8.8 fixes two crash bugs present in previous versions of the
+ 0.2.8.x series. Relays running 0.2.8.x should upgrade, as should users
+ who select public relays as their bridges.
+
+ o Major bugfixes (crash):
+ - Fix a complicated crash bug that could affect Tor clients
+ configured to use bridges when replacing a networkstatus consensus
+ in which one of their bridges was mentioned. OpenBSD users saw
+ more crashes here, but all platforms were potentially affected.
+ Fixes bug 20103; bugfix on 0.2.8.2-alpha.
+
+ o Major bugfixes (relay, OOM handler):
+ - Fix a timing-dependent assertion failure that could occur when we
+ tried to flush from a circuit after having freed its cells because
+ of an out-of-memory condition. Fixes bug 20203; bugfix on
+ 0.2.8.1-alpha. Thanks to "cypherpunks" for help diagnosing
+ this one.
+
+ o Minor feature (fallback directories):
+ - Remove broken fallbacks from the hard-coded fallback directory
+ list. Closes ticket 20190; patch by teor.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the September 6 2016 Maxmind GeoLite2
+ Country database.
+
+
+Changes in version 0.2.8.7 - 2016-08-24
+ Tor 0.2.8.7 fixes an important bug related to the ReachableAddresses
+ option in 0.2.8.6, and replaces a retiring bridge authority. Everyone
+ who sets the ReachableAddresses option, and all bridges, are strongly
+ encouraged to upgrade.
+
+ o Directory authority changes:
+ - The "Tonga" bridge authority has been retired; the new bridge
+ authority is "Bifroest". Closes tickets 19728 and 19690.
+
+ o Major bugfixes (client, security):
+ - Only use the ReachableAddresses option to restrict the first hop
+ in a path. In earlier versions of 0.2.8.x, it would apply to
+ every hop in the path, with a possible degradation in anonymity
+ for anyone using an uncommon ReachableAddress setting. Fixes bug
+ 19973; bugfix on 0.2.8.2-alpha.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the August 2 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (compilation):
+ - Remove an inappropriate "inline" in tortls.c that was causing
+ warnings on older versions of GCC. Fixes bug 19903; bugfix
+ on 0.2.8.1-alpha.
+
+ o Minor bugfixes (fallback directories):
+ - Avoid logging a NULL string pointer when loading fallback
+ directory information. Fixes bug 19947; bugfix on 0.2.4.7-alpha
+ and 0.2.8.1-alpha. Report and patch by "rubiate".
+
+
+Changes in version 0.2.8.6 - 2016-08-02
+
+ Tor 0.2.8.6 is the first stable version of the Tor 0.2.8 series.
+
+ The Tor 0.2.8 series improves client bootstrapping performance,
+ completes the authority-side implementation of improved identity
+ keys for relays, and includes numerous bugfixes and performance
+ improvements throughout the program. This release continues to
+ improve the coverage of Tor's test suite. For a full list of
+ changes since Tor 0.2.7, see the ReleaseNotes file.
+
+ Below is a list of the changes since Tor 0.2.7.
+
+ o New system requirements:
+ - Tor no longer attempts to support platforms where the "time_t"
+ type is unsigned. (To the best of our knowledge, only OpenVMS does
+ this, and Tor has never actually built on OpenVMS.) Closes
+ ticket 18184.
+ - Tor no longer supports versions of OpenSSL with a broken
+ implementation of counter mode. (This bug was present in OpenSSL
+ 1.0.0, and was fixed in OpenSSL 1.0.0a.) Tor still detects, but no
+ longer runs with, these versions.
+ - Tor now uses Autoconf version 2.63 or later, and Automake 1.11 or
+ later (released in 2008 and 2009 respectively). If you are
+ building Tor from the git repository instead of from the source
+ distribution, and your tools are older than this, you will need to
+ upgrade. Closes ticket 17732.
+
+ o Directory authority changes:
+ - Update the V3 identity key for the dannenberg directory authority:
+ it was changed on 18 November 2015. Closes task 17906. Patch
+ by teor.
+ - Urras is no longer a directory authority. Closes ticket 19271.
+
+ o Major features (directory system):
+ - Include a trial list of default fallback directories, based on an
+ opt-in survey of suitable relays. Doing this should make clients
+ bootstrap more quickly and reliably, and reduce the load on the
+ directory authorities. Closes ticket 15775. Patch by teor.
+ Candidates identified using an OnionOO script by weasel, teor,
+ gsathya, and karsten.
+ - Previously only relays that explicitly opened a directory port
+ (DirPort) accepted directory requests from clients. Now all
+ relays, with and without a DirPort, accept and serve tunneled
+ directory requests that they receive through their ORPort. You can
+ disable this behavior using the new DirCache option. Closes
+ ticket 12538.
+ - When bootstrapping multiple consensus downloads at a time, use the
+ first one that starts downloading, and close the rest. This
+ reduces failures when authorities or fallback directories are slow
+ or down. Together with the code for feature 15775, this feature
+ should reduces failures due to fallback churn. Implements ticket
+ 4483. Patch by teor. Implements IPv4 portions of proposal 210 by
+ mikeperry and teor.
+
+ o Major features (security, Linux):
+ - When Tor starts as root on Linux and is told to switch user ID, it
+ can now retain the capability to bind to low ports. By default,
+ Tor will do this only when it's switching user ID and some low
+ ports have been configured. You can change this behavior with the
+ new option KeepBindCapabilities. Closes ticket 8195.
+
+ o Major bugfixes (client, bootstrapping):
+ - Check if bootstrap consensus downloads are still needed when the
+ linked connection attaches. This prevents tor making unnecessary
+ begindir-style connections, which are the only directory
+ connections tor clients make since the fix for 18483 was merged.
+ - Fix some edge cases where consensus download connections may not
+ have been closed, even though they were not needed. Related to fix
+ for 18809.
+ - Make relays retry consensus downloads the correct number of times,
+ rather than the more aggressive client retry count. Fixes part of
+ ticket 18809.
+
+ o Major bugfixes (dns proxy mode, crash):
+ - Avoid crashing when running as a DNS proxy. Fixes bug 16248;
+ bugfix on 0.2.0.1-alpha. Patch from "cypherpunks".
+
+ o Major bugfixes (ed25519, voting):
+ - Actually enable support for authorities to match routers by their
+ Ed25519 identities. Previously, the code had been written, but
+ some debugging code that had accidentally been left in the
+ codebase made it stay turned off. Fixes bug 17702; bugfix
+ on 0.2.7.2-alpha.
+ - When collating votes by Ed25519 identities, authorities now
+ include a "NoEdConsensus" flag if the ed25519 value (or lack
+ thereof) for a server does not reflect the majority consensus.
+ Related to bug 17668; bugfix on 0.2.7.2-alpha.
+ - When generating a vote with keypinning disabled, never include two
+ entries for the same ed25519 identity. This bug was causing
+ authorities to generate votes that they could not parse when a
+ router violated key pinning by changing its RSA identity but
+ keeping its Ed25519 identity. Fixes bug 17668; fixes part of bug
+ 18318. Bugfix on 0.2.7.2-alpha.
+
+ o Major bugfixes (key management):
+ - If OpenSSL fails to generate an RSA key, do not retain a dangling
+ pointer to the previous (uninitialized) key value. The impact here
+ should be limited to a difficult-to-trigger crash, if OpenSSL is
+ running an engine that makes key generation failures possible, or
+ if OpenSSL runs out of memory. Fixes bug 19152; bugfix on
+ 0.2.1.10-alpha. Found by Yuan Jochen Kang, Suman Jana, and
+ Baishakhi Ray.
+
+ o Major bugfixes (security, client, DNS proxy):
+ - Stop a crash that could occur when a client running with DNSPort
+ received a query with multiple address types, and the first
+ address type was not supported. Found and fixed by Scott Dial.
+ Fixes bug 18710; bugfix on 0.2.5.4-alpha.
+
+ o Major bugfixes (security, compilation):
+ - Correctly detect compiler flags on systems where _FORTIFY_SOURCE
+ is predefined. Previously, our use of -D_FORTIFY_SOURCE would
+ cause a compiler warning, thereby making other checks fail, and
+ needlessly disabling compiler-hardening support. Fixes one case of
+ bug 18841; bugfix on 0.2.3.17-beta. Patch from "trudokal".
+ - Repair hardened builds under the clang compiler. Previously, our
+ use of _FORTIFY_SOURCE would conflict with clang's address
+ sanitizer. Fixes bug 14821; bugfix on 0.2.5.4-alpha.
+
+ o Major bugfixes (security, pointers):
+ - Avoid a difficult-to-trigger heap corruption attack when extending
+ a smartlist to contain over 16GB of pointers. Fixes bug 18162;
+ bugfix on 0.1.1.11-alpha, which fixed a related bug incompletely.
+ Reported by Guido Vranken.
+
+ o Major bugfixes (testing):
+ - Fix a bug that would block 'make test-network-all' on systems where
+ IPv6 packets were lost. Fixes bug 19008; bugfix on 0.2.7.3-rc.
+
+ o Major bugfixes (user interface):
+ - Correctly give a warning in the cases where a relay is specified
+ by nickname, and one such relay is found, but it is not officially
+ Named. Fixes bug 19203; bugfix on 0.2.3.1-alpha.
+
+ o Minor features (accounting):
+ - Added two modes to the AccountingRule option: One for limiting
+ only the number of bytes sent ("AccountingRule out"), and one for
+ limiting only the number of bytes received ("AccountingRule in").
+ Closes ticket 15989; patch from "unixninja92".
+
+ o Minor features (bug-resistance):
+ - Make Tor survive errors involving connections without a
+ corresponding event object. Previously we'd fail with an
+ assertion; now we produce a log message. Related to bug 16248.
+ - Use tor_snprintf() and tor_vsnprintf() even in external and low-
+ level code, to harden against accidental failures to NUL-
+ terminate. Part of ticket 17852. Patch from jsturgix. Found
+ with Flawfinder.
+
+ o Minor features (build):
+ - Detect systems with FreeBSD-derived kernels (such as GNU/kFreeBSD)
+ as having possible IPFW support. Closes ticket 18448. Patch from
+ Steven Chamberlain.
+ - Since our build process now uses "make distcheck", we no longer
+ force "make dist" to depend on "make check". Closes ticket 17893;
+ patch from "cypherpunks".
+ - Tor now builds once again with the recent OpenSSL 1.1 development
+ branch (tested against 1.1.0-pre5 and 1.1.0-pre6-dev). We have been
+ tracking OpenSSL 1.1 development as it has progressed, and fixing
+ numerous compatibility issues as they arose. See tickets
+ 17549, 17921, 17984, 19499, and 18286.
+ - When building manual pages, set the timezone to "UTC", so that the
+ output is reproducible. Fixes bug 19558; bugfix on 0.2.2.9-alpha.
+ Patch from intrigeri.
+
+ o Minor features (clients):
+ - Make clients, onion services, and bridge relays always use an
+ encrypted begindir connection for directory requests. Resolves
+ ticket 18483. Patch by teor.
+
+ o Minor features (controller):
+ - Add 'GETINFO exit-policy/reject-private/[default,relay]', so
+ controllers can examine the the reject rules added by
+ ExitPolicyRejectPrivate. This makes it easier for stem to display
+ exit policies.
+ - Adds the FallbackDir entries to 'GETINFO config/defaults'. Closes
+ tickets 16774 and 17817. Patch by George Tankersley.
+ - New 'GETINFO hs/service/desc/id/' command to retrieve a hidden
+ service descriptor from a service's local hidden service
+ descriptor cache. Closes ticket 14846.
+
+ o Minor features (crypto):
+ - Add SHA3 and SHAKE support to crypto.c. Closes ticket 17783.
+ - Add SHA512 support to crypto.c. Closes ticket 17663; patch from
+ George Tankersley.
+ - Improve performance when hashing non-multiple of 8 sized buffers,
+ based on Andrew Moon's public domain SipHash-2-4 implementation.
+ Fixes bug 17544; bugfix on 0.2.5.3-alpha.
+ - Validate the hard-coded Diffie-Hellman parameters and ensure that
+ p is a safe prime, and g is a suitable generator. Closes
+ ticket 18221.
+ - When allocating a digest state object, allocate no more space than
+ we actually need. Previously, we would allocate as much space as
+ the state for the largest algorithm would need. This change saves
+ up to 672 bytes per circuit. Closes ticket 17796.
+
+ o Minor features (directory downloads):
+ - Add UseDefaultFallbackDirs, which enables any hard-coded fallback
+ directory mirrors. The default is 1; set it to 0 to disable
+ fallbacks. Implements ticket 17576. Patch by teor.
+ - Wait for busy authorities and fallback directories to become non-
+ busy when bootstrapping. (A similar change was made in 6c443e987d
+ for directory caches chosen from the consensus.) Closes ticket
+ 17864; patch by teor.
+
+ o Minor features (geoip):
+ - Update geoip and geoip6 to the July 6 2016 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (hidden service directory):
+ - Streamline relay-side hsdir handling: when relays consider whether
+ to accept an uploaded hidden service descriptor, they no longer
+ check whether they are one of the relays in the network that is
+ "supposed" to handle that descriptor. Implements ticket 18332.
+
+ o Minor features (IPv6):
+ - Add ClientPreferIPv6DirPort, which is set to 0 by default. If set
+ to 1, tor prefers IPv6 directory addresses.
+ - Add ClientUseIPv4, which is set to 1 by default. If set to 0, tor
+ avoids using IPv4 for client OR and directory connections.
+ - Add address policy assume_action support for IPv6 addresses.
+ - Add an argument 'ipv6=address:orport' to the DirAuthority and
+ FallbackDir torrc options, to specify an IPv6 address for an
+ authority or fallback directory. Add hard-coded ipv6 addresses for
+ directory authorities that have them. Closes ticket 17327; patch
+ from Nick Mathewson and teor.
+ - Allow users to configure directory authorities and fallback
+ directory servers with IPv6 addresses and ORPorts. Resolves
+ ticket 6027.
+ - Limit IPv6 mask bits to 128.
+ - Make tor_ersatz_socketpair work on IPv6-only systems. Fixes bug
+ 17638; bugfix on 0.0.2pre8. Patch by teor.
+ - Try harder to obey the IP version restrictions "ClientUseIPv4 0",
+ "ClientUseIPv6 0", "ClientPreferIPv6ORPort", and
+ "ClientPreferIPv6DirPort". Closes ticket 17840; patch by teor.
+ - Warn when comparing against an AF_UNSPEC address in a policy, it's
+ almost always a bug. Closes ticket 17863; patch by teor.
+ - routerset_parse now accepts IPv6 literal addresses. Fixes bug
+ 17060; bugfix on 0.2.1.3-alpha. Patch by teor.
+
+ o Minor features (Linux seccomp2 sandbox):
+ - Reject attempts to change our Address with "Sandbox 1" enabled.
+ Changing Address with Sandbox turned on would never actually work,
+ but previously it would fail in strange and confusing ways. Found
+ while fixing 18548.
+
+ o Minor features (logging):
+ - When logging to syslog, allow a tag to be added to the syslog
+ identity (the string prepended to every log message). The tag can
+ be configured with SyslogIdentityTag and defaults to none. Setting
+ it to "foo" will cause logs to be tagged as "Tor-foo". Closes
+ ticket 17194.
+
+ o Minor features (portability):
+ - Use timingsafe_memcmp() where available. Closes ticket 17944;
+ patch from <logan@hackers.mu>.
+
+ o Minor features (relay, address discovery):
+ - Add a family argument to get_interface_addresses_raw() and
+ subfunctions to make network interface address interogation more
+ efficient. Now Tor can specifically ask for IPv4, IPv6 or both
+ types of interfaces from the operating system. Resolves
+ ticket 17950.
+ - When get_interface_address6_list(.,AF_UNSPEC,.) is called and
+ fails to enumerate interface addresses using the platform-specific
+ API, have it rely on the UDP socket fallback technique to try and
+ find out what IP addresses (both IPv4 and IPv6) our machine has.
+ Resolves ticket 17951.
+
+ o Minor features (replay cache):
+ - The replay cache now uses SHA256 instead of SHA1. Implements
+ feature 8961. Patch by teor, issue reported by rransom.
+
+ o Minor features (robustness):
+ - Exit immediately with an error message if the code attempts to use
+ Libevent without having initialized it. This should resolve some
+ frequently-made mistakes in our unit tests. Closes ticket 18241.
+
+ o Minor features (security, clock):
+ - Warn when the system clock appears to move back in time (when the
+ state file was last written in the future). Tor doesn't know that
+ consensuses have expired if the clock is in the past. Patch by
+ teor. Implements ticket 17188.
+
+ o Minor features (security, exit policies):
+ - ExitPolicyRejectPrivate now rejects more private addresses by
+ default. Specifically, it now rejects the relay's outbound bind
+ addresses (if configured), and the relay's configured port
+ addresses (such as ORPort and DirPort). Fixes bug 17027; bugfix on
+ 0.2.0.11-alpha. Patch by teor.
+
+ o Minor features (security, memory erasure):
+ - Make memwipe() do nothing when passed a NULL pointer or buffer of
+ zero size. Check size argument to memwipe() for underflow. Fixes
+ bug 18089; bugfix on 0.2.3.25 and 0.2.4.6-alpha. Reported by "gk",
+ patch by teor.
+ - Set the unused entries in a smartlist to NULL. This helped catch
+ a (harmless) bug, and shouldn't affect performance too much.
+ Implements ticket 17026.
+ - Use SecureMemoryWipe() function to securely clean memory on
+ Windows. Previously we'd use OpenSSL's OPENSSL_cleanse() function.
+ Implements feature 17986.
+ - Use explicit_bzero or memset_s when present. Previously, we'd use
+ OpenSSL's OPENSSL_cleanse() function. Closes ticket 7419; patches
+ from <logan@hackers.mu> and <selven@hackers.mu>.
+
+ o Minor features (security, RNG):
+ - Adjust Tor's use of OpenSSL's RNG APIs so that they absolutely,
+ positively are not allowed to fail. Previously we depended on
+ internal details of OpenSSL's behavior. Closes ticket 17686.
+ - Never use the system entropy output directly for anything besides
+ seeding the PRNG. When we want to generate important keys, instead
+ of using system entropy directly, we now hash it with the PRNG
+ stream. This may help resist certain attacks based on broken OS
+ entropy implementations. Closes part of ticket 17694.
+ - Use modern system calls (like getentropy() or getrandom()) to
+ generate strong entropy on platforms that have them. Closes
+ ticket 13696.
+
+ o Minor features (security, win32):
+ - Set SO_EXCLUSIVEADDRUSE on Win32 to avoid a local port-stealing
+ attack. Fixes bug 18123; bugfix on all tor versions. Patch
+ by teor.
+
+ o Minor features (unix domain sockets):
+ - Add a new per-socket option, RelaxDirModeCheck, to allow creating
+ Unix domain sockets without checking the permissions on the parent
+ directory. (Tor checks permissions by default because some
+ operating systems only check permissions on the parent directory.
+ However, some operating systems do look at permissions on the
+ socket, and tor's default check is unneeded.) Closes ticket 18458.
+ Patch by weasel.
+
+ o Minor features (unix file permissions):
+ - Defer creation of Unix sockets until after setuid. This avoids
+ needing CAP_CHOWN and CAP_FOWNER when using systemd's
+ CapabilityBoundingSet, or chown and fowner when using SELinux.
+ Implements part of ticket 17562. Patch from Jamie Nguyen.
+ - If any directory created by Tor is marked as group readable, the
+ filesystem group is allowed to be either the default GID or the
+ root user. Allowing root to read the DataDirectory prevents the
+ need for CAP_READ_SEARCH when using systemd's
+ CapabilityBoundingSet, or dac_read_search when using SELinux.
+ Implements part of ticket 17562. Patch from Jamie Nguyen.
+ - Introduce a new DataDirectoryGroupReadable option. If it is set to
+ 1, the DataDirectory will be made readable by the default GID.
+ Implements part of ticket 17562. Patch from Jamie Nguyen.
+
+ o Minor bugfixes (accounting):
+ - The max bandwidth when using 'AccountRule sum' is now correctly
+ logged. Fixes bug 18024; bugfix on 0.2.6.1-alpha. Patch
+ from "unixninja92".
+
+ o Minor bugfixes (assert, portability):
+ - Fix an assertion failure in memarea.c on systems where "long" is
+ shorter than the size of a pointer. Fixes bug 18716; bugfix
+ on 0.2.1.1-alpha.
+
+ o Minor bugfixes (bootstrap):
+ - Consistently use the consensus download schedule for authority
+ certificates. Fixes bug 18816; bugfix on 0.2.4.13-alpha.
+
+ o Minor bugfixes (build):
+ - Avoid spurious failures from configure files related to calling
+ exit(0) in TOR_SEARCH_LIBRARY. Fixes bug 18625; bugfix on
+ 0.2.0.1-alpha. Patch from "cypherpunks".
+ - Do not link the unit tests against both the testing and non-
+ testing versions of the static libraries. Fixes bug 18490; bugfix
+ on 0.2.7.1-alpha.
+ - Resolve warnings when building on systems that are concerned with
+ signed char. Fixes bug 18728; bugfix on 0.2.7.2-alpha
+ and 0.2.6.1-alpha.
+ - Silence spurious clang-scan warnings in the ed25519_donna code by
+ explicitly initializing some objects. Fixes bug 18384; bugfix on
+ 0.2.7.2-alpha. Patch by teor.
+ - When libscrypt.h is found, but no libscrypt library can be linked,
+ treat libscrypt as absent. Fixes bug 19161; bugfix
+ on 0.2.6.1-alpha.
+ - Cause the unit tests to compile correctly on mingw64 versions that
+ lack sscanf. Fixes bug 19213; bugfix on 0.2.7.1-alpha.
+ - Don't try to use the pthread_condattr_setclock() function unless
+ it actually exists. Fixes compilation on NetBSD-6.x. Fixes bug
+ 17819; bugfix on 0.2.6.3-alpha.
+ - Fix backtrace compilation on FreeBSD. Fixes bug 17827; bugfix
+ on 0.2.5.2-alpha.
+ - Fix search for libevent libraries on OpenBSD (and other systems
+ that install libevent 1 and libevent 2 in parallel). Fixes bug
+ 16651; bugfix on 0.1.0.7-rc. Patch from "rubiate".
+ - Isolate environment variables meant for tests from the rest of the
+ build system. Fixes bug 17818; bugfix on 0.2.7.3-rc.
+ - Mark all object files that include micro-revision.i as depending
+ on it, so as to make parallel builds more reliable. Fixes bug
+ 17826; bugfix on 0.2.5.1-alpha.
+ - Remove config.log only from make distclean, not from make clean.
+ Fixes bug 17924; bugfix on 0.2.4.1-alpha.
+ - Replace usage of 'INLINE' with 'inline'. Fixes bug 17804; bugfix
+ on 0.0.2pre8.
+ - Remove an #endif from configure.ac so that we correctly detect the
+ presence of in6_addr.s6_addr32. Fixes bug 17923; bugfix
+ on 0.2.0.13-alpha.
+
+ o Minor bugfixes (client, bootstrap):
+ - Count receipt of new microdescriptors as progress towards
+ bootstrapping. Previously, with EntryNodes set, Tor might not
+ successfully repopulate the guard set on bootstrapping. Fixes bug
+ 16825; bugfix on 0.2.3.1-alpha.
+
+ o Minor bugfixes (code correctness):
+ - Fix a bad memory handling bug that would occur if we had queued a
+ cell on a channel's incoming queue. Fortunately, we can't actually
+ queue a cell like that as our code is constructed today, but it's
+ best to avoid this kind of error, even if there isn't any code
+ that triggers it today. Fixes bug 18570; bugfix on 0.2.4.4-alpha.
+ - Assert that allocated memory held by the reputation code is freed
+ according to its internal counters. Fixes bug 17753; bugfix
+ on 0.1.1.1-alpha.
+ - Assert when the TLS contexts fail to initialize. Fixes bug 17683;
+ bugfix on 0.0.6.
+ - Update to the latest version of Trunnel, which tries harder to
+ avoid generating code that can invoke memcpy(p,NULL,0). Bug found
+ by clang address sanitizer. Fixes bug 18373; bugfix
+ on 0.2.7.2-alpha.
+ - When closing an entry connection, generate a warning if we should
+ have sent an end cell for it but we haven't. Fixes bug 17876;
+ bugfix on 0.2.3.2-alpha.
+
+ o Minor bugfixes (configuration):
+ - Fix a tiny memory leak when parsing a port configuration ending in
+ ":auto". Fixes bug 18374; bugfix on 0.2.3.3-alpha.
+
+ o Minor bugfixes (containers):
+ - If we somehow attempt to construct a heap with more than
+ 1073741822 elements, avoid an integer overflow when maintaining
+ the heap property. Fixes bug 18296; bugfix on 0.1.2.1-alpha.
+
+ o Minor bugfixes (controller, microdescriptors):
+ - Make GETINFO dir/status-vote/current/consensus conform to the
+ control specification by returning "551 Could not open cached
+ consensus..." when not caching consensuses. Fixes bug 18920;
+ bugfix on 0.2.2.6-alpha.
+
+ o Minor bugfixes (crypto):
+ - Check the return value of HMAC() and assert on failure. Fixes bug
+ 17658; bugfix on 0.2.3.6-alpha. Patch by teor.
+
+ o Minor bugfixes (directories):
+ - When fetching extrainfo documents, compare their SHA256 digests
+ and Ed25519 signing key certificates with the routerinfo that led
+ us to fetch them, rather than with the most recent routerinfo.
+ Otherwise we generate many spurious warnings about mismatches.
+ Fixes bug 17150; bugfix on 0.2.7.2-alpha.
+ - When generating a URL for a directory server on an IPv6 address,
+ wrap the IPv6 address in square brackets. Fixes bug 18051; bugfix
+ on 0.2.3.9-alpha. Patch from Malek.
+
+ o Minor bugfixes (downloading):
+ - Predict more correctly whether we'll be downloading over HTTP when
+ we determine the maximum length of a URL. This should avoid a
+ "BUG" warning about the Squid HTTP proxy and its URL limits. Fixes
+ bug 19191.
+
+ o Minor bugfixes (exit policies, security):
+ - Refresh an exit relay's exit policy when interface addresses
+ change. Previously, tor only refreshed the exit policy when the
+ configured external address changed. Fixes bug 18208; bugfix on
+ 0.2.7.3-rc. Patch by teor.
+
+ o Minor bugfixes (fallback directories):
+ - Mark fallbacks as "too busy" when they return a 503 response,
+ rather than just marking authorities. Fixes bug 17572; bugfix on
+ 0.2.4.7-alpha. Patch by teor.
+ - When requesting extrainfo descriptors from a trusted directory
+ server, check whether it is an authority or a fallback directory
+ which supports extrainfo descriptors. Fixes bug 18489; bugfix on
+ 0.2.4.7-alpha. Reported by atagar, patch by teor.
+
+ o Minor bugfixes (hidden service, client):
+ - Handle the case where the user makes several fast consecutive
+ requests to the same .onion address. Previously, the first six
+ requests would each trigger a descriptor fetch, each picking a
+ directory (there are 6 overall) and the seventh one would fail
+ because no directories were left, thereby triggering a close on
+ all current directory connections asking for the hidden service.
+ The solution here is to not close the connections if we have
+ pending directory fetches. Fixes bug 15937; bugfix
+ on 0.2.7.1-alpha.
+
+ o Minor bugfixes (hidden service, control port):
+ - Add the onion address to the HS_DESC event for the UPLOADED action
+ both on success or failure. It was previously hardcoded with
+ UNKNOWN. Fixes bug 16023; bugfix on 0.2.7.2-alpha.
+
+ o Minor bugfixes (hidden service, directory):
+ - Bridges now refuse "rendezvous2" (hidden service descriptor)
+ publish attempts. Suggested by ticket 18332.
+
+ o Minor bugfixes (IPv6):
+ - Update the limits in max_dl_per_request for IPv6 address length.
+ Fixes bug 17573; bugfix on 0.2.1.5-alpha.
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Allow more syscalls when running with "Sandbox 1" enabled:
+ sysinfo, getsockopt(SO_SNDBUF), and setsockopt(SO_SNDBUFFORCE). On
+ some systems, these are required for Tor to start. Fixes bug
+ 18397; bugfix on 0.2.5.1-alpha. Patch from Daniel Pinto.
+ - Allow IPPROTO_UDP datagram sockets when running with "Sandbox 1",
+ so that get_interface_address6_via_udp_socket_hack() can work.
+ Fixes bug 19660; bugfix on 0.2.5.1-alpha.
+ - Allow the setrlimit syscall, and the prlimit and prlimit64
+ syscalls, which some libc implementations use under the hood.
+ Fixes bug 15221; bugfix on 0.2.5.1-alpha.
+ - Avoid a 10-second delay when starting as a client with "Sandbox 1"
+ enabled and no DNS resolvers configured. This should help TAILS
+ start up faster. Fixes bug 18548; bugfix on 0.2.5.1-alpha.
+ - Fix a crash when using offline master ed25519 keys with the Linux
+ seccomp2 sandbox enabled. Fixes bug 17675; bugfix on 0.2.7.3-rc.
+ - Allow statistics to be written to disk when "Sandbox 1" is
+ enabled. Fixes bugs 19556 and 19957; bugfix on 0.2.5.1-alpha and
+ 0.2.6.1-alpha respectively.
+
+ o Minor bugfixes (logging):
+ - In log messages that include a function name, use __FUNCTION__
+ instead of __PRETTY_FUNCTION__. In GCC, these are synonymous, but
+ with clang __PRETTY_FUNCTION__ has extra information we don't
+ need. Fixes bug 16563; bugfix on 0.0.2pre8. Fix by Tom van
+ der Woerdt.
+ - Remove needless quotes from a log message about unparseable
+ addresses. Fixes bug 17843; bugfix on 0.2.3.3-alpha.
+ - Scrub service name in "unrecognized service ID" log messages.
+ Fixes bug 18600; bugfix on 0.2.4.11-alpha.
+ - When logging information about an unparsable networkstatus vote or
+ consensus, do not say "vote" when we mean consensus. Fixes bug
+ 18368; bugfix on 0.2.0.8-alpha.
+ - When we can't generate a signing key because OfflineMasterKey is
+ set, do not imply that we should have been able to load it. Fixes
+ bug 18133; bugfix on 0.2.7.2-alpha.
+ - When logging a malformed hostname received through socks4, scrub
+ it if SafeLogging says we should. Fixes bug 17419; bugfix
+ on 0.1.1.16-rc.
+
+ o Minor bugfixes (memory safety):
+ - Avoid freeing an uninitialized pointer when opening a socket fails
+ in get_interface_addresses_ioctl(). Fixes bug 18454; bugfix on
+ 0.2.3.11-alpha. Reported by toralf and "cypherpunks", patch
+ by teor.
+ - Fix a memory leak in "tor --list-fingerprint". Fixes part of bug
+ 18672; bugfix on 0.2.5.1-alpha.
+ - Fix a memory leak in tor-gencert. Fixes part of bug 18672; bugfix
+ on 0.2.0.1-alpha.
+
+ o Minor bugfixes (pluggable transports):
+ - Avoid reporting a spurious error when we decide that we don't need
+ to terminate a pluggable transport because it has already exited.
+ Fixes bug 18686; bugfix on 0.2.5.5-alpha.
+
+ o Minor bugfixes (pointer arithmetic):
+ - Fix a bug in memarea_alloc() that could have resulted in remote
+ heap write access, if Tor had ever passed an unchecked size to
+ memarea_alloc(). Fortunately, all the sizes we pass to
+ memarea_alloc() are pre-checked to be less than 128 kilobytes.
+ Fixes bug 19150; bugfix on 0.2.1.1-alpha. Bug found by
+ Guido Vranken.
+
+ o Minor bugfixes (private directory):
+ - Prevent a race condition when creating private directories. Fixes
+ part of bug 17852; bugfix on 0.0.2pre13. Part of ticket 17852.
+ Patch from jsturgix. Found with Flawfinder.
+
+ o Minor bugfixes (relays):
+ - Check that both the ORPort and DirPort (if present) are reachable
+ before publishing a relay descriptor. Otherwise, relays publish a
+ descriptor with DirPort 0 when the DirPort reachability test takes
+ longer than the ORPort reachability test. Fixes bug 18050; bugfix
+ on 0.1.0.1-rc. Reported by "starlight", patch by teor.
+ - Resolve some edge cases where we might launch an ORPort
+ reachability check even when DisableNetwork is set. Noticed while
+ fixing bug 18616; bugfix on 0.2.3.9-alpha.
+
+ o Minor bugfixes (relays, hidden services):
+ - Refuse connection requests to private OR addresses unless
+ ExtendAllowPrivateAddresses is set. Previously, tor would connect,
+ then refuse to send any cells to a private address. Fixes bugs
+ 17674 and 8976; bugfix on 0.2.3.21-rc. Patch by teor.
+
+ o Minor bugfixes (security, hidden services):
+ - Prevent hidden services connecting to client-supplied rendezvous
+ addresses that are reserved as internal or multicast. Fixes bug
+ 8976; bugfix on 0.2.3.21-rc. Patch by dgoulet and teor.
+
+ o Minor bugfixes (statistics):
+ - Consistently check for overflow in round_*_to_next_multiple_of
+ functions, and add unit tests with additional and maximal values.
+ Fixes part of bug 13192; bugfix on 0.2.2.1-alpha.
+ - Handle edge cases in the laplace functions: avoid division by
+ zero, avoid taking the log of zero, and silence clang type
+ conversion warnings using round and trunc. Add unit tests for edge
+ cases with maximal values. Fixes part of bug 13192; bugfix
+ on 0.2.6.2-alpha.
+ - We now include consensus downloads via IPv6 in our directory-
+ request statistics. Fixes bug 18460; bugfix on 0.2.3.14-alpha.
+
+ o Minor bugfixes (test networks, IPv6):
+ - Allow internal IPv6 addresses in descriptors in test networks.
+ Fixes bug 17153; bugfix on 0.2.3.16-alpha. Patch by teor, reported
+ by karsten.
+
+ o Minor bugfixes (testing):
+ - Check the full results of SHA256 and SHA512 digests in the unit
+ tests. Bugfix on 0.2.2.4-alpha. Patch by teor.
+ - Fix a memory leak in the ntor test. Fixes bug 17778; bugfix
+ on 0.2.4.8-alpha.
+ - Fix a small memory leak that would occur when the
+ TestingEnableCellStatsEvent option was turned on. Fixes bug 18673;
+ bugfix on 0.2.5.2-alpha.
+ - Make unit tests pass on IPv6-only systems, and systems without
+ localhost addresses (like some FreeBSD jails). Fixes bug 17632;
+ bugfix on 0.2.7.3-rc. Patch by teor.
+ - The test for log_heartbeat was incorrectly failing in timezones
+ with non-integer offsets. Instead of comparing the end of the time
+ string against a constant, compare it to the output of
+ format_local_iso_time when given the correct input. Fixes bug
+ 18039; bugfix on 0.2.5.4-alpha.
+ - We no longer disable assertions in the unit tests when coverage is
+ enabled. Instead, we require you to say --disable-asserts-in-tests
+ to the configure script if you need assertions disabled in the
+ unit tests (for example, if you want to perform branch coverage).
+ Fixes bug 18242; bugfix on 0.2.7.1-alpha.
+
+ o Minor bugfixes (time handling):
+ - When correcting a corrupt 'struct tm' value, fill in the tm_wday
+ field. Otherwise, our unit tests crash on Windows. Fixes bug
+ 18977; bugfix on 0.2.2.25-alpha.
+ - Avoid overflow in tor_timegm when parsing dates in and after 2038
+ on platforms with 32-bit time_t. Fixes bug 18479; bugfix on
+ 0.0.2pre14. Patch by teor.
+
+ o Minor bugfixes (tor-gencert):
+ - Correctly handle the case where an authority operator enters a
+ passphrase but sends an EOF before sending a newline. Fixes bug
+ 17443; bugfix on 0.2.0.20-rc. Found by junglefowl.
+
+ o Code simplification and refactoring:
+ - Clean up a little duplicated code in
+ crypto_expand_key_material_TAP(). Closes ticket 17587; patch
+ from "pfrankw".
+ - Decouple the list of streams waiting to be attached to circuits
+ from the overall connection list. This change makes it possible to
+ attach streams quickly while simplifying Tor's callgraph and
+ avoiding O(N) scans of the entire connection list. Closes
+ ticket 17590.
+ - Extract the more complicated parts of circuit_mark_for_close()
+ into a new function that we run periodically before circuits are
+ freed. This change removes more than half of the functions
+ currently in the "blob". Closes ticket 17218.
+ - Move logging of redundant policy entries in
+ policies_parse_exit_policy_internal into its own function. Closes
+ ticket 17608; patch from "juce".
+ - Quote all the string interpolations in configure.ac -- even those
+ which we are pretty sure can't contain spaces. Closes ticket
+ 17744. Patch from zerosion.
+ - Remove code for configuring OpenSSL dynamic locks; OpenSSL doesn't
+ use them. Closes ticket 17926.
+ - Remove specialized code for non-inplace AES_CTR. 99% of our AES is
+ inplace, so there's no need to have a separate implementation for
+ the non-inplace code. Closes ticket 18258. Patch from Malek.
+ - Simplify return types for some crypto functions that can't
+ actually fail. Patch from Hassan Alsibyani. Closes ticket 18259.
+ - When a direct directory request fails immediately on launch,
+ instead of relaunching that request from inside the code that
+ launches it, instead mark the connection for teardown. This change
+ simplifies Tor's callback and prevents the directory-request
+ launching code from invoking itself recursively. Closes
+ ticket 17589.
+
+ o Documentation:
+ - Add a description of the correct use of the '--keygen' command-
+ line option. Closes ticket 17583; based on text by 's7r'.
+ - Change build messages to refer to "Fedora" instead of "Fedora
+ Core", and "dnf" instead of "yum". Closes tickets 18459 and 18426.
+ Patches from "icanhasaccount" and "cypherpunks".
+ - Document the contents of the 'datadir/keys' subdirectory in the
+ manual page. Closes ticket 17621.
+ - Document the minimum HeartbeatPeriod value. Closes ticket 15638.
+ - Explain actual minima for BandwidthRate. Closes ticket 16382.
+ - Fix a minor formatting typo in the manpage. Closes ticket 17791.
+ - Mention torspec URL in the manpage and point the reader to it
+ whenever we mention a document that belongs in torspce. Fixes
+ issue 17392.
+ - Stop recommending use of nicknames to identify relays in our
+ MapAddress documentation. Closes ticket 18312.
+
+ o Removed features:
+ - Remove client-side support for connecting to Tor relays running
+ versions of Tor before 0.2.3.6-alpha. These relays didn't support
+ the v3 TLS handshake protocol, and are no longer allowed on the
+ Tor network. Implements the client side of ticket 11150. Based on
+ patches by Tom van der Woerdt.
+ - We no longer maintain an internal freelist in memarea.c.
+ Allocators should be good enough to make this code unnecessary,
+ and it's doubtful that it ever had any performance benefit.
+
+ o Testing:
+ - Add unit tests to check for common RNG failure modes, such as
+ returning all zeroes, identical values, or incrementing values
+ (OpenSSL's rand_predictable feature). Patch by teor.
+ - Always test both ed25519 backends, so that we can be sure that our
+ batch-open replacement code works. Part of ticket 16794.
+ - Cover dns_resolve_impl() in dns.c with unit tests. Implements a
+ portion of ticket 16831.
+ - Fix several warnings from clang's address sanitizer produced in
+ the unit tests.
+ - Log more information when the backtrace tests fail. Closes ticket
+ 17892. Patch from "cypherpunks."
+ - More unit tests for compat_libevent.c, procmon.c, tortls.c,
+ util_format.c, directory.c, and options_validate.c. Closes tickets
+ 17075, 17082, 17084, 17003, and 17076 respectively. Patches from
+ Ola Bini.
+ - Treat backtrace test failures as expected on FreeBSD until we
+ solve bug 17808. Closes ticket 18204.
+ - Unit tests for directory_handle_command_get. Closes ticket 17004.
+ Patch from Reinaldo de Souza Jr.
+
+
Changes in version 0.2.7.6 - 2015-12-10
Tor version 0.2.7.6 fixes a major bug in entry guard selection, as
well as a minor bug in hidden service reliability.
diff --git a/acinclude.m4 b/acinclude.m4
index 7b1aab2f99..ab12317139 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -42,10 +42,11 @@ AC_DEFUN([TOR_DEFINE_CODEPATH],
AC_SUBST(TOR_LDFLAGS_$2)
])
-dnl 1:flags
-dnl 2:also try to link (yes: non-empty string)
-dnl will set yes or no in $tor_can_link_$1 (as modified by AS_VAR_PUSHDEF)
-AC_DEFUN([TOR_CHECK_CFLAGS], [
+dnl 1: flags
+dnl 2: try to link too if this is nonempty.
+dnl 3: what to do on success compiling
+dnl 4: what to do on failure compiling
+AC_DEFUN([TOR_TRY_COMPILE_WITH_CFLAGS], [
AS_VAR_PUSHDEF([VAR],[tor_cv_cflags_$1])
AC_CACHE_CHECK([whether the compiler accepts $1], VAR, [
tor_saved_CFLAGS="$CFLAGS"
@@ -63,12 +64,21 @@ AC_DEFUN([TOR_CHECK_CFLAGS], [
CFLAGS="$tor_saved_CFLAGS"
])
if test x$VAR = xyes; then
- CFLAGS="$CFLAGS $1"
+ $3
+ else
+ $4
fi
AS_VAR_POPDEF([VAR])
])
dnl 1:flags
+dnl 2:also try to link (yes: non-empty string)
+dnl will set yes or no in $tor_can_link_$1 (as modified by AS_VAR_PUSHDEF)
+AC_DEFUN([TOR_CHECK_CFLAGS], [
+ TOR_TRY_COMPILE_WITH_CFLAGS($1, $2, CFLAGS="$CFLAGS $1", true)
+])
+
+dnl 1:flags
dnl 2:extra ldflags
dnl 3:extra libraries
AC_DEFUN([TOR_CHECK_LDFLAGS], [
diff --git a/changes/19271 b/changes/19271
deleted file mode 100644
index dc06ead999..0000000000
--- a/changes/19271
+++ /dev/null
@@ -1,2 +0,0 @@
- o Directory authority changes:
- - Urras is no longer a directory authority. Closes ticket 19271.
diff --git a/changes/19974 b/changes/19974
new file mode 100644
index 0000000000..5496143ddf
--- /dev/null
+++ b/changes/19974
@@ -0,0 +1,5 @@
+ o Minor bugfixes (unit tests):
+ - Fix tolerances in unit tests for monotonic time comparisons between
+ nanoseconds and microseconds. Previously, we accepted a 10 us
+ difference only, which is not realistic on every platform's
+ clock_gettime(). Fixes bug 19974; bugfix on 0.2.9.1-alpha.
diff --git a/changes/20460 b/changes/20460
new file mode 100644
index 0000000000..9fbb4a7986
--- /dev/null
+++ b/changes/20460
@@ -0,0 +1,4 @@
+ o Minor bugfixes (testing):
+ - Use ECDHE ciphers instead of ECDH in tortls tests. LibreSSL has
+ removed the ECDH ciphers which caused the tests to fail on
+ platforms which use it. Fixes bug 20460; bugfix on 0.2.8.1-alpha.
diff --git a/changes/20492 b/changes/20492
new file mode 100644
index 0000000000..fdcd4d0b4b
--- /dev/null
+++ b/changes/20492
@@ -0,0 +1,4 @@
+ o Minor bugfix (build):
+ - The current Git revision when building from a local repository is now
+ detected correctly when using git worktrees. Fixes bug 20492; bugfix on
+ 0.2.3.9-alpha.
diff --git a/changes/21359 b/changes/21359
new file mode 100644
index 0000000000..cc9b377d52
--- /dev/null
+++ b/changes/21359
@@ -0,0 +1,8 @@
+
+ o Minor features (portability, compilationc)
+ - Support building with recent LibreSSL code that uses opaque
+ structures. Closes ticket 21359.
+ - Autoconf now check to determine if OpenSSL
+ structures are opaque, instead of explicitly checking for
+ OpenSSL version numbers.
+ Part of ticket 21359.
diff --git a/changes/asciidoc-UTC b/changes/asciidoc-UTC
deleted file mode 100644
index 21fbfc1d67..0000000000
--- a/changes/asciidoc-UTC
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (build):
- - When building manual pages, set the timezone to "UTC", so that the
- output is reproducible. Fixes bug 19558; bugfix on 0.2.2.9-alpha.
- Patch from intrigeri.
diff --git a/changes/broken-028-fallbacks b/changes/broken-028-fallbacks
deleted file mode 100644
index 698fd6e37a..0000000000
--- a/changes/broken-028-fallbacks
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor feature (fallback directories):
- - Remove broken fallbacks from the hard-coded fallback directory list.
- Closes ticket 20190; patch by teor.
diff --git a/changes/buf-sentinel b/changes/buf-sentinel
deleted file mode 100644
index 7c5b829c19..0000000000
--- a/changes/buf-sentinel
+++ /dev/null
@@ -1,11 +0,0 @@
- o Major features (security fixes):
-
- - Prevent a class of security bugs caused by treating the contents
- of a buffer chunk as if they were a NUL-terminated string. At
- least one such bug seems to be present in all currently used
- versions of Tor, and would allow an attacker to remotely crash
- most Tor instances, especially those compiled with extra compiler
- hardening. With this defense in place, such bugs can't crash Tor,
- though we should still fix them as they occur. Closes ticket 20384
- (TROVE-2016-10-001).
-
diff --git a/changes/bug15582 b/changes/bug15582
new file mode 100644
index 0000000000..5ea6431cf8
--- /dev/null
+++ b/changes/bug15582
@@ -0,0 +1,4 @@
+ o Minor bugfixes (compilation):
+ - Avoid compiler warnings in the unit tests for running tor_sscanf()
+ with wide string outputs. Fixes bug 15582; bugfix on 0.2.6.2-alpha.
+
diff --git a/changes/bug17150 b/changes/bug17150
deleted file mode 100644
index 686cc34296..0000000000
--- a/changes/bug17150
+++ /dev/null
@@ -1,7 +0,0 @@
- o Minor bugfixes (directory warnings):
- - When fetching extrainfo documents, compare their SHA256 digests
- and Ed25519 signing key certificates
- with the routerinfo that led us to fetch them, rather than
- with the most recent routerinfo. Otherwise we generate many
- spurious warnings about mismatches. Fixes bug 17150; bugfix
- on 0.2.7.2-alpha.
diff --git a/changes/bug17744_redux b/changes/bug17744_redux
deleted file mode 100644
index d61e17fec3..0000000000
--- a/changes/bug17744_redux
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (build):
- - Remove a pair of redundant AM_CONDITIONAL declarations from
- configure.ac. Fixes one final case of bug 17744; bugfix on
- 0.2.8.2-alpha.
-
diff --git a/changes/bug18100 b/changes/bug18100
new file mode 100644
index 0000000000..cd3ba2c977
--- /dev/null
+++ b/changes/bug18100
@@ -0,0 +1,5 @@
+ o Major bugfixes (linux TPROXY support):
+ - Fix a typo that had prevented TPROXY-based transparent proxying from
+ working under Linux. Fixes bug 18100; bugfix on 0.2.6.3-alpha.
+ Patch from "d4fq0fQAgoJ".
+
diff --git a/changes/bug18133 b/changes/bug18133
deleted file mode 100644
index 177d286495..0000000000
--- a/changes/bug18133
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (logging):
- - When we can't generate a signing key because OfflineMasterKey is set,
- do not imply that we should have been able to load it.
- Fixes bug 18133; bugfix on 0.2.7.2-alpha.
diff --git a/changes/bug18286 b/changes/bug18286
deleted file mode 100644
index e398fb004b..0000000000
--- a/changes/bug18286
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor features (build):
- - Tor now builds again with the recent OpenSSL 1.1 development branch
- (tested against 1.1.0-pre4 and 1.1.0-pre5-dev). Closes ticket 18286.
-
-
diff --git a/changes/bug18312 b/changes/bug18312
deleted file mode 100644
index 7dcb3266bf..0000000000
--- a/changes/bug18312
+++ /dev/null
@@ -1,4 +0,0 @@
- o Documentation:
- - Stop recommending use of nicknames to identify relays in our
- MapAddress documentation. Closes ticket 18312.
-
diff --git a/changes/bug18397 b/changes/bug18397
deleted file mode 100644
index 53993da4e6..0000000000
--- a/changes/bug18397
+++ /dev/null
@@ -1,7 +0,0 @@
- o Minor bugfixes (Linux seccomp2 sandbox):
- - Add a few missing syscalls to the seccomp2 sandbox: sysinfo,
- getsockopt(SO_SNDBUF), and setsockopt(SO_SNDBUFFORCE). On
- some systems, these are required for Tor to start with
- "Sandbox 1" enabled.
- Fixes bug 18397; bugfix on 0.2.5.1-alpha. Patch from
- Daniel Pinto.
diff --git a/changes/bug18460 b/changes/bug18460
deleted file mode 100644
index a8c1a19774..0000000000
--- a/changes/bug18460
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (statistics):
- - We now include consensus downloads via IPv6 in our directory-request statistics.
- Fixes bug 18460; bugfix on 0.2.3.14-alpha.
-
diff --git a/changes/bug18481 b/changes/bug18481
deleted file mode 100644
index 6fd882b36b..0000000000
--- a/changes/bug18481
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (client):
- - Turn all TestingClientBootstrap* into non-testing torrc options. This
- changes simply renames them by removing "Testing" in front of them and
- they do not require TestingTorNetwork to be enabled anymore. Fixes
- bug 18481; bugfix on 0.2.8.1-alpha.
diff --git a/changes/bug18616 b/changes/bug18616
deleted file mode 100644
index ec59e846ed..0000000000
--- a/changes/bug18616
+++ /dev/null
@@ -1,14 +0,0 @@
- o Major bugfixes (directory mirrors):
- - Decide whether to advertise begindir support the same way we decide
- whether to advertise our DirPort. These decisions being out of sync
- led to surprising behavior like advertising begindir support when
- our hibernation config options made us not advertise a DirPort.
- Resolves bug 18616; bugfix on 0.2.8.1-alpha. Patch by teor.
-
- o Minor bugfixes:
- - Consider more config options when relays decide whether to regenerate
- their descriptor. Fixes more of bug 12538; bugfix on 0.2.8.1-alpha.
- - Resolve some edge cases where we might launch an ORPort reachability
- check even when DisableNetwork is set. Noticed while fixing bug
- 18616; bugfix on 0.2.3.9-alpha.
-
diff --git a/changes/bug18668 b/changes/bug18668
deleted file mode 100644
index 4b186b5c05..0000000000
--- a/changes/bug18668
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (tests):
- - Avoid "WSANOTINITIALISED" warnings in the unit tests. Fixes bug 18668;
- bugfix on 0.2.8.1-alpha.
diff --git a/changes/bug18673 b/changes/bug18673
deleted file mode 100644
index 5d6161718a..0000000000
--- a/changes/bug18673
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (memory leak):
- - Fix a small memory leak that would occur when the
- TestingEnableCellStatsEvent option was turned on. Fixes bug 18673;
- bugfix on 0.2.5.2-alpha.
diff --git a/changes/bug18686 b/changes/bug18686
deleted file mode 100644
index 23547d211d..0000000000
--- a/changes/bug18686
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (pluggable transports):
- - Avoid reporting a spurious error when we decide that we don't
- need to terminate a pluggable transport because it has already
- exited. Fixes bug 18686; bugfix on 0.2.5.5-alpha.
-
diff --git a/changes/bug18710 b/changes/bug18710
deleted file mode 100644
index 269395563d..0000000000
--- a/changes/bug18710
+++ /dev/null
@@ -1,6 +0,0 @@
- o Major bugfixes (DNS proxy):
- - Stop a crash that could occur when a client running with DNSPort
- received a query with multiple address types, where the first
- address type was not supported. Found and fixed by Scott Dial.
- Fixes bug 18710; bugfix on 0.2.5.4-alpha.
-
diff --git a/changes/bug18716 b/changes/bug18716
deleted file mode 100644
index b15a343f4c..0000000000
--- a/changes/bug18716
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (assert, portability):
- - Fix an assertion failure in memarea.c on systems where "long" is
- shorter than the size of a pointer.
- Fixes bug 18716; bugfix on 0.2.1.1-alpha
diff --git a/changes/bug18728 b/changes/bug18728
deleted file mode 100644
index e181c17e65..0000000000
--- a/changes/bug18728
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (build):
- - Resolve warnings when building on systems that are concerned with
- signed char. Fixes bug 18728; bugfix on 0.2.7.2-alpha and
- 0.2.6.1-alpha.
diff --git a/changes/bug18729 b/changes/bug18729
deleted file mode 100644
index 4ec9ca3254..0000000000
--- a/changes/bug18729
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor features (logging):
- - Stop blasting twelve lines per second from periodic_event_dispatch()
- at loglevel debug. Resolves ticket 18729; fix on 0.2.8.1-alpha.
diff --git a/changes/bug18761 b/changes/bug18761
deleted file mode 100644
index 78500a88ea..0000000000
--- a/changes/bug18761
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor feature (logging):
- - When rejecting a misformed INTRODUCE2 cell, only log at PROTOCOL_WARN
- severity. Closes ticket 18761.
diff --git a/changes/bug18809 b/changes/bug18809
deleted file mode 100644
index 1e151874b7..0000000000
--- a/changes/bug18809
+++ /dev/null
@@ -1,16 +0,0 @@
- o Major bugfixes (bootstrap):
- - Check if bootstrap consensus downloads are still needed
- when the linked connection attaches. This prevents tor
- making unnecessary begindir-style connections, which are
- the only directory connections tor clients make since
- the fix for 18483 was merged.
- - Fix some edge cases where consensus download connections
- may not have been closed, even though they were not needed.
- Related to fix 18809.
- - Make relays retry consensus downloads the correct number of
- times, rather than the more aggressive client retry count.
- Fixes part of ticket 18809.
- - Stop downloading consensuses when we have a consensus,
- even if we don't have all the certificates for it yet.
- Fixes bug 18809; bugfix on 0.2.8.1-alpha.
- Patches by arma and teor.
diff --git a/changes/bug18812 b/changes/bug18812
deleted file mode 100644
index 793e1102f7..0000000000
--- a/changes/bug18812
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (bootstrap):
- - When a fallback changes its fingerprint from the hard-coded
- fingerprint, log a less severe, more explanatory log message.
- Fixes bug 18812; bugfix on 0.2.8.1-alpha. Patch by teor.
diff --git a/changes/bug18816 b/changes/bug18816
deleted file mode 100644
index 103f816962..0000000000
--- a/changes/bug18816
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfix (bootstrap):
- - Consistently use the consensus download schedule for
- authority certificates.
- Fixes bug 18816; bugfix on 0.2.4.13-alpha.
diff --git a/changes/bug18841.1 b/changes/bug18841.1
deleted file mode 100644
index 205ee5a425..0000000000
--- a/changes/bug18841.1
+++ /dev/null
@@ -1,7 +0,0 @@
- o Major bugfixes (compilation):
- - Correctly detect compiler flags on systems where _FORTIFY_SOURCE
- is predefined. Previously, our use of -D_FORTIFY_SOURCE would
- cause a compiler warning, thereby making other checks fail.
- Fixes one case of bug 18841; bugfix on 0.2.3.17-beta. Patch from
- "trudokal".
-
diff --git a/changes/bug18849 b/changes/bug18849
deleted file mode 100644
index b12a8da011..0000000000
--- a/changes/bug18849
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfix (logging):
- - Reduce excessive logging when directories can't be found.
- Fixes bug 18849; bugfix on 0.2.8.3-alpha and 0.2.8.1-alpha.
- Patch by teor.
diff --git a/changes/bug18920 b/changes/bug18920
deleted file mode 100644
index 1babfd6656..0000000000
--- a/changes/bug18920
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (controller, microdescriptors):
- - Make GETINFO dir/status-vote/current/consensus conform to the control
- specification by returning "551 Could not open cached consensus..."
- when not caching consensuses.
- Fixes bug 18920; bugfix on 0.2.2.6-alpha.
diff --git a/changes/bug18921 b/changes/bug18921
deleted file mode 100644
index cdd868a005..0000000000
--- a/changes/bug18921
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (IPv6 bridges):
- - Fix directory address selection for IPv6 bridges.
- Fixes bug 18921; bugfix on 0.2.8.1-alpha.
- Patch by "teor".
diff --git a/changes/bug18929 b/changes/bug18929
deleted file mode 100644
index c607e630a6..0000000000
--- a/changes/bug18929
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (IPv6):
- - Make directory node selection more reliable, mainly for
- IPv6-only clients and clients with few reachable addresses.
- Fixes bug 18929; bugfix on 0.2.8.1-alpha.
- Patch by "teor".
diff --git a/changes/bug18943 b/changes/bug18943
deleted file mode 100644
index 6bcd868460..0000000000
--- a/changes/bug18943
+++ /dev/null
@@ -1,6 +0,0 @@
- o Major bugfixes (crypto, portability):
- - The SHA3 and SHAKE routines now produce the correct output on
- Big Endian systems, unbreaking the unit tests. No code calls
- either algorithm family yet, so this is primarily a build fix.
- Fixes bug 18943; bugfix on 0.2.8.1-alpha.
-
diff --git a/changes/bug18977 b/changes/bug18977
deleted file mode 100644
index 3f46b09fba..0000000000
--- a/changes/bug18977
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (time handling):
- - When correcting a corrupt 'struct tm' value, fill in the tm_wday
- field. Otherwise, our unit tests crash on Windows.
- Fixes bug 18977; bugfix on 0.2.2.25-alpha.
diff --git a/changes/bug19003 b/changes/bug19003
deleted file mode 100644
index ca94938ef9..0000000000
--- a/changes/bug19003
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (small networks):
- - Allow directories in small networks to bootstrap by
- skipping DirPort checks when the consensus has no exits.
- Fixes bug 19003; bugfix on 0.2.8.1-alpha.
- Patch by teor.
diff --git a/changes/bug19008 b/changes/bug19008
deleted file mode 100644
index c51c98faa6..0000000000
--- a/changes/bug19008
+++ /dev/null
@@ -1,3 +0,0 @@
- o Major bugfixes (testing):
- - Fix a bug that would block 'make test-network-all' on systems
- where IPv6 packets were lost. Fixes bug 19008; bugfix on tor-0.2.7.3-rc.
diff --git a/changes/bug19025 b/changes/bug19025
new file mode 100644
index 0000000000..0f365f52ba
--- /dev/null
+++ b/changes/bug19025
@@ -0,0 +1,4 @@
+ o Major bugfixes (DNS):
+ - Fix a bug that prevented exit nodes from caching DNS records for more
+ than 60 seconds.
+ Fixes bug 19025; bugfix on 0.2.4.7-alpha.
diff --git a/changes/bug19032 b/changes/bug19032
deleted file mode 100644
index 93f17c2f91..0000000000
--- a/changes/bug19032
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (security, directory authorities):
- - Fix a crash and out-of-bounds write during authority voting, when the
- list of relays includes duplicate ed25519 identity keys. Fixes bug 19032;
- bugfix on 0.2.8.2-alpha.
diff --git a/changes/bug19161 b/changes/bug19161
deleted file mode 100644
index 78c2165308..0000000000
--- a/changes/bug19161
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (compilation):
- - When libscrypt.h is found, but no libscrypt library can be linked,
- treat libscrypt as absent. Fixes bug 19161; bugfix on 0.2.6.1-alpha.
diff --git a/changes/bug19191 b/changes/bug19191
deleted file mode 100644
index 8670aaa7fd..0000000000
--- a/changes/bug19191
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (downloading):
- - Predict more correctly whether we'll be downloading over HTTP when we
- determine the maximum length of a URL. This should avoid a "BUG"
- warning about the Squid HTTP proxy and its URL limits. Fixes bug 19191;
- bugfix on ?????.
diff --git a/changes/bug19203 b/changes/bug19203
deleted file mode 100644
index 96bc1e855a..0000000000
--- a/changes/bug19203
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (user interface):
- - Correctly give a warning in the cases where a relay is specified by
- nickname, and one such relay is found, but it is not officially Named.
- Fixes bug 19203; bugfix on 0.2.3.1-alpha.
diff --git a/changes/bug19213 b/changes/bug19213
deleted file mode 100644
index 6217814fb4..0000000000
--- a/changes/bug19213
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (compilation):
- - Cause the unit tests to compile correctly on mingw64 versions
- that lack sscanf. Fixes bug 19213; bugfix on 0.2.7.1-alpha.
diff --git a/changes/bug19406 b/changes/bug19406
deleted file mode 100644
index e8b661b512..0000000000
--- a/changes/bug19406
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (build):
- - Tor now again builds with the recent OpenSSL 1.1 development branch
- (tested against 1.1.0-pre5 and 1.1.0-pre6-dev).
-
diff --git a/changes/bug19454 b/changes/bug19454
deleted file mode 100644
index 05650b5c61..0000000000
--- a/changes/bug19454
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (heartbeat):
- - Fix regression that crashes Tor when disabling heartbeats. Fixes bug
- 19454; bugfix on tor-0.2.8.1-alpha. Reported by "kubaku".
diff --git a/changes/bug19464 b/changes/bug19464
deleted file mode 100644
index 22c9e73dc7..0000000000
--- a/changes/bug19464
+++ /dev/null
@@ -1,6 +0,0 @@
- o Minor bugfixes (user interface):
- - Remove a warning message "Service [scrubbed] not found after
- descriptor upload". This message appears when one uses HSPOST control
- command to upload a service descriptor. Since there is only a descriptor
- and no service, showing this message is pointless and confusing.
- Fixes bug 19464; bugfix on 0.2.7.2-alpha.
diff --git a/changes/bug19499 b/changes/bug19499
deleted file mode 100644
index 59bdb29dfa..0000000000
--- a/changes/bug19499
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (build):
- - Tor now again builds with the recent OpenSSL 1.1 development branch
- (tested against 1.1.0-pre6-dev). Closes ticket 19499.
-
diff --git a/changes/bug19556 b/changes/bug19556
deleted file mode 100644
index 31856b3db9..0000000000
--- a/changes/bug19556
+++ /dev/null
@@ -1,7 +0,0 @@
- o Minor bugfixes (sandboxing):
- - When sandboxing is enabled, we could not write any stats to
- disk. check_or_create_data_subdir("stats"), which prepares the
- private stats directory, calls check_private_dir(), which also
- opens and not just stats() the directory. Therefore, we need to
- also allow open() for the stats dir in our sandboxing setup.
- Fixes bug 19556; bugfix on 0.2.5.1-alpha.
diff --git a/changes/bug19557 b/changes/bug19557
deleted file mode 100644
index 55214b0c97..0000000000
--- a/changes/bug19557
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (sandboxing):
- - Our sandboxing code would not allow us to write to stats/hidserv-stats,
- causing tor to abort while trying to write stats. This was previously
- masked by bug 19556. Fixes bug 19557; bugfix on 0.2.6.1-alpha.
diff --git a/changes/bug19608 b/changes/bug19608
deleted file mode 100644
index 66c2de214e..0000000000
--- a/changes/bug19608
+++ /dev/null
@@ -1,6 +0,0 @@
- o Minor bugfixes (IPv6, microdescriptors):
- - Don't check node addresses when we only have a routerstatus.
- This allows IPv6-only clients to bootstrap by fetching
- microdescriptors from fallback directory mirrors.
- (The microdescriptor consensus has no IPv6 addresses in it.)
- Fixes bug 19608; bugfix on c281c036 in 0.2.8.2-alpha.
diff --git a/changes/bug19660 b/changes/bug19660
deleted file mode 100644
index 72d32c8fe2..0000000000
--- a/changes/bug19660
+++ /dev/null
@@ -1,8 +0,0 @@
- o Minor bugfixes (sandboxing):
- - If we did not find a non-private IPaddress by iterating over
- interfaces, we would try to get one via
- get_interface_address6_via_udp_socket_hack(). This opens a
- datagram socket with IPPROTO_UDP. Previously all our datagram
- sockets (via libevent) used IPPROTO_IP, so we did not have that
- in the sandboxing whitelist. Add (SOCK_DGRAM, IPPROTO_UDP)
- sockets to the sandboxing whitelist. Fixes bug 19660.
diff --git a/changes/bug19682 b/changes/bug19682
deleted file mode 100644
index c799c417ac..0000000000
--- a/changes/bug19682
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (compilation):
- - Fix compilation warning in the unit tests on systems where
- char is signed. Fixes bug 19682; bugfix on 0.2.8.1-alpha.
diff --git a/changes/bug19728 b/changes/bug19728
deleted file mode 100644
index 98ba34290f..0000000000
--- a/changes/bug19728
+++ /dev/null
@@ -1,3 +0,0 @@
- o Directory authority changes:
- - The "Tonga" bridge authority has been retired; the new bridge
- authority is "Bifroest". Closes tickets 19728 and 19690.
diff --git a/changes/bug19782 b/changes/bug19782
deleted file mode 100644
index 37660ead73..0000000000
--- a/changes/bug19782
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (fallback directories):
- - Remove a fallback that was on the hardcoded list, then opted-out.
- Fixes bug 19782; update to fallback list from 0.2.8.2-alpha.
diff --git a/changes/bug19869 b/changes/bug19869
new file mode 100644
index 0000000000..430048f161
--- /dev/null
+++ b/changes/bug19869
@@ -0,0 +1,4 @@
+ o Minor bugfixes (DNSPort):
+ - On DNSPort, stop logging a BUG warning on a failed hostname lookup.
+ Fixes bug 19869; bugfix on 0.2.9.1-alpha.
+
diff --git a/changes/bug19903 b/changes/bug19903
deleted file mode 100644
index 33aa8789d7..0000000000
--- a/changes/bug19903
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (compilation):
- - Remove an inappropriate "inline" in tortls.c that was causing warnings
- on older versions of GCC. Fixes bug 19903; 0.2.8.1-alpha.
-
diff --git a/changes/bug19926_029_info b/changes/bug19926_029_info
new file mode 100644
index 0000000000..93fd81b6cb
--- /dev/null
+++ b/changes/bug19926_029_info
@@ -0,0 +1,3 @@
+ o Minor bugfixes (logging):
+ - Downgrade a harmless log message about the pending_entry_connections
+ list from "warn" to "info". Mitigates bug 19926.
diff --git a/changes/bug19947 b/changes/bug19947
deleted file mode 100644
index b9dce8b753..0000000000
--- a/changes/bug19947
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (fallback directories):
- - Avoid logging a NULL string pointer when loading fallback directory information.
- Fixes bug 19947; bugfix on 0.2.4.7-alpha and 0.2.8.1-alpha.
- Report and patch by "rubiate".
diff --git a/changes/bug19960 b/changes/bug19960
new file mode 100644
index 0000000000..5d655859a6
--- /dev/null
+++ b/changes/bug19960
@@ -0,0 +1,4 @@
+ o Minor bugfixes (netbsd, unit tests):
+ - Stop expecting NetBSD unit tests to report success for ipfw;
+ on NetBSD, it's only pf that's supported.
+ Part of a fix for bug 19960; bugfix on 0.2.9.5-alpha.
diff --git a/changes/bug19968 b/changes/bug19968
new file mode 100644
index 0000000000..b285706e70
--- /dev/null
+++ b/changes/bug19968
@@ -0,0 +1,11 @@
+ o Minor bugfixes (relay):
+ - Do not try to parallelize workers more than 16x without the
+ user explicitly configuring us to do so, even if we do detect more than
+ 16 CPU cores. Fixes bug 19968; bugfix on
+ 0.2.3.1-alpha.
+
+
+ o Minor bugfixes (testing):
+ - Avoid a unit test failure on systems with over 16 detectable
+ CPU cores. Fixes bug 19968; bugfix on
+ 0.2.3.1-alpha.
diff --git a/changes/bug19969 b/changes/bug19969
index 0bdd880bb7..c760c6de03 100644
--- a/changes/bug19969
+++ b/changes/bug19969
@@ -1,9 +1,9 @@
- o Major bugfixes (client performance);
+ o Major bugfixes (client performance):
- Clients now respond to new application stream requests when
they arrive, rather than waiting up to one second before starting
to handle them. Fixes part of bug 19969; bugfix on 0.2.8.1-alpha.
- o Major bugfixes (clients on flaky network connections);
+ o Major bugfixes (clients on flaky network connections):
- When Tor leaves standby because of a new application request, open
circuits as needed to serve that request. Previously, we would
potentially wait a very long time. Fixes part of bug 19969; bugfix
diff --git a/changes/bug19973 b/changes/bug19973
deleted file mode 100644
index 7bd5c554f9..0000000000
--- a/changes/bug19973
+++ /dev/null
@@ -1,6 +0,0 @@
- o Major bugfixes (client, security):
- - Only use the ReachableAddresses option to restrict the first hop
- in a path. Previously, it would apply to every hop in the path,
- with a possible degredation in anonymity for anyone using an
- uncommon ReachableAddress setting. Fixes bug 19973; bugfix on
- 0.2.8.2-alpha.
diff --git a/changes/bug20059 b/changes/bug20059
new file mode 100644
index 0000000000..091fab06d1
--- /dev/null
+++ b/changes/bug20059
@@ -0,0 +1,3 @@
+ o Minor bugfixes (relay):
+ - Avoid a double-marked-circuit warning that can happen when we receive
+ DESTROY cells under heavy load. Fixes bug 20059; bugfix on 0.1.0.1-rc.
diff --git a/changes/bug20085 b/changes/bug20085
new file mode 100644
index 0000000000..fd10e7eeeb
--- /dev/null
+++ b/changes/bug20085
@@ -0,0 +1,4 @@
+ o Documentation:
+ - Correct the minimum bandwidth value in torrc.sample, and queue a
+ corresponding change for torrc.minimal. Closes ticket 20085.
+
diff --git a/changes/bug20103 b/changes/bug20103
deleted file mode 100644
index bf0aeec009..0000000000
--- a/changes/bug20103
+++ /dev/null
@@ -1,7 +0,0 @@
- o Major bugfixes (crash):
-
- - Fix a complicated crash bug that could affect Tor clients
- configured to use bridges when replacing a networkstatus consensus
- in which one of their bridges was mentioned. OpenBSD users saw
- more crashes here, but all platforms were potentially affected.
- Fixes bug 20103; bugfix on 0.2.8.2-alpha.
diff --git a/changes/bug20203 b/changes/bug20203
deleted file mode 100644
index 711c91ba85..0000000000
--- a/changes/bug20203
+++ /dev/null
@@ -1,6 +0,0 @@
- o Major bugfixes (relay, OOM handler):
- - Fix a timing-dependent assertion failure that could occur when we
- tried to flush from a circuit after having freed its cells because
- of an out-of-memory condition. Fixes bug 20203; bugfix on
- 0.2.8.1-alpha. Thanks to "cypherpunks" for help diagnosing this
- one.
diff --git a/changes/bug20247 b/changes/bug20247
new file mode 100644
index 0000000000..731cf0046f
--- /dev/null
+++ b/changes/bug20247
@@ -0,0 +1,4 @@
+ o Minor bugfixes (linux seccomp2 sandbox):
+ - Avoid a sandbox failure when trying to re-bind to a socket and mark
+ it as IPv6-only. Fixes bug 20247; bugfix on 0.2.5.1-alpha.
+
diff --git a/changes/bug20306_029 b/changes/bug20306_029
new file mode 100644
index 0000000000..ada2676b2b
--- /dev/null
+++ b/changes/bug20306_029
@@ -0,0 +1,4 @@
+ o Minor bugfixes (fascistfirewall):
+ - Avoid spurious warnings when ReachableAddresses or FascistFirewall
+ is set. Fixes bug 20306; bugfix on 0.2.8.2-alpha.
+
diff --git a/changes/bug20307 b/changes/bug20307
new file mode 100644
index 0000000000..9112c9c78d
--- /dev/null
+++ b/changes/bug20307
@@ -0,0 +1,7 @@
+ o Minor bugfixes (circuit, hidden service)
+ - When closing a circuit, the reason for doing so was assigned from an int
+ value to a uint16_t which is quite a problem for negative values that are
+ our internal reasons (ex: END_CIRC_REASON_IP_NOW_REDUNDANT). On the HS
+ side, this was causing introduction points to be flagged as unusable
+ because the reason wasn't the right one due to the bad conversion.
+ Partially fixes bug 21056 and fixes bug 20307; Bugfix on 0.2.8.1-alpha.
diff --git a/changes/bug20401 b/changes/bug20401
new file mode 100644
index 0000000000..85ab3c7322
--- /dev/null
+++ b/changes/bug20401
@@ -0,0 +1,4 @@
+ o Minor bugfixes (relay):
+ - Avoid a small memory leak when informing worker threads about rotated
+ onion keys. Fixes bug 20401; bugfix on 0.2.6.3-alpha.
+
diff --git a/changes/bug20423 b/changes/bug20423
new file mode 100644
index 0000000000..32bdc3f081
--- /dev/null
+++ b/changes/bug20423
@@ -0,0 +1,6 @@
+ o Major bugfixes:
+ - For relays that don't know their own address, avoid attempting
+ a local hostname resolve for each descriptor we download. Also cut
+ down on the number of "Success: chose address 'x.x.x.x'" log lines.
+ Fixes bugs 20423 and 20610; bugfix on 0.2.8.1-alpha.
+
diff --git a/changes/bug20472 b/changes/bug20472
new file mode 100644
index 0000000000..4d90c39f5b
--- /dev/null
+++ b/changes/bug20472
@@ -0,0 +1,5 @@
+ o Minor bugfixes (circuits):
+ - Remove a BUG warning in circuit_pick_extend_handshake. Instead, assume
+ all nodes support EXTEND2. Use ntor whenever a key is available.
+ Fixes bug 20472; bugfix on 0.2.9.3-alpha.
+
diff --git a/changes/bug20484 b/changes/bug20484
new file mode 100644
index 0000000000..9a0b95cb39
--- /dev/null
+++ b/changes/bug20484
@@ -0,0 +1,5 @@
+ o Minor bugfixes (single onion services):
+ - Start correctly when creating a single onion service in a
+ directory that did not previously exist. Fixes bug 20484; bugfix on
+ 0.2.9.3-alpha.
+
diff --git a/changes/bug20487 b/changes/bug20487
new file mode 100644
index 0000000000..4435f14a95
--- /dev/null
+++ b/changes/bug20487
@@ -0,0 +1,4 @@
+ o Documentation:
+ - Clarify that setting HiddenServiceNonAnonymousMode requires
+ you to also set "SOCKSPort 0". Fixes bug 20487; bugfix on
+ 0.2.9.3-alpha.
diff --git a/changes/bug20509 b/changes/bug20509
new file mode 100644
index 0000000000..a39ca9f60b
--- /dev/null
+++ b/changes/bug20509
@@ -0,0 +1,5 @@
+ o Minor features:
+ - Directory authorities now reject relays running versions
+ 0.2.9.1-alpha through 0.2.9.4-alpha, because those relays
+ suffer from bug 20499 and don't keep their consensus cache
+ up-to-date. Resolves ticket 20509.
diff --git a/changes/bug20529 b/changes/bug20529
new file mode 100644
index 0000000000..276be5b2b6
--- /dev/null
+++ b/changes/bug20529
@@ -0,0 +1,4 @@
+ o Minor bugfixes (hidden services):
+ - When configuring hidden services, check every hidden service directory's
+ permissions. Previously, we only checked the last hidden service.
+ Fixes bug 20529; bugfix on 13942 commit 85bfad1 in 0.2.6.2-alpha.
diff --git a/changes/bug20533 b/changes/bug20533
new file mode 100644
index 0000000000..7d1a456328
--- /dev/null
+++ b/changes/bug20533
@@ -0,0 +1,7 @@
+ o Minor bugfixes (consensus downloads):
+ - If a consensus expires while we are waiting for certificates to download,
+ stop waiting for certificates.
+ - If we stop waiting for certificates less than a minute after we started
+ downloading them, do not consider the certificate download failure a
+ separate failure.
+ Fixes bug 20533; bugfix on commit e0204f21 in 0.2.0.9-alpha.
diff --git a/changes/bug20534 b/changes/bug20534
new file mode 100644
index 0000000000..49db433a01
--- /dev/null
+++ b/changes/bug20534
@@ -0,0 +1,8 @@
+ o Minor bugfixes (directory download scheduling):
+ - Remove the maximum delay on exponential-backoff scheduling.
+ Since we now allow an infinite number of failures (see ticket
+ 20536), we must now allow the time to grow longer on each failure.
+ Fixes part of bug 20534; bugfix on 0.2.9.1-alpha.
+ - Use initial delays and decrements in download scheduling closer to
+ those from 0.2.8. Fixes another part of bug 20534; bugfix on
+ 0.2.9.1-alpha.
diff --git a/changes/bug20536 b/changes/bug20536
new file mode 100644
index 0000000000..9e0dd164bb
--- /dev/null
+++ b/changes/bug20536
@@ -0,0 +1,6 @@
+ o Major bugfixes (download scheduling):
+ - When using an exponential backoff schedule, do not give up on
+ dowloading just because we have failed a bunch of times. Since
+ each delay is longer than the last, retrying indefinitely won't
+ hurt. Fixes bug 20536; bugfix on 0.2.9.1-alpha.
+
diff --git a/changes/bug20551 b/changes/bug20551
index 1e0746b666..b7ec4ca7cc 100644
--- a/changes/bug20551
+++ b/changes/bug20551
@@ -1,3 +1,3 @@
- o Minor bugfixes (compilation);
+ o Minor bugfixes (compilation):
- Fix implicit conversion warnings under OpenSSL 1.1.
Fixes bug 20551; bugfix on 0.2.1.1-alpha.
diff --git a/changes/bug20560 b/changes/bug20560
new file mode 100644
index 0000000000..43d605b296
--- /dev/null
+++ b/changes/bug20560
@@ -0,0 +1,4 @@
+ o Minor bugfixes (portability):
+ - Run correctly when built on Windows build environments that require
+ _vcsprintf(). Fixes bug 20560; bugfix on 0.2.2.11-alpha.
+
diff --git a/changes/bug20587 b/changes/bug20587
new file mode 100644
index 0000000000..341b001363
--- /dev/null
+++ b/changes/bug20587
@@ -0,0 +1,5 @@
+ o Minor bugfixes (download timing):
+ - When determining when to download a directory object, handle times
+ after 2038 if the operating system supports that. (Someday this will be
+ important!) Fixes bug 20587; bugfix on 0.2.8.1-alpha.
+
diff --git a/changes/bug20588 b/changes/bug20588
index be199b2de0..832ef81336 100644
--- a/changes/bug20588
+++ b/changes/bug20588
@@ -1,3 +1,3 @@
- o Minor bugfixes (portability):
+ o Minor features (portability):
- Fix compilation with OpenSSL 1.1 and less commonly-used
CPU architectures. Closes ticket 20588.
diff --git a/changes/bug20591 b/changes/bug20591
new file mode 100644
index 0000000000..deaa738f5e
--- /dev/null
+++ b/changes/bug20591
@@ -0,0 +1,3 @@
+ o Minor bugfixes (relay bootstrap):
+ - Ensure relays don't make multiple connections during bootstrap.
+ Fixes bug 20591; bugfix on 0.2.8.1-alpha.
diff --git a/changes/bug20593 b/changes/bug20593
new file mode 100644
index 0000000000..e9f54d317a
--- /dev/null
+++ b/changes/bug20593
@@ -0,0 +1,6 @@
+ o Minor bugfixes (client directory scheduling):
+ - Treat "relay too busy to answer request" as a failed request and a
+ reason to back off on our retry frequency. This is safe now that
+ exponential backups retry indefinitely, and avoids a bug where we would
+ reset our download schedule erroneously.
+ Fixes bug 20593; bugfix on 0.2.9.1-alpha.
diff --git a/changes/bug20597 b/changes/bug20597
new file mode 100644
index 0000000000..f199b63933
--- /dev/null
+++ b/changes/bug20597
@@ -0,0 +1,5 @@
+ o Minor bugfixes (test networks, exponential backoff):
+ - When using exponential backoff in test networks, use a lower exponent,
+ so the delays do not vary as much. This helps test networks bootstrap
+ consistently. Fixes bug 20597; bugfix on 20499; not in any released
+ version of tor.
diff --git a/changes/bug20613 b/changes/bug20613
new file mode 100644
index 0000000000..19bb61f4e0
--- /dev/null
+++ b/changes/bug20613
@@ -0,0 +1,6 @@
+ o Minor bugfixes (single onion services, Tor2web):
+ - Stop logging long-term one-hop circuits deliberately created by single
+ onion services and Tor2web. These log messages are intended to diagnose
+ issue 8387, which relates to circuits hanging around forever for no
+ reason.
+ Fixes bug 20613; bugfix on 0.2.9.1-alpha. Reported by "pastly".
diff --git a/changes/bug20634 b/changes/bug20634
new file mode 100644
index 0000000000..62fc9f4787
--- /dev/null
+++ b/changes/bug20634
@@ -0,0 +1,3 @@
+ o Minor bugfixes (unit tests):
+ - Stop spurious failures in the local interface address discovery unit
+ tests. Fixes bug 20634; bugfix on 0.2.8.1-alpha; patch by Neel Chauhan.
diff --git a/changes/bug20638 b/changes/bug20638
new file mode 100644
index 0000000000..260d7d0a75
--- /dev/null
+++ b/changes/bug20638
@@ -0,0 +1,5 @@
+ o Minor bugfixes (hidden services):
+ - Stop ignoring hidden service key anonymity when first starting tor.
+ Instead, refuse to start tor if any hidden service key has been used in
+ a different hidden service anonymity mode.
+ Fixes bug 20638; bugfix on 17178 in 0.2.9.3-alpha; reported by ahf.
diff --git a/changes/bug20710_025 b/changes/bug20710_025
new file mode 100644
index 0000000000..12bd07536c
--- /dev/null
+++ b/changes/bug20710_025
@@ -0,0 +1,4 @@
+ o Minor bugfixes (memory leak, use-after-free, linux seccomp2 sandbox):
+ - Fix a memory leak and use-after-free error when removing entries
+ from the sandbox's getaddrinfo() cache. Fixes bug 20710; bugfix on
+ 0.2.5.5-alpha. Patch from "cypherpunks".
diff --git a/changes/bug20715 b/changes/bug20715
new file mode 100644
index 0000000000..737a560cec
--- /dev/null
+++ b/changes/bug20715
@@ -0,0 +1,4 @@
+ o Minor bugfixes (memory leak)
+ - When moving a signed descriptor object from a source to an existing
+ destination, free the allocated memory inside that destination object.
+ Bugfix on tor-0.2.8.3-alpha; Closes #20715.
diff --git a/changes/bug20716 b/changes/bug20716
new file mode 100644
index 0000000000..37fd6feecf
--- /dev/null
+++ b/changes/bug20716
@@ -0,0 +1,3 @@
+ o Minor bugfixes (client, memory leak):
+ - Fix a small memory leak when receiving AF_UNIX connections on
+ a SocksPort. Fixes bug 20716; bugfix on 0.2.6.3-alpha.
diff --git a/changes/bug20810 b/changes/bug20810
new file mode 100644
index 0000000000..5420a73175
--- /dev/null
+++ b/changes/bug20810
@@ -0,0 +1,4 @@
+ o Minor bugfixes (relay)
+ - When computing old Tor protocol line version in protover, we were
+ looking at 0.2.7.5 twice instead of a specific case for 0.2.9.1-alpha.
+ Bugfix on tor-0.2.9.4-alpha.
diff --git a/changes/bug20864 b/changes/bug20864
new file mode 100644
index 0000000000..7b8c70fad6
--- /dev/null
+++ b/changes/bug20864
@@ -0,0 +1,4 @@
+ o Minor bugfixes (unit tests, hidden services):
+ - Remove a double-free in the single onion service unit test. Stop
+ ignoring a return value. Make future changes less error-prone.
+ Fixes bug 20864; bugfix on 0.2.9.6-rc.
diff --git a/changes/bug20865 b/changes/bug20865
deleted file mode 100644
index 575d886a3e..0000000000
--- a/changes/bug20865
+++ /dev/null
@@ -1,7 +0,0 @@
- o Minor bugfixes (portability):
- - Avoid compilation errors when building on OSX Sierra. Sierra began
- to support the getentropy() API, but created a few problems in
- doing so. Tor 0.2.9 has a more thorough set of workarounds; in
- 0.2.8, we are just using the /dev/urandom interface. Fixes
- bug 20865. Bugfix on 0.2.8.1-alpha.
-
diff --git a/changes/bug20875 b/changes/bug20875
new file mode 100644
index 0000000000..6bba2cbc12
--- /dev/null
+++ b/changes/bug20875
@@ -0,0 +1,4 @@
+ o Minor bugfixes (download scheduling)
+ - Resolve a "bug" warning when considering a download schedule whose
+ delay had approached INT_MAX. Fixes 20875; bugfix on 0.2.9.5-alpha.
+
diff --git a/changes/bug20935 b/changes/bug20935
new file mode 100644
index 0000000000..78068c7c06
--- /dev/null
+++ b/changes/bug20935
@@ -0,0 +1,3 @@
+ o Minor bugfixes (portability):
+ - Use the correct spelling of MAC_OS_X_VERSION_10_12 on configure.ac
+ Fixes bug 20935; bugfix on 0.2.9.6-rc.
diff --git a/changes/bug21035 b/changes/bug21035
new file mode 100644
index 0000000000..bbf3340787
--- /dev/null
+++ b/changes/bug21035
@@ -0,0 +1,6 @@
+ o Minor bugfixes (portability):
+ - Avoid crashing when Tor is built using headers that contain
+ CLOCK_MONOTONIC_COARSE, but then tries to run on an older kernel
+ without CLOCK_MONOTONIC_COARSE. Fixes bug 21035; bugfix on
+ 0.2.9.1-alpha.
+
diff --git a/changes/bug21051 b/changes/bug21051
new file mode 100644
index 0000000000..8bb4f80c8e
--- /dev/null
+++ b/changes/bug21051
@@ -0,0 +1,3 @@
+ o Minor bugfixes (compilation):
+ - Fix Libevent detection on platforms without Libevent 1 headers
+ installed. Fixes bug 21051; bugfix on 0.2.9.1-alpha.
diff --git a/changes/bug21108_029 b/changes/bug21108_029
new file mode 100644
index 0000000000..3a3f004fc6
--- /dev/null
+++ b/changes/bug21108_029
@@ -0,0 +1,6 @@
+ o Major bugfixes (directory authority):
+ - During voting, when marking a node as a probable sybil, do not
+ clear its BadExit flag: sybils can still be bad in other ways
+ too. (We still clear the other flags.) Fixes bug 21108; bugfix
+ on 0.2.0.13-alpha.
+
diff --git a/changes/bug21278_extras b/changes/bug21278_extras
new file mode 100644
index 0000000000..ffdf4a047b
--- /dev/null
+++ b/changes/bug21278_extras
@@ -0,0 +1,3 @@
+ o Minor bugfixes (code correctness):
+ - Repair a couple of (unreachable or harmless) cases of the risky
+ comparison-by-subtraction pattern that caused bug 21278.
diff --git a/changes/bug21278_prevention b/changes/bug21278_prevention
new file mode 100644
index 0000000000..e07f0a670c
--- /dev/null
+++ b/changes/bug21278_prevention
@@ -0,0 +1,4 @@
+ o Minor features (directory authority):
+ - Directory authorities now reject descriptors that claim to be
+ malformed versions of Tor. Helps prevent exploitation of bug 21278.
+
diff --git a/changes/bug21280 b/changes/bug21280
new file mode 100644
index 0000000000..e9f0bc174c
--- /dev/null
+++ b/changes/bug21280
@@ -0,0 +1,5 @@
+ o Minor bugfixes (tor-resolve):
+ - The tor-resolve command line tool now rejects hostnames over 255
+ characters in length. Previously, it would silently truncate
+ them, which could lead to bugs. Fixes bug 21280; bugfix on 0.0.9pre5.
+ Patch by "junglefowl".
diff --git a/changes/bug21357 b/changes/bug21357
new file mode 100644
index 0000000000..a1cb43a78a
--- /dev/null
+++ b/changes/bug21357
@@ -0,0 +1,7 @@
+ o Major bugfixes (IPv6 Exits):
+ - Stop rejecting all IPv6 traffic on Exits whose exit policy rejects IPv6
+ addresses. Instead, only reject a port over IPv6 if the exit policy
+ rejects that port on more than an IPv6 /16 of addresses. This bug was
+ made worse by 17027 in 0.2.8.1-alpha, which rejects a relay's own IPv6
+ address by default.
+ Fixes bug 21357; bugfix on commit 004f3f4e53 in 0.2.4.7-alpha.
diff --git a/changes/bug21394 b/changes/bug21394
new file mode 100644
index 0000000000..e5452e20ba
--- /dev/null
+++ b/changes/bug21394
@@ -0,0 +1,9 @@
+ o Major bugfixes (Exit nodes):
+ - Fix an issue causing high-bandwidth exit nodes to fail a majority
+ or all of their DNS requests, making them basically unsuitable for
+ regular usage in Tor circuits. The problem is related to
+ libevent's DNS handling, but we can work around it in Tor. Fixes
+ bugs 21394 and 18580; bugfix on 0.1.2.2-alpha which introduced
+ eventdns. Credit goes to Dhalgren for identifying and finding a
+ workaround to this bug and to gamambel, arthuredelstein and
+ arma in helping to track it down and analyze it.
diff --git a/changes/bug21450 b/changes/bug21450
new file mode 100644
index 0000000000..a1cf89ab41
--- /dev/null
+++ b/changes/bug21450
@@ -0,0 +1,4 @@
+ o Minor bugfixes (voting consistency):
+ - Reject version numbers with components that exceed INT32_MAX.
+ Otherwise 32-bit and 64-bit platforms would behave inconsistently.
+ Fixes bug 21450; bugfix on 0.0.8pre1.
diff --git a/changes/bug21507 b/changes/bug21507
new file mode 100644
index 0000000000..f83e291b63
--- /dev/null
+++ b/changes/bug21507
@@ -0,0 +1,5 @@
+ o Minor bugfixes (voting consistency):
+ - Reject version numbers with non-numeric prefixes (such as +, -, and
+ whitespace). Disallowing whitespace prevents differential version
+ parsing between POSIX-based and Windows platforms.
+ Fixes bug 21507 and part of 21508; bugfix on 0.0.8pre1.
diff --git a/changes/bug21576 b/changes/bug21576
new file mode 100644
index 0000000000..68d8471192
--- /dev/null
+++ b/changes/bug21576
@@ -0,0 +1,4 @@
+ o Major bugfixes (crash, directory connections):
+ - Fix a rare crash when sending a begin cell on a circuit whose linked
+ directory connection has already been closed. Fixes bug 21576;
+ bugfix on Tor 0.2.9.3-alpha. Reported by alecmuffett.
diff --git a/changes/bug21943 b/changes/bug21943
new file mode 100644
index 0000000000..dbe2c726d9
--- /dev/null
+++ b/changes/bug21943
@@ -0,0 +1,6 @@
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - The getpid() system call is now permitted under the Linux seccomp2
+ sandbox, to avoid crashing with versions of OpenSSL (and other
+ libraries) that attempt to learn the process's PID by using the
+ syscall rather than the VDSO code. Fixes bug 21943; bugfix on
+ 0.2.5.1-alpha.
diff --git a/changes/bug22034 b/changes/bug22034
new file mode 100644
index 0000000000..6d9e188740
--- /dev/null
+++ b/changes/bug22034
@@ -0,0 +1,4 @@
+ o Minor bugfixes (control port, regression):
+ - The GETINFO extra-info/digest/<digest> command was broken because of a
+ wrong base16 decode return value check. In was introduced in a refactor
+ of that API. Fixex bug #22034; bugfix on tor-0.2.9.1-alpha.
diff --git a/changes/bug22245 b/changes/bug22245
new file mode 100644
index 0000000000..6ae18593ea
--- /dev/null
+++ b/changes/bug22245
@@ -0,0 +1,5 @@
+ o Minor bugfixes (bandwidth accounting):
+ - Roll over monthly accounting at the configured hour and minute,
+ rather than always at 00:00.
+ Fixes bug 22245; bugfix on 0.0.9rc1.
+ Found by Andrey Karpov with PVS-Studio.
diff --git a/changes/bug22349 b/changes/bug22349
new file mode 100644
index 0000000000..bb43404bfe
--- /dev/null
+++ b/changes/bug22349
@@ -0,0 +1,9 @@
+ o Minor bugfixes (directory authority):
+ - When a directory authority rejects a descriptor or extrainfo with
+ a given digest, mark that digest as undownloadable, so that we
+ do not attempt to download it again over and over. We previously
+ tried to avoid downloading such descriptors by other means, but
+ we didn't notice if we accidentally downloaded one anyway. This
+ behavior became problematic in 0.2.7.2-alpha, when authorities
+ began pinning Ed25519 keys. Fixes ticket
+ 22349; bugfix on 0.2.1.19-alpha.
diff --git a/changes/bug22370 b/changes/bug22370
new file mode 100644
index 0000000000..e0e87e3339
--- /dev/null
+++ b/changes/bug22370
@@ -0,0 +1,4 @@
+ o Minor bugfixes (memory handling):
+ - When directory authorities reject a router descriptor due to keypinning,
+ free the router descriptor rather than leaking the memory.
+ Fixes bug 22370; bugfix on 0.2.7.2-alpha.
diff --git a/changes/bug22446 b/changes/bug22446
new file mode 100644
index 0000000000..eab65aac00
--- /dev/null
+++ b/changes/bug22446
@@ -0,0 +1,4 @@
+ o Minor features (code style, backport from 0.3.1.3-alpha):
+ - Add "Falls through" comments to our codebase, in order to silence
+ GCC 7's -Wimplicit-fallthrough warnings. Patch from Andreas
+ Stieger. Closes ticket 22446.
diff --git a/changes/bug22460_case2 b/changes/bug22460_case2
new file mode 100644
index 0000000000..0a11759832
--- /dev/null
+++ b/changes/bug22460_case2
@@ -0,0 +1,8 @@
+ o Major bugfixes (relay, link handshake):
+
+ - When performing the v3 link handshake on a TLS connection, report that
+ we have the x509 certificate that we actually used on that connection,
+ even if we have changed certificates since that connection was first
+ opened. Previously, we would claim to have used our most recent x509
+ link certificate, which would sometimes make the link handshake fail.
+ Fixes one case of bug 22460; bugfix on 0.2.3.6-alpha.
diff --git a/changes/bug22516 b/changes/bug22516
new file mode 100644
index 0000000000..f024a3c470
--- /dev/null
+++ b/changes/bug22516
@@ -0,0 +1,5 @@
+ o Minor bugfixes (linux seccomp2 sandbox):
+ - Permit the fchmod system call, to avoid crashing on startup when
+ starting with the seccomp2 sandbox and an unexpected set of permissions
+ on the data directory or its contents. Fixes bug 22516; bugfix on
+ 0.2.5.4-alpha.
diff --git a/changes/bug22644 b/changes/bug22644
new file mode 100644
index 0000000000..9b8742edaf
--- /dev/null
+++ b/changes/bug22644
@@ -0,0 +1,5 @@
+ o Minor bugfixes (controller):
+ - Do not crash when receiving a POSTDESCRIPTOR command with an
+ empty body. Fixes part of bug 22644; bugfix on 0.2.0.1-alpha.
+ - Do not crash when receiving a HSPOST command with an empty body.
+ Fixes part of bug 22644; bugfix on 0.2.7.1-alpha.
diff --git a/changes/bug22797 b/changes/bug22797
new file mode 100644
index 0000000000..619baaa409
--- /dev/null
+++ b/changes/bug22797
@@ -0,0 +1,4 @@
+ o Minor bugfixes (file limits):
+ - When setting the maximum number of connections allowed by the OS,
+ always allow some extra file descriptors for other files.
+ Fixes bug 22797; bugfix on 0.2.0.10-alpha.
diff --git a/changes/bug22801 b/changes/bug22801
new file mode 100644
index 0000000000..7edc79bc84
--- /dev/null
+++ b/changes/bug22801
@@ -0,0 +1,5 @@
+ o Minor bugfixes (compilation):
+ - When building with certain versions the mingw C header files, avoid
+ float-conversion warnings when calling the C functions isfinite(),
+ isnan(), and signbit(). Fixes bug 22801; bugfix on 0.2.8.1-alpha.
+
diff --git a/changes/bug22915 b/changes/bug22915
new file mode 100644
index 0000000000..17a9c6018f
--- /dev/null
+++ b/changes/bug22915
@@ -0,0 +1,3 @@
+ o Minor bugfixes (compilation warnings):
+ - Suppress -Wdouble-promotion warnings with clang 4.0. Fixes bug 22915;
+ bugfix on 0.2.8.1-alpha.
diff --git a/changes/bug22916_027 b/changes/bug22916_027
new file mode 100644
index 0000000000..5cf99c7d15
--- /dev/null
+++ b/changes/bug22916_027
@@ -0,0 +1,3 @@
+ o Minor bugfixes (Compilation):
+ - Fix warnings when building with libscrypt and openssl scrypt support
+ on Clang. Fixes bug 22916; bugfix on 0.2.7.2-alpha.
diff --git a/changes/bug23030_029 b/changes/bug23030_029
new file mode 100644
index 0000000000..89a1b507d7
--- /dev/null
+++ b/changes/bug23030_029
@@ -0,0 +1,7 @@
+ o Minor bugfixes (coverity builds):
+ - Avoid Coverity build warnings related to our BUG() macro. By
+ default, Coverity treats BUG() as the Linux kernel does: an
+ instant abort(). We need to override that so our BUG() macro
+ doesn't prevent Coverity from analyzing functions that use it.
+ Fixes bug 23030; bugfix on 0.2.9.1-alpha.
+
diff --git a/changes/bug23081 b/changes/bug23081
new file mode 100644
index 0000000000..76c4e30971
--- /dev/null
+++ b/changes/bug23081
@@ -0,0 +1,8 @@
+ o Minor bugfixes (Windows service):
+ - When running as a Windows service, set the ID of the main thread
+ correctly. Failure to do so made us fail to send log messages
+ to the controller in 0.2.1.16-rc, slowed down controller
+ event delivery in 0.2.7.3-rc and later, and crash with an assertion
+ failure in 0.3.1.1-alpha. Fixes bug 23081; bugfix on 0.2.1.6-alpha.
+ Patch and diagnosis from "Vort".
+
diff --git a/changes/bug23470 b/changes/bug23470
new file mode 100644
index 0000000000..33367b3a30
--- /dev/null
+++ b/changes/bug23470
@@ -0,0 +1,6 @@
+ o Minor bugfix (relay address resolution):
+ - Avoid unnecessary calls to directory_fetches_from_authorities()
+ on relays. This avoids spurious address resolutions and
+ descriptor rebuilds. This is a mitigation for 21789. The original
+ bug was introduced in commit 35bbf2e as part of prop210.
+ Fixes 23470 in 0.2.8.1-alpha.
diff --git a/changes/bug23693 b/changes/bug23693
new file mode 100644
index 0000000000..796398be51
--- /dev/null
+++ b/changes/bug23693
@@ -0,0 +1,6 @@
+ o Minor bugfixes (relay, crash):
+ - Avoid a crash when transitioning from client mode to bridge mode.
+ Previously, we would launch the worker threads whenever our "public
+ server" mode changed, but not when our "server" mode changed.
+ Fixes bug 23693; bugfix on 0.2.6.3-alpha.
+
diff --git a/changes/bug23874 b/changes/bug23874
new file mode 100644
index 0000000000..bf6620553d
--- /dev/null
+++ b/changes/bug23874
@@ -0,0 +1,3 @@
+ o Minor bugfixes (memory safety):
+ - Clear the address when node_get_prim_orport() returns early.
+ Fixes bug 23874; bugfix on 0.2.8.2-alpha.
diff --git a/changes/bug23985 b/changes/bug23985
new file mode 100644
index 0000000000..9cb5937962
--- /dev/null
+++ b/changes/bug23985
@@ -0,0 +1,9 @@
+ o Minor bugfixes (bootstrapping):
+ - Fetch descriptors aggressively whenever we lack enough
+ to build circuits, regardless of how many descriptors we are missing.
+ Previously, we would delay launching the fetch when we had fewer than
+ 15 missing descriptors, even if some of those descriptors were
+ blocking circuits from building. Fixes bug 23985; bugfix on
+ 0.1.1.11-alpha. The effects of this bug became worse in 0.3.0.3-alpha,
+ when we began treating missing descriptors from our primary guards
+ as a reason to delay circuits.
diff --git a/changes/bug8185_025 b/changes/bug8185_025
new file mode 100644
index 0000000000..1bfc12b1e4
--- /dev/null
+++ b/changes/bug8185_025
@@ -0,0 +1,6 @@
+ o Minor bugfixes (logging, relay shutdown, annoyance):
+ - When a circuit is marked for close, do not attempt to package any cells
+ for channels on that circuit. Previously, we would detect this
+ condition lower in the call stack, when we noticed that the circuit had
+ no attached channel, and log an annoying message. Fixes bug 8185;
+ bugfix on 0.2.5.4-alpha.
diff --git a/changes/doc17621 b/changes/doc17621
deleted file mode 100644
index ab37d29b50..0000000000
--- a/changes/doc17621
+++ /dev/null
@@ -1,3 +0,0 @@
- o Documentation:
- - Document the contents of the 'datadir/keys' subdirectory in the manual
- page. Closes ticket 17621.
diff --git a/changes/fallbacks-201604 b/changes/fallbacks-201604
deleted file mode 100644
index 7acefaaf08..0000000000
--- a/changes/fallbacks-201604
+++ /dev/null
@@ -1,9 +0,0 @@
- o Minor features (fallback directory mirrors):
- - Give each fallback the same weight for client selection;
- restrict fallbacks to one per operator;
- report fallback directory detail changes when rebuilding list;
- add new fallback directory mirrors to the whitelist;
- update fallback directories based on the latest OnionOO data;
- and any other minor simplifications and fixes.
- Closes tasks 17158, 17905, 18749, bug 18689, and fixes part of
- bug 18812 on 0.2.8.1-alpha; patch by "teor".
diff --git a/changes/feature18483 b/changes/feature18483
deleted file mode 100644
index d0fa8df58d..0000000000
--- a/changes/feature18483
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (clients):
- - Make clients, onion services, and bridge relays always
- use an encrypted begindir connection for directory requests.
- Resolves ticket 18483. Patch by "teor".
diff --git a/changes/geoip-april2016 b/changes/geoip-april2016
deleted file mode 100644
index c55aa179b5..0000000000
--- a/changes/geoip-april2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (geoip):
- - Update geoip and geoip6 to the April 5 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-august2016 b/changes/geoip-august2016
deleted file mode 100644
index 370ab64cac..0000000000
--- a/changes/geoip-august2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features:
- - Update geoip and geoip6 to the August 2 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-january2017 b/changes/geoip-january2017
index de1a4cbe2a..77bc9a5991 100644
--- a/changes/geoip-january2017
+++ b/changes/geoip-january2017
@@ -1,4 +1,4 @@
- o Minor features:
+ o Minor features (geoip):
- Update geoip and geoip6 to the January 4 2017 Maxmind GeoLite2
Country database.
diff --git a/changes/geoip-july2016 b/changes/geoip-july2016
deleted file mode 100644
index d9963bd6a8..0000000000
--- a/changes/geoip-july2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features:
- - Update geoip and geoip6 to the July 6 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-jun2016 b/changes/geoip-jun2016
deleted file mode 100644
index 6c9847ca58..0000000000
--- a/changes/geoip-jun2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (geoip):
- - Update geoip and geoip6 to the June 7 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-may2016 b/changes/geoip-may2016
deleted file mode 100644
index cf78ab10c7..0000000000
--- a/changes/geoip-may2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (geoip):
- - Update geoip and geoip6 to the May 4 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-november2016 b/changes/geoip-november2016
index 5190ed66f4..b3f9913bb1 100644
--- a/changes/geoip-november2016
+++ b/changes/geoip-november2016
@@ -1,4 +1,4 @@
- o Minor features:
+ o Minor features (ge0oip):
- Update geoip and geoip6 to the November 3 2016 Maxmind GeoLite2
Country database.
diff --git a/changes/geoip-october2016 b/changes/geoip-october2016
deleted file mode 100644
index fff9a1eeb5..0000000000
--- a/changes/geoip-october2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features:
- - Update geoip and geoip6 to the October 4 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/geoip-september2016 b/changes/geoip-september2016
deleted file mode 100644
index 1bf5570f2d..0000000000
--- a/changes/geoip-september2016
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (geoip):
- - Update geoip and geoip6 to the September 6 2016 Maxmind GeoLite2
- Country database.
-
diff --git a/changes/memarea_overflow b/changes/memarea_overflow
deleted file mode 100644
index 8fdc38cc09..0000000000
--- a/changes/memarea_overflow
+++ /dev/null
@@ -1,7 +0,0 @@
- o Minor bugfixes (pointer arithmetic):
- - Fix a bug in memarea_alloc() that could have resulted in remote heap
- write access, if Tor had ever passed an unchecked size to
- memarea_alloc(). Fortunately, all the sizes we pass to memarea_alloc()
- are pre-checked to be less than 128 kilobytes. Fixes bug 19150; bugfix
- on 0.2.1.1-alpha. Bug found by Guido Vranken.
-
diff --git a/changes/more_module_docs b/changes/more_module_docs
new file mode 100644
index 0000000000..0066ddfcf0
--- /dev/null
+++ b/changes/more_module_docs
@@ -0,0 +1,4 @@
+ o Documentation:
+ - Module-level documentation for several more modules. Closes tickets
+ 19287 and
+ 19290.
diff --git a/changes/prop275-minimal b/changes/prop275-minimal
new file mode 100644
index 0000000000..83d42f850b
--- /dev/null
+++ b/changes/prop275-minimal
@@ -0,0 +1,9 @@
+ o Minor features (future-proofing):
+
+ - Tor no longer refuses to download microdescriptors or descriptors if
+ they are listed as "published in the future". This change will
+ eventually allow us to stop listing meaningful "published" dates
+ in microdescriptor consensuses, and thereby allow us to reduce the
+ resources required to download consensus diffs by over 50%.
+ Implements part of ticket 21642; implements part of proposal 275.
+
diff --git a/changes/rsa_init_bug b/changes/rsa_init_bug
deleted file mode 100644
index 6b5fb4f2f9..0000000000
--- a/changes/rsa_init_bug
+++ /dev/null
@@ -1,7 +0,0 @@
- o Major bugfixes (key management):
- - If OpenSSL fails to generate an RSA key, do not retain a dangling pointer
- to the previous (uninitialized) key value. The impact here should be
- limited to a difficult-to-trigger crash, if OpenSSL is running an
- engine that makes key generation failures possible, or if OpenSSL runs
- out of memory. Fixes bug 19152; bugfix on 0.2.1.10-alpha. Found by
- Yuan Jochen Kang, Suman Jana, and Baishakhi Ray.
diff --git a/changes/ticket19071-19480 b/changes/ticket19071-19480
deleted file mode 100644
index ab5c72a2d1..0000000000
--- a/changes/ticket19071-19480
+++ /dev/null
@@ -1,13 +0,0 @@
- o Minor bugfixes (fallback directory selection):
- - Avoid errors during fallback selection if there are no eligible
- fallbacks. Fixes bug 19480; bugfix on ba76910 and 78ec782 in
- 0.2.8.3-alpha. Patch by teor.
- o Minor features (fallback directory list):
- - Update hard-coded fallback list to remove unsuitable fallbacks.
- Resolves ticket 19071. Patch by teor.
- - Add a comment to the generated list that explains how to comment-out
- unsuitable fallbacks in a way that's compatible with the stem fallback
- parser.
- - Update fallback whitelist and blacklist based on relay operator
- emails. Blacklist unsuitable fallbacks. Resolves ticket 19071.
- Patch by teor.
diff --git a/changes/ticket19769 b/changes/ticket19769
new file mode 100644
index 0000000000..9fc05c3e9e
--- /dev/null
+++ b/changes/ticket19769
@@ -0,0 +1,7 @@
+ o Major features (security):
+ - Change the algorithm used to decide DNS TTLs on client and server side,
+ to better resist DNS-based correlation attacks like the DefecTor attack
+ of Greschbach, Pulls, Roberts, Winter, and Feamster). Now
+ relays only return one of two possible DNS TTL values, and clients
+ are willing to believe DNS TTL values up to 3 hours long.
+ Closes ticket 19769.
diff --git a/changes/ticket21953 b/changes/ticket21953
new file mode 100644
index 0000000000..7cc84f506d
--- /dev/null
+++ b/changes/ticket21953
@@ -0,0 +1,6 @@
+ o Minor features:
+ - Enable a couple of pieces of Windows hardening: one
+ (HeapEnableTerminationOnCorruption) that has been on-by-default since
+ Windows 8, and unavailable before Windows 7, and one
+ (PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION) which we believe doesn't
+ affect us, but shouldn't do any harm. Closes ticket 21953.
diff --git a/changes/ticket22895 b/changes/ticket22895
new file mode 100644
index 0000000000..a3f7b86019
--- /dev/null
+++ b/changes/ticket22895
@@ -0,0 +1,3 @@
+ o Minor bugfixes (compilation):
+ - Fix unused variable warnings in donna's Curve25519 SSE2 code.
+ Fixes bug 22895; bugfix on 0.2.7.2-alpha.
diff --git a/changes/trove-2017-001 b/changes/trove-2017-001
new file mode 100644
index 0000000000..5187e6d5f1
--- /dev/null
+++ b/changes/trove-2017-001
@@ -0,0 +1,8 @@
+ o Major bugfixes (security):
+ - Downgrade the "-ftrapv" option from "always on" to "only on when
+ --enable-expensive-hardening is provided." This hardening option, like
+ others, can turn survivable bugs into crashes--and having it on by
+ default made a (relatively harmless) integer overflow bug into a
+ denial-of-service bug. Fixes bug 21278 (TROVE-2017-001); bugfix on
+ 0.2.9.1-alpha.
+
diff --git a/changes/trove-2017-010 b/changes/trove-2017-010
new file mode 100644
index 0000000000..d5bf9333da
--- /dev/null
+++ b/changes/trove-2017-010
@@ -0,0 +1,6 @@
+ o Major bugfixes (security):
+ - Fix a denial-of-service issue where an attacker could crash
+ a directory authority using a malformed router descriptor.
+ Fixes bug 24245; bugfix on 0.2.9.4-alpha. Also tracked
+ as TROVE-2017-010 and CVE-2017-8820.
+
diff --git a/configure.ac b/configure.ac
index 18bad71a4c..17e6fca0d6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,7 +4,7 @@ dnl Copyright (c) 2007-2015, The Tor Project, Inc.
dnl See LICENSE for licensing information
AC_PREREQ([2.63])
-AC_INIT([tor],[0.2.8.17-dev])
+AC_INIT([tor],[0.2.9.14-dev])
AC_CONFIG_SRCDIR([src/or/main.c])
AC_CONFIG_MACRO_DIR([m4])
@@ -16,6 +16,7 @@ AM_INIT_AUTOMAKE([foreign 1.11 subdir-objects])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_CONFIG_HEADERS([orconfig.h])
+AC_USE_SYSTEM_EXTENSIONS
AC_CANONICAL_HOST
PKG_PROG_PKG_CONFIG
@@ -30,11 +31,8 @@ fi
# the += operator on it in src/or/Makefile.am
CPPFLAGS="$CPPFLAGS -I\${top_srcdir}/src/common"
-#XXXX020 We should make these enabled or not, before 0.2.0.x-final
AC_ARG_ENABLE(openbsd-malloc,
AS_HELP_STRING(--enable-openbsd-malloc, [use malloc code from OpenBSD. Linux only]))
-AC_ARG_ENABLE(instrument-downloads,
- AS_HELP_STRING(--enable-instrument-downloads, [instrument downloads of directory resources etc.]))
AC_ARG_ENABLE(static-openssl,
AS_HELP_STRING(--enable-static-openssl, [link against a static openssl library. Requires --with-openssl-dir]))
AC_ARG_ENABLE(static-libevent,
@@ -73,18 +71,6 @@ if test "$enable_system_torrc" = "no"; then
fi
AM_CONDITIONAL(USE_OPENBSD_MALLOC, test "x$enable_openbsd_malloc" = "xyes")
-if test "x$enable_instrument_downloads" = "xyes"; then
- AC_DEFINE(INSTRUMENT_DOWNLOADS, 1,
- [Defined if we want to keep track of how much of each kind of resource we download.])
-fi
-
-AC_ARG_ENABLE(transparent,
- AS_HELP_STRING(--disable-transparent, [disable transparent proxy support]),
- [case "${enableval}" in
- "yes") transparent=true ;;
- "no") transparent=false ;;
- *) AC_MSG_ERROR(bad value for --enable-transparent) ;;
- esac], [transparent=true])
AC_ARG_ENABLE(asciidoc,
AS_HELP_STRING(--disable-asciidoc, [don't use asciidoc (disables building of manpages)]),
@@ -143,9 +129,11 @@ case "$host" in
esac
AC_ARG_ENABLE(gcc-warnings,
- AS_HELP_STRING(--enable-gcc-warnings, [enable verbose warnings]))
+ AS_HELP_STRING(--enable-gcc-warnings, [deprecated alias for enable-fatal-warnings]))
+AC_ARG_ENABLE(fatal-warnings,
+ AS_HELP_STRING(--enable-fatal-warnings, [tell the compiler to treat all warnings as errors.]))
AC_ARG_ENABLE(gcc-warnings-advisory,
- AS_HELP_STRING(--enable-gcc-warnings-advisory, [enable verbose warnings, excluding -Werror]))
+ AS_HELP_STRING(--disable-gcc-warnings-advisory, [disable the regular verbose warnings]))
dnl Others suggest '/gs /safeseh /nxcompat /dynamicbase' for non-gcc on Windows
AC_ARG_ENABLE(gcc-hardening,
@@ -173,9 +161,6 @@ AC_ARG_ENABLE(tor2web-mode,
CFLAGS="$CFLAGS -D ENABLE_TOR2WEB_MODE=1"
fi])
-AC_ARG_ENABLE(bufferevents,
- AS_HELP_STRING(--enable-bufferevents, [use Libevent's buffered IO]))
-
AC_ARG_ENABLE(tool-name-check,
AS_HELP_STRING(--disable-tool-name-check, [check for sanely named toolchain when cross-compiling]))
@@ -430,6 +415,7 @@ AC_CHECK_FUNCS(
strtoull \
sysconf \
sysctl \
+ truncate \
uname \
usleep \
vasprintf \
@@ -444,11 +430,11 @@ AC_MSG_CHECKING([for a pre-Sierra OSX build target])
AC_TRY_COMPILE([
#ifdef __APPLE__
# include <AvailabilityMacros.h>
-# ifndef MAC_OS_VERSION_10_12
-# define MAC_OS_VERSION_10_12 101200
+# ifndef MAC_OS_X_VERSION_10_12
+# define MAC_OS_X_VERSION_10_12 101200
# endif
# if defined(MAC_OS_X_VERSION_MIN_REQUIRED)
-# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_10_12
+# if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
# error "Running on Mac OSX 10.11 or earlier"
# endif
# endif
@@ -518,40 +504,30 @@ TOR_SEARCH_LIBRARY(libevent, $trylibeventdir, [-levent $STATIC_LIBEVENT_FLAGS $T
#endif
#include <sys/time.h>
#include <sys/types.h>
-#include <event.h>], [
+#include <event2/event.h>], [
#ifdef _WIN32
#include <winsock2.h>
#endif
-void *event_init(void);],
+struct event_base;
+struct event_base *event_base_new(void);],
[
#ifdef _WIN32
{WSADATA d; WSAStartup(0x101,&d); }
#endif
-event_init();
+event_base_new();
], [--with-libevent-dir], [/opt/libevent])
-dnl Now check for particular libevent functions.
+dnl Determine the incantation needed to link libevent.
save_LIBS="$LIBS"
save_LDFLAGS="$LDFLAGS"
save_CPPFLAGS="$CPPFLAGS"
-LIBS="-levent $STATIC_LIBEVENT_FLAGS $TOR_LIB_WS32 $LIBS"
+
+LIBS="$STATIC_LIBEVENT_FLAGS $TOR_LIB_WS32 $save_LIBS"
LDFLAGS="$TOR_LDFLAGS_libevent $LDFLAGS"
CPPFLAGS="$TOR_CPPFLAGS_libevent $CPPFLAGS"
-AC_CHECK_FUNCS([event_get_version_number \
- evutil_secure_rng_set_urandom_device_file \
- evutil_secure_rng_init \
- ])
-AC_CHECK_MEMBERS([struct event.min_heap_idx], , ,
-[#include <event.h>
-])
AC_CHECK_HEADERS(event2/event.h event2/dns.h event2/bufferevent_ssl.h)
-LIBS="$STATIC_LIBEVENT_FLAGS $TOR_LIB_WS32 $save_LIBS"
-
-AM_CONDITIONAL(USE_EXTERNAL_EVDNS,
- test "x$ac_cv_header_event2_dns_h" = "xyes")
-
if test "$enable_static_libevent" = "yes"; then
if test "$tor_cv_library_libevent_dir" = "(system)"; then
AC_MSG_ERROR("You must specify an explicit --with-libevent-dir=x option when using --enable-static-libevent")
@@ -570,67 +546,39 @@ else
TOR_LIBEVENT_LIBS="$ac_cv_search_evdns_base_new $TOR_LIBEVENT_LIBS"
fi
else
- TOR_LIBEVENT_LIBS="-levent"
+ AC_MSG_ERROR("libevent2 is required but the headers could not be found")
fi
fi
+dnl Now check for particular libevent functions.
+AC_CHECK_FUNCS([evutil_secure_rng_set_urandom_device_file \
+ evutil_secure_rng_add_bytes \
+])
+
LIBS="$save_LIBS"
LDFLAGS="$save_LDFLAGS"
CPPFLAGS="$save_CPPFLAGS"
-dnl This isn't the best test for Libevent 2.0.3-alpha. Once it's released,
-dnl we can do much better.
-if test "$enable_bufferevents" = "yes"; then
- if test "$ac_cv_header_event2_bufferevent_ssl_h" != "yes"; then
- AC_MSG_ERROR([You've asked for bufferevent support, but you're using a version of Libevent without SSL support. This won't work. We need Libevent 2.0.8-rc or later, and you don't seem to even have Libevent 2.0.3-alpha.])
- else
-
- CPPFLAGS="$CPPFLAGS $TOR_CPPFLAGS_libevent"
-
- # Check for the right version. First see if version detection works.
- AC_MSG_CHECKING([whether we can detect the Libevent version])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([
-#include <event2/event.h>
-#if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 10
-#error
-int x = y(zz);
-#else
-int x = 1;
-#endif
- ])], [event_version_number_works=yes; AC_MSG_RESULT([yes]) ],
- [event_version_number_works=no; AC_MSG_RESULT([no])])
- if test "$event_version_number_works" != "yes"; then
- AC_MSG_WARN([Version detection on Libevent seems broken. Your Libevent installation is probably screwed up or very old.])
- else
- AC_MSG_CHECKING([whether Libevent is new enough for bufferevents])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+dnl Check that libevent is at least at version 2.0.10, the first stable
+dnl release of its series
+CPPFLAGS="$CPPFLAGS $TOR_CPPFLAGS_libevent"
+AC_MSG_CHECKING([whether Libevent is new enough])
+AC_COMPILE_IFELSE([AC_LANG_SOURCE([
#include <event2/event.h>
-#if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000d00
+#if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000a00
#error
int x = y(zz);
#else
int x = 1;
#endif
- ])], [ AC_MSG_RESULT([yes]) ],
- [ AC_MSG_RESULT([no])
- AC_MSG_ERROR([Libevent does not seem new enough to support bufferevents. We require 2.0.13-stable or later]) ] )
- fi
- fi
-fi
+])], [ AC_MSG_RESULT([yes]) ],
+ [ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([Libevent is not new enough. We require 2.0.10-stable or later]) ] )
LIBS="$save_LIBS"
LDFLAGS="$save_LDFLAGS"
CPPFLAGS="$save_CPPFLAGS"
-AM_CONDITIONAL(USE_BUFFEREVENTS, test "$enable_bufferevents" = "yes")
-if test "$enable_bufferevents" = "yes"; then
- AC_DEFINE(USE_BUFFEREVENTS, 1, [Defined if we're going to use Libevent's buffered IO API])
- if test "$enable_static_libevent" = "yes"; then
- TOR_LIBEVENT_LIBS="$TOR_LIBDIR_libevent/libevent_openssl.a $TOR_LIBEVENT_LIBS"
- else
- TOR_LIBEVENT_LIBS="-levent_openssl $TOR_LIBEVENT_LIBS"
- fi
-fi
AC_SUBST(TOR_LIBEVENT_LIBS)
dnl ------------------------------------------------------
@@ -693,12 +641,12 @@ CPPFLAGS="$TOR_CPPFLAGS_openssl $CPPFLAGS"
AC_TRY_COMPILE([
#include <openssl/opensslv.h>
-#if OPENSSL_VERSION_NUMBER < 0x1000000fL
+#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x1000100fL
#error "too old"
#endif
], [],
[ : ],
- [ AC_ERROR([OpenSSL is too old. We require 1.0.0 or later. You can specify a path to a newer one with --with-openssl-dir.]) ])
+ [ AC_ERROR([OpenSSL is too old. We require 1.0.1 or later. You can specify a path to a newer one with --with-openssl-dir.]) ])
AC_TRY_COMPILE([
#include <openssl/opensslv.h>
@@ -729,6 +677,11 @@ AC_CHECK_FUNCS([ \
dnl Check if OpenSSL has scrypt implementation.
AC_CHECK_FUNCS([ EVP_PBE_scrypt ])
+dnl Check if OpenSSL structures are opaque
+AC_CHECK_MEMBERS([SSL.state], , ,
+[#include <openssl/ssl.h>
+])
+
LIBS="$save_LIBS"
LDFLAGS="$save_LDFLAGS"
CPPFLAGS="$save_CPPFLAGS"
@@ -781,6 +734,11 @@ dnl use it with a build of a library.
all_ldflags_for_check="$TOR_LDFLAGS_zlib $TOR_LDFLAGS_openssl $TOR_LDFLAGS_libevent"
all_libs_for_check="$TOR_ZLIB_LIBS $TOR_LIB_MATH $TOR_LIBEVENT_LIBS $TOR_OPENSSL_LIBS $TOR_SYSTEMD_LIBS $TOR_LIB_WS32 $TOR_LIB_GDI $TOR_CAP_LIBS"
+CFLAGS_FTRAPV=
+CFLAGS_FWRAPV=
+CFLAGS_ASAN=
+CFLAGS_UBSAN=
+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
#if !defined(__clang__)
#error
@@ -803,20 +761,89 @@ m4_ifdef([AS_VAR_IF],[
AS_VAR_POPDEF([can_link])
AS_VAR_POPDEF([can_compile])
TOR_CHECK_CFLAGS(-Wstack-protector)
- TOR_CHECK_CFLAGS(-fwrapv)
TOR_CHECK_CFLAGS(--param ssp-buffer-size=1)
if test "$bwin32" = "false"; then
TOR_CHECK_CFLAGS(-fPIE)
TOR_CHECK_LDFLAGS(-pie, "$all_ldflags_for_check", "$all_libs_for_check")
fi
+ TOR_TRY_COMPILE_WITH_CFLAGS(-fwrapv, also_link, CFLAGS_FWRAPV="-fwrapv", true)
fi
if test "x$enable_expensive_hardening" = "xyes"; then
- TOR_CHECK_CFLAGS([-fsanitize=address])
- TOR_CHECK_CFLAGS([-fsanitize=undefined])
- TOR_CHECK_CFLAGS([-fno-omit-frame-pointer])
+ TOR_TRY_COMPILE_WITH_CFLAGS(-ftrapv, also_link, CFLAGS_FTRAPV="-ftrapv", true)
+ if test "$tor_cv_cflags__ftrapv" = "yes" && test "$tor_can_link__ftrapv" != "yes"; then
+ AC_MSG_WARN([The compiler supports -ftrapv, but for some reason I was not able to link with -ftrapv. Are you missing run-time support? Run-time hardening will not work as well as it should.])
+ fi
+
+ if test "$tor_cv_cflags__ftrapv" != "yes"; then
+ AC_MSG_ERROR([You requested expensive hardening, but the compiler does not seem to support -ftrapv.])
+ fi
+
+ TOR_TRY_COMPILE_WITH_CFLAGS([-fsanitize=address], also_link, CFLAGS_ASAN="-fsanitize=address", true)
+ if test "$tor_cv_cflags__fsanitize_address" = "yes" && test "$tor_can_link__fsanitize_address" != "yes"; then
+ AC_MSG_ERROR([The compiler supports -fsanitize=address, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libubsan.so, and with Clang you need libclang_rt.ubsan*])
+ fi
+
+ TOR_TRY_COMPILE_WITH_CFLAGS([-fsanitize=undefined], also_link, CFLAGS_UBSAN="-fsanitize=undefined", true)
+ if test "$tor_cv_cflags__fsanitize_address" = "yes" && test "$tor_can_link__fsanitize_address" != "yes"; then
+ AC_MSG_ERROR([The compiler supports -fsanitize=undefined, but for some reason I was not able to link when using it. Are you missing run-time support? With GCC you need libasan.so, and with Clang you need libclang_rt.ubsan*])
+ fi
+
+TOR_CHECK_CFLAGS([-fno-omit-frame-pointer])
+fi
+
+CFLAGS_BUGTRAP="$CFLAGS_FTRAPV $CFLAGS_ASAN $CFLAGS_UBSAN"
+CFLAGS_CONSTTIME="$CFLAGS_FWRAPV"
+
+mulodi_fixes_ftrapv=no
+if test "$have_clang" = "yes"; then
+ saved_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $CFLAGS_FTRAPV"
+ AC_MSG_CHECKING([whether clang -ftrapv can link a 64-bit int multiply])
+ AC_LINK_IFELSE([
+ AC_LANG_SOURCE([[
+ #include <stdint.h>
+ #include <stdlib.h>
+ int main(int argc, char **argv)
+ {
+ int64_t x = ((int64_t)atoi(argv[1])) * (int64_t)atoi(argv[2])
+ * (int64_t)atoi(argv[3]);
+ return x == 9;
+ } ]])],
+ [ftrapv_can_link=yes; AC_MSG_RESULT([yes])],
+ [ftrapv_can_link=no; AC_MSG_RESULT([no])])
+ if test "$ftrapv_can_link" = "no"; then
+ AC_MSG_CHECKING([whether defining __mulodi4 fixes that])
+ AC_LINK_IFELSE([
+ AC_LANG_SOURCE([[
+ #include <stdint.h>
+ #include <stdlib.h>
+ int64_t __mulodi4(int64_t a, int64_t b, int *overflow) {
+ *overflow=0;
+ return a;
+ }
+ int main(int argc, char **argv)
+ {
+ int64_t x = ((int64_t)atoi(argv[1])) * (int64_t)atoi(argv[2])
+ * (int64_t)atoi(argv[3]);
+ return x == 9;
+ } ]])],
+ [mulodi_fixes_ftrapv=yes; AC_MSG_RESULT([yes])],
+ [mulodi_fixes_ftrapv=no; AC_MSG_RESULT([no])])
+ fi
+ CFLAGS="$saved_CFLAGS"
fi
+AM_CONDITIONAL(ADD_MULODI4, test "$mulodi_fixes_ftrapv" = "yes")
+
+dnl These cflags add bunches of branches, and we haven't been able to
+dnl persuade ourselves that they're suitable for code that needs to be
+dnl constant time.
+AC_SUBST(CFLAGS_BUGTRAP)
+dnl These cflags are variant ones sutable for code that needs to be
+dnl constant-time.
+AC_SUBST(CFLAGS_CONSTTIME)
+
if test "x$enable_linker_hardening" != "xno"; then
TOR_CHECK_LDFLAGS(-z relro -z now, "$all_ldflags_for_check", "$all_libs_for_check")
fi
@@ -969,64 +996,58 @@ AC_SUBST(CURVE25519_LIBS)
dnl Make sure to enable support for large off_t if available.
AC_SYS_LARGEFILE
-AC_CHECK_HEADERS(
- assert.h \
- errno.h \
- fcntl.h \
- signal.h \
- string.h \
- sys/capability.h \
- sys/fcntl.h \
- sys/stat.h \
- sys/time.h \
- sys/types.h \
- time.h \
- unistd.h
- , , AC_MSG_WARN(Some headers were not found, compilation may fail. If compilation succeeds, please send your orconfig.h to the developers so we can fix this warning.))
-
-dnl These headers are not essential
-
-AC_CHECK_HEADERS(
- arpa/inet.h \
- crt_externs.h \
- execinfo.h \
- grp.h \
- ifaddrs.h \
- inttypes.h \
- limits.h \
- linux/types.h \
- machine/limits.h \
- malloc.h \
- malloc/malloc.h \
- malloc_np.h \
- netdb.h \
- netinet/in.h \
- netinet/in6.h \
- pwd.h \
- readpassphrase.h \
- stdint.h \
- sys/eventfd.h \
- sys/file.h \
- sys/ioctl.h \
- sys/limits.h \
- sys/mman.h \
- sys/param.h \
- sys/prctl.h \
- sys/resource.h \
- sys/select.h \
- sys/socket.h \
- sys/statvfs.h \
- sys/syscall.h \
- sys/sysctl.h \
- sys/syslimits.h \
- sys/time.h \
- sys/types.h \
- sys/un.h \
- sys/utime.h \
- sys/wait.h \
- syslog.h \
- utime.h
-)
+AC_CHECK_HEADERS([assert.h \
+ errno.h \
+ fcntl.h \
+ signal.h \
+ string.h \
+ sys/capability.h \
+ sys/fcntl.h \
+ sys/stat.h \
+ sys/time.h \
+ sys/types.h \
+ time.h \
+ unistd.h \
+ arpa/inet.h \
+ crt_externs.h \
+ execinfo.h \
+ grp.h \
+ ifaddrs.h \
+ inttypes.h \
+ limits.h \
+ linux/types.h \
+ machine/limits.h \
+ malloc.h \
+ malloc/malloc.h \
+ malloc_np.h \
+ netdb.h \
+ netinet/in.h \
+ netinet/in6.h \
+ pwd.h \
+ readpassphrase.h \
+ stdint.h \
+ sys/eventfd.h \
+ sys/file.h \
+ sys/ioctl.h \
+ sys/limits.h \
+ sys/mman.h \
+ sys/param.h \
+ sys/prctl.h \
+ sys/random.h \
+ sys/resource.h \
+ sys/select.h \
+ sys/socket.h \
+ sys/statvfs.h \
+ sys/syscall.h \
+ sys/sysctl.h \
+ sys/syslimits.h \
+ sys/time.h \
+ sys/types.h \
+ sys/un.h \
+ sys/utime.h \
+ sys/wait.h \
+ syslog.h \
+ utime.h])
AC_CHECK_HEADERS(sys/param.h)
@@ -1103,26 +1124,24 @@ AC_CHECK_HEADERS(linux/netfilter_ipv6/ip6_tables.h,
#include <linux/if.h>
#endif])
-if test "x$transparent" = "xtrue"; then
- transparent_ok=0
- if test "x$net_if_found" = "x1" && test "x$net_pfvar_found" = "x1"; then
- transparent_ok=1
- fi
- if test "x$linux_netfilter_ipv4" = "x1"; then
- transparent_ok=1
- fi
- if test "x$linux_netfilter_ipv6_ip6_tables" = "x1"; then
- transparent_ok=1
- fi
- if test "x$transparent_ok" = "x1"; then
- AC_DEFINE(USE_TRANSPARENT, 1, "Define to enable transparent proxy support")
- case "$host" in
- *-*-openbsd* | *-*-bitrig*)
- AC_DEFINE(OPENBSD, 1, "Define to handle pf on OpenBSD properly") ;;
- esac
- else
- AC_MSG_NOTICE([Transparent proxy support enabled, but missing headers.])
- fi
+transparent_ok=0
+if test "x$net_if_found" = "x1" && test "x$net_pfvar_found" = "x1"; then
+ transparent_ok=1
+fi
+if test "x$linux_netfilter_ipv4" = "x1"; then
+ transparent_ok=1
+fi
+if test "x$linux_netfilter_ipv6_ip6_tables" = "x1"; then
+ transparent_ok=1
+fi
+if test "x$transparent_ok" = "x1"; then
+ AC_DEFINE(USE_TRANSPARENT, 1, "Define to enable transparent proxy support")
+ case "$host" in
+ *-*-openbsd* | *-*-bitrig*)
+ AC_DEFINE(OPENBSD, 1, "Define to handle pf on OpenBSD properly") ;;
+ esac
+else
+ AC_MSG_NOTICE([Transparent proxy support enabled, but missing headers.])
fi
AC_CHECK_MEMBERS([struct timeval.tv_sec], , ,
@@ -1516,9 +1535,6 @@ int main(int c, char **v) { puts(__FUNCTION__); }])],
AC_CACHE_CHECK([whether we have extern char **environ already declared],
tor_cv_have_environ_declared,
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
-/* We define _GNU_SOURCE here because it is also defined in compat.c.
- * Without it environ doesn't get declared. */
-#define _GNU_SOURCE
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -1603,6 +1619,12 @@ else
enable_gcc_warnings_advisory=no
fi
+# Warnings implies advisory-warnings and -Werror.
+if test "$enable_gcc_warnings" = "yes"; then
+ enable_gcc_warnings_advisory=yes
+ enable_fatal_warnings=yes
+fi
+
# OS X Lion started deprecating the system openssl. Let's just disable
# all deprecation warnings on OS X. Also, to potentially make the binary
# a little smaller, let's enable dead_strip.
@@ -1615,35 +1637,11 @@ esac
# Add some more warnings which we use in development but not in the
# released versions. (Some relevant gcc versions can't handle these.)
-if test "x$enable_gcc_warnings" = "xyes" ||
- test "x$enable_gcc_warnings_advisory" = "xyes"; then
-
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
-#if !defined(__GNUC__) || (__GNUC__ < 4)
-#error
-#endif])], have_gcc4=yes, have_gcc4=no)
-
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
-#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)
-#error
-#endif])], have_gcc42=yes, have_gcc42=no)
-
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
-#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 3)
-#error
-#endif])], have_gcc43=yes, have_gcc43=no)
-
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [
-#if !defined(__GNUC__) || (__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)
-#error
-#endif])], have_gcc46=yes, have_gcc46=no)
-
-
- save_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS -Wshorten-64-to-32"
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])], have_shorten64_flag=yes,
- have_shorten64_flag=no)
- CFLAGS="$save_CFLAGS"
+#
+# Note that we have to do this near the end of the autoconf process, or
+# else we may run into problems when these warnings hit on the testing C
+# programs that autoconf wants to build.
+if test "x$enable_gcc_warnings_advisory" != "xno"; then
case "$host" in
*-*-openbsd* | *-*-bitrig*)
@@ -1654,62 +1652,266 @@ if test "x$enable_gcc_warnings" = "xyes" ||
CFLAGS="$CFLAGS -Wno-system-headers" ;;
esac
+ # GCC4.3 users once report trouble with -Wstrict-overflow=5. GCC5 users
+ # have it work better.
+ # CFLAGS="$CFLAGS -Wstrict-overflow=1"
+
+ # This warning was added in gcc 4.3, but it appears to generate
+ # spurious warnings in gcc 4.4. I don't know if it works in 4.5.
+ #CFLAGS="$CFLAGS -Wlogical-op"
+
+ m4_foreach_w([warning_flag], [
+ -Waddress
+ -Waddress-of-array-temporary
+ -Waddress-of-temporary
+ -Wambiguous-macro
+ -Wanonymous-pack-parens
+ -Warc
+ -Warc-abi
+ -Warc-bridge-casts-disallowed-in-nonarc
+ -Warc-maybe-repeated-use-of-weak
+ -Warc-performSelector-leaks
+ -Warc-repeated-use-of-weak
+ -Warray-bounds
+ -Warray-bounds-pointer-arithmetic
+ -Wasm
+ -Wasm-operand-widths
+ -Watomic-properties
+ -Watomic-property-with-user-defined-accessor
+ -Wauto-import
+ -Wauto-storage-class
+ -Wauto-var-id
+ -Wavailability
+ -Wbackslash-newline-escape
+ -Wbad-array-new-length
+ -Wbind-to-temporary-copy
+ -Wbitfield-constant-conversion
+ -Wbool-conversion
+ -Wbool-conversions
+ -Wbuiltin-requires-header
+ -Wchar-align
+ -Wcompare-distinct-pointer-types
+ -Wcomplex-component-init
+ -Wconditional-type-mismatch
+ -Wconfig-macros
+ -Wconstant-conversion
+ -Wconstant-logical-operand
+ -Wconstexpr-not-const
+ -Wcustom-atomic-properties
+ -Wdangling-field
+ -Wdangling-initializer-list
+ -Wdate-time
+ -Wdelegating-ctor-cycles
+ -Wdeprecated-implementations
+ -Wdeprecated-register
+ -Wdirect-ivar-access
+ -Wdiscard-qual
+ -Wdistributed-object-modifiers
+ -Wdivision-by-zero
+ -Wdollar-in-identifier-extension
+ -Wdouble-promotion
+ -Wduplicate-decl-specifier
+ -Wduplicate-enum
+ -Wduplicate-method-arg
+ -Wduplicate-method-match
+ -Wduplicated-cond
+ -Wdynamic-class-memaccess
+ -Wembedded-directive
+ -Wempty-translation-unit
+ -Wenum-conversion
+ -Wexit-time-destructors
+ -Wexplicit-ownership-type
+ -Wextern-initializer
+ -Wextra
+ -Wextra-semi
+ -Wextra-tokens
+ -Wflexible-array-extensions
+ -Wfloat-conversion
+ -Wformat-non-iso
+ -Wfour-char-constants
+ -Wgcc-compat
+ -Wglobal-constructors
+ -Wgnu-array-member-paren-init
+ -Wgnu-designator
+ -Wgnu-static-float-init
+ -Wheader-guard
+ -Wheader-hygiene
+ -Widiomatic-parentheses
+ -Wignored-attributes
+ -Wimplicit-atomic-properties
+ -Wimplicit-conversion-floating-point-to-bool
+ -Wimplicit-exception-spec-mismatch
+ -Wimplicit-fallthrough
+ -Wimplicit-fallthrough-per-function
+ -Wimplicit-retain-self
+ -Wimport-preprocessor-directive-pedantic
+ -Wincompatible-library-redeclaration
+ -Wincompatible-pointer-types-discards-qualifiers
+ -Wincomplete-implementation
+ -Wincomplete-module
+ -Wincomplete-umbrella
+ -Winit-self
+ -Wint-conversions
+ -Wint-to-void-pointer-cast
+ -Winteger-overflow
+ -Winvalid-constexpr
+ -Winvalid-iboutlet
+ -Winvalid-noreturn
+ -Winvalid-pp-token
+ -Winvalid-source-encoding
+ -Winvalid-token-paste
+ -Wknr-promoted-parameter
+ -Wlanguage-extension-token
+ -Wlarge-by-value-copy
+ -Wliteral-conversion
+ -Wliteral-range
+ -Wlocal-type-template-args
+ -Wlogical-op
+ -Wloop-analysis
+ -Wmain-return-type
+ -Wmalformed-warning-check
+ -Wmethod-signatures
+ -Wmicrosoft
+ -Wmicrosoft-exists
+ -Wmismatched-parameter-types
+ -Wmismatched-return-types
+ -Wmissing-field-initializers
+ -Wmissing-format-attribute
+ -Wmissing-noreturn
+ -Wmissing-selector-name
+ -Wmissing-sysroot
+ -Wmissing-variable-declarations
+ -Wmodule-conflict
+ -Wnested-anon-types
+ -Wnewline-eof
+ -Wnon-literal-null-conversion
+ -Wnon-pod-varargs
+ -Wnonportable-cfstrings
+ -Wnormalized=id
+ -Wnull-arithmetic
+ -Wnull-character
+ -Wnull-conversion
+ -Wnull-dereference
+ -Wout-of-line-declaration
+ -Wover-aligned
+ -Woverlength-strings
+ -Woverride-init
+ -Woverriding-method-mismatch
+ -Wpointer-type-mismatch
+ -Wpredefined-identifier-outside-function
+ -Wprotocol-property-synthesis-ambiguity
+ -Wreadonly-iboutlet-property
+ -Wreadonly-setter-attrs
+ -Wreceiver-expr
+ -Wreceiver-forward-class
+ -Wreceiver-is-weak
+ -Wreinterpret-base-class
+ -Wrequires-super-attribute
+ -Wreserved-user-defined-literal
+ -Wreturn-stack-address
+ -Wsection
+ -Wselector-type-mismatch
+ -Wsentinel
+ -Wserialized-diagnostics
+ -Wshadow
+ -Wshift-count-negative
+ -Wshift-count-overflow
+ -Wshift-negative-value
+ -Wshift-overflow=2
+ -Wshift-sign-overflow
+ -Wshorten-64-to-32
+ -Wsizeof-array-argument
+ -Wsource-uses-openmp
+ -Wstatic-float-init
+ -Wstatic-in-inline
+ -Wstatic-local-in-inline
+ -Wstrict-overflow=1
+ -Wstring-compare
+ -Wstring-conversion
+ -Wstrlcpy-strlcat-size
+ -Wstrncat-size
+ -Wsuggest-attribute=format
+ -Wsuggest-attribute=noreturn
+ -Wsuper-class-method-mismatch
+ -Wswitch-bool
+ -Wsync-nand
+ -Wtautological-constant-out-of-range-compare
+ -Wtentative-definition-incomplete-type
+ -Wtrampolines
+ -Wtype-safety
+ -Wtypedef-redefinition
+ -Wtypename-missing
+ -Wundefined-inline
+ -Wundefined-internal
+ -Wundefined-reinterpret-cast
+ -Wunicode
+ -Wunicode-whitespace
+ -Wunknown-warning-option
+ -Wunnamed-type-template-args
+ -Wunneeded-member-function
+ -Wunsequenced
+ -Wunsupported-visibility
+ -Wunused-but-set-parameter
+ -Wunused-but-set-variable
+ -Wunused-command-line-argument
+ -Wunused-const-variable=2
+ -Wunused-exception-parameter
+ -Wunused-local-typedefs
+ -Wunused-member-function
+ -Wunused-sanitize-argument
+ -Wunused-volatile-lvalue
+ -Wuser-defined-literals
+ -Wvariadic-macros
+ -Wvector-conversion
+ -Wvector-conversions
+ -Wvexing-parse
+ -Wvisibility
+ -Wvla-extension
+ -Wzero-length-array
+ ], [ TOR_CHECK_CFLAGS([warning_flag]) ])
+
+dnl We should re-enable this in some later version. Clang doesn't
+dnl mind, but it causes trouble with GCC.
+dnl -Wstrict-overflow=2
+
+dnl These seem to require annotations that we don't currently use,
+dnl and they give false positives in our pthreads wrappers. (Clang 4)
+dnl -Wthread-safety
+dnl -Wthread-safety-analysis
+dnl -Wthread-safety-attributes
+dnl -Wthread-safety-beta
+dnl -Wthread-safety-precise
+
CFLAGS="$CFLAGS -W -Wfloat-equal -Wundef -Wpointer-arith"
CFLAGS="$CFLAGS -Wstrict-prototypes -Wmissing-prototypes -Wwrite-strings"
CFLAGS="$CFLAGS -Wredundant-decls -Wchar-subscripts -Wcomment -Wformat=2"
- CFLAGS="$CFLAGS -Wwrite-strings -Wmissing-declarations -Wredundant-decls"
+ CFLAGS="$CFLAGS -Wwrite-strings"
CFLAGS="$CFLAGS -Wnested-externs -Wbad-function-cast -Wswitch-enum"
-
- if test "x$enable_gcc_warnings" = "xyes"; then
- CFLAGS="$CFLAGS -Werror"
+ CFLAGS="$CFLAGS -Waggregate-return -Wpacked -Wunused"
+ CFLAGS="$CFLAGS -Wunused-parameter "
+ # These interfere with building main() { return 0; }, which autoconf
+ # likes to use as its default program.
+ CFLAGS="$CFLAGS -Wold-style-definition -Wmissing-declarations"
+
+ if test "$tor_cv_cflags__Wnull_dereference" = "yes"; then
+ AC_DEFINE([HAVE_CFLAG_WNULL_DEREFERENCE], 1, [True if we have -Wnull-dereference])
fi
-
- # Disabled, so we can use mallinfo(): -Waggregate-return
-
- if test "x$have_gcc4" = "xyes"; then
- # These warnings break gcc 3.3.5 and work on gcc 4.0.2
- CFLAGS="$CFLAGS -Winit-self -Wmissing-field-initializers -Wold-style-definition"
+ if test "$tor_cv_cflags__Woverlength_strings" = "yes"; then
+ AC_DEFINE([HAVE_CFLAG_WOVERLENGTH_STRINGS], 1, [True if we have -Woverlength-strings])
fi
- if test "x$have_gcc42" = "xyes"; then
- # These warnings break gcc 4.0.2 and work on gcc 4.2
- # XXXX020 See if any of these work with earlier versions.
- CFLAGS="$CFLAGS -Waddress -Wmissing-noreturn -Wstrict-overflow=1"
-
- # We used to use -Wstrict-overflow=5, but that breaks us heavily under 4.3.
- fi
-
- if test "x$have_gcc42" = "xyes" && test "x$have_clang" = "xno"; then
- # These warnings break gcc 4.0.2 and clang, but work on gcc 4.2
- CFLAGS="$CFLAGS -Wnormalized=id -Woverride-init"
- fi
-
- if test "x$have_gcc43" = "xyes"; then
- # These warnings break gcc 4.2 and work on gcc 4.3
- # XXXX020 See if any of these work with earlier versions.
- CFLAGS="$CFLAGS -Wextra -Warray-bounds"
- fi
-
- if test "x$have_gcc46" = "xyes"; then
- # This warning was added in gcc 4.3, but it appears to generate
- # spurious warnings in gcc 4.4. I don't know if it works in 4.5.
- CFLAGS="$CFLAGS -Wlogical-op"
- fi
-
- if test "x$have_shorten64_flag" = "xyes"; then
- CFLAGS="$CFLAGS -Wshorten-64-to-32"
+ if test "x$enable_fatal_warnings" = "xyes"; then
+ # I'd like to use TOR_CHECK_CFLAGS here, but I can't, since the
+ # default autoconf programs are full of errors.
+ CFLAGS="$CFLAGS -Werror"
fi
-
-
-##This will break the world on some 64-bit architectures
-# CFLAGS="$CFLAGS -Winline"
fi
if test "$enable_coverage" = "yes" && test "$have_clang" = "no"; then
case "$host_os" in
darwin*)
- AC_MSG_WARN([Tried to enable coverage on OSX without using the clang compiler. This might not work! If coverage fails, use CC=clang when configuring with --enable-profiling.])
+ AC_MSG_WARN([Tried to enable coverage on OSX without using the clang compiler. This might not work! If coverage fails, use CC=clang when configuring with --enable-coverage.])
esac
fi
diff --git a/contrib/clang/sanitize_blacklist.txt b/contrib/clang/sanitize_blacklist.txt
index 03d1e70f31..c71cc4d878 100644
--- a/contrib/clang/sanitize_blacklist.txt
+++ b/contrib/clang/sanitize_blacklist.txt
@@ -2,6 +2,17 @@
# syntax specified in http://clang.llvm.org/docs/SanitizerSpecialCaseList.html
# for more info see http://clang.llvm.org/docs/AddressSanitizer.html
+#
+# Tor notes: This file is obsolete!
+#
+# It was necessary in order to apply the sanitizers to all of tor. But
+# we don't believe that's a good idea: some parts of tor need constant-time
+# behavior that is hard to guarantee with these sanitizers.
+#
+# If you need this behavior, then please consider --enable-expensive-hardening,
+# and report bugs as needed.
+#
+
# usage:
# 1. configure tor build:
# ./configure \
diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in
index 2ab2b14efd..83c6d5c5e9 100644
--- a/contrib/win32build/tor-mingw.nsi.in
+++ b/contrib/win32build/tor-mingw.nsi.in
@@ -8,7 +8,7 @@
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!insertmacro GetParameters
-!define VERSION "0.2.8.17-dev"
+!define VERSION "0.2.9.14-dev"
!define INSTALLER "tor-${VERSION}-win32.exe"
!define WEBSITE "https://www.torproject.org/"
!define LICENSE "LICENSE"
diff --git a/doc/HACKING/CodingStandards.md b/doc/HACKING/CodingStandards.md
index 4aafa5ddd4..f1c65850a4 100644
--- a/doc/HACKING/CodingStandards.md
+++ b/doc/HACKING/CodingStandards.md
@@ -3,7 +3,7 @@ Coding conventions for Tor
tl;dr:
- - Run configure with `--enable-gcc-warnings`
+ - Run configure with `--enable-fatal-warnings`
- Run `make check-spaces` to catch whitespace errors
- Document your functions
- Write unit tests
@@ -21,7 +21,7 @@ preference)
Did you remember...
- - To build your code while configured with `--enable-gcc-warnings`?
+ - To build your code while configured with `--enable-fatal-warnings`?
- To run `make check-spaces` on your code?
- To run `make check-docs` to see whether all new options are on
the manpage?
@@ -125,10 +125,10 @@ deviations from our C whitespace style. Generally, we use:
`puts (x)`.
- Function declarations at the start of the line.
-We try hard to build without warnings everywhere. In particular, if you're
-using gcc, you should invoke the configure script with the option
-`--enable-gcc-warnings`. This will give a bunch of extra warning flags to
-the compiler, and help us find divergences from our preferred C style.
+We try hard to build without warnings everywhere. In particular, if
+you're using gcc, you should invoke the configure script with the
+option `--enable-fatal-warnings`. This will tell the compiler
+to make all warnings into errors.
Functions to use; functions not to use
--------------------------------------
diff --git a/doc/HACKING/HowToReview.md b/doc/HACKING/HowToReview.md
index de7891c923..d53318942f 100644
--- a/doc/HACKING/HowToReview.md
+++ b/doc/HACKING/HowToReview.md
@@ -15,7 +15,7 @@ Top-level smell-checks
(Difficulty: easy)
-- Does it compile with `--enable-gcc-warnings`?
+- Does it compile with `--enable-fatal-warnings`?
- Does `make check-spaces` pass?
diff --git a/doc/HACKING/ReleasingTor.md b/doc/HACKING/ReleasingTor.md
index 2378aef568..7595398241 100644
--- a/doc/HACKING/ReleasingTor.md
+++ b/doc/HACKING/ReleasingTor.md
@@ -2,7 +2,16 @@
Putting out a new release
-------------------------
-Here are the steps Roger takes when putting out a new Tor release:
+Here are the steps that the maintainer should take when putting out a
+new Tor release:
+
+=== 0. Preliminaries
+
+1. Get at least three of weasel/arma/Sebastian/Sina to put the new
+ version number in their approved versions list.
+
+
+=== I. Make sure it works
1. Use it for a while, as a client, as a relay, as a hidden service,
and as a directory authority. See if it has any obvious bugs, and
@@ -10,7 +19,28 @@ Here are the steps Roger takes when putting out a new Tor release:
As applicable, merge the `maint-X` branch into the `release-X` branch.
-2. Gather the `changes/*` files into a changelog entry, rewriting many
+2. Are all of the jenkins builders happy? See jenkins.torproject.org.
+
+ What about the bsd buildbots?
+ See http://buildbot.pixelminers.net/builders/
+
+ What about Coverity Scan?
+
+ Is make check-spaces happy?
+
+ Does 'make distcheck' compain?
+
+ How about 'make test-stem' and 'make test-network'?
+
+ - Are all those tests still happy with --enable-expensive-hardening ?
+
+ Any memory leaks?
+
+
+=== II. Write a changelog.
+
+
+1. Gather the `changes/*` files into a changelog entry, rewriting many
of them and reordering to focus on what users and funders would find
interesting and understandable.
@@ -62,13 +92,13 @@ Here are the steps Roger takes when putting out a new Tor release:
7. Run `./scripts/maint/format_changelog.py` to make it prettier.
-3. Compose a short release blurb to highlight the user-facing
+2. Compose a short release blurb to highlight the user-facing
changes. Insert said release blurb into the ChangeLog stanza. If it's
a stable release, add it to the ReleaseNotes file too. If we're adding
to a release-0.2.x branch, manually commit the changelogs to the later
git branches too.
- If you're doing the first stable release in a series, you need to
+3. If you're doing the first stable release in a series, you need to
create a ReleaseNotes for the series as a whole. To get started
there, copy all of the Changelog entries from the series into a new
file, and run `./scripts/maint/sortChanges.py` on it. That will
@@ -78,7 +108,10 @@ Here are the steps Roger takes when putting out a new Tor release:
to start sorting and condensing entries. (Generally, we don't edit the
text of existing entries, though.)
-4. In `maint-0.2.x`, bump the version number in `configure.ac` and run
+
+=== III. Making the source release.
+
+1. In `maint-0.2.x`, bump the version number in `configure.ac` and run
`scripts/maint/updateVersions.pl` to update version numbers in other
places, and commit. Then merge `maint-0.2.x` into `release-0.2.x`.
@@ -86,20 +119,19 @@ Here are the steps Roger takes when putting out a new Tor release:
either `make`, or `perl scripts/maint/updateVersions.pl`, depending on
your version.)
-5. Make distcheck, put the tarball up somewhere, and tell `#tor` about
+2. Make distcheck, put the tarball up somewhere, and tell `#tor` about
it. Wait a while to see if anybody has problems building it. Try to
get Sebastian or somebody to try building it on Windows.
-6. Get at least two of weasel/arma/Sebastian to put the new version number
- in their approved versions list.
+=== IV. Commit, upload, announce
-7. Sign the tarball, then sign and push the git tag:
+1. Sign the tarball, then sign and push the git tag:
gpg -ba <the_tarball>
git tag -u <keyid> tor-0.2.x.y-status
git push origin tag tor-0.2.x.y-status
-8. scp the tarball and its sig to the dist website, i.e.
+2. scp the tarball and its sig to the dist website, i.e.
`/srv/dist-master.torproject.org/htdocs/` on dist-master. When you want
it to go live, you run "static-update-component dist.torproject.org"
on dist-master.
@@ -110,34 +142,44 @@ Here are the steps Roger takes when putting out a new Tor release:
once. Nonetheless, do not call your version "alpha" if it is stable,
or people will get confused.)
-9. Email the packagers (cc'ing tor-assistants) that a new tarball is up.
+3. Email the packagers (cc'ing tor-assistants) that a new tarball is up.
The current list of packagers is:
- {weasel,gk,mikeperry} at torproject dot org
- {blueness} at gentoo dot org
- {paul} at invizbox dot io
- - {ondrej.mikle} at gmail dot com
- {lfleischer} at archlinux dot org
- - {tails-dev} at boum dot org
+ - {Nathan} at freitas dot net
+ - {mike} at tig dot as
+ - {tails-rm} at boum dot org (for pre-release announcments)
+
+
+ - {tails-dev} at boum dot org (for at-release announcements)
+
-10. Add the version number to Trac. To do this, go to Trac, log in,
+4. Add the version number to Trac. To do this, go to Trac, log in,
select "Admin" near the top of the screen, then select "Versions" from
the menu on the left. At the right, there will be an "Add version"
box. By convention, we enter the version in the form "Tor:
0.2.2.23-alpha" (or whatever the version is), and we select the date as
the date in the ChangeLog.
-11. Forward-port the ChangeLog (and ReleaseNotes if appropriate).
-
-12. Wait up to a day or two (for a development release), or until most
+5. Wait up to a day or two (for a development release), or until most
packages are up (for a stable release), and mail the release blurb and
changelog to tor-talk or tor-announce.
(We might be moving to faster announcements, but don't announce until
the website is at least updated.)
-13. If it's a stable release, bump the version number in the `maint-x.y.z`
+
+=== V. Aftermath and cleanup
+
+1. If it's a stable release, bump the version number in the `maint-x.y.z`
branch to "newversion-dev", and do a `merge -s ours` merge to avoid
taking that change into master. Do a similar `merge -s theirs`
merge to get the change (and only that change) into release. (Some
of the build scripts require that maint merge cleanly into release.)
+
+2. Forward-port the ChangeLog (and ReleaseNotes if appropriate).
+
+
diff --git a/doc/HACKING/WritingTests.md b/doc/HACKING/WritingTests.md
index 4e98d3d645..de80bbdef2 100644
--- a/doc/HACKING/WritingTests.md
+++ b/doc/HACKING/WritingTests.md
@@ -109,6 +109,19 @@ To count new or modified uncovered lines in D2, you can run:
./scripts/test/cov-diff ${D1} ${D2}" | grep '^+ *\#' | wc -l
+### Marking lines as unreachable by tests
+
+You can mark a specific line as unreachable by using the special
+string LCOV_EXCL_LINE. You can mark a range of lines as unreachable
+with LCOV_EXCL_START... LCOV_EXCL_STOP. Note that older versions of
+lcov don't understand these lines.
+
+You can post-process .gcov files to make these lines 'unreached' by
+running ./scripts/test/cov-exclude on them.
+
+Note: you should never do this unless the line is meant to 100%
+unreachable by actual code.
+
What kinds of test should I write?
----------------------------------
@@ -139,6 +152,11 @@ or create a new C file there. Each test is a single function that must
be indexed in the table at the end of the file. We use the label "done:" as
a cleanup point for all test functions.
+If you have created a new test file, you will need to:
+1. Add the new test file to include.am
+2. In `test.h`, include the new test cases (testcase_t)
+3. In `test.c`, add the new test cases to testgroup_t testgroups
+
(Make sure you read `tinytest-manual.md` before proceeding.)
I use the term "unit test" and "regression tests" very sloppily here.
@@ -417,18 +435,50 @@ makefile exports them.
Writing integration tests with Stem
-----------------------------------
-The 'stem' library includes extensive unit tests for the Tor controller
-protocol.
-
-For more information on writing new tests for stem, have a look around
-the `test/*` directory in stem, and find a good example to emulate. You
-might want to start with
-`https://gitweb.torproject.org/stem.git/tree/test/integ/control/controller.py`
-to improve Tor's test coverage.
-
+The 'stem' library includes extensive tests for the Tor controller protocol.
You can run stem tests from tor with `make test-stem`, or see
`https://stem.torproject.org/faq.html#how-do-i-run-the-tests`.
+To see what tests are available, have a look around the `test/*` directory in
+stem. The first thing you'll notice is that there are both `unit` and `integ`
+tests. The former are for tests of the facilities provided by stem itself that
+can be tested on their own, without the need to hook up a tor process. These
+are less relevant, unless you want to develop a new stem feature. The latter,
+however, are a very useful tool to write tests for controller features. They
+provide a default environment with a connected tor instance that can be
+modified and queried. Adding more integration tests is a great way to increase
+the test coverage inside Tor, especially for controller features.
+
+Let's assume you actually want to write a test for a previously untested
+controller feature. I'm picking the `exit-policy/*` GETINFO queries. Since
+these are a controller feature that we want to write an integration test for,
+the right file to modify is
+`https://gitweb.torproject.org/stem.git/tree/test/integ/control/controller.py`.
+
+First off we notice that there is an integration test called
+`test_get_exit_policy()` that's already written. This exercises the interaction
+of stem's `Controller.get_exit_policy()` method, and is not relevant for our
+test since there are no stem methods to make use of all `exit-policy/*`
+queries (if there were, likely they'd be tested already. Maybe you want to
+write a stem feature, but I chose to just add tests).
+
+Our test requires a tor controller connection, so we'll use the
+`@require_controller` annotation for our `test_exit_policy()` method. We need a
+controller instance, which we get from
+`test.runner.get_runner().get_tor_controller()`. The attached Tor instance is
+configured as a client, but the exit-policy GETINFO queries need a relay to
+work, so we have to change the config (using `controller.set_options()`). This
+is OK for us to do, we just have to remember to set DisableNetwork so we don't
+actually start an exit relay and also to undo the changes we made (by calling
+`controller.reset_conf()` at the end of our test). Additionally, we have to
+configure a static Address for Tor to use, because it refuses to build a
+descriptor when it can't guess a suitable IP address. Unfortunately, these
+kinds of tripwires are everywhere. Don't forget to file appropriate tickets if
+you notice any strange behaviour that seems totally unreasonable.
+
+Check out the `test_exit_policy()` function in abovementioned file to see the
+final implementation for this test.
+
System testing with Chutney
---------------------------
diff --git a/doc/tor-gencert.1.txt b/doc/tor-gencert.1.txt
index aa61ec3ec6..6bba548b87 100644
--- a/doc/tor-gencert.1.txt
+++ b/doc/tor-gencert.1.txt
@@ -68,7 +68,7 @@ OPTIONS
Number of months that the certificate should be valid. Default: 12.
**--passphrase-fd** __FILEDES__::
- Filedescriptor to read the file descriptor from. Ends at the first NUL or
+ Filedescriptor to read the passphrase from. Ends at the first NUL or
newline. Default: read from the terminal.
**-a** __address__:__port__::
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index 74915b7119..f1068cedc6 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -84,6 +84,10 @@ COMMAND-LINE OPTIONS
[[opt-list-torrc-options]] **--list-torrc-options**::
List all valid options.
+[[opt-list-deprecated-options]] **--list-deprecated-options**::
+ List all valid options that are scheduled to become obsolete in a
+ future version. (This is a warning, not a promise.)
+
[[opt-version]] **--version**::
Display Tor version and exit.
@@ -118,6 +122,13 @@ COMMAND-LINE OPTIONS
directory of your Tor daemon, and make sure that they are owned by the
user actually running the Tor daemon on your system.
+**--passphrase-fd** __FILEDES__::
+ Filedescriptor to read the passphrase from. Note that unlike with the
+ tor-gencert program, the entire file contents are read and used as
+ the passphrase, including any trailing newlines.
+ Default: read from the terminal.
+
+
Other options can be specified on the command-line in the format "--option
value", in the format "option value", or in a configuration file. For
instance, you can tell Tor to start listening for SOCKS connections on port
@@ -310,8 +321,10 @@ GENERAL OPTIONS
specify one or more of **HashedControlPassword** or
**CookieAuthentication**, setting this option will cause Tor to allow
any process on the local host to control it. (Setting both authentication
- methods means eithermethod is sufficient to authenticate to Tor.) This
+ methods means either method is sufficient to authenticate to Tor.) This
option is required for many Tor controllers; most use the value of 9051.
+ If a unix domain socket is used, you may quote the path using standard
+ C escape sequences.
Set it to "auto" to have Tor pick a port for you. (Default: 0) +
+
Recognized flags are...
@@ -595,6 +608,13 @@ GENERAL OPTIONS
message currently has at least one domain; most currently have exactly
one. This doesn't affect controller log messages. (Default: 0)
+[[MaxUnparseableDescSizeToLog]] **MaxUnparseableDescSizeToLog** __N__ **bytes**|**KBytes**|**MBytes**|**GBytes**::
+ Unparseable descriptors (e.g. for votes, consensuses, routers) are logged
+ in separate files by hash, up to the specified size in total. Note that
+ only files logged during the lifetime of this Tor process count toward the
+ total; this is intended to be used to debug problems without opening live
+ servers to resource exhaustion attacks. (Default: 10 MB)
+
[[OutboundBindAddress]] **OutboundBindAddress** __IP__::
Make all outbound connections originate from the IP address specified. This
is only useful when you have multiple network interfaces, and you want all
@@ -650,7 +670,7 @@ GENERAL OPTIONS
relay, all log messages generated when acting as a relay are sanitized, but
all messages generated when acting as a client are not. (Default: 1)
-[[User]] **User** __UID__::
+[[User]] **User** __Username__::
On startup, setuid to this user and setgid to their primary group.
[[KeepBindCapabilities]] **KeepBindCapabilities** **0**|**1**|**auto**::
@@ -691,26 +711,6 @@ GENERAL OPTIONS
networkstatus. This is an advanced option; you generally shouldn't have
to mess with it. (Default: not set)
-[[DisableIOCP]] **DisableIOCP** **0**|**1**::
- If Tor was built to use the Libevent's "bufferevents" networking code
- and you're running on Windows, setting this option to 1 will tell Libevent
- not to use the Windows IOCP networking API. (Default: 1)
-
-[[UserspaceIOCPBuffers]] **UserspaceIOCPBuffers** **0**|**1**::
- If IOCP is enabled (see DisableIOCP above), setting this option to 1
- will tell Tor to disable kernel-space TCP buffers, in order to avoid
- needless copy operations and try not to run out of non-paged RAM.
- This feature is experimental; don't use it yet unless you're eager to
- help tracking down bugs. (Default: 0)
-
-[[UseFilteringSSLBufferevents]] **UseFilteringSSLBufferevents** **0**|**1**::
- Tells Tor to do its SSL communication using a chain of
- bufferevents: one for SSL and one for networking. This option has no
- effect if bufferevents are disabled (in which case it can't turn on), or
- if IOCP bufferevents are enabled (in which case it can't turn off). This
- option is useful for debugging only; most users shouldn't touch it.
- (Default: 0)
-
[[CountPrivateBandwidth]] **CountPrivateBandwidth** **0**|**1**::
If this option is set, then Tor's rate-limiting applies not only to
remote connections, but also to connections to private addresses like
@@ -1034,7 +1034,9 @@ The following options are useful only for clients (that is, if
applications. Set this to 0 if you don't want to allow application
connections via SOCKS. Set it to "auto" to have Tor pick a port for
you. This directive can be specified multiple times to bind
- to multiple addresses/ports. (Default: 9050) +
+ to multiple addresses/ports. If a unix domain socket is used, you may
+ quote the path using standard C escape sequences.
+ (Default: 9050) +
+
NOTE: Although this option allows you to specify an IP address
other than localhost, you should do so only with extreme caution.
@@ -1048,8 +1050,9 @@ The following options are useful only for clients (that is, if
another. Recognized isolation flags are:
**IsolateClientAddr**;;
Don't share circuits with streams from a different
- client address. (On by default and strongly recommended;
- you can disable it with **NoIsolateClientAddr**.)
+ client address. (On by default and strongly recommended when
+ supported; you can disable it with **NoIsolateClientAddr**.
+ Unsupported and force-disabled when using Unix domain sockets.)
**IsolateSOCKSAuth**;;
Don't share circuits with streams for which different
SOCKS authentication was provided. (On by default;
@@ -1085,7 +1088,18 @@ The following options are useful only for clients (that is, if
IPv6.)
**PreferIPv6**;;
Tells exits that, if a host has both an IPv4 and an IPv6 address,
- we would prefer to connect to it via IPv6. (IPv4 is the default.) +
+ we would prefer to connect to it via IPv6. (IPv4 is the default.)
+ **NoDNSRequest**;;
+ Do not ask exits to resolve DNS addresses in SOCKS5 requests. Tor will
+ connect to IPv4 addresses, IPv6 addresses (if IPv6Traffic is set) and
+ .onion addresses.
+ **NoOnionTraffic**;;
+ Do not connect to .onion addresses in SOCKS5 requests.
+ **OnionTrafficOnly**;;
+ Tell the tor client to only connect to .onion addresses in response to
+ SOCKS5 requests on this connection. This is equivalent to NoDNSRequest,
+ NoIPv4Traffic, NoIPv6Traffic. The corresponding NoOnionTrafficOnly
+ flag is not supported.
**CacheIPv4DNS**;;
Tells the client to remember IPv4 DNS answers we receive from exit
nodes via this connection. (On by default.)
@@ -1127,6 +1141,10 @@ The following options are useful only for clients (that is, if
authentication" when IsolateSOCKSAuth is disabled, or when this
option is set.
+ Flags are processed left to right. If flags conflict, the last flag on the
+ line is used, and all earlier flags are ignored. No error is issued for
+ conflicting flags.
+
[[SocksListenAddress]] **SocksListenAddress** __IP__[:__PORT__]::
Bind to this address to listen for connections from Socks-speaking
applications. (Default: 127.0.0.1) You can also specify a port (e.g.
@@ -1186,7 +1204,9 @@ The following options are useful only for clients (that is, if
If this option is set to 1, we pick a few long-term entry servers, and try
to stick with them. This is desirable because constantly changing servers
increases the odds that an adversary who owns some servers will observe a
- fraction of your paths. (Default: 1)
+ fraction of your paths. Entry Guards can not be used by Directory
+ Authorities, Single Onion Services, and Tor2web clients. In these cases,
+ the this option is ignored. (Default: 1)
[[UseEntryGuardsAsDirGuards]] **UseEntryGuardsAsDirGuards** **0**|**1**::
If this option is set to 1, and UseEntryGuards is also set to 1,
@@ -1257,11 +1277,14 @@ The following options are useful only for clients (that is, if
+
When providing proxy server service to a network of computers using a tool
like dns-proxy-tor, change the IPv4 network to "10.192.0.0/10" or
- "172.16.0.0/12" and change the IPv6 network to "[FC00]/7".
+ "172.16.0.0/12" and change the IPv6 network to "[FC00::]/7".
The default **VirtualAddrNetwork** address ranges on a
properly configured machine will route to the loopback or link-local
- interface. For
- local use, no change to the default VirtualAddrNetwork setting is needed.
+ interface. The maximum number of bits for the network prefix is set to 104
+ for IPv6 and 16 for IPv4. However, a wider network - smaller prefix length
+ - is preferable since it reduces the chances for an attacker to guess the
+ used IP. For local use, no change to the default VirtualAddrNetwork setting
+ is needed.
[[AllowNonRFC953Hostnames]] **AllowNonRFC953Hostnames** **0**|**1**::
When this option is disabled, Tor blocks hostnames containing illegal
@@ -1426,8 +1449,10 @@ The following options are useful only for clients (that is, if
**non-anonymously**. This option also disables client connections to
non-hidden-service hostnames through Tor. It **must only** be used when
running a tor2web Hidden Service web proxy.
- To enable this option the compile time flag --enable-tor2webmode must be
- specified. (Default: 0)
+ To enable this option the compile time flag --enable-tor2web-mode must be
+ specified. Since Tor2webMode is non-anonymous, you can not run an
+ anonymous Hidden Service on a tor version compiled with Tor2webMode.
+ (Default: 0)
[[Tor2webRendezvousPoints]] **Tor2webRendezvousPoints** __node__,__node__,__...__::
A list of identity fingerprints, nicknames, country codes and
@@ -1454,16 +1479,6 @@ The following options are useful only for clients (that is, if
"auto" (recommended) then it is on for all clients that do not set
FetchUselessDescriptors. (Default: auto)
-[[UseNTorHandshake]] **UseNTorHandshake** **0**|**1**|**auto**::
- The "ntor" circuit-creation handshake is faster and (we think) more
- secure than the original ("TAP") circuit handshake, but starting to use
- it too early might make your client stand out. If this option is 0, your
- Tor client won't use the ntor handshake. If it's 1, your Tor client
- will use the ntor handshake to extend circuits through servers that
- support it. If this option is "auto", then your client
- will use the ntor handshake once enough directory authorities recommend
- it. (Default: 1)
-
[[PathBiasCircThreshold]] **PathBiasCircThreshold** __NUM__ +
[[PathBiasNoticeRate]] **PathBiasNoticeRate** __NUM__ +
@@ -1555,8 +1570,9 @@ The following options are useful only for clients (that is, if
that fraction of possible paths. Note that setting this option too low
can make your Tor client less anonymous, and setting it too high can
prevent your Tor client from bootstrapping. If this option is negative,
- Tor will use a default value chosen by the directory
- authorities. (Default: -1.)
+ Tor will use a default value chosen by the directory authorities. If the
+ directory authorities do not choose a value, Tor will default to 0.6.
+ (Default: -1.)
[[ClientBootstrapConsensusAuthorityDownloadSchedule]] **ClientBootstrapConsensusAuthorityDownloadSchedule** __N__,__N__,__...__::
Schedule for when clients should download consensuses from authorities
@@ -1687,15 +1703,16 @@ is non-zero):
used with accept6/reject6.) +
+
Private addresses are rejected by default (at the beginning of your exit
- policy), along with any configured primary public IPv4 and IPv6 addresses,
- and any public IPv4 and IPv6 addresses on any interface on the relay.
+ policy), along with any configured primary public IPv4 and IPv6 addresses.
These private addresses are rejected unless you set the
ExitPolicyRejectPrivate config option to 0. For example, once you've done
that, you could allow HTTP to 127.0.0.1 and block all other connections to
internal networks with "accept 127.0.0.1:80,reject private:\*", though that
may also allow connections to your own computer that are addressed to its
public (external) IP address. See RFC 1918 and RFC 3330 for more details
- about internal and reserved IP address space. +
+ about internal and reserved IP address space. See
+ ExitPolicyRejectLocalInterfaces if you want to block every address on the
+ relay, even those that aren't advertised in the descriptor. +
+
This directive can be specified multiple times so you don't have to put it
all on one line. +
@@ -1725,16 +1742,23 @@ is non-zero):
IPv4 and IPv6 addresses.
[[ExitPolicyRejectPrivate]] **ExitPolicyRejectPrivate** **0**|**1**::
- Reject all private (local) networks, along with any configured public
- IPv4 and IPv6 addresses, at the beginning of your exit policy. (This
- includes the IPv4 and IPv6 addresses advertised by the relay, any
- OutboundBindAddress, and the bind addresses of any port options, such as
- ORPort and DirPort.) This also rejects any public IPv4 and IPv6 addresses
- on any interface on the relay. (If IPv6Exit is not set, all IPv6 addresses
- will be rejected anyway.)
+ Reject all private (local) networks, along with the relay's advertised
+ public IPv4 and IPv6 addresses, at the beginning of your exit policy.
See above entry on ExitPolicy.
(Default: 1)
+[[ExitPolicyRejectLocalInterfaces]] **ExitPolicyRejectLocalInterfaces** **0**|**1**::
+ Reject all IPv4 and IPv6 addresses that the relay knows about, at the
+ beginning of your exit policy. This includes any OutboundBindAddress, the
+ bind addresses of any port options, such as ControlPort or DNSPort, and any
+ public IPv4 and IPv6 addresses on any interface on the relay. (If IPv6Exit
+ is not set, all IPv6 addresses will be rejected anyway.)
+ See above entry on ExitPolicy.
+ This option is off by default, because it lists all public relay IP
+ addresses in the ExitPolicy, even those relay operators might prefer not
+ to disclose.
+ (Default: 0)
+
[[IPv6Exit]] **IPv6Exit** **0**|**1**::
If set, and we are an exit node, allow clients to use us for IPv6
traffic. (Default: 0)
@@ -2035,6 +2059,12 @@ is non-zero):
this. If this option is set to 0, Tor will try to pick a reasonable
default based on your system's physical memory. (Default: 0)
+[[DisableOOSCheck]] **DisableOOSCheck** **0**|**1**::
+ This option disables the code that closes connections when Tor notices
+ that it is running low on sockets. Right now, it is on by default,
+ since the existing out-of-sockets mechanism tends to kill OR connections
+ more than it should. (Default: 1)
+
[[SigningKeyLifetime]] **SigningKeyLifetime** __N__ **days**|**weeks**|**months**::
For how long should each Ed25519 signing key be valid? Tor uses a
permanent master identity key that can be kept offline, and periodically
@@ -2103,14 +2133,13 @@ on the public Tor network.
server. Instead of caching the directory, it generates its own list of
good servers, signs it, and sends that to the clients. Unless the clients
already have you listed as a trusted directory, you probably do not want
- to set this option. Please coordinate with the other admins at
- tor-ops@torproject.org if you think you should be a directory.
+ to set this option.
[[V3AuthoritativeDirectory]] **V3AuthoritativeDirectory** **0**|**1**::
When this option is set in addition to **AuthoritativeDirectory**, Tor
generates version 3 network statuses and serves descriptors, etc as
described in dir-spec.txt file of https://spec.torproject.org/[torspec]
- (for Tor clients and servers running atleast 0.2.0.x).
+ (for Tor clients and servers running at least 0.2.0.x).
[[VersioningAuthoritativeDirectory]] **VersioningAuthoritativeDirectory** **0**|**1**::
When this option is set to 1, Tor adds information on which versions of
@@ -2162,7 +2191,9 @@ on the public Tor network.
[[DirAllowPrivateAddresses]] **DirAllowPrivateAddresses** **0**|**1**::
If set to 1, Tor will accept server descriptors with arbitrary "Address"
elements. Otherwise, if the address is not an IP address or is a private IP
- address, it will reject the server descriptor. (Default: 0)
+ address, it will reject the server descriptor. Additionally, Tor
+ will allow exit policies for private networks to fulfill Exit flag
+ requirements. (Default: 0)
[[AuthDirBadExit]] **AuthDirBadExit** __AddressPattern...__::
Authoritative directories only. A set of address patterns for servers that
@@ -2226,6 +2257,12 @@ on the public Tor network.
in a journal if it is new, or if it differs from the most recently
accepted pinning for one of the keys it contains. (Default: 0)
+[[AuthDirSharedRandomness]] **AuthDirSharedRandomness** **0**|**1**::
+ Authoritative directories only. Switch for the shared random protocol.
+ If zero, the authority won't participate in the protocol. If non-zero
+ (default), the flag "shared-rand-participate" is added to the authority
+ vote indicating participation in the protocol. (Default: 1)
+
[[BridgePassword]] **BridgePassword** __Password__::
If set, contains an HTTP authenticator that tells a bridge authority to
serve all requested bridge information. Used by the (only partially
@@ -2310,7 +2347,8 @@ The following options are used to configure a hidden service.
recent HiddenServiceDir. By default, this option maps the virtual port to
the same port on 127.0.0.1 over TCP. You may override the target port,
address, or both by specifying a target of addr, port, addr:port, or
- **unix:**__path__. (You can specify an IPv6 target as [addr]:port.)
+ **unix:**__path__. (You can specify an IPv6 target as [addr]:port. Unix
+ paths may be quoted, and may use standard C escapes.)
You may also have multiple lines with the same VIRTPORT: when a user
connects to that VIRTPORT, one of the TARGETs from those lines will be
chosen at random.
@@ -2368,6 +2406,40 @@ The following options are used to configure a hidden service.
Number of introduction points the hidden service will have. You can't
have more than 10. (Default: 3)
+[[HiddenServiceSingleHopMode]] **HiddenServiceSingleHopMode** **0**|**1**::
+ **Experimental - Non Anonymous** Hidden Services on a tor instance in
+ HiddenServiceSingleHopMode make one-hop (direct) circuits between the onion
+ service server, and the introduction and rendezvous points. (Onion service
+ descriptors are still posted using 3-hop paths, to avoid onion service
+ directories blocking the service.)
+ This option makes every hidden service instance hosted by a tor instance a
+ Single Onion Service. One-hop circuits make Single Onion servers easily
+ locatable, but clients remain location-anonymous. However, the fact that a
+ client is accessing a Single Onion rather than a Hidden Service may be
+ statistically distinguishable.
+
+ **WARNING:** Once a hidden service directory has been used by a tor
+ instance in HiddenServiceSingleHopMode, it can **NEVER** be used again for
+ a hidden service. It is best practice to create a new hidden service
+ directory, key, and address for each new Single Onion Service and Hidden
+ Service. It is not possible to run Single Onion Services and Hidden
+ Services from the same tor instance: they should be run on different
+ servers with different IP addresses.
+
+ HiddenServiceSingleHopMode requires HiddenServiceNonAnonymousMode to be set
+ to 1. Since a Single Onion service is non-anonymous, you can not configure
+ a SOCKSPort on a tor instance that is running in
+ **HiddenServiceSingleHopMode**.
+ (Default: 0)
+
+[[HiddenServiceNonAnonymousMode]] **HiddenServiceNonAnonymousMode** **0**|**1**::
+ Makes hidden services non-anonymous on this tor instance. Allows the
+ non-anonymous HiddenServiceSingleHopMode. Enables direct connections in the
+ server-side hidden service protocol. If you are using this option,
+ you need to disable all client-side services on your Tor instance,
+ including setting SOCKSPort to "0".
+ (Default: 0)
+
TESTING NETWORK OPTIONS
-----------------------
@@ -2827,6 +2899,10 @@ __HiddenServiceDirectory__**/client_keys**::
Authorization data for a hidden service that is only accessible by
authorized clients.
+__HiddenServiceDirectory__**/onion_service_non_anonymous**::
+ This file is present if a hidden service key was created in
+ **HiddenServiceNonAnonymousMode**.
+
SEE ALSO
--------
**torsocks**(1), **torify**(1) +
diff --git a/m4/pc_from_ucontext.m4 b/m4/pc_from_ucontext.m4
index 6bedcbb2da..8a9dc459e6 100644
--- a/m4/pc_from_ucontext.m4
+++ b/m4/pc_from_ucontext.m4
@@ -79,24 +79,21 @@ AC_DEFUN([AC_PC_FROM_UCONTEXT],
if ! $pc_field_found; then
# Prefer sys/ucontext.h to ucontext.h, for OS X's sake.
if test "x$ac_cv_header_cygwin_signal_h" = xyes; then
- AC_TRY_COMPILE([#define _GNU_SOURCE 1
- #include <cygwin/signal.h>],
+ AC_TRY_COMPILE([#include <cygwin/signal.h>],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
AC_MSG_RESULT([$pc_field])
pc_field_found=true)
elif test "x$ac_cv_header_sys_ucontext_h" = xyes; then
- AC_TRY_COMPILE([#define _GNU_SOURCE 1
- #include <sys/ucontext.h>],
+ AC_TRY_COMPILE([#include <sys/ucontext.h>],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
AC_MSG_RESULT([$pc_field])
pc_field_found=true)
elif test "x$ac_cv_header_ucontext_h" = xyes; then
- AC_TRY_COMPILE([#define _GNU_SOURCE 1
- #include <ucontext.h>],
+ AC_TRY_COMPILE([#include <ucontext.h>],
[ucontext_t u; return u.$pc_field == 0;],
AC_DEFINE_UNQUOTED(PC_FROM_UCONTEXT, $pc_field,
How to access the PC from a struct ucontext)
diff --git a/scripts/maint/checkSpace.pl b/scripts/maint/checkSpace.pl
index 906281112d..e90f5b330b 100755
--- a/scripts/maint/checkSpace.pl
+++ b/scripts/maint/checkSpace.pl
@@ -156,6 +156,25 @@ for $fn (@ARGV) {
$in_func_head = 0;
}
}
+
+ ## Check for forbidden functions except when they are
+ # explicitly permitted
+ if (/\bassert\(/ && not /assert OK/) {
+ print "assert :$fn:$. (use tor_assert)\n";
+ }
+ if (/\bmemcmp\(/ && not /memcmp OK/) {
+ print "memcmp :$fn:$. (use {tor,fast}_mem{eq,neq,cmp}\n";
+ }
+ # always forbidden.
+ if (not / OVERRIDE /) {
+ if (/\bstrcat\(/ or /\bstrcpy\(/ or /\bsprintf\(/) {
+ print "$& :$fn:$.\n";
+ }
+ if (/\bmalloc\(/ or /\bfree\(/ or /\brealloc\(/ or
+ /\bstrdup\(/ or /\bstrndup\(/ or /\bcalloc\(/) {
+ print "$& :$fn:$. (use tor_malloc, tor_free, etc)\n";
+ }
+ }
}
}
## Warn if the file doesn't end with a blank line.
diff --git a/scripts/maint/format_changelog.py b/scripts/maint/format_changelog.py
index 5e4c8cac9a..e909fc550a 100755
--- a/scripts/maint/format_changelog.py
+++ b/scripts/maint/format_changelog.py
@@ -398,16 +398,24 @@ class ChangeLog(object):
self.dumpEndOfSections()
self.dumpEndOfChangelog()
+# Let's turn bugs to html.
+BUG_PAT = re.compile('(bug|ticket|feature)\s+(\d{4,5})', re.I)
+def bug_html(m):
+ return "%s <a href='https://bugs.torproject.org/%s'>%s</a>" % (m.group(1), m.group(2), m.group(2))
+
class HTMLChangeLog(ChangeLog):
def __init__(self, *args, **kwargs):
ChangeLog.__init__(self, *args, **kwargs)
def htmlText(self, graf):
+ output = []
for line in graf:
line = line.rstrip().replace("&","&amp;")
line = line.rstrip().replace("<","&lt;").replace(">","&gt;")
- sys.stdout.write(line.strip())
- sys.stdout.write(" ")
+ output.append(line.strip())
+ output = " ".join(output)
+ output = BUG_PAT.sub(bug_html, output)
+ sys.stdout.write(output)
def htmlPar(self, graf):
sys.stdout.write("<p>")
diff --git a/scripts/maint/lintChanges.py b/scripts/maint/lintChanges.py
index 48edd06fde..b63a4eb3a1 100755
--- a/scripts/maint/lintChanges.py
+++ b/scripts/maint/lintChanges.py
@@ -44,7 +44,6 @@ def lintfile(fname):
warn("bug number {} does not appear".format(bugnum))
lines = contents.split("\n")
- isBug = ("bug" in lines[0] or "fix" in lines[0])
m = re.match(r'^[ ]{2}o ([^\(:]*)([^:]*):', contents)
if not m:
@@ -56,6 +55,10 @@ def lintfile(fname):
'(' not in m.group(2)):
warn("Missing subcategory on %s"%m.group(1))
+ if m:
+ isBug = ("bug" in m.group(1).lower() or "fix" in m.group(1).lower())
+ else:
+ isBug = False
contents = " ".join(contents.split())
diff --git a/scripts/maint/redox.py b/scripts/maint/redox.py
index 5933d49773..43f5b6eb16 100755
--- a/scripts/maint/redox.py
+++ b/scripts/maint/redox.py
@@ -33,8 +33,6 @@
# files that we've snarfed in from somebody else, whose C we do no intend
# to document for them.
SKIP_FILES = [ "OpenBSD_malloc_Linux.c",
- "eventdns.c",
- "eventdns.h",
"strlcat.c",
"strlcpy.c",
"sha256.c",
diff --git a/scripts/test/cov-diff b/scripts/test/cov-diff
index 48dbec9d54..7da7f0be9d 100755
--- a/scripts/test/cov-diff
+++ b/scripts/test/cov-diff
@@ -9,8 +9,8 @@ DIRB="$2"
for A in $DIRA/*; do
B=$DIRB/`basename $A`
- perl -pe 's/^\s*\d+:/ 1:/; s/^([^:]+:)[\d\s]+:/$1/; s/^ *-:(Runs|Programs):.*//;' "$A" > "$A.tmp"
- perl -pe 's/^\s*\d+:/ 1:/; s/^([^:]+:)[\d\s]+:/$1/; s/^ *-:(Runs|Programs):.*//;' "$B" > "$B.tmp"
+ perl -pe 's/^\s*\!*\d+:/ 1:/; s/^([^:]+:)[\d\s]+:/$1/; s/^ *-:(Runs|Programs):.*//;' "$A" > "$A.tmp"
+ perl -pe 's/^\s*\!*\d+:/ 1:/; s/^([^:]+:)[\d\s]+:/$1/; s/^ *-:(Runs|Programs):.*//;' "$B" > "$B.tmp"
diff -u "$A.tmp" "$B.tmp"
rm "$A.tmp" "$B.tmp"
done
diff --git a/scripts/test/cov-exclude b/scripts/test/cov-exclude
new file mode 100755
index 0000000000..5117f11ec4
--- /dev/null
+++ b/scripts/test/cov-exclude
@@ -0,0 +1,28 @@
+#!/usr/bin/perl -p -i
+
+use warnings;
+use strict;
+our $excluding;
+
+# This script is meant to post-process a .gcov file for an input source
+# that was annotated with LCOV_EXCL_START, LCOV_EXCL_STOP, and LCOV_EXCL_LINE
+# entries. It doesn't understand the LCOV_EXCL_BR* variations.
+#
+# It replaces unreached reached lines with x:, and reached excluded lines
+# with !!!num:.
+
+BEGIN { our $excluding = 0; }
+
+if (m/LCOV_EXCL_START/) {
+ $excluding = 1;
+}
+if ($excluding and m/LCOV_EXCL_STOP/) {
+ $excluding = 0;
+}
+
+my $exclude_this = (m/LCOV_EXCL_LINE/);
+
+if ($excluding or $exclude_this) {
+ s{^\s*\#\#+:}{ x:};
+ s{^ (\s*)(\d+):}{$1!!!$2:};
+}
diff --git a/src/common/address.c b/src/common/address.c
index 793a40effc..773e688554 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -131,7 +131,8 @@ tor_addr_to_sockaddr(const tor_addr_t *a,
#endif
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
- memcpy(&sin6->sin6_addr, tor_addr_to_in6(a), sizeof(struct in6_addr));
+ memcpy(&sin6->sin6_addr, tor_addr_to_in6_assert(a),
+ sizeof(struct in6_addr));
return sizeof(struct sockaddr_in6);
} else {
return 0;
@@ -334,7 +335,7 @@ tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr)
} else if (ent->h_addrtype == AF_INET6) {
tor_addr_from_in6(addr, (struct in6_addr*) ent->h_addr);
} else {
- tor_assert(0); /* gethostbyname() returned a bizarre addrtype */
+ tor_assert(0); // LCOV_EXCL_LINE: gethostbyname() returned bizarre type
}
return 0;
}
@@ -905,8 +906,10 @@ tor_addr_is_loopback(const tor_addr_t *addr)
case AF_UNSPEC:
return 0;
default:
+ /* LCOV_EXCL_START */
tor_fragile_assert();
return 0;
+ /* LCOV_EXCL_STOP */
}
}
@@ -1027,7 +1030,7 @@ tor_addr_copy_tight(tor_addr_t *dest, const tor_addr_t *src)
case AF_UNSPEC:
break;
default:
- tor_fragile_assert();
+ tor_fragile_assert(); // LCOV_EXCL_LINE
}
}
@@ -1038,6 +1041,10 @@ tor_addr_copy_tight(tor_addr_t *dest, const tor_addr_t *src)
* Different address families (IPv4 vs IPv6) are always considered unequal if
* <b>how</b> is CMP_EXACT; otherwise, IPv6-mapped IPv4 addresses are
* considered equivalent to their IPv4 equivalents.
+ *
+ * As a special case, all pointer-wise distinct AF_UNIX addresses are always
+ * considered unequal since tor_addr_t currently does not contain the
+ * information required to make the comparison.
*/
int
tor_addr_compare(const tor_addr_t *addr1, const tor_addr_t *addr2,
@@ -1096,6 +1103,7 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
case AF_INET6: {
if (mbits > 128)
mbits = 128;
+
const uint8_t *a1 = tor_addr_to_in6_addr8(addr1);
const uint8_t *a2 = tor_addr_to_in6_addr8(addr2);
const int bytes = mbits >> 3;
@@ -1110,9 +1118,29 @@ tor_addr_compare_masked(const tor_addr_t *addr1, const tor_addr_t *addr2,
return 0;
}
}
+ case AF_UNIX:
+ /* HACKHACKHACKHACKHACK:
+ * tor_addr_t doesn't contain a copy of sun_path, so it's not
+ * possible to comapre this at all.
+ *
+ * Since the only time we currently actually should be comparing
+ * 2 AF_UNIX addresses is when dealing with ISO_CLIENTADDR (which
+ * is disabled for AF_UNIX SocksPorts anyway), this just does
+ * a pointer comparison.
+ *
+ * See: #20261.
+ */
+ if (addr1 < addr2)
+ return -1;
+ else if (addr1 == addr2)
+ return 0;
+ else
+ return 1;
default:
+ /* LCOV_EXCL_START */
tor_fragile_assert();
return 0;
+ /* LCOV_EXCL_STOP */
}
} else if (how == CMP_EXACT) {
/* Unequal families and an exact comparison? Stop now! */
@@ -1165,14 +1193,16 @@ tor_addr_hash(const tor_addr_t *addr)
case AF_INET6:
return siphash24g(&addr->addr.in6_addr.s6_addr, 16);
default:
+ /* LCOV_EXCL_START */
tor_fragile_assert();
return 0;
+ /* LCOV_EXCL_END */
}
}
/** Return a newly allocated string with a representation of <b>addr</b>. */
char *
-tor_dup_addr(const tor_addr_t *addr)
+tor_addr_to_str_dup(const tor_addr_t *addr)
{
char buf[TOR_ADDR_BUF_LEN];
if (tor_addr_to_str(buf, addr, sizeof(buf), 0)) {
@@ -1595,6 +1625,7 @@ get_interface_addresses_raw,(int severity, sa_family_t family))
return result;
#endif
(void) severity;
+ (void) result;
return NULL;
}
@@ -1761,13 +1792,13 @@ MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
{
if (tor_addr_is_loopback(a) ||
tor_addr_is_multicast(a)) {
- SMARTLIST_DEL_CURRENT(addrs, a);
+ SMARTLIST_DEL_CURRENT_KEEPORDER(addrs, a);
tor_free(a);
continue;
}
if (!include_internal && tor_addr_is_internal(a, 0)) {
- SMARTLIST_DEL_CURRENT(addrs, a);
+ SMARTLIST_DEL_CURRENT_KEEPORDER(addrs, a);
tor_free(a);
continue;
}
@@ -1809,7 +1840,7 @@ MOCK_IMPL(smartlist_t *,get_interface_address6_list,(int severity,
/* ======
* IPv4 helpers
- * XXXX024 IPv6 deprecate some of these.
+ * XXXX IPv6 deprecate some of these.
*/
/** Given an address of the form "ip:port", try to divide it into its
diff --git a/src/common/address.h b/src/common/address.h
index 53712bde02..51db42c315 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -74,6 +74,8 @@ typedef struct tor_addr_port_t
#define TOR_ADDR_NULL {AF_UNSPEC, {0}}
static inline const struct in6_addr *tor_addr_to_in6(const tor_addr_t *a);
+static inline const struct in6_addr *tor_addr_to_in6_assert(
+ const tor_addr_t *a);
static inline uint32_t tor_addr_to_ipv4n(const tor_addr_t *a);
static inline uint32_t tor_addr_to_ipv4h(const tor_addr_t *a);
static inline uint32_t tor_addr_to_mapped_ipv4h(const tor_addr_t *a);
@@ -97,21 +99,31 @@ tor_addr_to_in6(const tor_addr_t *a)
return a->family == AF_INET6 ? &a->addr.in6_addr : NULL;
}
+/** As tor_addr_to_in6, but assert that the address truly is an IPv6
+ * address. */
+static inline const struct in6_addr *
+tor_addr_to_in6_assert(const tor_addr_t *a)
+{
+ tor_assert(a->family == AF_INET6);
+ return &a->addr.in6_addr;
+}
+
/** Given an IPv6 address <b>x</b>, yield it as an array of uint8_t.
*
* Requires that <b>x</b> is actually an IPv6 address.
*/
-#define tor_addr_to_in6_addr8(x) tor_addr_to_in6(x)->s6_addr
+#define tor_addr_to_in6_addr8(x) tor_addr_to_in6_assert(x)->s6_addr
+
/** Given an IPv6 address <b>x</b>, yield it as an array of uint16_t.
*
* Requires that <b>x</b> is actually an IPv6 address.
*/
-#define tor_addr_to_in6_addr16(x) S6_ADDR16(*tor_addr_to_in6(x))
+#define tor_addr_to_in6_addr16(x) S6_ADDR16(*tor_addr_to_in6_assert(x))
/** Given an IPv6 address <b>x</b>, yield it as an array of uint32_t.
*
* Requires that <b>x</b> is actually an IPv6 address.
*/
-#define tor_addr_to_in6_addr32(x) S6_ADDR32(*tor_addr_to_in6(x))
+#define tor_addr_to_in6_addr32(x) S6_ADDR32(*tor_addr_to_in6_assert(x))
/** Return an IPv4 address in network order for <b>a</b>, or 0 if
* <b>a</b> is not an IPv4 address. */
@@ -179,7 +191,7 @@ tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u)
#define TOR_ADDR_BUF_LEN 48
int tor_addr_lookup(const char *name, uint16_t family, tor_addr_t *addr_out);
-char *tor_dup_addr(const tor_addr_t *addr) ATTR_MALLOC;
+char *tor_addr_to_str_dup(const tor_addr_t *addr) ATTR_MALLOC;
/** Wrapper function of fmt_addr_impl(). It does not decorate IPv6
* addresses. */
diff --git a/src/common/aes.c b/src/common/aes.c
index 8edfc5d334..35c2d1e3a5 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -23,18 +23,7 @@
#error "We require OpenSSL >= 1.0.0"
#endif
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#endif
-
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic push
-#endif
-/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
- * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#endif
+DISABLE_GCC_WARNING(redundant-decls)
#include <assert.h>
#include <stdlib.h>
@@ -44,13 +33,7 @@
#include <openssl/engine.h>
#include <openssl/modes.h>
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic pop
-#else
-#pragma GCC diagnostic warning "-Wredundant-decls"
-#endif
-#endif
+ENABLE_GCC_WARNING(redundant-decls)
#include "compat.h"
#include "aes.h"
@@ -65,7 +48,7 @@
/* We have five strategies for implementing AES counter mode.
*
- * Best with x86 and x86_64: Use EVP_aes_ctr128() and EVP_EncryptUpdate().
+ * Best with x86 and x86_64: Use EVP_aes_*_ctr() and EVP_EncryptUpdate().
* This is possible with OpenSSL 1.0.1, where the counter-mode implementation
* can use bit-sliced or vectorized AES or AESNI as appropriate.
*
@@ -113,11 +96,17 @@
/* We don't actually define the struct here. */
aes_cnt_cipher_t *
-aes_new_cipher(const char *key, const char *iv)
+aes_new_cipher(const uint8_t *key, const uint8_t *iv, int key_bits)
{
EVP_CIPHER_CTX *cipher = EVP_CIPHER_CTX_new();
- EVP_EncryptInit(cipher, EVP_aes_128_ctr(),
- (const unsigned char*)key, (const unsigned char *)iv);
+ const EVP_CIPHER *c;
+ switch (key_bits) {
+ case 128: c = EVP_aes_128_ctr(); break;
+ case 192: c = EVP_aes_192_ctr(); break;
+ case 256: c = EVP_aes_256_ctr(); break;
+ default: tor_assert(0); // LCOV_EXCL_LINE
+ }
+ EVP_EncryptInit(cipher, c, key, iv);
return (aes_cnt_cipher_t *) cipher;
}
void
@@ -262,9 +251,11 @@ evaluate_ctr_for_aes(void)
if (fast_memneq(output, encrypt_zero, 16)) {
/* Counter mode is buggy */
+ /* LCOV_EXCL_START */
log_err(LD_CRYPTO, "This OpenSSL has a buggy version of counter mode; "
"quitting tor.");
exit(1);
+ /* LCOV_EXCL_STOP */
}
return 0;
}
@@ -275,20 +266,20 @@ evaluate_ctr_for_aes(void)
#define COUNTER(c, n) ((c)->counter ## n)
#endif
-static void aes_set_key(aes_cnt_cipher_t *cipher, const char *key,
+static void aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key,
int key_bits);
-static void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv);
+static void aes_set_iv(aes_cnt_cipher_t *cipher, const uint8_t *iv);
/**
* Return a newly allocated counter-mode AES128 cipher implementation,
* using the 128-bit key <b>key</b> and the 128-bit IV <b>iv</b>.
*/
aes_cnt_cipher_t*
-aes_new_cipher(const char *key, const char *iv)
+aes_new_cipher(const uint8_t *key, const uint8_t *iv, int bits)
{
aes_cnt_cipher_t* result = tor_malloc_zero(sizeof(aes_cnt_cipher_t));
- aes_set_key(result, key, 128);
+ aes_set_key(result, key, bits);
aes_set_iv(result, iv);
return result;
@@ -299,7 +290,7 @@ aes_new_cipher(const char *key, const char *iv)
* the counter to 0.
*/
static void
-aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits)
+aes_set_key(aes_cnt_cipher_t *cipher, const uint8_t *key, int key_bits)
{
if (should_use_EVP) {
const EVP_CIPHER *c = 0;
@@ -307,12 +298,12 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits)
case 128: c = EVP_aes_128_ecb(); break;
case 192: c = EVP_aes_192_ecb(); break;
case 256: c = EVP_aes_256_ecb(); break;
- default: tor_assert(0);
+ default: tor_assert(0); // LCOV_EXCL_LINE
}
- EVP_EncryptInit(&cipher->key.evp, c, (const unsigned char*)key, NULL);
+ EVP_EncryptInit(&cipher->key.evp, c, key, NULL);
cipher->using_evp = 1;
} else {
- AES_set_encrypt_key((const unsigned char *)key, key_bits,&cipher->key.aes);
+ AES_set_encrypt_key(key, key_bits,&cipher->key.aes);
cipher->using_evp = 0;
}
@@ -370,6 +361,8 @@ evp_block128_fn(const uint8_t in[16],
void
aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
{
+ /* Note that the "128" below refers to the length of the counter,
+ * not the length of the AES key. */
if (cipher->using_evp) {
/* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If
* it weren't disabled, it might be better just to use that.
@@ -396,7 +389,7 @@ aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
/** Reset the 128-bit counter of <b>cipher</b> to the 16-bit big-endian value
* in <b>iv</b>. */
static void
-aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv)
+aes_set_iv(aes_cnt_cipher_t *cipher, const uint8_t *iv)
{
#ifdef USING_COUNTER_VARS
cipher->counter3 = ntohl(get_uint32(iv));
diff --git a/src/common/aes.h b/src/common/aes.h
index 821fb742be..1cda53f2fa 100644
--- a/src/common/aes.h
+++ b/src/common/aes.h
@@ -15,7 +15,8 @@
typedef struct aes_cnt_cipher aes_cnt_cipher_t;
-aes_cnt_cipher_t* aes_new_cipher(const char *key, const char *iv);
+aes_cnt_cipher_t* aes_new_cipher(const uint8_t *key, const uint8_t *iv,
+ int key_bits);
void aes_cipher_free(aes_cnt_cipher_t *cipher);
void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len);
diff --git a/src/common/backtrace.c b/src/common/backtrace.c
index 3b762b68e3..81e04e94eb 100644
--- a/src/common/backtrace.c
+++ b/src/common/backtrace.c
@@ -13,9 +13,6 @@
* detect crashes.
*/
-#define __USE_GNU
-#define _GNU_SOURCE 1
-
#include "orconfig.h"
#include "compat.h"
#include "util.h"
@@ -112,13 +109,15 @@ log_backtrace(int severity, int domain, const char *msg)
tor_log(severity, domain, "%s. Stack trace:", msg);
if (!symbols) {
+ /* LCOV_EXCL_START -- we can't provoke this. */
tor_log(severity, domain, " Unable to generate backtrace.");
goto done;
+ /* LCOV_EXCL_STOP */
}
for (i=0; i < depth; ++i) {
tor_log(severity, domain, " %s", symbols[i]);
}
- free(symbols);
+ raw_free(symbols);
done:
tor_mutex_release(&cb_buf_mutex);
@@ -176,8 +175,10 @@ install_bt_handler(void)
for (i = 0; trap_signals[i] >= 0; ++i) {
if (sigaction(trap_signals[i], &sa, NULL) == -1) {
+ /* LCOV_EXCL_START */
log_warn(LD_BUG, "Sigaction failed: %s", strerror(errno));
rv = -1;
+ /* LCOV_EXCL_STOP */
}
}
@@ -189,7 +190,7 @@ install_bt_handler(void)
size_t depth = backtrace(cb_buf, MAX_DEPTH);
symbols = backtrace_symbols(cb_buf, (int) depth);
if (symbols)
- free(symbols);
+ raw_free(symbols);
}
return rv;
diff --git a/src/common/compat.c b/src/common/compat.c
index ede850792f..e16dfb1d2b 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -12,17 +12,6 @@
* the platform.
**/
-/* This is required on rh7 to make strptime not complain.
- * We also need it to make memmem get defined (where available)
- */
-/* XXXX024 We should just use AC_USE_SYSTEM_EXTENSIONS in our autoconf,
- * and get this (and other important stuff!) automatically. Once we do that,
- * make sure to also change the extern char **environ detection in
- * configure.ac, because whether that is declared or not depends on whether
- * we have _GNU_SOURCE defined! Maybe that means that once we take this out,
- * we can also take out the configure check. */
-#define _GNU_SOURCE
-
#define COMPAT_PRIVATE
#include "compat.h"
@@ -44,6 +33,12 @@
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -100,12 +95,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#include "tor_readpassphrase.h"
#endif
-#ifndef HAVE_GETTIMEOFDAY
-#ifdef HAVE_FTIME
-#include <sys/timeb.h>
-#endif
-#endif
-
/* Includes for the process attaching prevention */
#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
/* Only use the linux prctl; the IRIX prctl is totally different */
@@ -127,12 +116,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#ifdef HAVE_SIGNAL_H
#include <signal.h>
#endif
-#ifdef HAVE_UTIME_H
-#include <utime.h>
-#endif
-#ifdef HAVE_SYS_UTIME_H
-#include <sys/utime.h>
-#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
@@ -142,12 +125,6 @@ SecureZeroMemory(PVOID ptr, SIZE_T cnt)
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
-#ifdef TOR_UNIT_TESTS
-#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H)
-/* as fallback implementation for tor_sleep_msec */
-#include <sys/select.h>
-#endif
-#endif
#include "torlog.h"
#include "util.h"
@@ -525,8 +502,10 @@ tor_asprintf(char **strp, const char *fmt, ...)
r = tor_vasprintf(strp, fmt, args);
va_end(args);
if (!*strp || r < 0) {
+ /* LCOV_EXCL_START */
log_err(LD_BUG, "Internal error in asprintf");
tor_assert(0);
+ /* LCOV_EXCL_STOP */
}
return r;
}
@@ -553,7 +532,10 @@ tor_vasprintf(char **strp, const char *fmt, va_list args)
/* On Windows, _vsnprintf won't tell us the length of the string if it
* overflows, so we need to use _vcsprintf to tell how much to allocate */
int len, r;
- len = _vscprintf(fmt, args);
+ va_list tmp_args;
+ va_copy(tmp_args, args);
+ len = _vscprintf(fmt, tmp_args);
+ va_end(tmp_args);
if (len < 0) {
*strp = NULL;
return -1;
@@ -666,7 +648,7 @@ const uint32_t TOR_ISLOWER_TABLE[8] = { 0, 0, 0, 0x7fffffe, 0, 0, 0, 0 };
/** Upper-casing and lowercasing tables to map characters to upper/lowercase
* equivalents. Used by tor_toupper() and tor_tolower(). */
/**@{*/
-const char TOR_TOUPPER_TABLE[256] = {
+const uint8_t TOR_TOUPPER_TABLE[256] = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
@@ -684,7 +666,7 @@ const char TOR_TOUPPER_TABLE[256] = {
224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
};
-const char TOR_TOLOWER_TABLE[256] = {
+const uint8_t TOR_TOLOWER_TABLE[256] = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
@@ -1131,8 +1113,8 @@ tor_close_socket_simple(tor_socket_t s)
/** As tor_close_socket_simple(), but keeps track of the number
* of open sockets. Returns 0 on success, -1 on failure. */
-int
-tor_close_socket(tor_socket_t s)
+MOCK_IMPL(int,
+tor_close_socket,(tor_socket_t s))
{
int r = tor_close_socket_simple(s);
@@ -1154,14 +1136,12 @@ tor_close_socket(tor_socket_t s)
--n_sockets_open;
#else
if (r != EBADF)
- --n_sockets_open;
+ --n_sockets_open; // LCOV_EXCL_LINE -- EIO and EINTR too hard to force.
#endif
r = -1;
}
- if (n_sockets_open < 0)
- log_warn(LD_BUG, "Our socket count is below zero: %d. Please submit a "
- "bug report.", n_sockets_open);
+ tor_assert_nonfatal(n_sockets_open >= 0);
socket_accounting_unlock();
return r;
}
@@ -1204,10 +1184,10 @@ tor_open_socket,(int domain, int type, int protocol))
/** Mockable wrapper for connect(). */
MOCK_IMPL(tor_socket_t,
-tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address,
+tor_connect_socket,(tor_socket_t sock, const struct sockaddr *address,
socklen_t address_len))
{
- return connect(socket,address,address_len);
+ return connect(sock,address,address_len);
}
/** As socket(), but creates a nonblocking socket and
@@ -1382,31 +1362,31 @@ get_n_open_sockets(void)
/** Mockable wrapper for getsockname(). */
MOCK_IMPL(int,
-tor_getsockname,(tor_socket_t socket, struct sockaddr *address,
+tor_getsockname,(tor_socket_t sock, struct sockaddr *address,
socklen_t *address_len))
{
- return getsockname(socket, address, address_len);
+ return getsockname(sock, address, address_len);
}
/** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1
* on failure.
*/
int
-set_socket_nonblocking(tor_socket_t socket)
+set_socket_nonblocking(tor_socket_t sock)
{
#if defined(_WIN32)
unsigned long nonblocking = 1;
- ioctlsocket(socket, FIONBIO, (unsigned long*) &nonblocking);
+ ioctlsocket(sock, FIONBIO, (unsigned long*) &nonblocking);
#else
int flags;
- flags = fcntl(socket, F_GETFL, 0);
+ flags = fcntl(sock, F_GETFL, 0);
if (flags == -1) {
log_warn(LD_NET, "Couldn't get file status flags: %s", strerror(errno));
return -1;
}
flags |= O_NONBLOCK;
- if (fcntl(socket, F_SETFL, flags) == -1) {
+ if (fcntl(sock, F_SETFL, flags) == -1) {
log_warn(LD_NET, "Couldn't set file status flags: %s", strerror(errno));
return -1;
}
@@ -1738,19 +1718,24 @@ set_max_file_descriptors(rlim_t limit, int *max_out)
if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
int bad = 1;
#ifdef OPEN_MAX
- if (errno == EINVAL && OPEN_MAX < rlim.rlim_cur) {
+ uint64_t try_limit = OPEN_MAX - ULIMIT_BUFFER;
+ if (errno == EINVAL && try_limit < (uint64_t) rlim.rlim_cur) {
/* On some platforms, OPEN_MAX is the real limit, and getrlimit() is
* full of nasty lies. I'm looking at you, OSX 10.5.... */
- rlim.rlim_cur = OPEN_MAX;
+ rlim.rlim_cur = try_limit;
if (setrlimit(RLIMIT_NOFILE, &rlim) == 0) {
if (rlim.rlim_cur < (rlim_t)limit) {
log_warn(LD_CONFIG, "We are limited to %lu file descriptors by "
- "OPEN_MAX, and ConnLimit is %lu. Changing ConnLimit; sorry.",
- (unsigned long)OPEN_MAX, (unsigned long)limit);
+ "OPEN_MAX (%lu), and ConnLimit is %lu. Changing "
+ "ConnLimit; sorry.",
+ (unsigned long)try_limit, (unsigned long)OPEN_MAX,
+ (unsigned long)limit);
} else {
- log_info(LD_CONFIG, "Dropped connection limit to OPEN_MAX (%lu); "
- "Apparently, %lu was too high and rlimit lied to us.",
- (unsigned long)OPEN_MAX, (unsigned long)rlim.rlim_max);
+ log_info(LD_CONFIG, "Dropped connection limit to %lu based on "
+ "OPEN_MAX (%lu); Apparently, %lu was too high and rlimit "
+ "lied to us.",
+ (unsigned long)try_limit, (unsigned long)OPEN_MAX,
+ (unsigned long)rlim.rlim_max);
}
bad = 0;
}
@@ -1941,7 +1926,7 @@ tor_getpwnam(const char *username)
return NULL;
if (! strcmp(username, passwd_cached->pw_name))
- return passwd_cached;
+ return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky
return NULL;
}
@@ -1967,7 +1952,7 @@ tor_getpwuid(uid_t uid)
return NULL;
if (uid == passwd_cached->pw_uid)
- return passwd_cached;
+ return passwd_cached; // LCOV_EXCL_LINE - would need to make getpwnam flaky
return NULL;
}
@@ -2350,28 +2335,15 @@ get_parent_directory(char *fname)
static char *
alloc_getcwd(void)
{
- int saved_errno = errno;
-/* We use this as a starting path length. Not too large seems sane. */
-#define START_PATH_LENGTH 128
-/* Nobody has a maxpath longer than this, as far as I know. And if they
- * do, they shouldn't. */
-#define MAX_SANE_PATH_LENGTH 4096
- size_t path_length = START_PATH_LENGTH;
- char *path = tor_malloc(path_length);
-
- errno = 0;
- while (getcwd(path, path_length) == NULL) {
- if (errno == ERANGE && path_length < MAX_SANE_PATH_LENGTH) {
- path_length*=2;
- path = tor_realloc(path, path_length);
- } else {
- tor_free(path);
- path = NULL;
- break;
- }
- }
- errno = saved_errno;
- return path;
+#ifdef PATH_MAX
+#define MAX_CWD PATH_MAX
+#else
+#define MAX_CWD 4096
+#endif
+
+ char path_buf[MAX_CWD];
+ char *path = getcwd(path_buf, sizeof(path_buf));
+ return path ? tor_strdup(path) : NULL;
}
#endif
@@ -2386,7 +2358,7 @@ make_path_absolute(char *fname)
/* We don't want to assume that tor_free can free a string allocated
* with malloc. On failure, return fname (it's better than nothing). */
char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname);
- if (absfname_malloced) free(absfname_malloced);
+ if (absfname_malloced) raw_free(absfname_malloced);
return absfname;
#else
@@ -2402,11 +2374,13 @@ make_path_absolute(char *fname)
tor_asprintf(&absfname, "%s/%s", path, fname);
tor_free(path);
} else {
+ /* LCOV_EXCL_START Can't make getcwd fail. */
/* If getcwd failed, the best we can do here is keep using the
* relative path. (Perhaps / isn't readable by this UID/GID.) */
log_warn(LD_GENERAL, "Unable to find current working directory: %s",
strerror(errno));
absfname = tor_strdup(fname);
+ /* LCOV_EXCL_STOP */
}
}
return absfname;
@@ -2774,7 +2748,9 @@ MOCK_IMPL(const char *, get_uname, (void))
}
#endif
#else
+ /* LCOV_EXCL_START -- can't provoke uname failure */
strlcpy(uname_result, "Unknown platform", sizeof(uname_result));
+ /* LCOV_EXCL_STOP */
#endif
}
uname_result_is_set = 1;
@@ -2848,59 +2824,19 @@ compute_num_cpus(void)
if (num_cpus == -2) {
num_cpus = compute_num_cpus_impl();
tor_assert(num_cpus != -2);
- if (num_cpus > MAX_DETECTABLE_CPUS)
+ if (num_cpus > MAX_DETECTABLE_CPUS) {
+ /* LCOV_EXCL_START */
log_notice(LD_GENERAL, "Wow! I detected that you have %d CPUs. I "
"will not autodetect any more than %d, though. If you "
"want to configure more, set NumCPUs in your torrc",
num_cpus, MAX_DETECTABLE_CPUS);
+ num_cpus = MAX_DETECTABLE_CPUS;
+ /* LCOV_EXCL_STOP */
+ }
}
return num_cpus;
}
-/** Set *timeval to the current time of day. On error, log and terminate.
- * (Same as gettimeofday(timeval,NULL), but never returns -1.)
- */
-void
-tor_gettimeofday(struct timeval *timeval)
-{
-#ifdef _WIN32
- /* Epoch bias copied from perl: number of units between windows epoch and
- * Unix epoch. */
-#define EPOCH_BIAS U64_LITERAL(116444736000000000)
-#define UNITS_PER_SEC U64_LITERAL(10000000)
-#define USEC_PER_SEC U64_LITERAL(1000000)
-#define UNITS_PER_USEC U64_LITERAL(10)
- union {
- uint64_t ft_64;
- FILETIME ft_ft;
- } ft;
- /* number of 100-nsec units since Jan 1, 1601 */
- GetSystemTimeAsFileTime(&ft.ft_ft);
- if (ft.ft_64 < EPOCH_BIAS) {
- log_err(LD_GENERAL,"System time is before 1970; failing.");
- exit(1);
- }
- ft.ft_64 -= EPOCH_BIAS;
- timeval->tv_sec = (unsigned) (ft.ft_64 / UNITS_PER_SEC);
- timeval->tv_usec = (unsigned) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC);
-#elif defined(HAVE_GETTIMEOFDAY)
- if (gettimeofday(timeval, NULL)) {
- log_err(LD_GENERAL,"gettimeofday failed.");
- /* If gettimeofday dies, we have either given a bad timezone (we didn't),
- or segfaulted.*/
- exit(1);
- }
-#elif defined(HAVE_FTIME)
- struct timeb tb;
- ftime(&tb);
- timeval->tv_sec = tb.time;
- timeval->tv_usec = tb.millitm * 1000;
-#else
-#error "No way to get time."
-#endif
- return;
-}
-
#if !defined(_WIN32)
/** Defined iff we need to add locks when defining fake versions of reentrant
* versions of time-related functions. */
@@ -2979,11 +2915,12 @@ correct_tm(int islocal, const time_t *timep, struct tm *resultbuf,
/* If we get here, then gmtime/localtime failed without getting an extreme
* value for *timep */
-
+ /* LCOV_EXCL_START */
tor_fragile_assert();
r = resultbuf;
memset(resultbuf, 0, sizeof(struct tm));
outcome="can't recover";
+ /* LCOV_EXCL_STOP */
done:
log_warn(LD_BUG, "%s("I64_FORMAT") failed with error %s: %s",
islocal?"localtime":"gmtime",
@@ -3377,9 +3314,11 @@ get_total_system_memory_impl(void)
return result * 1024;
err:
+ /* LCOV_EXCL_START Can't reach this unless proc is broken. */
tor_free(s);
close(fd);
return 0;
+ /* LCOV_EXCL_STOP */
#elif defined (_WIN32)
/* Windows has MEMORYSTATUSEX; pretty straightforward. */
MEMORYSTATUSEX ms;
@@ -3428,6 +3367,7 @@ get_total_system_memory(size_t *mem_out)
static size_t mem_cached=0;
uint64_t m = get_total_system_memory_impl();
if (0 == m) {
+ /* LCOV_EXCL_START -- can't make this happen without mocking. */
/* We couldn't find our memory total */
if (0 == mem_cached) {
/* We have no cached value either */
@@ -3437,6 +3377,7 @@ get_total_system_memory(size_t *mem_out)
*mem_out = mem_cached;
return 0;
+ /* LCOV_EXCL_STOP */
}
#if SIZE_MAX != UINT64_MAX
@@ -3453,26 +3394,6 @@ get_total_system_memory(size_t *mem_out)
return 0;
}
-#ifdef TOR_UNIT_TESTS
-/** Delay for <b>msec</b> milliseconds. Only used in tests. */
-void
-tor_sleep_msec(int msec)
-{
-#ifdef _WIN32
- Sleep(msec);
-#elif defined(HAVE_USLEEP)
- sleep(msec / 1000);
- /* Some usleep()s hate sleeping more than 1 sec */
- usleep((msec % 1000) * 1000);
-#elif defined(HAVE_SYS_SELECT_H)
- struct timeval tv = { msec / 1000, (msec % 1000) * 1000};
- select(0, NULL, NULL, NULL, &tv);
-#else
- sleep(CEIL_DIV(msec, 1000));
-#endif
-}
-#endif
-
/** Emit the password prompt <b>prompt</b>, then read up to <b>buflen</b>
* bytes of passphrase into <b>output</b>. Return the number of bytes in
* the passphrase, excluding terminating NUL.
diff --git a/src/common/compat.h b/src/common/compat.h
index 8cf84580c6..ee1c9454de 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -42,6 +42,8 @@
#include <netinet6/in6.h>
#endif
+#include "compat_time.h"
+
#if defined(__has_feature)
# if __has_feature(address_sanitizer)
/* Some of the fancy glibc strcmp() macros include references to memory that
@@ -82,6 +84,44 @@
#define CHECK_SCANF(formatIdx, firstArg)
#endif
+/* What GCC do we have? */
+#ifdef __GNUC__
+#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+#else
+#define GCC_VERSION 0
+#endif
+
+/* Temporarily enable and disable warnings. */
+#ifdef __GNUC__
+# define PRAGMA_STRINGIFY_(s) #s
+# define PRAGMA_JOIN_STRINGIFY_(a,b) PRAGMA_STRINGIFY_(a ## b)
+/* Support for macro-generated pragmas (c99) */
+# define PRAGMA_(x) _Pragma (#x)
+# ifdef __clang__
+# define PRAGMA_DIAGNOSTIC_(x) PRAGMA_(clang diagnostic x)
+# else
+# define PRAGMA_DIAGNOSTIC_(x) PRAGMA_(GCC diagnostic x)
+# endif
+# if defined(__clang__) || GCC_VERSION >= 406
+/* we have push/pop support */
+# define DISABLE_GCC_WARNING(warningopt) \
+ PRAGMA_DIAGNOSTIC_(push) \
+ PRAGMA_DIAGNOSTIC_(ignored PRAGMA_JOIN_STRINGIFY_(-W,warningopt))
+# define ENABLE_GCC_WARNING(warningopt) \
+ PRAGMA_DIAGNOSTIC_(pop)
+# else
+/* 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__ */
+/* not gcc at all */
+# define DISABLE_GCC_WARNING(warning)
+# define ENABLE_GCC_WARNING(warning)
+#endif
+
/* inline is __inline on windows. */
#ifdef _WIN32
#define inline __inline
@@ -320,8 +360,8 @@ DECLARE_CTYPE_FN(ISXDIGIT)
DECLARE_CTYPE_FN(ISPRINT)
DECLARE_CTYPE_FN(ISLOWER)
DECLARE_CTYPE_FN(ISUPPER)
-extern const char TOR_TOUPPER_TABLE[];
-extern const char TOR_TOLOWER_TABLE[];
+extern const uint8_t TOR_TOUPPER_TABLE[];
+extern const uint8_t TOR_TOLOWER_TABLE[];
#define TOR_TOLOWER(c) (TOR_TOLOWER_TABLE[(uint8_t)c])
#define TOR_TOUPPER(c) (TOR_TOUPPER_TABLE[(uint8_t)c])
@@ -341,15 +381,6 @@ const char *tor_fix_source_file(const char *fname);
#endif
/* ===== Time compatibility */
-#if !defined(HAVE_GETTIMEOFDAY) && !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
-
-void tor_gettimeofday(struct timeval *timeval);
struct tm *tor_localtime_r(const time_t *timep, struct tm *result);
struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
@@ -430,7 +461,7 @@ typedef int socklen_t;
#ifdef _WIN32
/* XXX Actually, this should arguably be SOCKET; we use intptr_t here so that
- * any inadvertant checks for the socket being <= 0 or > 0 will probably
+ * any inadvertent checks for the socket being <= 0 or > 0 will probably
* still work. */
#define tor_socket_t intptr_t
#define TOR_SOCKET_T_FORMAT INTPTR_T_FORMAT
@@ -447,7 +478,7 @@ typedef int socklen_t;
#endif
int tor_close_socket_simple(tor_socket_t s);
-int tor_close_socket(tor_socket_t s);
+MOCK_DECL(int, tor_close_socket, (tor_socket_t s));
tor_socket_t tor_open_socket_with_extensions(
int domain, int type, int protocol,
int cloexec, int nonblock);
@@ -699,10 +730,6 @@ char *format_win32_error(DWORD err);
#endif
-#ifdef TOR_UNIT_TESTS
-void tor_sleep_msec(int msec);
-#endif
-
#ifdef COMPAT_PRIVATE
#if !defined(HAVE_SOCKETPAIR) || defined(_WIN32) || defined(TOR_UNIT_TESTS)
#define NEED_ERSATZ_SOCKETPAIR
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index cc58883750..4a3b1af922 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -3,11 +3,8 @@
/**
* \file compat_libevent.c
- * \brief Wrappers to handle porting between different versions of libevent.
- *
- * In an ideal world, we'd just use Libevent 2.0 from now on. But as of June
- * 2012, Libevent 1.4 is still all over, and some poor souls are stuck on
- * Libevent 1.3e. */
+ * \brief Wrappers and utility functions for Libevent.
+ */
#include "orconfig.h"
#include "compat.h"
@@ -19,15 +16,8 @@
#include "util.h"
#include "torlog.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
#include <event2/thread.h>
-#ifdef USE_BUFFEREVENTS
-#include <event2/bufferevent.h>
-#endif
-#else
-#include <event.h>
-#endif
/** A string which, if it appears in a libevent log, should be ignored. */
static const char *suppress_msg = NULL;
@@ -69,6 +59,7 @@ configure_libevent_logging(void)
{
event_set_log_callback(libevent_logging_callback);
}
+
/** Ignore any libevent log message that contains <b>msg</b>. */
void
suppress_libevent_log_msg(const char *msg)
@@ -76,44 +67,6 @@ suppress_libevent_log_msg(const char *msg)
suppress_msg = msg;
}
-#ifndef HAVE_EVENT2_EVENT_H
-/** Work-alike replacement for event_new() on pre-Libevent-2.0 systems. */
-struct event *
-tor_event_new(struct event_base *base, int sock, short what,
- void (*cb)(int, short, void *), void *arg)
-{
- struct event *e = tor_malloc_zero(sizeof(struct event));
- event_set(e, sock, what, cb, arg);
- if (! base)
- base = tor_libevent_get_base();
- event_base_set(base, e);
- return e;
-}
-/** Work-alike replacement for evtimer_new() on pre-Libevent-2.0 systems. */
-struct event *
-tor_evtimer_new(struct event_base *base,
- void (*cb)(int, short, void *), void *arg)
-{
- return tor_event_new(base, -1, 0, cb, arg);
-}
-/** Work-alike replacement for evsignal_new() on pre-Libevent-2.0 systems. */
-struct event *
-tor_evsignal_new(struct event_base * base, int sig,
- void (*cb)(int, short, void *), void *arg)
-{
- return tor_event_new(base, sig, EV_SIGNAL|EV_PERSIST, cb, arg);
-}
-/** Work-alike replacement for event_free() on pre-Libevent-2.0 systems,
- * except tolerate tor_event_free(NULL). */
-void
-tor_event_free(struct event *ev)
-{
- if (ev == NULL)
- return;
- event_del(ev);
- tor_free(ev);
-}
-#else
/* Wrapper for event_free() that tolerates tor_event_free(NULL) */
void
tor_event_free(struct event *ev)
@@ -122,10 +75,9 @@ tor_event_free(struct event *ev)
return;
event_free(ev);
}
-#endif
/** Global event base for use by the main thread. */
-struct event_base *the_event_base = NULL;
+static struct event_base *the_event_base = NULL;
/* This is what passes for version detection on OSX. We set
* MACOSX_KQUEUE_IS_BROKEN to true iff we're on a version of OSX before
@@ -139,17 +91,6 @@ struct event_base *the_event_base = NULL;
#endif
#endif
-#ifdef USE_BUFFEREVENTS
-static int using_iocp_bufferevents = 0;
-static void tor_libevent_set_tick_timeout(int msec_per_tick);
-
-int
-tor_libevent_using_iocp_bufferevents(void)
-{
- return using_iocp_bufferevents;
-}
-#endif
-
/** Initialize the Libevent library and set up the event base. */
void
tor_libevent_initialize(tor_libevent_cfg *torcfg)
@@ -158,89 +99,40 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
/* some paths below don't use torcfg, so avoid unused variable warnings */
(void)torcfg;
-#ifdef HAVE_EVENT2_EVENT_H
{
int attempts = 0;
- int using_threads;
struct event_config *cfg;
- retry:
++attempts;
- using_threads = 0;
cfg = event_config_new();
tor_assert(cfg);
-#if defined(_WIN32) && defined(USE_BUFFEREVENTS)
- if (! torcfg->disable_iocp) {
- evthread_use_windows_threads();
- event_config_set_flag(cfg, EVENT_BASE_FLAG_STARTUP_IOCP);
- using_iocp_bufferevents = 1;
- using_threads = 1;
- } else {
- using_iocp_bufferevents = 0;
- }
-#elif defined(__COVERITY__)
- /* Avoid a 'dead code' warning below. */
- using_threads = ! torcfg->disable_iocp;
-#endif
+ /* Telling Libevent not to try to turn locking on can avoid a needless
+ * socketpair() attempt. */
+ event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK);
- if (!using_threads) {
- /* Telling Libevent not to try to turn locking on can avoid a needless
- * socketpair() attempt. */
- event_config_set_flag(cfg, EVENT_BASE_FLAG_NOLOCK);
- }
-
-#if defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER >= V(2,0,7)
if (torcfg->num_cpus > 0)
event_config_set_num_cpus_hint(cfg, torcfg->num_cpus);
-#endif
-#if LIBEVENT_VERSION_NUMBER >= V(2,0,9)
/* We can enable changelist support with epoll, since we don't give
* Libevent any dup'd fds. This lets us avoid some syscalls. */
event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);
-#endif
the_event_base = event_base_new_with_config(cfg);
event_config_free(cfg);
-
- if (using_threads && the_event_base == NULL && attempts < 2) {
- /* This could be a socketpair() failure, which can happen sometimes on
- * windows boxes with obnoxious firewall rules. Downgrade and try
- * again. */
-#if defined(_WIN32) && defined(USE_BUFFEREVENTS)
- if (torcfg->disable_iocp == 0) {
- log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again "
- "with IOCP disabled.");
- } else
-#endif
- {
- log_warn(LD_GENERAL, "Unable to initialize Libevent. Trying again.");
- }
-
- torcfg->disable_iocp = 1;
- goto retry;
- }
}
-#else
- the_event_base = event_init();
-#endif
if (!the_event_base) {
+ /* LCOV_EXCL_START */
log_err(LD_GENERAL, "Unable to initialize Libevent: cannot continue.");
exit(1);
+ /* LCOV_EXCL_STOP */
}
- /* Making this a NOTICE for now so we can link bugs to a libevent versions
- * or methods better. */
log_info(LD_GENERAL,
"Initialized libevent version %s using method %s. Good.",
event_get_version(), tor_libevent_get_method());
-
-#ifdef USE_BUFFEREVENTS
- tor_libevent_set_tick_timeout(torcfg->msec_per_tick);
-#endif
}
/** Return the current Libevent event base that we're set up to use. */
@@ -255,62 +147,7 @@ tor_libevent_get_base, (void))
const char *
tor_libevent_get_method(void)
{
-#ifdef HAVE_EVENT2_EVENT_H
return event_base_get_method(the_event_base);
-#else
- return event_get_method();
-#endif
-}
-
-/** Return the le_version_t for the version of libevent specified in the
- * string <b>v</b>. If the version is very new or uses an unrecognized
- * version, format, return LE_OTHER. */
-STATIC le_version_t
-tor_decode_libevent_version(const char *v)
-{
- unsigned major, minor, patchlevel;
- char c, e, extra;
- int fields;
-
- /* Try the new preferred "1.4.11-stable" format.
- * Also accept "1.4.14b-stable". */
- fields = tor_sscanf(v, "%u.%u.%u%c%c", &major, &minor, &patchlevel, &c, &e);
- if (fields == 3 ||
- ((fields == 4 || fields == 5 ) && (c == '-' || c == '_')) ||
- (fields == 5 && TOR_ISALPHA(c) && (e == '-' || e == '_'))) {
- return V(major,minor,patchlevel);
- }
-
- /* Try the old "1.3e" format. */
- fields = tor_sscanf(v, "%u.%u%c%c", &major, &minor, &c, &extra);
- if (fields == 3 && TOR_ISALPHA(c)) {
- return V_OLD(major, minor, c);
- } else if (fields == 2) {
- return V(major, minor, 0);
- }
-
- return LE_OTHER;
-}
-
-/** Return an integer representing the binary interface of a Libevent library.
- * Two different versions with different numbers are sure not to be binary
- * compatible. Two different versions with the same numbers have a decent
- * chance of binary compatibility.*/
-STATIC int
-le_versions_compatibility(le_version_t v)
-{
- if (v == LE_OTHER)
- return 0;
- if (v < V_OLD(1,0,'c'))
- return 1;
- else if (v < V(1,4,0))
- return 2;
- else if (v < V(1,4,99))
- return 3;
- else if (v < V(2,0,1))
- return 4;
- else /* Everything 2.0 and later should be compatible. */
- return 5;
}
/** Return a string representation of the version of the currently running
@@ -321,101 +158,14 @@ tor_libevent_get_version_str(void)
return event_get_version();
}
-#if defined(LIBEVENT_VERSION)
-#define HEADER_VERSION LIBEVENT_VERSION
-#elif defined(_EVENT_VERSION)
-#define HEADER_VERSION _EVENT_VERSION
-#endif
-
/** Return a string representation of the version of Libevent that was used
* at compilation time. */
const char *
tor_libevent_get_header_version_str(void)
{
- return HEADER_VERSION;
-}
-
-/** See whether the headers we were built against differ from the library we
- * linked against so much that we're likely to crash. If so, warn the
- * user. */
-void
-tor_check_libevent_header_compatibility(void)
-{
- (void) le_versions_compatibility;
- (void) tor_decode_libevent_version;
-
- /* In libevent versions before 2.0, it's hard to keep binary compatibility
- * between upgrades, and unpleasant to detect when the version we compiled
- * against is unlike the version we have linked against. Here's how. */
-#if defined(HEADER_VERSION)
- /* We have a header-file version and a function-call version. Easy. */
- if (strcmp(HEADER_VERSION, event_get_version())) {
- le_version_t v1, v2;
- int compat1 = -1, compat2 = -1;
- int verybad;
- v1 = tor_decode_libevent_version(HEADER_VERSION);
- v2 = tor_decode_libevent_version(event_get_version());
- compat1 = le_versions_compatibility(v1);
- compat2 = le_versions_compatibility(v2);
-
- verybad = compat1 != compat2;
-
- tor_log(verybad ? LOG_WARN : LOG_NOTICE,
- LD_GENERAL, "We were compiled with headers from version %s "
- "of Libevent, but we're using a Libevent library that says it's "
- "version %s.", HEADER_VERSION, event_get_version());
- if (verybad)
- log_warn(LD_GENERAL, "This will almost certainly make Tor crash.");
- else
- log_info(LD_GENERAL, "I think these versions are binary-compatible.");
- }
-#else
- /* event_get_version but no _EVENT_VERSION. We might be in 1.4.0-beta or
- earlier, where that's normal. To see whether we were compiled with an
- earlier version, let's see whether the struct event defines MIN_HEAP_IDX.
- */
-#ifdef HAVE_STRUCT_EVENT_MIN_HEAP_IDX
- /* The header files are 1.4.0-beta or later. If the version is not
- * 1.4.0-beta, we are incompatible. */
- {
- if (strcmp(event_get_version(), "1.4.0-beta")) {
- log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
- "Libevent 1.4.0-beta header files, whereas you have linked "
- "against Libevent %s. This will probably make Tor crash.",
- event_get_version());
- }
- }
-#else
- /* Our headers are 1.3e or earlier. If the library version is not 1.4.x or
- later, we're probably fine. */
- {
- const char *v = event_get_version();
- if ((v[0] == '1' && v[2] == '.' && v[3] > '3') || v[0] > '1') {
- log_warn(LD_GENERAL, "It's a little hard to tell, but you seem to have "
- "Libevent header file from 1.3e or earlier, whereas you have "
- "linked against Libevent %s. This will probably make Tor "
- "crash.", event_get_version());
- }
- }
-#endif
-
- /* Your libevent is ancient. */
-#endif
+ return LIBEVENT_VERSION;
}
-/*
- If possible, we're going to try to use Libevent's periodic timer support,
- since it does a pretty good job of making sure that periodic events get
- called exactly M seconds apart, rather than starting each one exactly M
- seconds after the time that the last one was run.
- */
-#ifdef HAVE_EVENT2_EVENT_H
-#define HAVE_PERIODIC
-#define PERIODIC_FLAGS EV_PERSIST
-#else
-#define PERIODIC_FLAGS 0
-#endif
-
/** Represents a timer that's run every N microseconds by Libevent. */
struct periodic_timer_t {
/** Underlying event used to implement this periodic event. */
@@ -424,11 +174,6 @@ struct periodic_timer_t {
void (*cb)(struct periodic_timer_t *, void *);
/** User-supplied data for the callback */
void *data;
-#ifndef HAVE_PERIODIC
- /** If Libevent doesn't know how to invoke events every N microseconds,
- * we'll need to remember the timeout interval here. */
- struct timeval tv;
-#endif
};
/** Libevent callback to implement a periodic event. */
@@ -438,10 +183,6 @@ periodic_timer_cb(evutil_socket_t fd, short what, void *arg)
periodic_timer_t *timer = arg;
(void) what;
(void) fd;
-#ifndef HAVE_PERIODIC
- /** reschedule the event as needed. */
- event_add(timer->ev, &timer->tv);
-#endif
timer->cb(timer, timer->data);
}
@@ -459,16 +200,13 @@ periodic_timer_new(struct event_base *base,
tor_assert(tv);
tor_assert(cb);
timer = tor_malloc_zero(sizeof(periodic_timer_t));
- if (!(timer->ev = tor_event_new(base, -1, PERIODIC_FLAGS,
+ if (!(timer->ev = tor_event_new(base, -1, EV_PERSIST,
periodic_timer_cb, timer))) {
tor_free(timer);
return NULL;
}
timer->cb = cb;
timer->data = data;
-#ifndef HAVE_PERIODIC
- memcpy(&timer->tv, tv, sizeof(struct timeval));
-#endif
event_add(timer->ev, (struct timeval *)tv); /*drop const for old libevent*/
return timer;
}
@@ -483,72 +221,19 @@ periodic_timer_free(periodic_timer_t *timer)
tor_free(timer);
}
-#ifdef USE_BUFFEREVENTS
-static const struct timeval *one_tick = NULL;
-/**
- * Return a special timeout to be passed whenever libevent's O(1) timeout
- * implementation should be used. Only use this when the timer is supposed
- * to fire after msec_per_tick ticks have elapsed.
-*/
-const struct timeval *
-tor_libevent_get_one_tick_timeout(void)
-{
- tor_assert(one_tick);
- return one_tick;
-}
-
-/** Initialize the common timeout that we'll use to refill the buckets every
- * time a tick elapses. */
-static void
-tor_libevent_set_tick_timeout(int msec_per_tick)
-{
- struct event_base *base = tor_libevent_get_base();
- struct timeval tv;
-
- tor_assert(! one_tick);
- tv.tv_sec = msec_per_tick / 1000;
- tv.tv_usec = (msec_per_tick % 1000) * 1000;
- one_tick = event_base_init_common_timeout(base, &tv);
-}
-
-static struct bufferevent *
-tor_get_root_bufferevent(struct bufferevent *bev)
-{
- struct bufferevent *u;
- while ((u = bufferevent_get_underlying(bev)) != NULL)
- bev = u;
- return bev;
-}
-
-int
-tor_set_bufferevent_rate_limit(struct bufferevent *bev,
- struct ev_token_bucket_cfg *cfg)
-{
- return bufferevent_set_rate_limit(tor_get_root_bufferevent(bev), cfg);
-}
-
-int
-tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev,
- struct bufferevent_rate_limit_group *g)
-{
- return bufferevent_add_to_rate_limit_group(tor_get_root_bufferevent(bev), g);
-}
-#endif
-
int
tor_init_libevent_rng(void)
{
int rv = 0;
-#ifdef HAVE_EVUTIL_SECURE_RNG_INIT
char buf[256];
if (evutil_secure_rng_init() < 0) {
rv = -1;
}
- /* Older libevent -- manually initialize the RNG */
crypto_rand(buf, 32);
+#ifdef HAVE_EVUTIL_SECURE_RNG_ADD_BYTES
evutil_secure_rng_add_bytes(buf, 32);
- evutil_secure_rng_get_bytes(buf, sizeof(buf));
#endif
+ evutil_secure_rng_get_bytes(buf, sizeof(buf));
return rv;
}
@@ -598,33 +283,3 @@ tor_gettimeofday_cache_set(const struct timeval *tv)
#endif
#endif
-/**
- * As tor_gettimeofday_cached, but can never move backwards in time.
- *
- * The returned value may diverge from wall-clock time, since wall-clock time
- * can trivially be adjusted backwards, and this can't. Don't mix wall-clock
- * time with these values in the same calculation.
- *
- * Depending on implementation, this function may or may not "smooth out" huge
- * jumps forward in wall-clock time. It may or may not keep its results
- * advancing forward (as opposed to stalling) if the wall-clock time goes
- * backwards. The current implementation does neither of of these.
- *
- * This function is not thread-safe; do not call it outside the main thread.
- *
- * In future versions of Tor, this may return a time does not have its
- * origin at the Unix epoch.
- */
-void
-tor_gettimeofday_cached_monotonic(struct timeval *tv)
-{
- struct timeval last_tv = { 0, 0 };
-
- tor_gettimeofday_cached(tv);
- if (timercmp(tv, &last_tv, OP_LT)) {
- memcpy(tv, &last_tv, sizeof(struct timeval));
- } else {
- memcpy(&last_tv, tv, sizeof(struct timeval));
- }
-}
-
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 4b8b300112..c2e34764e4 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -7,40 +7,17 @@
#include "orconfig.h"
#include "testsupport.h"
-struct event;
-struct event_base;
-#ifdef USE_BUFFEREVENTS
-struct bufferevent;
-struct ev_token_bucket_cfg;
-struct bufferevent_rate_limit_group;
-#endif
-
-#ifdef HAVE_EVENT2_EVENT_H
-#include <event2/util.h>
-#elif !defined(EVUTIL_SOCKET_DEFINED)
-#define EVUTIL_SOCKET_DEFINED
-#define evutil_socket_t int
-#endif
+#include <event2/event.h>
void configure_libevent_logging(void);
void suppress_libevent_log_msg(const char *msg);
-#ifdef HAVE_EVENT2_EVENT_H
#define tor_event_new event_new
#define tor_evtimer_new evtimer_new
#define tor_evsignal_new evsignal_new
#define tor_evdns_add_server_port(sock, tcp, cb, data) \
evdns_add_server_port_with_base(tor_libevent_get_base(), \
(sock),(tcp),(cb),(data));
-#else
-struct event *tor_event_new(struct event_base * base, evutil_socket_t sock,
- short what, void (*cb)(evutil_socket_t, short, void *), void *arg);
-struct event *tor_evtimer_new(struct event_base * base,
- void (*cb)(evutil_socket_t, short, void *), void *arg);
-struct event *tor_evsignal_new(struct event_base * base, int sig,
- void (*cb)(evutil_socket_t, short, void *), void *arg);
-#define tor_evdns_add_server_port evdns_add_server_port
-#endif
void tor_event_free(struct event *ev);
@@ -57,12 +34,10 @@ void periodic_timer_free(periodic_timer_t *);
/** Defines a configuration for using libevent with Tor: passed as an argument
* to tor_libevent_initialize() to describe how we want to set up. */
typedef struct tor_libevent_cfg {
- /** Flag: if true, disable IOCP (assuming that it could be enabled). */
- int disable_iocp;
- /** How many CPUs should we use (relevant only with IOCP). */
+ /** How many CPUs should we use (not currently useful). */
int num_cpus;
/** How many milliseconds should we allow between updating bandwidth limits?
- * (relevant only with bufferevents). */
+ * (Not currently useful). */
int msec_per_tick;
} tor_libevent_cfg;
@@ -73,15 +48,6 @@ void tor_check_libevent_header_compatibility(void);
const char *tor_libevent_get_version_str(void);
const char *tor_libevent_get_header_version_str(void);
-#ifdef USE_BUFFEREVENTS
-const struct timeval *tor_libevent_get_one_tick_timeout(void);
-int tor_libevent_using_iocp_bufferevents(void);
-int tor_set_bufferevent_rate_limit(struct bufferevent *bev,
- struct ev_token_bucket_cfg *cfg);
-int tor_add_bufferevent_to_rate_limit_group(struct bufferevent *bev,
- struct bufferevent_rate_limit_group *g);
-#endif
-
int tor_init_libevent_rng(void);
void tor_gettimeofday_cached(struct timeval *tv);
@@ -89,43 +55,20 @@ void tor_gettimeofday_cache_clear(void);
#ifdef TOR_UNIT_TESTS
void tor_gettimeofday_cache_set(const struct timeval *tv);
#endif
-void tor_gettimeofday_cached_monotonic(struct timeval *tv);
#ifdef COMPAT_LIBEVENT_PRIVATE
-/** A number representing a version of Libevent.
- This is a 4-byte number, with the first three bytes representing the
- major, minor, and patchlevel respectively of the library. The fourth
- byte is unused.
+/** Macro: returns the number of a Libevent version as a 4-byte number,
+ with the first three bytes representing the major, minor, and patchlevel
+ respectively of the library. The fourth byte is unused.
This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
- 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
- format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
- to be equivalent to 1.0.1, and so on.
-*/
-typedef uint32_t le_version_t;
-
-/** @{ */
-/** Macros: returns the number of a libevent version as a le_version_t */
+ 2.0.1 or later. */
#define V(major, minor, patch) \
(((major) << 24) | ((minor) << 16) | ((patch) << 8))
-#define V_OLD(major, minor, patch) \
- V((major), (minor), (patch)-'a'+1)
-/** @} */
-
-/** Represetns a version of libevent so old we can't figure out what version
- * it is. */
-#define LE_OLD V(0,0,0)
-/** Represents a version of libevent so weird we can't figure out what version
- * it is. */
-#define LE_OTHER V(0,0,99)
STATIC void
libevent_logging_callback(int severity, const char *msg);
-STATIC le_version_t
-tor_decode_libevent_version(const char *v);
-STATIC int
-le_versions_compatibility(le_version_t v);
#endif
#endif
diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h
index a7bdb0a224..1bfe188075 100644
--- a/src/common/compat_openssl.h
+++ b/src/common/compat_openssl.h
@@ -15,8 +15,9 @@
* \brief compatability definitions for working with different openssl forks
**/
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
-#error "We require OpenSSL >= 1.0.0"
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,1)
+#error "We require OpenSSL >= 1.0.1"
#endif
#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) && \
diff --git a/src/common/compat_pthreads.c b/src/common/compat_pthreads.c
index 962b5fc0e4..c1ae66c1d2 100644
--- a/src/common/compat_pthreads.c
+++ b/src/common/compat_pthreads.c
@@ -10,8 +10,6 @@
* functions.
*/
-#define _GNU_SOURCE
-
#include "orconfig.h"
#include <pthread.h>
#include <signal.h>
@@ -21,11 +19,6 @@
#include "torlog.h"
#include "util.h"
-#ifdef __APPLE__
-#undef CLOCK_MONOTONIC
-#undef HAVE_CLOCK_GETTIME
-#endif
-
/** Wraps a void (*)(void*) function and its argument so we can
* invoke them in a way pthreads would expect.
*/
@@ -109,11 +102,13 @@ void
tor_mutex_init(tor_mutex_t *mutex)
{
if (PREDICT_UNLIKELY(!threads_initialized))
- tor_threads_init();
+ tor_threads_init(); // LCOV_EXCL_LINE
const int err = pthread_mutex_init(&mutex->mutex, &attr_recursive);
if (PREDICT_UNLIKELY(err)) {
+ // LCOV_EXCL_START
log_err(LD_GENERAL, "Error %d creating a mutex.", err);
- tor_fragile_assert();
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
}
}
@@ -123,12 +118,14 @@ void
tor_mutex_init_nonrecursive(tor_mutex_t *mutex)
{
int err;
- if (PREDICT_UNLIKELY(!threads_initialized))
- tor_threads_init();
+ if (!threads_initialized)
+ tor_threads_init(); // LCOV_EXCL_LINE
err = pthread_mutex_init(&mutex->mutex, NULL);
if (PREDICT_UNLIKELY(err)) {
+ // LCOV_EXCL_START
log_err(LD_GENERAL, "Error %d creating a mutex.", err);
- tor_fragile_assert();
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
}
}
@@ -140,8 +137,10 @@ tor_mutex_acquire(tor_mutex_t *m)
tor_assert(m);
err = pthread_mutex_lock(&m->mutex);
if (PREDICT_UNLIKELY(err)) {
+ // LCOV_EXCL_START
log_err(LD_GENERAL, "Error %d locking a mutex.", err);
- tor_fragile_assert();
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
}
}
/** Release the lock <b>m</b> so another thread can have it. */
@@ -152,8 +151,10 @@ tor_mutex_release(tor_mutex_t *m)
tor_assert(m);
err = pthread_mutex_unlock(&m->mutex);
if (PREDICT_UNLIKELY(err)) {
+ // LCOV_EXCL_START
log_err(LD_GENERAL, "Error %d unlocking a mutex.", err);
- tor_fragile_assert();
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
}
}
/** Clean up the mutex <b>m</b> so that it no longer uses any system
@@ -166,8 +167,10 @@ tor_mutex_uninit(tor_mutex_t *m)
tor_assert(m);
err = pthread_mutex_destroy(&m->mutex);
if (PREDICT_UNLIKELY(err)) {
+ // LCOV_EXCL_START
log_err(LD_GENERAL, "Error %d destroying a mutex.", err);
- tor_fragile_assert();
+ tor_assert_unreached();
+ // LCOV_EXCL_STOP
}
}
/** Return an integer representing this thread. */
@@ -197,14 +200,21 @@ tor_cond_init(tor_cond_t *cond)
return -1;
}
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) \
- && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
+#if defined(HAVE_CLOCK_GETTIME)
+#if defined(CLOCK_MONOTONIC) && defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
/* 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;
}
-#endif
+#define USE_COND_CLOCK CLOCK_MONOTONIC
+#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 */
if (pthread_cond_init(&cond->cond, &condattr)) {
return -1;
}
@@ -217,8 +227,10 @@ void
tor_cond_uninit(tor_cond_t *cond)
{
if (pthread_cond_destroy(&cond->cond)) {
+ // LCOV_EXCL_START
log_warn(LD_GENERAL,"Error freeing condition: %s", strerror(errno));
return;
+ // LCOV_EXCL_STOP
}
}
/** Wait until one of the tor_cond_signal functions is called on <b>cond</b>.
@@ -239,7 +251,7 @@ tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex, const struct timeval *tv)
/* EINTR should be impossible according to POSIX, but POSIX, like the
* Pirate's Code, is apparently treated "more like what you'd call
* guidelines than actual rules." */
- continue;
+ continue; // LCOV_EXCL_LINE
}
return r ? -1 : 0;
}
@@ -247,12 +259,12 @@ tor_cond_wait(tor_cond_t *cond, tor_mutex_t *mutex, const struct timeval *tv)
struct timeval tvnow, tvsum;
struct timespec ts;
while (1) {
-#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
- if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) {
+#if defined(HAVE_CLOCK_GETTIME) && defined(USE_COND_CLOCK)
+ if (clock_gettime(USE_COND_CLOCK, &ts) < 0) {
return -1;
}
tvnow.tv_sec = ts.tv_sec;
- tvnow.tv_usec = ts.tv_nsec / 1000;
+ tvnow.tv_usec = (int)(ts.tv_nsec / 1000);
timeradd(tv, &tvnow, &tvsum);
#else
if (gettimeofday(&tvnow, NULL) < 0)
diff --git a/src/common/compat_threads.c b/src/common/compat_threads.c
index 8f9001258a..f4809060d6 100644
--- a/src/common/compat_threads.c
+++ b/src/common/compat_threads.c
@@ -11,8 +11,6 @@
* modules.)
*/
-#define _GNU_SOURCE
-
#include "orconfig.h"
#include <stdlib.h>
#include "compat.h"
@@ -63,8 +61,8 @@ tor_cond_t *
tor_cond_new(void)
{
tor_cond_t *cond = tor_malloc(sizeof(tor_cond_t));
- if (tor_cond_init(cond)<0)
- tor_free(cond);
+ if (BUG(tor_cond_init(cond)<0))
+ tor_free(cond); // LCOV_EXCL_LINE
return cond;
}
@@ -242,8 +240,11 @@ alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
if (socks[0] >= 0) {
if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) < 0 ||
set_socket_nonblocking(socks[0]) < 0) {
+ // LCOV_EXCL_START -- if eventfd succeeds, fcntl will.
+ tor_assert_nonfatal_unreached();
close(socks[0]);
return -1;
+ // LCOV_EXCL_STOP
}
}
}
@@ -277,9 +278,12 @@ alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
fcntl(socks[1], F_SETFD, FD_CLOEXEC) < 0 ||
set_socket_nonblocking(socks[0]) < 0 ||
set_socket_nonblocking(socks[1]) < 0) {
+ // LCOV_EXCL_START -- if pipe succeeds, you can fcntl the output
+ tor_assert_nonfatal_unreached();
close(socks[0]);
close(socks[1]);
return -1;
+ // LCOV_EXCL_STOP
}
socks_out->read_fd = socks[0];
socks_out->write_fd = socks[1];
@@ -294,9 +298,12 @@ alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
tor_socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == 0) {
if (set_socket_nonblocking(socks[0]) < 0 ||
set_socket_nonblocking(socks[1])) {
+ // LCOV_EXCL_START -- if socketpair worked, you can make it nonblocking.
+ tor_assert_nonfatal_unreached();
tor_close_socket(socks[0]);
tor_close_socket(socks[1]);
return -1;
+ // LCOV_EXCL_STOP
}
socks_out->read_fd = socks[0];
socks_out->write_fd = socks[1];
diff --git a/src/common/compat_time.c b/src/common/compat_time.c
new file mode 100644
index 0000000000..d044bbe1d7
--- /dev/null
+++ b/src/common/compat_time.c
@@ -0,0 +1,657 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file compat_time.c
+ * \brief Portable wrappers for finding out the current time, running
+ * timers, etc.
+ **/
+
+#define COMPAT_TIME_PRIVATE
+#include "compat.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#include <windows.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef TOR_UNIT_TESTS
+#if !defined(HAVE_USLEEP) && defined(HAVE_SYS_SELECT_H)
+/* as fallback implementation for tor_sleep_msec */
+#include <sys/select.h>
+#endif
+#endif
+
+#ifdef __APPLE__
+#include <mach/mach_time.h>
+#endif
+
+#include "torlog.h"
+#include "util.h"
+#include "container.h"
+
+#ifndef HAVE_GETTIMEOFDAY
+#ifdef HAVE_FTIME
+#include <sys/timeb.h>
+#endif
+#endif
+
+#ifdef _WIN32
+#undef HAVE_CLOCK_GETTIME
+#endif
+
+#ifdef TOR_UNIT_TESTS
+/** Delay for <b>msec</b> milliseconds. Only used in tests. */
+void
+tor_sleep_msec(int msec)
+{
+#ifdef _WIN32
+ Sleep(msec);
+#elif defined(HAVE_USLEEP)
+ sleep(msec / 1000);
+ /* Some usleep()s hate sleeping more than 1 sec */
+ usleep((msec % 1000) * 1000);
+#elif defined(HAVE_SYS_SELECT_H)
+ struct timeval tv = { msec / 1000, (msec % 1000) * 1000};
+ select(0, NULL, NULL, NULL, &tv);
+#else
+ sleep(CEIL_DIV(msec, 1000));
+#endif
+}
+#endif
+
+/** Set *timeval to the current time of day. On error, log and terminate.
+ * (Same as gettimeofday(timeval,NULL), but never returns -1.)
+ */
+void
+tor_gettimeofday(struct timeval *timeval)
+{
+#ifdef _WIN32
+ /* Epoch bias copied from perl: number of units between windows epoch and
+ * Unix epoch. */
+#define EPOCH_BIAS U64_LITERAL(116444736000000000)
+#define UNITS_PER_SEC U64_LITERAL(10000000)
+#define USEC_PER_SEC U64_LITERAL(1000000)
+#define UNITS_PER_USEC U64_LITERAL(10)
+ union {
+ uint64_t ft_64;
+ FILETIME ft_ft;
+ } ft;
+ /* number of 100-nsec units since Jan 1, 1601 */
+ GetSystemTimeAsFileTime(&ft.ft_ft);
+ if (ft.ft_64 < EPOCH_BIAS) {
+ /* LCOV_EXCL_START */
+ log_err(LD_GENERAL,"System time is before 1970; failing.");
+ exit(1);
+ /* LCOV_EXCL_STOP */
+ }
+ ft.ft_64 -= EPOCH_BIAS;
+ timeval->tv_sec = (unsigned) (ft.ft_64 / UNITS_PER_SEC);
+ timeval->tv_usec = (unsigned) ((ft.ft_64 / UNITS_PER_USEC) % USEC_PER_SEC);
+#elif defined(HAVE_GETTIMEOFDAY)
+ if (gettimeofday(timeval, NULL)) {
+ /* LCOV_EXCL_START */
+ log_err(LD_GENERAL,"gettimeofday failed.");
+ /* If gettimeofday dies, we have either given a bad timezone (we didn't),
+ or segfaulted.*/
+ exit(1);
+ /* LCOV_EXCL_STOP */
+ }
+#elif defined(HAVE_FTIME)
+ struct timeb tb;
+ ftime(&tb);
+ timeval->tv_sec = tb.time;
+ timeval->tv_usec = tb.millitm * 1000;
+#else
+#error "No way to get time."
+#endif
+ return;
+}
+
+#define ONE_MILLION ((int64_t) (1000 * 1000))
+#define ONE_BILLION ((int64_t) (1000 * 1000 * 1000))
+
+/** True iff monotime_init has been called. */
+static int monotime_initialized = 0;
+
+static monotime_t initialized_at;
+#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
+static monotime_coarse_t initialized_at_coarse;
+#endif
+
+#ifdef TOR_UNIT_TESTS
+/** True if we are running unit tests and overriding the current monotonic
+ * time. Note that mocked monotonic time might not be monotonic.
+ */
+static int monotime_mocking_enabled = 0;
+static monotime_t initialized_at_saved;
+
+static int64_t mock_time_nsec = 0;
+#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
+static int64_t mock_time_nsec_coarse = 0;
+static monotime_coarse_t initialized_at_coarse_saved;
+#endif
+
+void
+monotime_enable_test_mocking(void)
+{
+ if (BUG(monotime_initialized == 0)) {
+ monotime_init();
+ }
+
+ tor_assert_nonfatal(monotime_mocking_enabled == 0);
+ monotime_mocking_enabled = 1;
+ memcpy(&initialized_at_saved,
+ &initialized_at, sizeof(monotime_t));
+ memset(&initialized_at, 0, sizeof(monotime_t));
+#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
+ memcpy(&initialized_at_coarse_saved,
+ &initialized_at_coarse, sizeof(monotime_coarse_t));
+ memset(&initialized_at_coarse, 0, sizeof(monotime_coarse_t));
+#endif
+}
+
+void
+monotime_disable_test_mocking(void)
+{
+ tor_assert_nonfatal(monotime_mocking_enabled == 1);
+ monotime_mocking_enabled = 0;
+
+ memcpy(&initialized_at,
+ &initialized_at_saved, sizeof(monotime_t));
+#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
+ memcpy(&initialized_at_coarse,
+ &initialized_at_coarse_saved, sizeof(monotime_coarse_t));
+#endif
+}
+
+void
+monotime_set_mock_time_nsec(int64_t nsec)
+{
+ tor_assert_nonfatal(monotime_mocking_enabled == 1);
+ mock_time_nsec = nsec;
+}
+
+#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
+void
+monotime_coarse_set_mock_time_nsec(int64_t nsec)
+{
+ tor_assert_nonfatal(monotime_mocking_enabled == 1);
+ mock_time_nsec_coarse = nsec;
+}
+#endif
+#endif
+
+/* "ratchet" functions for monotonic time. */
+
+#if defined(_WIN32) || defined(TOR_UNIT_TESTS)
+
+/** Protected by lock: last value returned by monotime_get(). */
+static int64_t last_pctr = 0;
+/** Protected by lock: offset we must add to monotonic time values. */
+static int64_t pctr_offset = 0;
+/* If we are using GetTickCount(), how many times has it rolled over? */
+static uint32_t rollover_count = 0;
+/* If we are using GetTickCount(), what's the last value it returned? */
+static int64_t last_tick_count = 0;
+
+/** Helper for windows: Called with a sequence of times that are supposed
+ * to be monotonic; increments them as appropriate so that they actually
+ * _are_ monotonic.
+ *
+ * Caller must hold lock. */
+STATIC int64_t
+ratchet_performance_counter(int64_t count_raw)
+{
+ /* must hold lock */
+ const int64_t count_adjusted = count_raw + pctr_offset;
+
+ if (PREDICT_UNLIKELY(count_adjusted < last_pctr)) {
+ /* Monotonicity failed! Pretend no time elapsed. */
+ pctr_offset = last_pctr - count_raw;
+ return last_pctr;
+ } else {
+ last_pctr = count_adjusted;
+ return count_adjusted;
+ }
+}
+
+STATIC int64_t
+ratchet_coarse_performance_counter(const int64_t count_raw)
+{
+ int64_t count = count_raw + (((int64_t)rollover_count) << 32);
+ while (PREDICT_UNLIKELY(count < last_tick_count)) {
+ ++rollover_count;
+ count = count_raw + (((int64_t)rollover_count) << 32);
+ }
+ last_tick_count = count;
+ return count;
+}
+#endif
+
+#if defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS)
+static struct timeval last_timeofday = { 0, 0 };
+static struct timeval timeofday_offset = { 0, 0 };
+
+/** Helper for gettimeofday(): Called with a sequence of times that are
+ * supposed to be monotonic; increments them as appropriate so that they
+ * actually _are_ monotonic.
+ *
+ * Caller must hold lock. */
+STATIC void
+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, <))) {
+ /* time ran backwards. Instead, declare that no time occurred. */
+ timersub(&last_timeofday, timeval_raw, &timeofday_offset);
+ memcpy(out, &last_timeofday, sizeof(struct timeval));
+ } else {
+ memcpy(&last_timeofday, out, sizeof(struct timeval));
+ }
+}
+#endif
+
+#ifdef TOR_UNIT_TESTS
+/** For testing: reset all the ratchets */
+void
+monotime_reset_ratchets_for_testing(void)
+{
+ last_pctr = pctr_offset = last_tick_count = 0;
+ rollover_count = 0;
+ memset(&last_timeofday, 0, sizeof(struct timeval));
+ memset(&timeofday_offset, 0, sizeof(struct timeval));
+}
+#endif
+
+#ifdef __APPLE__
+
+/** Initialized on startup: tells is how to convert from ticks to
+ * nanoseconds.
+ */
+static struct mach_timebase_info mach_time_info;
+
+static void
+monotime_init_internal(void)
+{
+ tor_assert(!monotime_initialized);
+ int r = mach_timebase_info(&mach_time_info);
+ tor_assert(r == 0);
+ tor_assert(mach_time_info.denom != 0);
+}
+
+/**
+ * Set "out" to the most recent monotonic time value
+ */
+void
+monotime_get(monotime_t *out)
+{
+#ifdef TOR_UNIT_TESTS
+ if (monotime_mocking_enabled) {
+ out->abstime_ = (mock_time_nsec * mach_time_info.denom)
+ / mach_time_info.numer;
+ return;
+ }
+#endif
+ out->abstime_ = mach_absolute_time();
+}
+
+/**
+ * Return the number of nanoseconds between <b>start</b> and <b>end</b>.
+ */
+int64_t
+monotime_diff_nsec(const monotime_t *start,
+ const monotime_t *end)
+{
+ if (BUG(mach_time_info.denom == 0)) {
+ monotime_init();
+ }
+ const int64_t diff_ticks = end->abstime_ - start->abstime_;
+ const int64_t diff_nsec =
+ (diff_ticks * mach_time_info.numer) / mach_time_info.denom;
+ return diff_nsec;
+}
+
+/* end of "__APPLE__" */
+#elif defined(HAVE_CLOCK_GETTIME)
+
+#ifdef CLOCK_MONOTONIC_COARSE
+/**
+ * Which clock should we use for coarse-grained monotonic time? By default
+ * this is CLOCK_MONOTONIC_COARSE, but it might not work -- for example,
+ * if we're compiled with newer Linux headers and then we try to run on
+ * an old Linux kernel. In that case, we will fall back to CLOCK_MONOTONIC.
+ */
+static int clock_monotonic_coarse = CLOCK_MONOTONIC_COARSE;
+#endif
+
+static void
+monotime_init_internal(void)
+{
+#ifdef CLOCK_MONOTONIC_COARSE
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) < 0) {
+ log_info(LD_GENERAL, "CLOCK_MONOTONIC_COARSE isn't working (%s); "
+ "falling back to CLOCK_MONOTONIC.", strerror(errno));
+ clock_monotonic_coarse = CLOCK_MONOTONIC;
+ }
+#endif
+}
+
+void
+monotime_get(monotime_t *out)
+{
+#ifdef TOR_UNIT_TESTS
+ if (monotime_mocking_enabled) {
+ out->ts_.tv_sec = (time_t) (mock_time_nsec / ONE_BILLION);
+ out->ts_.tv_nsec = (int) (mock_time_nsec % ONE_BILLION);
+ return;
+ }
+#endif
+ int r = clock_gettime(CLOCK_MONOTONIC, &out->ts_);
+ tor_assert(r == 0);
+}
+
+#ifdef CLOCK_MONOTONIC_COARSE
+void
+monotime_coarse_get(monotime_coarse_t *out)
+{
+#ifdef TOR_UNIT_TESTS
+ if (monotime_mocking_enabled) {
+ out->ts_.tv_sec = (time_t) (mock_time_nsec_coarse / ONE_BILLION);
+ out->ts_.tv_nsec = (int) (mock_time_nsec_coarse % ONE_BILLION);
+ return;
+ }
+#endif
+ int r = clock_gettime(clock_monotonic_coarse, &out->ts_);
+ if (PREDICT_UNLIKELY(r < 0) &&
+ errno == EINVAL &&
+ clock_monotonic_coarse == CLOCK_MONOTONIC_COARSE) {
+ /* We should have caught this at startup in monotime_init_internal!
+ */
+ log_warn(LD_BUG, "Falling back to non-coarse monotonic time %s initial "
+ "system start?", monotime_initialized?"after":"without");
+ clock_monotonic_coarse = CLOCK_MONOTONIC;
+ r = clock_gettime(clock_monotonic_coarse, &out->ts_);
+ }
+
+ tor_assert(r == 0);
+}
+#endif
+
+int64_t
+monotime_diff_nsec(const monotime_t *start,
+ const monotime_t *end)
+{
+ const int64_t diff_sec = end->ts_.tv_sec - start->ts_.tv_sec;
+ const int64_t diff_nsec = diff_sec * ONE_BILLION +
+ (end->ts_.tv_nsec - start->ts_.tv_nsec);
+
+ return diff_nsec;
+}
+
+/* end of "HAVE_CLOCK_GETTIME" */
+#elif defined (_WIN32)
+
+/** Result of QueryPerformanceFrequency, in terms needed to
+ * convert ticks to nanoseconds. */
+static int64_t nsec_per_tick_numer = 1;
+static int64_t nsec_per_tick_denom = 1;
+
+/** Lock to protect last_pctr and pctr_offset */
+static CRITICAL_SECTION monotime_lock;
+/** Lock to protect rollover_count and last_tick_count */
+static CRITICAL_SECTION monotime_coarse_lock;
+
+typedef ULONGLONG (WINAPI *GetTickCount64_fn_t)(void);
+static GetTickCount64_fn_t GetTickCount64_fn = NULL;
+
+static void
+monotime_init_internal(void)
+{
+ tor_assert(!monotime_initialized);
+ BOOL ok = InitializeCriticalSectionAndSpinCount(&monotime_lock, 200);
+ tor_assert(ok);
+ ok = InitializeCriticalSectionAndSpinCount(&monotime_coarse_lock, 200);
+ tor_assert(ok);
+ LARGE_INTEGER li;
+ ok = QueryPerformanceFrequency(&li);
+ tor_assert(ok);
+ tor_assert(li.QuadPart);
+
+ uint64_t n = ONE_BILLION;
+ uint64_t d = li.QuadPart;
+ /* We need to simplify this or we'll probably overflow the int64. */
+ simplify_fraction64(&n, &d);
+ tor_assert(n <= INT64_MAX);
+ tor_assert(d <= INT64_MAX);
+
+ nsec_per_tick_numer = (int64_t) n;
+ nsec_per_tick_denom = (int64_t) d;
+
+ last_pctr = 0;
+ pctr_offset = 0;
+
+ HANDLE h = load_windows_system_library(TEXT("kernel32.dll"));
+ if (h) {
+ GetTickCount64_fn = (GetTickCount64_fn_t)
+ GetProcAddress(h, "GetTickCount64");
+ }
+ // FreeLibrary(h) ?
+}
+
+void
+monotime_get(monotime_t *out)
+{
+ if (BUG(monotime_initialized == 0)) {
+ monotime_init();
+ }
+
+#ifdef TOR_UNIT_TESTS
+ if (monotime_mocking_enabled) {
+ out->pcount_ = (mock_time_nsec * nsec_per_tick_denom)
+ / nsec_per_tick_numer;
+ return;
+ }
+#endif
+
+ /* Alas, QueryPerformanceCounter is not always monotonic: see bug list at
+
+ https://www.python.org/dev/peps/pep-0418/#windows-queryperformancecounter
+ */
+
+ EnterCriticalSection(&monotime_lock);
+ LARGE_INTEGER res;
+ BOOL ok = QueryPerformanceCounter(&res);
+ tor_assert(ok);
+ const int64_t count_raw = res.QuadPart;
+ out->pcount_ = ratchet_performance_counter(count_raw);
+ LeaveCriticalSection(&monotime_lock);
+}
+
+void
+monotime_coarse_get(monotime_coarse_t *out)
+{
+#ifdef TOR_UNIT_TESTS
+ if (monotime_mocking_enabled) {
+ out->tick_count_ = mock_time_nsec_coarse / ONE_MILLION;
+ return;
+ }
+#endif
+
+ if (GetTickCount64_fn) {
+ out->tick_count_ = (int64_t)GetTickCount64_fn();
+ } else {
+ EnterCriticalSection(&monotime_coarse_lock);
+ DWORD tick = GetTickCount();
+ out->tick_count_ = ratchet_coarse_performance_counter(tick);
+ LeaveCriticalSection(&monotime_coarse_lock);
+ }
+}
+
+int64_t
+monotime_diff_nsec(const monotime_t *start,
+ const monotime_t *end)
+{
+ if (BUG(monotime_initialized == 0)) {
+ monotime_init();
+ }
+ const int64_t diff_ticks = end->pcount_ - start->pcount_;
+ return (diff_ticks * nsec_per_tick_numer) / nsec_per_tick_denom;
+}
+
+int64_t
+monotime_coarse_diff_msec(const monotime_coarse_t *start,
+ const monotime_coarse_t *end)
+{
+ const int64_t diff_ticks = end->tick_count_ - start->tick_count_;
+ return diff_ticks;
+}
+
+int64_t
+monotime_coarse_diff_usec(const monotime_coarse_t *start,
+ const monotime_coarse_t *end)
+{
+ return monotime_coarse_diff_msec(start, end) * 1000;
+}
+
+int64_t
+monotime_coarse_diff_nsec(const monotime_coarse_t *start,
+ const monotime_coarse_t *end)
+{
+ return monotime_coarse_diff_msec(start, end) * ONE_MILLION;
+}
+
+/* end of "_WIN32" */
+#elif defined(MONOTIME_USING_GETTIMEOFDAY)
+
+static tor_mutex_t monotime_lock;
+
+/** Initialize the monotonic timer subsystem. */
+static void
+monotime_init_internal(void)
+{
+ tor_assert(!monotime_initialized);
+ tor_mutex_init(&monotime_lock);
+}
+
+void
+monotime_get(monotime_t *out)
+{
+ if (BUG(monotime_initialized == 0)) {
+ monotime_init();
+ }
+
+ tor_mutex_acquire(&monotime_lock);
+ struct timeval timeval_raw;
+ tor_gettimeofday(&timeval_raw);
+ ratchet_timeval(&timeval_raw, &out->tv_);
+ tor_mutex_release(&monotime_lock);
+}
+
+int64_t
+monotime_diff_nsec(const monotime_t *start,
+ const monotime_t *end)
+{
+ struct timeval diff;
+ timersub(&end->tv_, &start->tv_, &diff);
+ return (diff.tv_sec * ONE_BILLION + diff.tv_usec * 1000);
+}
+
+/* end of "MONOTIME_USING_GETTIMEOFDAY" */
+#else
+#error "No way to implement monotonic timers."
+#endif
+
+/**
+ * Initialize the monotonic timer subsystem. Must be called before any
+ * monotonic timer functions. This function is idempotent.
+ */
+void
+monotime_init(void)
+{
+ if (!monotime_initialized) {
+ monotime_init_internal();
+ monotime_initialized = 1;
+ monotime_get(&initialized_at);
+#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
+ monotime_coarse_get(&initialized_at_coarse);
+#endif
+ }
+}
+
+int64_t
+monotime_diff_usec(const monotime_t *start,
+ const monotime_t *end)
+{
+ const int64_t nsec = monotime_diff_nsec(start, end);
+ return CEIL_DIV(nsec, 1000);
+}
+
+int64_t
+monotime_diff_msec(const monotime_t *start,
+ const monotime_t *end)
+{
+ const int64_t nsec = monotime_diff_nsec(start, end);
+ return CEIL_DIV(nsec, ONE_MILLION);
+}
+
+uint64_t
+monotime_absolute_nsec(void)
+{
+ monotime_t now;
+ if (BUG(monotime_initialized == 0)) {
+ monotime_init();
+ }
+
+ monotime_get(&now);
+ return monotime_diff_nsec(&initialized_at, &now);
+}
+
+uint64_t
+monotime_absolute_usec(void)
+{
+ return monotime_absolute_nsec() / 1000;
+}
+
+uint64_t
+monotime_absolute_msec(void)
+{
+ return monotime_absolute_nsec() / ONE_MILLION;
+}
+
+#ifdef MONOTIME_COARSE_FN_IS_DIFFERENT
+uint64_t
+monotime_coarse_absolute_nsec(void)
+{
+ if (BUG(monotime_initialized == 0)) {
+ monotime_init();
+ }
+
+ monotime_coarse_t now;
+ monotime_coarse_get(&now);
+ return monotime_coarse_diff_nsec(&initialized_at_coarse, &now);
+}
+
+uint64_t
+monotime_coarse_absolute_usec(void)
+{
+ return monotime_coarse_absolute_nsec() / 1000;
+}
+
+uint64_t
+monotime_coarse_absolute_msec(void)
+{
+ return monotime_coarse_absolute_nsec() / ONE_MILLION;
+}
+#endif
+
diff --git a/src/common/compat_time.h b/src/common/compat_time.h
new file mode 100644
index 0000000000..2262446e57
--- /dev/null
+++ b/src/common/compat_time.h
@@ -0,0 +1,162 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file compat_time.h
+ *
+ * \brief Functions and types for monotonic times.
+ *
+ * monotime_* functions try to provide a high-resolution monotonic timer with
+ * something the best resolution the system provides. monotime_coarse_*
+ * functions run faster (if the operating system gives us a way to do that)
+ * but produce a less accurate timer: accuracy will probably be on the order
+ * of tens of milliseconds.
+ */
+
+#ifndef TOR_COMPAT_TIME_H
+#define TOR_COMPAT_TIME_H
+
+#include "orconfig.h"
+#ifdef _WIN32
+#undef HAVE_CLOCK_GETTIME
+#endif
+
+#if defined(HAVE_CLOCK_GETTIME)
+/* to ensure definition of CLOCK_MONOTONIC_COARSE if it's there */
+#include <time.h>
+#endif
+
+#if !defined(HAVE_GETTIMEOFDAY) && !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
+
+/** Represents a monotonic timer in a platform-dependent way. */
+typedef struct monotime_t {
+#ifdef __APPLE__
+ /* On apple, there is a 64-bit counter whose precision we must look up. */
+ uint64_t abstime_;
+#elif defined(HAVE_CLOCK_GETTIME)
+ /* It sure would be nice to use clock_gettime(). Posix is a nice thing. */
+ struct timespec ts_;
+#elif defined (_WIN32)
+ /* On Windows, there is a 64-bit counter whose precision we must look up. */
+ int64_t pcount_;
+#else
+#define MONOTIME_USING_GETTIMEOFDAY
+ /* Otherwise, we will be stuck using gettimeofday. */
+ struct timeval tv_;
+#endif
+} monotime_t;
+
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC_COARSE)
+#define MONOTIME_COARSE_FN_IS_DIFFERENT
+#define monotime_coarse_t monotime_t
+#elif defined(_WIN32)
+#define MONOTIME_COARSE_FN_IS_DIFFERENT
+#define MONOTIME_COARSE_TYPE_IS_DIFFERENT
+/** Represents a coarse monotonic time in a platform-independent way. */
+typedef struct monotime_coarse_t {
+ uint64_t tick_count_;
+} monotime_coarse_t;
+#else
+#define monotime_coarse_t monotime_t
+#endif
+
+/**
+ * Initialize the timing subsystem. This function is idempotent.
+ */
+void monotime_init(void);
+/**
+ * Set <b>out</b> to the current time.
+ */
+void monotime_get(monotime_t *out);
+/**
+ * Return the number of nanoseconds between <b>start</b> and <b>end</b>.
+ */
+int64_t monotime_diff_nsec(const monotime_t *start, const monotime_t *end);
+/**
+ * Return the number of microseconds between <b>start</b> and <b>end</b>.
+ */
+int64_t monotime_diff_usec(const monotime_t *start, const monotime_t *end);
+/**
+ * Return the number of milliseconds between <b>start</b> and <b>end</b>.
+ */
+int64_t monotime_diff_msec(const monotime_t *start, const monotime_t *end);
+/**
+ * Return the number of nanoseconds since the timer system was initialized.
+ */
+uint64_t monotime_absolute_nsec(void);
+/**
+ * Return the number of microseconds since the timer system was initialized.
+ */
+uint64_t monotime_absolute_usec(void);
+/**
+ * Return the number of milliseconds since the timer system was initialized.
+ */
+uint64_t monotime_absolute_msec(void);
+
+#if defined(MONOTIME_COARSE_FN_IS_DIFFERENT)
+/**
+ * Set <b>out</b> to the current coarse time.
+ */
+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
+#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
+
+#if defined(MONOTIME_COARSE_TYPE_IS_DIFFERENT)
+int64_t monotime_coarse_diff_nsec(const monotime_coarse_t *start,
+ const monotime_coarse_t *end);
+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
+#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
+
+void tor_gettimeofday(struct timeval *timeval);
+
+#ifdef TOR_UNIT_TESTS
+void tor_sleep_msec(int msec);
+
+void monotime_enable_test_mocking(void);
+void monotime_disable_test_mocking(void);
+void monotime_set_mock_time_nsec(int64_t);
+#if defined(MONOTIME_COARSE_FN_IS_DIFFERENT)
+void monotime_coarse_set_mock_time_nsec(int64_t);
+#else
+#define monotime_coarse_set_mock_time_nsec monotime_set_mock_time_nsec
+#endif
+#endif
+
+#ifdef COMPAT_TIME_PRIVATE
+#if defined(_WIN32) || defined(TOR_UNIT_TESTS)
+STATIC int64_t ratchet_performance_counter(int64_t count_raw);
+STATIC int64_t ratchet_coarse_performance_counter(int64_t count_raw);
+#endif
+#if defined(MONOTIME_USING_GETTIMEOFDAY) || defined(TOR_UNIT_TESTS)
+STATIC void ratchet_timeval(const struct timeval *timeval_raw,
+ struct timeval *out);
+#endif
+#ifdef TOR_UNIT_TESTS
+void monotime_reset_ratchets_for_testing(void);
+#endif
+#endif
+
+#endif
+
diff --git a/src/common/container.c b/src/common/container.c
index ddf3bafa91..ec59dccf62 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -544,7 +544,7 @@ smartlist_sort(smartlist_t *sl, int (*compare)(const void **a, const void **b))
/** Given a smartlist <b>sl</b> sorted with the function <b>compare</b>,
* return the most frequent member in the list. Break ties in favor of
* later elements. If the list is empty, return NULL. If count_out is
- * non-null, set it to the most frequent member.
+ * non-null, set it to the count of the most frequent member.
*/
void *
smartlist_get_most_frequent_(const smartlist_t *sl,
diff --git a/src/common/container.h b/src/common/container.h
index 92ad3f5ec7..71495b660a 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -526,7 +526,7 @@ void* strmap_remove_lc(strmap_t *map, const char *key);
return (valtype*)digestmap_remove((digestmap_t*)map, key); \
} \
ATTR_UNUSED static inline void \
- prefix##free(maptype *map, void (*free_val)(void*)) \
+ prefix##f##ree(maptype *map, void (*free_val)(void*)) \
{ \
digestmap_free((digestmap_t*)map, free_val); \
} \
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 2f7e053c89..39c8cc2b0a 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -29,18 +29,7 @@
#include "crypto_ed25519.h"
#include "crypto_format.h"
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#endif
-
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic push
-#endif
-/* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice.
- * Suppress the GCC warning so we can build with -Wredundant-decl. */
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#endif
+DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/err.h>
#include <openssl/rsa.h>
@@ -53,6 +42,8 @@
#include <openssl/conf.h>
#include <openssl/hmac.h>
+ENABLE_GCC_WARNING(redundant-decls)
+
#if __GNUC__ && GCC_VERSION >= 402
#if GCC_VERSION >= 406
#pragma GCC diagnostic pop
@@ -65,7 +56,6 @@
#include <ctype.h>
#endif
#ifdef HAVE_UNISTD_H
-#define _GNU_SOURCE
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
@@ -77,8 +67,12 @@
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
#endif
+#ifdef HAVE_SYS_RANDOM_H
+#include <sys/random.h>
+#endif
#include "torlog.h"
+#include "torint.h"
#include "aes.h"
#include "util.h"
#include "container.h"
@@ -88,15 +82,6 @@
#include "keccak-tiny/keccak-tiny.h"
-#ifdef __APPLE__
-/* Apple messed up their getentropy definitions in Sierra. It's not insecure
- * or anything (as far as I know) but it makes compatible builds hard. 0.2.9
- * contains the necessary tricks to do it right: in 0.2.8, we're just using
- * this blunt instrument.
- */
-#undef HAVE_GETENTROPY
-#endif
-
#ifdef ANDROID
/* Android's OpenSSL seems to have removed all of its Engine support. */
#define DISABLE_ENGINES
@@ -138,15 +123,6 @@ struct crypto_pk_t
RSA *key; /**< The key itself */
};
-/** Key and stream information for a stream cipher. */
-struct crypto_cipher_t
-{
- char key[CIPHER_KEY_LEN]; /**< The raw key. */
- char iv[CIPHER_IV_LEN]; /**< The initial IV. */
- aes_cnt_cipher_t *cipher; /**< The key in format usable for counter-mode AES
- * encryption */
-};
-
/** A structure to hold the first half (x, g^x) of a Diffie-Hellman handshake
* while we're waiting for the second.*/
struct crypto_dh_t {
@@ -164,7 +140,7 @@ crypto_get_rsa_padding_overhead(int padding)
switch (padding)
{
case RSA_PKCS1_OAEP_PADDING: return PKCS1_OAEP_PADDING_OVERHEAD;
- default: tor_assert(0); return -1;
+ default: tor_assert(0); return -1; // LCOV_EXCL_LINE
}
}
@@ -176,7 +152,7 @@ crypto_get_rsa_padding(int padding)
switch (padding)
{
case PK_PKCS1_OAEP_PADDING: return RSA_PKCS1_OAEP_PADDING;
- default: tor_assert(0); return -1;
+ default: tor_assert(0); return -1; // LCOV_EXCL_LINE
}
}
@@ -201,13 +177,9 @@ crypto_log_errors(int severity, const char *doing)
if (!msg) msg = "(null)";
if (!lib) lib = "(null)";
if (!func) func = "(null)";
- if (doing) {
- tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
+ if (BUG(!doing)) doing = "(null)";
+ tor_log(severity, LD_CRYPTO, "crypto error while %s: %s (in %s:%s)",
doing, msg, lib, func);
- } else {
- tor_log(severity, LD_CRYPTO, "crypto error: %s (in %s:%s)",
- msg, lib, func);
- }
}
}
@@ -573,38 +545,48 @@ crypto_pk_free(crypto_pk_t *env)
}
/** Allocate and return a new symmetric cipher using the provided key and iv.
- * The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes. If you
- * provide NULL in place of either one, it is generated at random.
- */
+ * The key is <b>bits</b> bits long; the IV is CIPHER_IV_LEN bytes. Both
+ * must be provided. Key length must be 128, 192, or 256 */
crypto_cipher_t *
-crypto_cipher_new_with_iv(const char *key, const char *iv)
+crypto_cipher_new_with_iv_and_bits(const uint8_t *key,
+ const uint8_t *iv,
+ int bits)
{
- crypto_cipher_t *env;
-
- env = tor_malloc_zero(sizeof(crypto_cipher_t));
-
- if (key == NULL)
- crypto_rand(env->key, CIPHER_KEY_LEN);
- else
- memcpy(env->key, key, CIPHER_KEY_LEN);
- if (iv == NULL)
- crypto_rand(env->iv, CIPHER_IV_LEN);
- else
- memcpy(env->iv, iv, CIPHER_IV_LEN);
+ tor_assert(key);
+ tor_assert(iv);
- env->cipher = aes_new_cipher(env->key, env->iv);
+ return aes_new_cipher((const uint8_t*)key, (const uint8_t*)iv, bits);
+}
- return env;
+/** Allocate and return a new symmetric cipher using the provided key and iv.
+ * The key is CIPHER_KEY_LEN bytes; the IV is CIPHER_IV_LEN bytes. Both
+ * must be provided.
+ */
+crypto_cipher_t *
+crypto_cipher_new_with_iv(const char *key, const char *iv)
+{
+ return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)iv,
+ 128);
}
/** Return a new crypto_cipher_t with the provided <b>key</b> and an IV of all
- * zero bytes. */
+ * zero bytes and key length <b>bits</b>. Key length must be 128, 192, or
+ * 256. */
crypto_cipher_t *
-crypto_cipher_new(const char *key)
+crypto_cipher_new_with_bits(const char *key, int bits)
{
char zeroiv[CIPHER_IV_LEN];
memset(zeroiv, 0, sizeof(zeroiv));
- return crypto_cipher_new_with_iv(key, zeroiv);
+ return crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)zeroiv,
+ bits);
+}
+
+/** Return a new crypto_cipher_t with the provided <b>key</b> (of
+ * CIPHER_KEY_LEN bytes) and an IV of all zero bytes. */
+crypto_cipher_t *
+crypto_cipher_new(const char *key)
+{
+ return crypto_cipher_new_with_bits(key, 128);
}
/** Free a symmetric cipher.
@@ -615,10 +597,7 @@ crypto_cipher_free(crypto_cipher_t *env)
if (!env)
return;
- tor_assert(env->cipher);
- aes_cipher_free(env->cipher);
- memwipe(env, 0, sizeof(crypto_cipher_t));
- tor_free(env);
+ aes_cipher_free(env);
}
/* public key crypto */
@@ -1016,6 +995,20 @@ crypto_pk_dup_key(crypto_pk_t *env)
return env;
}
+#ifdef TOR_UNIT_TESTS
+/** For testing: replace dest with src. (Dest must have a refcount
+ * of 1) */
+void
+crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src)
+{
+ tor_assert(dest);
+ tor_assert(dest->refs == 1);
+ tor_assert(src);
+ RSA_free(dest->key);
+ dest->key = RSAPrivateKey_dup(src->key);
+}
+#endif
+
/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
* Returns NULL on failure. */
crypto_pk_t *
@@ -1033,6 +1026,10 @@ crypto_pk_copy_full(crypto_pk_t *env)
new_key = RSAPublicKey_dup(env->key);
}
if (!new_key) {
+ /* LCOV_EXCL_START
+ *
+ * We can't cause RSA*Key_dup() to fail, so we can't really test this.
+ */
log_err(LD_CRYPTO, "Unable to duplicate a %s key: openssl failed.",
privatekey?"private":"public");
crypto_log_errors(LOG_ERR,
@@ -1040,6 +1037,7 @@ crypto_pk_copy_full(crypto_pk_t *env)
"Duplicating a public key");
tor_fragile_assert();
return NULL;
+ /* LCOV_EXCL_STOP */
}
return crypto_new_pk_from_rsa_(new_key);
@@ -1279,10 +1277,12 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
tor_assert(tolen >= fromlen + overhead + CIPHER_KEY_LEN);
tor_assert(tolen >= pkeylen);
- cipher = crypto_cipher_new(NULL); /* generate a new key. */
+ char key[CIPHER_KEY_LEN];
+ crypto_rand(key, sizeof(key)); /* generate a new key. */
+ cipher = crypto_cipher_new(key);
buf = tor_malloc(pkeylen+1);
- memcpy(buf, cipher->key, CIPHER_KEY_LEN);
+ memcpy(buf, key, CIPHER_KEY_LEN);
memcpy(buf+CIPHER_KEY_LEN, from, pkeylen-overhead-CIPHER_KEY_LEN);
/* Length of symmetrically encrypted data. */
@@ -1297,6 +1297,7 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
if (r<0) goto err;
memwipe(buf, 0, pkeylen);
+ memwipe(key, 0, sizeof(key));
tor_free(buf);
crypto_cipher_free(cipher);
tor_assert(outlen+symlen < INT_MAX);
@@ -1304,6 +1305,7 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
err:
memwipe(buf, 0, pkeylen);
+ memwipe(key, 0, sizeof(key));
tor_free(buf);
crypto_cipher_free(cipher);
return -1;
@@ -1595,14 +1597,6 @@ crypto_pk_base64_decode(const char *str, size_t len)
/* symmetric crypto */
-/** Return a pointer to the key set for the cipher in <b>env</b>.
- */
-const char *
-crypto_cipher_get_key(crypto_cipher_t *env)
-{
- return env->key;
-}
-
/** Encrypt <b>fromlen</b> bytes from <b>from</b> using the cipher
* <b>env</b>; on success, store the result to <b>to</b> and return 0.
* Does not check for failure.
@@ -1612,14 +1606,14 @@ crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
const char *from, size_t fromlen)
{
tor_assert(env);
- tor_assert(env->cipher);
+ tor_assert(env);
tor_assert(from);
tor_assert(fromlen);
tor_assert(to);
tor_assert(fromlen < SIZE_T_CEILING);
memcpy(to, from, fromlen);
- aes_crypt_inplace(env->cipher, to, fromlen);
+ aes_crypt_inplace(env, to, fromlen);
return 0;
}
@@ -1637,7 +1631,7 @@ crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
tor_assert(fromlen < SIZE_T_CEILING);
memcpy(to, from, fromlen);
- aes_crypt_inplace(env->cipher, to, fromlen);
+ aes_crypt_inplace(env, to, fromlen);
return 0;
}
@@ -1648,7 +1642,7 @@ void
crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
{
tor_assert(len < SIZE_T_CEILING);
- aes_crypt_inplace(env->cipher, buf, len);
+ aes_crypt_inplace(env, buf, len);
}
/** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
@@ -1672,11 +1666,14 @@ crypto_cipher_encrypt_with_iv(const char *key,
if (tolen < fromlen + CIPHER_IV_LEN)
return -1;
- cipher = crypto_cipher_new_with_iv(key, NULL);
+ char iv[CIPHER_IV_LEN];
+ crypto_rand(iv, sizeof(iv));
+ cipher = crypto_cipher_new_with_iv(key, iv);
- memcpy(to, cipher->iv, CIPHER_IV_LEN);
+ memcpy(to, iv, CIPHER_IV_LEN);
crypto_cipher_encrypt(cipher, to+CIPHER_IV_LEN, from, fromlen);
crypto_cipher_free(cipher);
+ memwipe(iv, 0, sizeof(iv));
return (int)(fromlen + CIPHER_IV_LEN);
}
@@ -1790,8 +1787,10 @@ crypto_digest_algorithm_get_name(digest_algorithm_t alg)
case DIGEST_SHA3_512:
return "sha3-512";
default:
+ // LCOV_EXCL_START
tor_fragile_assert();
return "??unknown_digest??";
+ // LCOV_EXCL_STOP
}
}
@@ -1815,7 +1814,7 @@ crypto_digest_algorithm_parse_name(const char *name)
}
/** Given an algorithm, return the digest length in bytes. */
-static inline size_t
+size_t
crypto_digest_algorithm_get_length(digest_algorithm_t alg)
{
switch (alg) {
@@ -1830,8 +1829,8 @@ crypto_digest_algorithm_get_length(digest_algorithm_t alg)
case DIGEST_SHA3_512:
return DIGEST512_LEN;
default:
- tor_assert(0);
- return 0; /* Unreachable */
+ tor_assert(0); // LCOV_EXCL_LINE
+ return 0; /* Unreachable */ // LCOV_EXCL_LINE
}
}
@@ -1874,23 +1873,53 @@ crypto_digest_alloc_bytes(digest_algorithm_t alg)
case DIGEST_SHA3_512:
return END_OF_FIELD(d.sha3);
default:
- tor_assert(0);
- return 0;
+ tor_assert(0); // LCOV_EXCL_LINE
+ return 0; // LCOV_EXCL_LINE
}
#undef END_OF_FIELD
#undef STRUCT_FIELD_SIZE
}
+/**
+ * Internal function: create and return a new digest object for 'algorithm'.
+ * Does not typecheck the algorithm.
+ */
+static crypto_digest_t *
+crypto_digest_new_internal(digest_algorithm_t algorithm)
+{
+ crypto_digest_t *r = tor_malloc(crypto_digest_alloc_bytes(algorithm));
+ r->algorithm = algorithm;
+
+ switch (algorithm)
+ {
+ case DIGEST_SHA1:
+ SHA1_Init(&r->d.sha1);
+ break;
+ case DIGEST_SHA256:
+ SHA256_Init(&r->d.sha2);
+ break;
+ case DIGEST_SHA512:
+ SHA512_Init(&r->d.sha512);
+ break;
+ case DIGEST_SHA3_256:
+ keccak_digest_init(&r->d.sha3, 256);
+ break;
+ case DIGEST_SHA3_512:
+ keccak_digest_init(&r->d.sha3, 512);
+ break;
+ default:
+ tor_assert_unreached();
+ }
+
+ return r;
+}
+
/** Allocate and return a new digest object to compute SHA1 digests.
*/
crypto_digest_t *
crypto_digest_new(void)
{
- crypto_digest_t *r;
- r = tor_malloc(crypto_digest_alloc_bytes(DIGEST_SHA1));
- SHA1_Init(&r->d.sha1);
- r->algorithm = DIGEST_SHA1;
- return r;
+ return crypto_digest_new_internal(DIGEST_SHA1);
}
/** Allocate and return a new digest object to compute 256-bit digests
@@ -1898,15 +1927,8 @@ crypto_digest_new(void)
crypto_digest_t *
crypto_digest256_new(digest_algorithm_t algorithm)
{
- crypto_digest_t *r;
tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256);
- r = tor_malloc(crypto_digest_alloc_bytes(algorithm));
- if (algorithm == DIGEST_SHA256)
- SHA256_Init(&r->d.sha2);
- else
- keccak_digest_init(&r->d.sha3, 256);
- r->algorithm = algorithm;
- return r;
+ return crypto_digest_new_internal(algorithm);
}
/** Allocate and return a new digest object to compute 512-bit digests
@@ -1914,15 +1936,8 @@ crypto_digest256_new(digest_algorithm_t algorithm)
crypto_digest_t *
crypto_digest512_new(digest_algorithm_t algorithm)
{
- crypto_digest_t *r;
tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512);
- r = tor_malloc(crypto_digest_alloc_bytes(algorithm));
- if (algorithm == DIGEST_SHA512)
- SHA512_Init(&r->d.sha512);
- else
- keccak_digest_init(&r->d.sha3, 512);
- r->algorithm = algorithm;
- return r;
+ return crypto_digest_new_internal(algorithm);
}
/** Deallocate a digest object.
@@ -1965,8 +1980,10 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
keccak_digest_update(&digest->d.sha3, (const uint8_t *)data, len);
break;
default:
+ /* LCOV_EXCL_START */
tor_fragile_assert();
break;
+ /* LCOV_EXCL_STOP */
}
}
@@ -2005,13 +2022,15 @@ crypto_digest_get_digest(crypto_digest_t *digest,
case DIGEST_SHA512:
SHA512_Final(r, &tmpenv.d.sha512);
break;
+//LCOV_EXCL_START
case DIGEST_SHA3_256: /* FALLSTHROUGH */
case DIGEST_SHA3_512:
- log_warn(LD_BUG, "Handling unexpected algorithm %d", digest->algorithm);
- tor_assert(0); /* This is fatal, because it should never happen. */
default:
- tor_assert(0); /* Unreachable. */
+ log_warn(LD_BUG, "Handling unexpected algorithm %d", digest->algorithm);
+ /* This is fatal, because it should never happen. */
+ tor_assert_unreached();
break;
+//LCOV_EXCL_STOP
}
memcpy(out, r, out_len);
memwipe(r, 0, sizeof(r));
@@ -2070,27 +2089,7 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *append,
digest_algorithm_t alg)
{
- crypto_digest_t *d = NULL;
- switch (alg) {
- case DIGEST_SHA1:
- d = crypto_digest_new();
- break;
- case DIGEST_SHA256: /* FALLSTHROUGH */
- case DIGEST_SHA3_256:
- d = crypto_digest256_new(alg);
- break;
- case DIGEST_SHA512: /* FALLSTHROUGH */
- case DIGEST_SHA3_512:
- d = crypto_digest512_new(alg);
- break;
- default:
- log_warn(LD_BUG, "Called with unknown algorithm %d", alg);
- /* If fragile_assert is not enabled, wipe output and return
- * without running any calculations */
- memwipe(digest_out, 0xff, len_out);
- tor_fragile_assert();
- goto free;
- }
+ crypto_digest_t *d = crypto_digest_new_internal(alg);
if (prepend)
crypto_digest_add_bytes(d, prepend, strlen(prepend));
SMARTLIST_FOREACH(lst, const char *, cp,
@@ -2098,8 +2097,6 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
if (append)
crypto_digest_add_bytes(d, append, strlen(append));
crypto_digest_get_digest(d, digest_out, len_out);
-
- free:
crypto_digest_free(d);
}
@@ -2268,9 +2265,14 @@ crypto_set_tls_dh_prime(void)
int r;
/* If the space is occupied, free the previous TLS DH prime */
- if (dh_param_p_tls) {
+ if (BUG(dh_param_p_tls)) {
+ /* LCOV_EXCL_START
+ *
+ * We shouldn't be calling this twice.
+ */
BN_clear_free(dh_param_p_tls);
dh_param_p_tls = NULL;
+ /* LCOV_EXCL_STOP */
}
tls_prime = BN_new();
@@ -2302,8 +2304,8 @@ init_dh_param(void)
{
BIGNUM *circuit_dh_prime;
int r;
- if (dh_param_p && dh_param_g)
- return;
+ if (BUG(dh_param_p && dh_param_g))
+ return; // LCOV_EXCL_LINE This function isn't supposed to be called twice.
circuit_dh_prime = BN_new();
tor_assert(circuit_dh_prime);
@@ -2393,10 +2395,13 @@ crypto_dh_new(int dh_type)
return res;
err:
+ /* LCOV_EXCL_START
+ * This error condition is only reached when an allocation fails */
crypto_log_errors(LOG_WARN, "creating DH object");
if (res->dh) DH_free(res->dh); /* frees p and g too */
tor_free(res);
return NULL;
+ /* LCOV_EXCL_STOP */
}
/** Return a copy of <b>dh</b>, sharing its internal state. */
@@ -2430,8 +2435,11 @@ crypto_dh_generate_public(crypto_dh_t *dh)
again:
#endif
if (!DH_generate_key(dh->dh)) {
+ /* LCOV_EXCL_START
+ * To test this we would need some way to tell openssl to break DH. */
crypto_log_errors(LOG_WARN, "generating DH key");
return -1;
+ /* LCOV_EXCL_STOP */
}
#ifdef OPENSSL_1_1_API
/* OpenSSL 1.1.x doesn't appear to let you regenerate a DH key, without
@@ -2447,6 +2455,8 @@ crypto_dh_generate_public(crypto_dh_t *dh)
}
#else
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. */
log_warn(LD_CRYPTO, "Weird! Our own DH key was invalid. I guess once-in-"
"the-universe chances really do happen. Trying again.");
/* Free and clear the keys, so OpenSSL will actually try again. */
@@ -2454,6 +2464,7 @@ crypto_dh_generate_public(crypto_dh_t *dh)
BN_clear_free(dh->dh->priv_key);
dh->dh->pub_key = dh->dh->priv_key = NULL;
goto again;
+ /* LCOV_EXCL_STOP */
}
#endif
return 0;
@@ -2518,8 +2529,8 @@ tor_check_dh_key(int severity, const BIGNUM *bn)
tor_assert(bn);
x = BN_new();
tor_assert(x);
- if (!dh_param_p)
- init_dh_param();
+ if (BUG(!dh_param_p))
+ init_dh_param(); //LCOV_EXCL_LINE we already checked whether we did this.
BN_set_word(x, 1);
if (BN_cmp(bn,x)<=0) {
log_fn(severity, LD_CRYPTO, "DH key must be at least 2.");
@@ -2541,8 +2552,6 @@ tor_check_dh_key(int severity, const BIGNUM *bn)
return -1;
}
-#undef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
/** Given a DH key exchange object, and our peer's value of g^y (as a
* <b>pubkey_len</b>-byte value in <b>pubkey</b>) generate
* <b>secret_bytes_out</b> bytes of shared key material and write them
@@ -2730,6 +2739,11 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng)
tor_init_weak_random(rng, seed);
}
+#ifdef TOR_UNIT_TESTS
+int break_strongest_rng_syscall = 0;
+int break_strongest_rng_fallback = 0;
+#endif
+
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
* via system calls, storing it into <b>out</b>. Return 0 on success, -1 on
* failure. A maximum request size of 256 bytes is imposed.
@@ -2739,6 +2753,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
{
tor_assert(out_len <= MAX_STRONGEST_RAND_SIZE);
+#ifdef TOR_UNIT_TESTS
+ if (break_strongest_rng_syscall)
+ return -1;
+#endif
+
#if defined(_WIN32)
static int provider_set = 0;
static HCRYPTPROV provider;
@@ -2788,6 +2807,7 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
} while (ret == -1 && ((errno == EINTR) ||(errno == EAGAIN)));
if (PREDICT_UNLIKELY(ret == -1)) {
+ /* LCOV_EXCL_START we can't actually make the syscall fail in testing. */
tor_assert(errno != EAGAIN);
tor_assert(errno != EINTR);
@@ -2795,6 +2815,7 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
log_warn(LD_CRYPTO, "Can't get entropy from getrandom().");
getrandom_works = 0; /* Don't bother trying again. */
return -1;
+ /* LCOV_EXCL_STOP */
}
tor_assert(ret == (long)out_len);
@@ -2823,6 +2844,11 @@ crypto_strongest_rand_syscall(uint8_t *out, size_t out_len)
static int
crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
{
+#ifdef TOR_UNIT_TESTS
+ if (break_strongest_rng_fallback)
+ return -1;
+#endif
+
#ifdef _WIN32
/* Windows exclusively uses crypto_strongest_rand_syscall(). */
(void)out;
@@ -2843,10 +2869,13 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
n = read_all(fd, (char*)out, out_len, 0);
close(fd);
if (n != out_len) {
+ /* LCOV_EXCL_START
+ * We can't make /dev/foorandom actually fail. */
log_warn(LD_CRYPTO,
"Error reading from entropy source (read only %lu bytes).",
(unsigned long)n);
return -1;
+ /* LCOV_EXCL_STOP */
}
return 0;
@@ -2860,7 +2889,7 @@ crypto_strongest_rand_fallback(uint8_t *out, size_t out_len)
* storing it into <b>out</b>. Return 0 on success, -1 on failure. A maximum
* request size of 256 bytes is imposed.
*/
-static int
+STATIC int
crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
{
static const size_t sanity_min_size = 16;
@@ -2894,13 +2923,17 @@ crypto_strongest_rand_raw(uint8_t *out, size_t out_len)
return 0;
}
- /* We tried max_attempts times to fill a buffer >= 128 bits long,
+ /* LCOV_EXCL_START
+ *
+ * We tried max_attempts times to fill a buffer >= 128 bits long,
* and each time it returned all '0's. Either the system entropy
* source is busted, or the user should go out and buy a ticket to
* every lottery on the planet.
*/
log_warn(LD_CRYPTO, "Strong OS entropy returned all zero buffer.");
+
return -1;
+ /* LCOV_EXCL_STOP */
}
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
@@ -2919,10 +2952,12 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
while (out_len) {
crypto_rand((char*) inp, DLEN);
if (crypto_strongest_rand_raw(inp+DLEN, DLEN) < 0) {
+ // LCOV_EXCL_START
log_err(LD_CRYPTO, "Failed to load strong entropy when generating an "
"important key. Exiting.");
/* Die with an assertion so we get a stack trace. */
tor_assert(0);
+ // LCOV_EXCL_STOP
}
if (out_len >= DLEN) {
SHA512(inp, sizeof(inp), out);
@@ -2953,7 +2988,7 @@ crypto_seed_rng(void)
* functions. If one succeeds, we'll accept the RNG as seeded. */
rand_poll_ok = RAND_poll();
if (rand_poll_ok == 0)
- log_warn(LD_CRYPTO, "RAND_poll() failed.");
+ log_warn(LD_CRYPTO, "RAND_poll() failed."); // LCOV_EXCL_LINE
load_entropy_ok = !crypto_strongest_rand_raw(buf, sizeof(buf));
if (load_entropy_ok) {
@@ -3088,8 +3123,8 @@ crypto_rand_double(void)
{
/* We just use an unsigned int here; we don't really care about getting
* more than 32 bits of resolution */
- unsigned int uint;
- crypto_rand((char*)&uint, sizeof(uint));
+ unsigned int u;
+ crypto_rand((char*)&u, sizeof(u));
#if SIZEOF_INT == 4
#define UINT_MAX_AS_DOUBLE 4294967296.0
#elif SIZEOF_INT == 8
@@ -3097,7 +3132,7 @@ crypto_rand_double(void)
#else
#error SIZEOF_INT is neither 4 nor 8
#endif
- return ((double)uint) / UINT_MAX_AS_DOUBLE;
+ return ((double)u) / UINT_MAX_AS_DOUBLE;
}
/** Generate and return a new random hostname starting with <b>prefix</b>,
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 682c4e3253..116e0a62fd 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -13,6 +13,8 @@
#ifndef TOR_CRYPTO_H
#define TOR_CRYPTO_H
+#include "orconfig.h"
+
#include <stdio.h>
#include "torint.h"
#include "testsupport.h"
@@ -115,7 +117,7 @@ typedef struct {
} common_digests_t;
typedef struct crypto_pk_t crypto_pk_t;
-typedef struct crypto_cipher_t crypto_cipher_t;
+typedef struct aes_cnt_cipher crypto_cipher_t;
typedef struct crypto_digest_t crypto_digest_t;
typedef struct crypto_xof_t crypto_xof_t;
typedef struct crypto_dh_t crypto_dh_t;
@@ -136,7 +138,11 @@ void crypto_pk_free(crypto_pk_t *env);
void crypto_set_tls_dh_prime(void);
crypto_cipher_t *crypto_cipher_new(const char *key);
+crypto_cipher_t *crypto_cipher_new_with_bits(const char *key, int bits);
crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv);
+crypto_cipher_t *crypto_cipher_new_with_iv_and_bits(const uint8_t *key,
+ const uint8_t *iv,
+ int bits);
void crypto_cipher_free(crypto_cipher_t *env);
/* public key crypto */
@@ -233,6 +239,7 @@ void crypto_digest_smartlist(char *digest_out, size_t len_out,
const struct smartlist_t *lst, const char *append,
digest_algorithm_t alg);
const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
+size_t crypto_digest_algorithm_get_length(digest_algorithm_t alg);
int crypto_digest_algorithm_parse_name(const char *name);
crypto_digest_t *crypto_digest_new(void);
crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm);
@@ -317,6 +324,16 @@ 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);
+
+#ifdef TOR_UNIT_TESTS
+extern int break_strongest_rng_syscall;
+extern int break_strongest_rng_fallback;
+#endif
+#endif
+
+#ifdef TOR_UNIT_TESTS
+void crypto_pk_assign_(crypto_pk_t *dest, const crypto_pk_t *src);
#endif
#endif
diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c
index 57c878b79a..fcbee3aba2 100644
--- a/src/common/crypto_curve25519.c
+++ b/src/common/crypto_curve25519.c
@@ -5,6 +5,14 @@
* \file crypto_curve25519.c
*
* \brief Wrapper code for a curve25519 implementation.
+ *
+ * Curve25519 is an Elliptic-Curve Diffie Hellman handshake, designed by
+ * Dan Bernstein. For more information, see https://cr.yp.to/ecdh.html
+ *
+ * Tor uses Curve25519 as the basis of its "ntor" circuit extension
+ * handshake, and in related code. The functions in this module are
+ * used to find the most suitable available Curve25519 implementation,
+ * to provide wrappers around it, and so on.
*/
#define CRYPTO_CURVE25519_PRIVATE
@@ -39,15 +47,23 @@ int curve25519_donna(uint8_t *mypublic,
static void pick_curve25519_basepoint_impl(void);
+/** This is set to 1 if we have an optimized Ed25519-based
+ * implementation for multiplying a value by the basepoint; to 0 if we
+ * don't, and to -1 if we haven't checked. */
static int curve25519_use_ed = -1;
+/**
+ * Helper function: call the most appropriate backend to compute the
+ * scalar "secret" times the point "point". Store the result in
+ * "output". Return 0 on success, negative on failure.
+ **/
STATIC int
curve25519_impl(uint8_t *output, const uint8_t *secret,
- const uint8_t *basepoint)
+ const uint8_t *point)
{
uint8_t bp[CURVE25519_PUBKEY_LEN];
int r;
- memcpy(bp, basepoint, CURVE25519_PUBKEY_LEN);
+ memcpy(bp, point, CURVE25519_PUBKEY_LEN);
/* Clear the high bit, in case our backend foolishly looks at it. */
bp[31] &= 0x7f;
#ifdef USE_CURVE25519_DONNA
@@ -61,12 +77,19 @@ curve25519_impl(uint8_t *output, const uint8_t *secret,
return r;
}
+/**
+ * Helper function: Multiply the scalar "secret" by the Curve25519
+ * basepoint (X=9), and store the result in "output". Return 0 on
+ * success, -1 on false.
+ */
STATIC int
curve25519_basepoint_impl(uint8_t *output, const uint8_t *secret)
{
int r = 0;
- if (PREDICT_UNLIKELY(curve25519_use_ed == -1)) {
+ if (BUG(curve25519_use_ed == -1)) {
+ /* LCOV_EXCL_START - Only reached if we forgot to call curve25519_init() */
pick_curve25519_basepoint_impl();
+ /* LCOV_EXCL_STOP */
}
/* TODO: Someone should benchmark curved25519_scalarmult_basepoint versus
@@ -83,6 +106,10 @@ curve25519_basepoint_impl(uint8_t *output, const uint8_t *secret)
return r;
}
+/**
+ * Override the decision of whether to use the Ed25519-based basepoint
+ * multiply function. Used for testing.
+ */
void
curve25519_set_impl_params(int use_ed)
{
@@ -140,6 +167,10 @@ curve25519_secret_key_generate(curve25519_secret_key_t *key_out,
return 0;
}
+/**
+ * Given a secret key in <b>seckey</b>, create the corresponding public
+ * key in <b>key_out</b>.
+ */
void
curve25519_public_key_generate(curve25519_public_key_t *key_out,
const curve25519_secret_key_t *seckey)
@@ -147,6 +178,10 @@ curve25519_public_key_generate(curve25519_public_key_t *key_out,
curve25519_basepoint_impl(key_out->public_key, seckey->secret_key);
}
+/**
+ * Construct a new keypair in *<b>keypair_out</b>. If <b>extra_strong</b>
+ * is true, this key is possibly going to get used more than once, so
+ * use a better-than-usual RNG. Return 0 on success, -1 on failure. */
int
curve25519_keypair_generate(curve25519_keypair_t *keypair_out,
int extra_strong)
@@ -157,7 +192,13 @@ curve25519_keypair_generate(curve25519_keypair_t *keypair_out,
return 0;
}
-/* DOCDOC */
+/** Store the keypair <b>keypair</b>, including its secret and public
+ * parts, to the file <b>fname</b>. Use the string tag <b>tag</b> to
+ * distinguish this from other Curve25519 keypairs. Return 0 on success,
+ * -1 on failure.
+ *
+ * See crypto_write_tagged_contents_to_file() for more information on
+ * the metaformat used for these keys.*/
int
curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair,
const char *fname,
@@ -180,7 +221,10 @@ curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair,
return r;
}
-/* DOCDOC */
+/** Read a curve25519 keypair from a file named <b>fname</b> created by
+ * curve25519_keypair_write_to_file(). Store the keypair in
+ * <b>keypair_out</b>, and the associated tag string in <b>tag_out</b>.
+ * Return 0 on success, and -1 on failure. */
int
curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out,
char **tag_out,
@@ -195,6 +239,7 @@ curve25519_keypair_read_from_file(curve25519_keypair_t *keypair_out,
if (len != sizeof(content))
goto end;
+ /* Make sure that the public key matches the secret key */
memcpy(keypair_out->seckey.secret_key, content, CURVE25519_SECKEY_LEN);
curve25519_public_key_generate(&keypair_out->pubkey, &keypair_out->seckey);
if (tor_memneq(keypair_out->pubkey.public_key,
@@ -290,10 +335,13 @@ pick_curve25519_basepoint_impl(void)
if (curve25519_basepoint_spot_check() == 0)
return;
- log_warn(LD_CRYPTO, "The ed25519-based curve25519 basepoint "
+ /* LCOV_EXCL_START
+ * only reachable if our basepoint implementation broken */
+ log_warn(LD_BUG|LD_CRYPTO, "The ed25519-based curve25519 basepoint "
"multiplication seems broken; using the curve25519 "
"implementation.");
curve25519_use_ed = 0;
+ /* LCOV_EXCL_STOP */
}
/** Initialize the curve25519 implementations. This is necessary if you're
diff --git a/src/common/crypto_curve25519.h b/src/common/crypto_curve25519.h
index 547e393567..4011820949 100644
--- a/src/common/crypto_curve25519.h
+++ b/src/common/crypto_curve25519.h
@@ -14,12 +14,20 @@
/** Length of the result of a curve25519 handshake. */
#define CURVE25519_OUTPUT_LEN 32
-/** Wrapper type for a curve25519 public key */
+/** Wrapper type for a curve25519 public key.
+ *
+ * (We define a separate type for these to make it less likely that we'll
+ * mistake them for secret keys.)
+ * */
typedef struct curve25519_public_key_t {
uint8_t public_key[CURVE25519_PUBKEY_LEN];
} curve25519_public_key_t;
-/** Wrapper type for a curve25519 secret key */
+/** Wrapper type for a curve25519 secret key
+ *
+ * (We define a separate type for these to make it less likely that we'll
+ * mistake them for public keys.)
+ **/
typedef struct curve25519_secret_key_t {
uint8_t secret_key[CURVE25519_SECKEY_LEN];
} curve25519_secret_key_t;
diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c
index ea2d8e3892..30ed772274 100644
--- a/src/common/crypto_ed25519.c
+++ b/src/common/crypto_ed25519.c
@@ -5,6 +5,14 @@
* \file crypto_ed25519.c
*
* \brief Wrapper code for an ed25519 implementation.
+ *
+ * Ed25519 is a Schnorr signature on a Twisted Edwards curve, defined
+ * by Dan Bernstein. For more information, see https://ed25519.cr.yp.to/
+ *
+ * This module wraps our choice of Ed25519 backend, and provides a few
+ * convenience functions for checking and generating signatures. It also
+ * provides Tor-specific tools for key blinding and for converting Ed25519
+ * keys to and from the corresponding Curve25519 keys.
*/
#include "orconfig.h"
@@ -28,7 +36,7 @@
static void pick_ed25519_impl(void);
static int ed25519_impl_spot_check(void);
-/** An Ed25519 implementation */
+/** An Ed25519 implementation, as a set of function pointers. */
typedef struct {
int (*selftest)(void);
@@ -53,6 +61,8 @@ typedef struct {
int);
} ed25519_impl_t;
+/** The Ref10 Ed25519 implementation. This one is pure C and lightly
+ * optimized. */
static const ed25519_impl_t impl_ref10 = {
NULL,
@@ -71,6 +81,8 @@ static const ed25519_impl_t impl_ref10 = {
ed25519_ref10_pubkey_from_curve25519_pubkey,
};
+/** The Ref10 Ed25519 implementation. This one is heavily optimized, but still
+ * mostly C. The C still tends to be heavily platform-specific. */
static const ed25519_impl_t impl_donna = {
ed25519_donna_selftest,
@@ -89,19 +101,31 @@ static const ed25519_impl_t impl_donna = {
ed25519_donna_pubkey_from_curve25519_pubkey,
};
+/** Which Ed25519 implementation are we using? NULL if we haven't decided
+ * yet. */
static const ed25519_impl_t *ed25519_impl = NULL;
+/** Helper: Return our chosen Ed25519 implementation.
+ *
+ * This should only be called after we've picked an implementation, but
+ * it _does_ recover if you forget this.
+ **/
static inline const ed25519_impl_t *
get_ed_impl(void)
{
- if (PREDICT_UNLIKELY(ed25519_impl == NULL)) {
- pick_ed25519_impl();
+ if (BUG(ed25519_impl == NULL)) {
+ pick_ed25519_impl(); // LCOV_EXCL_LINE - We always call ed25519_init().
}
return ed25519_impl;
}
#ifdef TOR_UNIT_TESTS
+/** For testing: used to remember our actual choice of Ed25519
+ * implementation */
static const ed25519_impl_t *saved_ed25519_impl = NULL;
+/** For testing: Use the Ed25519 implementation called <b>name</b> until
+ * crypto_ed25519_testing_restore_impl is called. Recognized names are
+ * "donna" and "ref10". */
void
crypto_ed25519_testing_force_impl(const char *name)
{
@@ -114,6 +138,9 @@ crypto_ed25519_testing_force_impl(const char *name)
ed25519_impl = &impl_ref10;
}
}
+/** For testing: go back to whatever Ed25519 implementation we had picked
+ * before crypto_ed25519_testing_force_impl was called.
+ */
void
crypto_ed25519_testing_restore_impl(void)
{
@@ -184,9 +211,43 @@ ed25519_keypair_generate(ed25519_keypair_t *keypair_out, int extra_strong)
return 0;
}
+/* Return a heap-allocated array that contains <b>msg</b> prefixed by the
+ * string <b>prefix_str</b>. Set <b>final_msg_len_out</b> to the size of the
+ * final array. If an error occured, return NULL. It's the resonsibility of the
+ * caller to free the returned array. */
+static uint8_t *
+get_prefixed_msg(const uint8_t *msg, size_t msg_len,
+ const char *prefix_str,
+ size_t *final_msg_len_out)
+{
+ size_t prefixed_msg_len, prefix_len;
+ uint8_t *prefixed_msg;
+
+ tor_assert(prefix_str);
+ tor_assert(final_msg_len_out);
+
+ prefix_len = strlen(prefix_str);
+
+ /* msg_len + strlen(prefix_str) must not overflow. */
+ if (msg_len > SIZE_T_CEILING - prefix_len) {
+ return NULL;
+ }
+
+ prefixed_msg_len = msg_len + prefix_len;
+ prefixed_msg = tor_malloc_zero(prefixed_msg_len);
+
+ memcpy(prefixed_msg, prefix_str, prefix_len);
+ memcpy(prefixed_msg + prefix_len, msg, msg_len);
+
+ *final_msg_len_out = prefixed_msg_len;
+ return prefixed_msg;
+}
+
/**
* Set <b>signature_out</b> to a signature of the <b>len</b>-byte message
* <b>msg</b>, using the secret and public key in <b>keypair</b>.
+ *
+ * Return 0 if we successfuly signed the message, otherwise return -1.
*/
int
ed25519_sign(ed25519_signature_t *signature_out,
@@ -203,6 +264,37 @@ ed25519_sign(ed25519_signature_t *signature_out,
}
/**
+ * Like ed25519_sign(), but also prefix <b>msg</b> with <b>prefix_str</b>
+ * before signing. <b>prefix_str</b> must be a NUL-terminated string.
+ */
+int
+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)
+{
+ int retval;
+ size_t prefixed_msg_len;
+ uint8_t *prefixed_msg;
+
+ tor_assert(prefix_str);
+
+ prefixed_msg = get_prefixed_msg(msg, msg_len, prefix_str,
+ &prefixed_msg_len);
+ if (!prefixed_msg) {
+ log_warn(LD_GENERAL, "Failed to get prefixed msg.");
+ return -1;
+ }
+
+ retval = ed25519_sign(signature_out,
+ prefixed_msg, prefixed_msg_len,
+ keypair);
+ tor_free(prefixed_msg);
+
+ return retval;
+}
+
+/**
* Check whether if <b>signature</b> is a valid signature for the
* <b>len</b>-byte message in <b>msg</b> made with the key <b>pubkey</b>.
*
@@ -217,6 +309,36 @@ ed25519_checksig(const ed25519_signature_t *signature,
get_ed_impl()->open(signature->sig, msg, len, pubkey->pubkey) < 0 ? -1 : 0;
}
+/**
+ * Like ed2519_checksig(), but also prefix <b>msg</b> with <b>prefix_str</b>
+ * before verifying signature. <b>prefix_str</b> must be a NUL-terminated
+ * string.
+ */
+int
+ed25519_checksig_prefixed(const ed25519_signature_t *signature,
+ const uint8_t *msg, size_t msg_len,
+ const char *prefix_str,
+ const ed25519_public_key_t *pubkey)
+{
+ int retval;
+ size_t prefixed_msg_len;
+ uint8_t *prefixed_msg;
+
+ prefixed_msg = get_prefixed_msg(msg, msg_len, prefix_str,
+ &prefixed_msg_len);
+ if (!prefixed_msg) {
+ log_warn(LD_GENERAL, "Failed to get prefixed msg.");
+ return -1;
+ }
+
+ retval = ed25519_checksig(signature,
+ prefixed_msg, prefixed_msg_len,
+ pubkey);
+ tor_free(prefixed_msg);
+
+ return retval;
+}
+
/** Validate every signature among those in <b>checkable</b>, which contains
* exactly <b>n_checkable</b> elements. If <b>okay_out</b> is non-NULL, set
* the i'th element of <b>okay_out</b> to 1 if the i'th element of
@@ -259,11 +381,11 @@ ed25519_checksig_batch(int *okay_out,
int *oks;
int all_ok;
- ms = tor_malloc(sizeof(uint8_t*)*n_checkable);
- lens = tor_malloc(sizeof(size_t)*n_checkable);
- pks = tor_malloc(sizeof(uint8_t*)*n_checkable);
- sigs = tor_malloc(sizeof(uint8_t*)*n_checkable);
- oks = okay_out ? okay_out : tor_malloc(sizeof(int)*n_checkable);
+ ms = tor_calloc(n_checkable, sizeof(uint8_t*));
+ lens = tor_calloc(n_checkable, sizeof(size_t));
+ pks = tor_calloc(n_checkable, sizeof(uint8_t*));
+ sigs = tor_calloc(n_checkable, sizeof(uint8_t*));
+ oks = okay_out ? okay_out : tor_calloc(n_checkable, sizeof(int));
for (i = 0; i < n_checkable; ++i) {
ms[i] = checkable[i].msg;
@@ -433,6 +555,7 @@ ed25519_seckey_read_from_file(ed25519_secret_key_t *seckey_out,
errno = EINVAL;
}
+ tor_free(*tag_out);
return -1;
}
@@ -472,6 +595,7 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
errno = EINVAL;
}
+ tor_free(*tag_out);
return -1;
}
@@ -594,9 +718,12 @@ pick_ed25519_impl(void)
if (ed25519_impl_spot_check() == 0)
return;
+ /* LCOV_EXCL_START
+ * unreachable unless ed25519_donna is broken */
log_warn(LD_CRYPTO, "The Ed25519-donna implementation seems broken; using "
"the ref10 implementation.");
ed25519_impl = &impl_ref10;
+ /* LCOV_EXCL_STOP */
}
/* Initialize the Ed25519 implementation. This is neccessary if you're
diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h
index 44c2ad9775..31afc49ccc 100644
--- a/src/common/crypto_ed25519.h
+++ b/src/common/crypto_ed25519.h
@@ -55,6 +55,17 @@ int ed25519_checksig(const ed25519_signature_t *signature,
const uint8_t *msg, size_t len,
const ed25519_public_key_t *pubkey);
+int
+ed25519_sign_prefixed(ed25519_signature_t *signature_out,
+ const uint8_t *msg, size_t len,
+ const char *prefix_str,
+ const ed25519_keypair_t *keypair);
+int
+ed25519_checksig_prefixed(const ed25519_signature_t *signature,
+ const uint8_t *msg, size_t len,
+ const char *prefix_str,
+ const ed25519_public_key_t *pubkey);
+
/**
* A collection of information necessary to check an Ed25519 signature. Used
* for batch verification.
diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c
index bdf9bfd613..2f6d847c83 100644
--- a/src/common/crypto_format.c
+++ b/src/common/crypto_format.c
@@ -123,6 +123,10 @@ crypto_read_tagged_contents_from_file(const char *fname,
return r;
}
+/** Encode <b>pkey</b> as a base64-encoded string, without trailing "="
+ * characters, in the buffer <b>output</b>, which must have at least
+ * CURVE25519_BASE64_PADDED_LEN+1 bytes available. Return 0 on success, -1 on
+ * failure. */
int
curve25519_public_to_base64(char *output,
const curve25519_public_key_t *pkey)
@@ -135,6 +139,9 @@ curve25519_public_to_base64(char *output,
return 0;
}
+/** Try to decode a base64-encoded curve25519 public key from <b>input</b>
+ * into the object at <b>pkey</b>. Return 0 on success, -1 on failure.
+ * Accepts keys with or without a trailing "=". */
int
curve25519_public_from_base64(curve25519_public_key_t *pkey,
const char *input)
diff --git a/src/common/crypto_pwbox.c b/src/common/crypto_pwbox.c
index 819dc0c39d..31e37c007d 100644
--- a/src/common/crypto_pwbox.c
+++ b/src/common/crypto_pwbox.c
@@ -61,7 +61,7 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
pwbox_encoded_getarray_skey_header(enc),
S2K_MAXLEN,
s2k_flags);
- if (spec_len < 0 || spec_len > S2K_MAXLEN)
+ if (BUG(spec_len < 0 || spec_len > S2K_MAXLEN))
goto err;
pwbox_encoded_setlen_skey_header(enc, spec_len);
enc->header_len = spec_len;
@@ -76,10 +76,11 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
/* Now that all the data is in position, derive some keys, encrypt, and
* digest */
- if (secret_to_key_derivekey(keys, sizeof(keys),
+ const int s2k_rv = secret_to_key_derivekey(keys, sizeof(keys),
pwbox_encoded_getarray_skey_header(enc),
spec_len,
- secret, secret_len) < 0)
+ secret, secret_len);
+ if (BUG(s2k_rv < 0))
goto err;
cipher = crypto_cipher_new_with_iv((char*)keys, (char*)enc->iv);
@@ -87,11 +88,11 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
crypto_cipher_free(cipher);
result_len = pwbox_encoded_encoded_len(enc);
- if (result_len < 0)
+ if (BUG(result_len < 0))
goto err;
result = tor_malloc(result_len);
enc_len = pwbox_encoded_encode(result, result_len, enc);
- if (enc_len < 0)
+ if (BUG(enc_len < 0))
goto err;
tor_assert(enc_len == result_len);
@@ -107,9 +108,24 @@ crypto_pwbox(uint8_t **out, size_t *outlen_out,
goto out;
err:
+ /* LCOV_EXCL_START
+
+ This error case is often unreachable if we're correctly coded, unless
+ somebody adds a new error case somewhere, or unless you're building
+ without scrypto support.
+
+ - make_specifier can't fail, unless S2K_MAX_LEN is too short.
+ - secret_to_key_derivekey can't really fail unless we're missing
+ scrypt, or the underlying function fails, or we pass it a bogus
+ algorithm or parameters.
+ - pwbox_encoded_encoded_len can't fail unless we're using trunnel
+ incorrectly.
+ - pwbox_encoded_encode can't fail unless we're using trunnel wrong,
+ or it's buggy.
+ */
tor_free(result);
rv = -1;
-
+ /* LCOV_EXCL_STOP */
out:
pwbox_encoded_free(enc);
memwipe(keys, 0, sizeof(keys));
diff --git a/src/common/crypto_s2k.c b/src/common/crypto_s2k.c
index 3bc05f1cf9..5dbd2ad91f 100644
--- a/src/common/crypto_s2k.c
+++ b/src/common/crypto_s2k.c
@@ -57,7 +57,8 @@
#define SCRYPT_KEY_LEN 32
/** Given an algorithm ID (one of S2K_TYPE_*), return the length of the
- * specifier part of it, without the prefix type byte. */
+ * specifier part of it, without the prefix type byte. Return -1 if it is not
+ * a valid algorithm ID. */
static int
secret_to_key_spec_len(uint8_t type)
{
@@ -86,7 +87,8 @@ secret_to_key_key_len(uint8_t type)
case S2K_TYPE_SCRYPT:
return DIGEST256_LEN;
default:
- return -1;
+ tor_fragile_assert(); // LCOV_EXCL_LINE
+ return -1; // LCOV_EXCL_LINE
}
}
@@ -168,7 +170,7 @@ make_specifier(uint8_t *spec_out, uint8_t type, unsigned flags)
spec_out[SCRYPT_SPEC_LEN-1] = (3u << 4) | (1u << 0);
break;
default:
- tor_fragile_assert();
+ tor_fragile_assert(); // LCOV_EXCL_LINE - we should have returned above.
return S2K_BAD_ALGORITHM;
}
diff --git a/src/common/di_ops.c b/src/common/di_ops.c
index 5dfe828066..4ed49e1164 100644
--- a/src/common/di_ops.c
+++ b/src/common/di_ops.c
@@ -226,3 +226,49 @@ safe_mem_is_zero(const void *mem, size_t sz)
return 1 & ((total - 1) >> 8);
}
+/** Time-invariant 64-bit greater-than; works on two integers in the range
+ * (0,INT64_MAX). */
+#if SIZEOF_VOID_P == 8
+#define gt_i64_timei(a,b) ((a) > (b))
+#else
+static inline int
+gt_i64_timei(uint64_t a, uint64_t b)
+{
+ int64_t diff = (int64_t) (b - a);
+ int res = diff >> 63;
+ return res & 1;
+}
+#endif
+
+/**
+ * Given an array of list of <b>n_entries</b> uint64_t values, whose sum is
+ * <b>total</b>, find the first i such that the total of all elements 0...i is
+ * greater than rand_val.
+ *
+ * Try to perform this operation in a constant-time way.
+ */
+int
+select_array_member_cumulative_timei(const uint64_t *entries, int n_entries,
+ uint64_t total, uint64_t rand_val)
+{
+ int i, i_chosen=-1, n_chosen=0;
+ uint64_t total_so_far = 0;
+
+ for (i = 0; i < n_entries; ++i) {
+ total_so_far += entries[i];
+ if (gt_i64_timei(total_so_far, rand_val)) {
+ i_chosen = i;
+ n_chosen++;
+ /* Set rand_val to INT64_MAX rather than stopping the loop. This way,
+ * the time we spend in the loop does not leak which element we chose. */
+ rand_val = INT64_MAX;
+ }
+ }
+ tor_assert(total_so_far == total);
+ tor_assert(n_chosen == 1);
+ tor_assert(i_chosen >= 0);
+ tor_assert(i_chosen < n_entries);
+
+ return i_chosen;
+}
+
diff --git a/src/common/di_ops.h b/src/common/di_ops.h
index 6e77b5cfd7..0a154302bf 100644
--- a/src/common/di_ops.h
+++ b/src/common/di_ops.h
@@ -42,6 +42,9 @@ void dimap_add_entry(di_digest256_map_t **map,
const uint8_t *key, void *val);
void *dimap_search(const di_digest256_map_t *map, const uint8_t *key,
void *dflt_val);
+int select_array_member_cumulative_timei(const uint64_t *entries,
+ int n_entries,
+ uint64_t total, uint64_t rand_val);
#endif
diff --git a/src/common/handles.h b/src/common/handles.h
new file mode 100644
index 0000000000..1ee2322579
--- /dev/null
+++ b/src/common/handles.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file handles.h
+ * \brief Macros for C weak-handle implementation.
+ *
+ * A 'handle' is a pointer to an object that is allowed to go away while
+ * the handle stays alive. When you dereference the handle, you might get
+ * the object, or you might get "NULL".
+ *
+ * Use this pattern when an object has a single obvious lifespan, so you don't
+ * want to use reference counting, but when other objects might need to refer
+ * to the first object without caring about its lifetime.
+ *
+ * To enable a type to have handles, add a HANDLE_ENTRY() field in its
+ * definition, as in:
+ *
+ * struct walrus {
+ * HANDLE_ENTRY(wlr, walrus);
+ * // ...
+ * };
+ *
+ * And invoke HANDLE_DECL(wlr, walrus, [static]) to declare the handle
+ * manipulation functions (typically in a header):
+ *
+ * // opaque handle to walrus.
+ * typedef struct wlr_handle_t wlr_handle_t;
+ *
+ * // make a new handle
+ * struct wlr_handle_t *wlr_handle_new(struct walrus *);
+ *
+ * // release a handle
+ * void wlr_handle_free(wlr_handle_t *);
+ *
+ * // return the pointed-to walrus, or NULL.
+ * struct walrus *wlr_handle_get(wlr_handle_t *).
+ *
+ * // call this function when you're about to free the walrus;
+ * // it invalidates all handles. (IF YOU DON'T, YOU WILL HAVE
+ * // DANGLING REFERENCES)
+ * void wlr_handles_clear(struct walrus *);
+ *
+ * Finally, use HANDLE_IMPL() to define the above functions in some
+ * appropriate C file: HANDLE_IMPL(wlr, walrus, [static])
+ *
+ **/
+
+#ifndef TOR_HANDLE_H
+#define TOR_HANDLE_H
+
+#include "orconfig.h"
+#include "tor_queue.h"
+#include "util.h"
+
+#define HANDLE_ENTRY(name, structname) \
+ struct name ## _handle_head_t *handle_head
+
+#define HANDLE_DECL(name, structname, linkage) \
+ typedef struct name ## _handle_t name ## _handle_t; \
+ linkage name ## _handle_t *name ## _handle_new(struct structname *object); \
+ linkage void name ## _handle_free(name ## _handle_t *); \
+ linkage struct structname *name ## _handle_get(name ## _handle_t *); \
+ linkage void name ## _handles_clear(struct structname *object);
+
+/*
+ * Implementation notes: there are lots of possible implementations here. We
+ * could keep a linked list of handles, each with a backpointer to the object,
+ * and set all of their backpointers to NULL when the object is freed. Or we
+ * could have the clear function invalidate the object, but not actually let
+ * the object get freed until the all the handles went away. We could even
+ * have a hash-table mapping unique identifiers to objects, and have each
+ * handle be a copy of the unique identifier. (We'll want to build that last
+ * one eventually if we want cross-process handles.)
+ *
+ * But instead we're opting for a single independent 'head' that knows how
+ * many handles there are, and where the object is (or isn't). This makes
+ * all of our functions O(1), and most as fast as a single pointer access.
+ *
+ * The handles themselves are opaque structures holding a pointer to the head.
+ * We could instead have each foo_handle_t* be identical to foo_handle_head_t
+ * *, and save some allocations ... but doing so would make handle leaks
+ * harder to debug. As it stands, every handle leak is a memory leak, and
+ * existing memory debugging tools should help with those. We can revisit
+ * this decision if handles are too slow.
+ */
+
+#define HANDLE_IMPL(name, structname, linkage) \
+ /* The 'head' object for a handle-accessible type. This object */ \
+ /* persists for as long as the object, or any handles, exist. */ \
+ typedef struct name ## _handle_head_t { \
+ struct structname *object; /* pointed-to object, or NULL */ \
+ unsigned int references; /* number of existing handles */ \
+ } name ## _handle_head_t; \
+ \
+ struct name ## _handle_t { \
+ struct name ## _handle_head_t *head; /* reference to the 'head'. */ \
+ }; \
+ \
+ linkage struct name ## _handle_t * \
+ name ## _handle_new(struct structname *object) \
+ { \
+ tor_assert(object); \
+ name ## _handle_head_t *head = object->handle_head; \
+ if (PREDICT_UNLIKELY(head == NULL)) { \
+ head = object->handle_head = tor_malloc_zero(sizeof(*head)); \
+ head->object = object; \
+ } \
+ name ## _handle_t *new_ref = tor_malloc_zero(sizeof(*new_ref)); \
+ new_ref->head = head; \
+ ++head->references; \
+ return new_ref; \
+ } \
+ \
+ linkage void \
+ name ## _handle_free(struct name ## _handle_t *ref) \
+ { \
+ if (! ref) return; \
+ name ## _handle_head_t *head = ref->head; \
+ tor_assert(head); \
+ --head->references; \
+ tor_free(ref); \
+ if (head->object == NULL && head->references == 0) { \
+ tor_free(head); \
+ return; \
+ } \
+ } \
+ \
+ linkage struct structname * \
+ name ## _handle_get(struct name ## _handle_t *ref) \
+ { \
+ tor_assert(ref); \
+ name ## _handle_head_t *head = ref->head; \
+ tor_assert(head); \
+ return head->object; \
+ } \
+ \
+ linkage void \
+ name ## _handles_clear(struct structname *object) \
+ { \
+ tor_assert(object); \
+ name ## _handle_head_t *head = object->handle_head; \
+ if (! head) \
+ return; \
+ object->handle_head = NULL; \
+ head->object = NULL; \
+ if (head->references == 0) { \
+ tor_free(head); \
+ } \
+ }
+
+#endif /* TOR_HANDLE_H */
+
diff --git a/src/common/include.am b/src/common/include.am
index 5afb30da6a..40c463c9d9 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -1,12 +1,14 @@
noinst_LIBRARIES += \
src/common/libor.a \
+ src/common/libor-ctime.a \
src/common/libor-crypto.a \
src/common/libor-event.a
if UNITTESTS_ENABLED
noinst_LIBRARIES += \
src/common/libor-testing.a \
+ src/common/libor-ctime-testing.a \
src/common/libor-crypto-testing.a \
src/common/libor-event-testing.a
endif
@@ -27,12 +29,14 @@ src_common_libcurve25519_donna_a_CFLAGS=
if BUILD_CURVE25519_DONNA
src_common_libcurve25519_donna_a_SOURCES=\
src/ext/curve25519_donna/curve25519-donna.c
+# See bug 13538 -- this code is known to have signed overflow issues.
src_common_libcurve25519_donna_a_CFLAGS+=\
- @F_OMIT_FRAME_POINTER@
+ @F_OMIT_FRAME_POINTER@ @CFLAGS_CONSTTIME@
noinst_LIBRARIES+=src/common/libcurve25519_donna.a
LIBDONNA=src/common/libcurve25519_donna.a
else
if BUILD_CURVE25519_DONNA_C64
+src_common_libcurve25519_donna_a_CFLAGS+=@CFLAGS_CONSTTIME@
src_common_libcurve25519_donna_a_SOURCES=\
src/ext/curve25519_donna/curve25519-donna-c64.c
noinst_LIBRARIES+=src/common/libcurve25519_donna.a
@@ -58,22 +62,38 @@ else
readpassphrase_source=
endif
-LIBOR_A_SOURCES = \
+if ADD_MULODI4
+mulodi4_source=src/ext/mulodi/mulodi4.c
+else
+mulodi4_source=
+endif
+
+LIBOR_CTIME_A_SRC = \
+ $(mulodi4_source) \
+ src/ext/csiphash.c \
+ src/common/di_ops.c
+
+src_common_libor_ctime_a_SOURCES = $(LIBOR_CTIME_A_SRC)
+src_common_libor_ctime_testing_a_SOURCES = $(LIBOR_CTIME_A_SRC)
+src_common_libor_ctime_a_CFLAGS = @CFLAGS_CONSTTIME@
+src_common_libor_ctime_testing_a_CFLAGS = @CFLAGS_CONSTTIME@ $(TEST_CFLAGS)
+
+LIBOR_A_SRC = \
src/common/address.c \
src/common/backtrace.c \
src/common/compat.c \
src/common/compat_threads.c \
+ src/common/compat_time.c \
src/common/container.c \
- src/common/di_ops.c \
src/common/log.c \
src/common/memarea.c \
+ src/common/pubsub.c \
src/common/util.c \
+ src/common/util_bug.c \
src/common/util_format.c \
src/common/util_process.c \
src/common/sandbox.c \
src/common/workqueue.c \
- src/ext/csiphash.c \
- src/ext/trunnel/trunnel.c \
$(libor_extra_source) \
$(threads_impl_source) \
$(readpassphrase_source)
@@ -81,7 +101,7 @@ LIBOR_A_SOURCES = \
src/common/src_common_libor_testing_a-log.$(OBJEXT) \
src/common/log.$(OBJEXT): micro-revision.i
-LIBOR_CRYPTO_A_SOURCES = \
+LIBOR_CRYPTO_A_SRC = \
src/common/aes.c \
src/common/crypto.c \
src/common/crypto_pwbox.c \
@@ -89,21 +109,22 @@ LIBOR_CRYPTO_A_SOURCES = \
src/common/crypto_format.c \
src/common/torgzip.c \
src/common/tortls.c \
- src/trunnel/pwbox.c \
src/common/crypto_curve25519.c \
src/common/crypto_ed25519.c
-LIBOR_EVENT_A_SOURCES = \
+LIBOR_EVENT_A_SRC = \
src/common/compat_libevent.c \
- src/common/procmon.c
+ src/common/procmon.c \
+ src/common/timers.c \
+ src/ext/timeouts/timeout.c
-src_common_libor_a_SOURCES = $(LIBOR_A_SOURCES)
-src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
-src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SOURCES)
+src_common_libor_a_SOURCES = $(LIBOR_A_SRC)
+src_common_libor_crypto_a_SOURCES = $(LIBOR_CRYPTO_A_SRC)
+src_common_libor_event_a_SOURCES = $(LIBOR_EVENT_A_SRC)
-src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES)
-src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
-src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES)
+src_common_libor_testing_a_SOURCES = $(LIBOR_A_SRC)
+src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SRC)
+src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SRC)
src_common_libor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_common_libor_crypto_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
@@ -121,6 +142,7 @@ COMMONHEADERS = \
src/common/compat_libevent.h \
src/common/compat_openssl.h \
src/common/compat_threads.h \
+ src/common/compat_time.h \
src/common/container.h \
src/common/crypto.h \
src/common/crypto_curve25519.h \
@@ -129,16 +151,20 @@ COMMONHEADERS = \
src/common/crypto_pwbox.h \
src/common/crypto_s2k.h \
src/common/di_ops.h \
+ src/common/handles.h \
src/common/memarea.h \
src/common/linux_syscalls.inc \
src/common/procmon.h \
+ src/common/pubsub.h \
src/common/sandbox.h \
src/common/testsupport.h \
+ src/common/timers.h \
src/common/torgzip.h \
src/common/torint.h \
src/common/torlog.h \
src/common/tortls.h \
src/common/util.h \
+ src/common/util_bug.h \
src/common/util_format.h \
src/common/util_process.h \
src/common/workqueue.h
diff --git a/src/common/log.c b/src/common/log.c
index 6c387c6244..56adc77f84 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -47,6 +47,8 @@
#define TRUNCATED_STR_LEN 14
/** @} */
+#define raw_assert(x) assert(x) // assert OK
+
/** Information for a single logfile; only used in log.c */
typedef struct logfile_t {
struct logfile_t *next; /**< Next logfile_t in the linked list. */
@@ -75,7 +77,7 @@ sev_to_string(int severity)
case LOG_ERR: return "err";
default: /* Call assert, not tor_assert, since tor_assert
* calls log on failure. */
- assert(0); return "UNKNOWN";
+ raw_assert(0); return "UNKNOWN"; // LCOV_EXCL_LINE
}
}
@@ -95,7 +97,7 @@ should_log_function_name(log_domain_mask_t domain, int severity)
return (domain & (LD_BUG|LD_NOFUNCNAME)) == LD_BUG;
default:
/* Call assert, not tor_assert, since tor_assert calls log on failure. */
- assert(0); return 0;
+ raw_assert(0); return 0; // LCOV_EXCL_LINE
}
}
@@ -270,7 +272,7 @@ log_tor_version(logfile_t *lf, int reset)
return 0;
}
-const char bug_suffix[] = " (on Tor " VERSION
+static const char bug_suffix[] = " (on Tor " VERSION
#ifndef _MSC_VER
" "
#include "micro-revision.i"
@@ -293,7 +295,7 @@ format_msg(char *buf, size_t buf_len,
char *end_of_prefix;
char *buf_end;
- assert(buf_len >= 16); /* prevent integer underflow and general stupidity */
+ raw_assert(buf_len >= 16); /* prevent integer underflow and stupidity */
buf_len -= 2; /* subtract 2 characters so we have room for \n\0 */
buf_end = buf+buf_len; /* point *after* the last char we can write to */
@@ -482,12 +484,12 @@ logv,(int severity, log_domain_mask_t domain, const char *funcname,
int callbacks_deferred = 0;
/* Call assert, not tor_assert, since tor_assert calls log on failure. */
- assert(format);
+ raw_assert(format);
/* check that severity is sane. Overrunning the masks array leads to
* interesting and hard to diagnose effects */
- assert(severity >= LOG_ERR && severity <= LOG_DEBUG);
+ raw_assert(severity >= LOG_ERR && severity <= LOG_DEBUG);
/* check that we've initialised the log mutex before we try to lock it */
- assert(log_mutex_initialized);
+ raw_assert(log_mutex_initialized);
LOCK_LOGS();
if ((! (domain & LD_NOCB)) && pending_cb_messages
@@ -534,6 +536,11 @@ tor_log(int severity, log_domain_mask_t domain, const char *format, ...)
if (severity > log_global_min_severity_)
return;
va_start(ap,format);
+#ifdef TOR_UNIT_TESTS
+ if (domain & LD_NO_MOCK)
+ logv__real(severity, domain, NULL, NULL, format, ap);
+ else
+#endif
logv(severity, domain, NULL, NULL, format, ap);
va_end(ap);
}
@@ -653,7 +660,7 @@ tor_log_update_sigsafe_err_fds(void)
if (!found_real_stderr &&
int_array_contains(sigsafe_log_fds, n_sigsafe_log_fds, STDOUT_FILENO)) {
/* Don't use a virtual stderr when we're also logging to stdout. */
- assert(n_sigsafe_log_fds >= 2); /* Don't use assert inside log functions*/
+ raw_assert(n_sigsafe_log_fds >= 2); /* Don't tor_assert inside log fns */
sigsafe_log_fds[0] = sigsafe_log_fds[--n_sigsafe_log_fds];
}
@@ -1071,13 +1078,13 @@ mark_logs_temp(void)
*/
int
add_file_log(const log_severity_list_t *severity, const char *filename,
- const int truncate)
+ const int truncate_log)
{
int fd;
logfile_t *lf;
int open_flags = O_WRONLY|O_CREAT;
- open_flags |= truncate ? O_TRUNC : O_APPEND;
+ open_flags |= truncate_log ? O_TRUNC : O_APPEND;
fd = tor_open_cloexec(filename, open_flags, 0644);
if (fd<0)
diff --git a/src/common/memarea.c b/src/common/memarea.c
index 173ed4e1cb..7d16b702e3 100644
--- a/src/common/memarea.c
+++ b/src/common/memarea.c
@@ -131,7 +131,7 @@ alloc_chunk(size_t sz)
/** Release <b>chunk</b> from a memarea. */
static void
-chunk_free_unchecked(memarea_chunk_t *chunk)
+memarea_chunk_free_unchecked(memarea_chunk_t *chunk)
{
CHECK_SENTINEL(chunk);
tor_free(chunk);
@@ -154,7 +154,7 @@ memarea_drop_all(memarea_t *area)
memarea_chunk_t *chunk, *next;
for (chunk = area->first; chunk; chunk = next) {
next = chunk->next_chunk;
- chunk_free_unchecked(chunk);
+ memarea_chunk_free_unchecked(chunk);
}
area->first = NULL; /*fail fast on */
tor_free(area);
@@ -170,7 +170,7 @@ memarea_clear(memarea_t *area)
if (area->first->next_chunk) {
for (chunk = area->first->next_chunk; chunk; chunk = next) {
next = chunk->next_chunk;
- chunk_free_unchecked(chunk);
+ memarea_chunk_free_unchecked(chunk);
}
area->first->next_chunk = NULL;
}
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 12d53fcd41..c485c760c7 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -10,11 +10,7 @@
#include "util.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
@@ -116,11 +112,11 @@ struct tor_process_monitor_t {
* periodically check whether the process we have a handle to has
* ended. */
HANDLE hproc;
- /* XXX023 We can and should have Libevent watch hproc for us,
- * if/when some version of Libevent 2.x can be told to do so. */
+ /* XXXX We should have Libevent watch hproc for us,
+ * if/when some version of Libevent can be told to do so. */
#endif
- /* XXX023 On Linux, we can and should receive the 22nd
+ /* XXXX On Linux, we can and should receive the 22nd
* (space-delimited) field (‘starttime’) of /proc/$PID/stat from the
* owning controller and store it, and poll once in a while to see
* whether it has changed -- if so, the kernel has *definitely*
@@ -130,7 +126,8 @@ struct tor_process_monitor_t {
* systems whose admins have mounted procfs, or the start-time field
* of the process-information structure returned by kvmgetprocs() on
* any system. The latter is ickier. */
- /* XXX023 On FreeBSD (and possibly other kqueue systems), we can and
+
+ /* XXXX On FreeBSD (and possibly other kqueue systems), we can and
* should arrange to receive EVFILT_PROC NOTE_EXIT notifications for
* pid, so we don't have to do such a heavyweight poll operation in
* order to avoid the PID-reassignment race condition. (We would
@@ -163,18 +160,10 @@ tor_validate_process_specifier(const char *process_spec,
}
/* XXXX we should use periodic_timer_new() for this stuff */
-#ifdef HAVE_EVENT2_EVENT_H
#define PERIODIC_TIMER_FLAGS EV_PERSIST
-#else
-#define PERIODIC_TIMER_FLAGS (0)
-#endif
/* DOCDOC poll_interval_tv */
-static struct timeval poll_interval_tv = {15, 0};
-/* Note: If you port this file to plain Libevent 2, you can make
- * poll_interval_tv const. It has to be non-const here because in
- * libevent 1.x, event_add expects a pointer to a non-const struct
- * timeval. */
+static const struct timeval poll_interval_tv = {15, 0};
/** Create a process-termination monitor for the process specifier
* given in <b>process_spec</b>. Return a newly allocated
@@ -330,10 +319,6 @@ tor_process_monitor_poll_cb(evutil_socket_t unused1, short unused2,
if (its_dead_jim) {
procmon->cb(procmon->cb_arg);
-#ifndef HAVE_EVENT2_EVENT_H
- } else {
- evtimer_add(procmon->e, &poll_interval_tv);
-#endif
}
}
#endif
diff --git a/src/common/pubsub.c b/src/common/pubsub.c
new file mode 100644
index 0000000000..b3faf40e00
--- /dev/null
+++ b/src/common/pubsub.c
@@ -0,0 +1,129 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file pubsub.c
+ *
+ * \brief DOCDOC
+ */
+
+#include "orconfig.h"
+#include "pubsub.h"
+#include "container.h"
+
+/** Helper: insert <b>s</b> into <b>topic's</b> list of subscribers, keeping
+ * them sorted in priority order. */
+static void
+subscriber_insert(pubsub_topic_t *topic, pubsub_subscriber_t *s)
+{
+ int i;
+ smartlist_t *sl = topic->subscribers;
+ for (i = 0; i < smartlist_len(sl); ++i) {
+ pubsub_subscriber_t *other = smartlist_get(sl, i);
+ if (s->priority < other->priority) {
+ break;
+ }
+ }
+ smartlist_insert(sl, i, s);
+}
+
+/**
+ * Add a new subscriber to <b>topic</b>, where (when an event is triggered),
+ * we'll notify the function <b>fn</b> by passing it <b>subscriber_data</b>.
+ * Return a handle to the subscribe which can later be passed to
+ * pubsub_unsubscribe_().
+ *
+ * Functions are called in priority order, from lowest to highest.
+ *
+ * See pubsub.h for <b>subscribe_flags</b>.
+ */
+const pubsub_subscriber_t *
+pubsub_subscribe_(pubsub_topic_t *topic,
+ pubsub_subscriber_fn_t fn,
+ void *subscriber_data,
+ unsigned subscribe_flags,
+ unsigned priority)
+{
+ tor_assert(! topic->locked);
+ if (subscribe_flags & SUBSCRIBE_ATSTART) {
+ tor_assert(topic->n_events_fired == 0);
+ }
+ pubsub_subscriber_t *r = tor_malloc_zero(sizeof(*r));
+ r->priority = priority;
+ r->subscriber_flags = subscribe_flags;
+ r->fn = fn;
+ r->subscriber_data = subscriber_data;
+ if (topic->subscribers == NULL) {
+ topic->subscribers = smartlist_new();
+ }
+ subscriber_insert(topic, r);
+ return r;
+}
+
+/**
+ * Remove the subscriber <b>s</b> from <b>topic</b>. After calling this
+ * function, <b>s</b> may no longer be used.
+ */
+int
+pubsub_unsubscribe_(pubsub_topic_t *topic,
+ const pubsub_subscriber_t *s)
+{
+ tor_assert(! topic->locked);
+ smartlist_t *sl = topic->subscribers;
+ if (sl == NULL)
+ return -1;
+ int i = smartlist_pos(sl, s);
+ if (i == -1)
+ return -1;
+ pubsub_subscriber_t *tmp = smartlist_get(sl, i);
+ tor_assert(tmp == s);
+ smartlist_del_keeporder(sl, i);
+ tor_free(tmp);
+ return 0;
+}
+
+/**
+ * For every subscriber s in <b>topic</b>, invoke notify_fn on s and
+ * event_data. Return 0 if there were no nonzero return values, and -1 if
+ * there were any.
+ */
+int
+pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn,
+ void *event_data, unsigned notify_flags)
+{
+ tor_assert(! topic->locked);
+ (void) notify_flags;
+ smartlist_t *sl = topic->subscribers;
+ int n_bad = 0;
+ ++topic->n_events_fired;
+ if (sl == NULL)
+ return -1;
+ topic->locked = 1;
+ SMARTLIST_FOREACH_BEGIN(sl, pubsub_subscriber_t *, s) {
+ int r = notify_fn(s, event_data);
+ if (r != 0)
+ ++n_bad;
+ } SMARTLIST_FOREACH_END(s);
+ topic->locked = 0;
+ return (n_bad == 0) ? 0 : -1;
+}
+
+/**
+ * Release all storage held by <b>topic</b>.
+ */
+void
+pubsub_clear_(pubsub_topic_t *topic)
+{
+ tor_assert(! topic->locked);
+
+ smartlist_t *sl = topic->subscribers;
+ if (sl == NULL)
+ return;
+ SMARTLIST_FOREACH_BEGIN(sl, pubsub_subscriber_t *, s) {
+ tor_free(s);
+ } SMARTLIST_FOREACH_END(s);
+ smartlist_free(sl);
+ topic->subscribers = NULL;
+ topic->n_events_fired = 0;
+}
+
diff --git a/src/common/pubsub.h b/src/common/pubsub.h
new file mode 100644
index 0000000000..bbb4f02a42
--- /dev/null
+++ b/src/common/pubsub.h
@@ -0,0 +1,179 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file pubsub.h
+ * \brief Macros to implement publish/subscribe abstractions.
+ *
+ * To use these macros, call DECLARE_PUBSUB_TOPIC() with an identifier to use
+ * as your topic. Below, I'm going to assume you say DECLARE_PUBSUB_TOPIC(T).
+ *
+ * Doing this will declare the following types:
+ * typedef struct T_event_data_t T_event_data_t; // you define this struct
+ * typedef struct T_subscriber_data_t T_subscriber_data_t; // this one too.
+ * typedef struct T_subscriber_t T_subscriber_t; // opaque
+ * typedef int (*T_subscriber_fn_t)(T_event_data_t*, T_subscriber_data_t*);
+ *
+ * and it will declare the following functions:
+ * const T_subscriber_t *T_subscribe(T_subscriber_fn_t,
+ * T_subscriber_data_t *,
+ * unsigned flags,
+ * unsigned priority);
+ * int T_unsubscribe(const T_subscriber_t *)
+ *
+ * Elsewhere you can say DECLARE_NOTIFY_PUBSUB_TOPIC(static, T), which
+ * declares:
+ *
+ * static int T_notify(T_event_data_t *, unsigned notify_flags);
+ * static void T_clear(void);
+ *
+ * And in some C file, you would define these functions with:
+ * IMPLEMENT_PUBSUB_TOPIC(static, T).
+ *
+ * The implementations will be small typesafe wrappers over generic versions
+ * of the above functions.
+ *
+ * To use the typesafe functions, you add any number of subscribers with
+ * T_subscribe(). Each has an associated function pointer, data pointer,
+ * and priority. Later, you can invoke T_notify() to declare that the
+ * event has occurred. Each of the subscribers will be invoked once.
+ **/
+
+#ifndef TOR_PUBSUB_H
+#define TOR_PUBSUB_H
+
+#include "torint.h"
+
+/**
+ * Flag for T_subscribe: die with an assertion failure if the event
+ * have ever been published before. Used when a subscriber must absolutely
+ * never have missed an event.
+ */
+#define SUBSCRIBE_ATSTART (1u<<0)
+
+#define DECLARE_PUBSUB_STRUCT_TYPES(name) \
+ /* You define this type. */ \
+ typedef struct name ## _event_data_t name ## _event_data_t; \
+ /* You define this type. */ \
+ typedef struct name ## _subscriber_data_t name ## _subscriber_data_t;
+
+#define DECLARE_PUBSUB_TOPIC(name) \
+ /* This type is opaque. */ \
+ typedef struct name ## _subscriber_t name ## _subscriber_t; \
+ /* You declare functions matching this type. */ \
+ typedef int (*name ## _subscriber_fn_t)( \
+ name ## _event_data_t *data, \
+ name ## _subscriber_data_t *extra); \
+ /* Call this function to subscribe to a topic. */ \
+ const name ## _subscriber_t *name ## _subscribe( \
+ name##_subscriber_fn_t subscriber, \
+ name##_subscriber_data_t *extra_data, \
+ unsigned flags, \
+ unsigned priority); \
+ /* Call this function to unsubscribe from a topic. */ \
+ int name ## _unsubscribe(const name##_subscriber_t *s);
+
+#define DECLARE_NOTIFY_PUBSUB_TOPIC(linkage, name) \
+ /* Call this function to notify all subscribers. Flags not yet used. */ \
+ linkage int name ## _notify(name ## _event_data_t *data, unsigned flags); \
+ /* Call this function to release storage held by the topic. */ \
+ linkage void name ## _clear(void);
+
+/**
+ * Type used to hold a generic function for a subscriber.
+ *
+ * [Yes, it is safe to cast to this, so long as we cast back to the original
+ * type before calling. From C99: "A pointer to a function of one type may be
+ * converted to a pointer to a function of another type and back again; the
+ * result shall compare equal to the original pointer."]
+*/
+typedef int (*pubsub_subscriber_fn_t)(void *, void *);
+
+/**
+ * Helper type to implement pubsub abstraction. Don't use this directly.
+ * It represents a subscriber.
+ */
+typedef struct pubsub_subscriber_t {
+ /** Function to invoke when the event triggers. */
+ pubsub_subscriber_fn_t fn;
+ /** Data associated with this subscriber. */
+ void *subscriber_data;
+ /** Priority for this subscriber. Low priorities happen first. */
+ unsigned priority;
+ /** Flags set on this subscriber. Not yet used.*/
+ unsigned subscriber_flags;
+} pubsub_subscriber_t;
+
+/**
+ * Helper type to implement pubsub abstraction. Don't use this directly.
+ * It represents a topic, and keeps a record of subscribers.
+ */
+typedef struct pubsub_topic_t {
+ /** List of subscribers to this topic. May be NULL. */
+ struct smartlist_t *subscribers;
+ /** Total number of times that pubsub_notify_() has ever been called on this
+ * topic. */
+ uint64_t n_events_fired;
+ /** True iff we're running 'notify' on this topic, and shouldn't allow
+ * any concurrent modifications or events. */
+ unsigned locked;
+} pubsub_topic_t;
+
+const pubsub_subscriber_t *pubsub_subscribe_(pubsub_topic_t *topic,
+ pubsub_subscriber_fn_t fn,
+ void *subscriber_data,
+ unsigned subscribe_flags,
+ unsigned priority);
+int pubsub_unsubscribe_(pubsub_topic_t *topic, const pubsub_subscriber_t *sub);
+void pubsub_clear_(pubsub_topic_t *topic);
+typedef int (*pubsub_notify_fn_t)(pubsub_subscriber_t *subscriber,
+ void *notify_data);
+int pubsub_notify_(pubsub_topic_t *topic, pubsub_notify_fn_t notify_fn,
+ void *notify_data, unsigned notify_flags);
+
+#define IMPLEMENT_PUBSUB_TOPIC(notify_linkage, name) \
+ static pubsub_topic_t name ## _topic_ = { NULL, 0, 0 }; \
+ const name ## _subscriber_t * \
+ name ## _subscribe(name##_subscriber_fn_t subscriber, \
+ name##_subscriber_data_t *extra_data, \
+ unsigned flags, \
+ unsigned priority) \
+ { \
+ const pubsub_subscriber_t *s; \
+ s = pubsub_subscribe_(&name##_topic_, \
+ (pubsub_subscriber_fn_t)subscriber, \
+ extra_data, \
+ flags, \
+ priority); \
+ return (const name##_subscriber_t *)s; \
+ } \
+ int \
+ name ## _unsubscribe(const name##_subscriber_t *subscriber) \
+ { \
+ return pubsub_unsubscribe_(&name##_topic_, \
+ (const pubsub_subscriber_t *)subscriber); \
+ } \
+ static int \
+ name##_call_the_notify_fn_(pubsub_subscriber_t *subscriber, \
+ void *notify_data) \
+ { \
+ name ## _subscriber_fn_t fn; \
+ fn = (name ## _subscriber_fn_t) subscriber->fn; \
+ return fn(notify_data, subscriber->subscriber_data); \
+ } \
+ notify_linkage int \
+ name ## _notify(name ## _event_data_t *event_data, unsigned flags) \
+ { \
+ return pubsub_notify_(&name##_topic_, \
+ name##_call_the_notify_fn_, \
+ event_data, \
+ flags); \
+ } \
+ notify_linkage void \
+ name ## _clear(void) \
+ { \
+ pubsub_clear_(&name##_topic_); \
+ }
+
+#endif /* TOR_PUBSUB_H */
+
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index 74187e5d63..7f4511db2a 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -39,8 +39,6 @@
#if defined(USE_LIBSECCOMP)
-#define _GNU_SOURCE
-
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/types.h>
@@ -138,6 +136,9 @@ static int filter_nopar_gen[] = {
#ifdef HAVE_PIPE
SCMP_SYS(pipe),
#endif
+#ifdef __NR_fchmod
+ SCMP_SYS(fchmod),
+#endif
SCMP_SYS(fcntl),
SCMP_SYS(fstat),
#ifdef __NR_fstat64
@@ -157,6 +158,7 @@ static int filter_nopar_gen[] = {
#ifdef __NR_getgid32
SCMP_SYS(getgid32),
#endif
+ SCMP_SYS(getpid),
#ifdef __NR_getrlimit
SCMP_SYS(getrlimit),
#endif
@@ -186,11 +188,17 @@ static int filter_nopar_gen[] = {
SCMP_SYS(read),
SCMP_SYS(rt_sigreturn),
SCMP_SYS(sched_getaffinity),
+#ifdef __NR_sched_yield
+ SCMP_SYS(sched_yield),
+#endif
SCMP_SYS(sendmsg),
SCMP_SYS(set_robust_list),
#ifdef __NR_setrlimit
SCMP_SYS(setrlimit),
#endif
+#ifdef __NR_sigaltstack
+ SCMP_SYS(sigaltstack),
+#endif
#ifdef __NR_sigreturn
SCMP_SYS(sigreturn),
#endif
@@ -720,6 +728,14 @@ sb_setsockopt(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
return rc;
#endif
+#ifdef IPV6_V6ONLY
+ rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(setsockopt),
+ SCMP_CMP(1, SCMP_CMP_EQ, IPPROTO_IPV6),
+ SCMP_CMP(2, SCMP_CMP_EQ, IPV6_V6ONLY));
+ if (rc)
+ return rc;
+#endif
+
return 0;
}
@@ -840,7 +856,7 @@ sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
}
/**
- * Function responsible for setting up the fcntl64 syscall for
+ * Function responsible for setting up the prctl syscall for
* the seccomp filter sandbox.
*
* NOTE: if multiple filters need to be added, the PR_SECCOMP parameter needs
@@ -861,7 +877,7 @@ sb_prctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
}
/**
- * Function responsible for setting up the fcntl64 syscall for
+ * Function responsible for setting up the mprotect syscall for
* the seccomp filter sandbox.
*
* NOTE: does not NEED to be here.. currently only occurs before filter; will
@@ -1260,7 +1276,7 @@ prot_strings(scmp_filter_ctx ctx, sandbox_cfg_t* cfg)
/**
* Auxiliary function used in order to allocate a sandbox_cfg_t element and set
- * it's values according the the parameter list. All elements are initialised
+ * its values according the parameter list. All elements are initialised
* with the 'prot' field set to false, as the pointer is not protected at this
* point.
*/
@@ -1462,7 +1478,7 @@ static HT_HEAD(getaddrinfo_cache, cached_getaddrinfo_item_t)
HT_PROTOTYPE(getaddrinfo_cache, cached_getaddrinfo_item_t, node,
cached_getaddrinfo_item_hash,
- cached_getaddrinfo_items_eq);
+ cached_getaddrinfo_items_eq)
HT_GENERATE2(getaddrinfo_cache, cached_getaddrinfo_item_t, node,
cached_getaddrinfo_item_hash,
cached_getaddrinfo_items_eq,
@@ -1575,13 +1591,14 @@ sandbox_add_addrinfo(const char *name)
void
sandbox_free_getaddrinfo_cache(void)
{
- cached_getaddrinfo_item_t **next, **item;
+ cached_getaddrinfo_item_t **next, **item, *this;
for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache);
item;
item = next) {
+ this = *item;
next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item);
- cached_getaddrinfo_item_free(*item);
+ cached_getaddrinfo_item_free(this);
}
HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache);
diff --git a/src/common/sandbox.h b/src/common/sandbox.h
index 2defd8bbd4..c5963e3119 100644
--- a/src/common/sandbox.h
+++ b/src/common/sandbox.h
@@ -39,12 +39,6 @@ typedef struct sandbox_cfg_elem sandbox_cfg_t;
*/
#ifdef USE_LIBSECCOMP
-#ifndef __USE_GNU
-#define __USE_GNU
-#endif
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
#include <sys/ucontext.h>
#include <seccomp.h>
#include <netdb.h>
diff --git a/src/common/testsupport.h b/src/common/testsupport.h
index 3bb11a7e41..9ad2ba77e0 100644
--- a/src/common/testsupport.h
+++ b/src/common/testsupport.h
@@ -6,8 +6,10 @@
#ifdef TOR_UNIT_TESTS
#define STATIC
+#define EXTERN(type, name) extern type name;
#else
#define STATIC static
+#define EXTERN(type, name)
#endif
/** Quick and dirty macros to implement test mocking.
@@ -60,6 +62,12 @@
#define MOCK_IMPL(rv, funcname, arglist) \
rv(*funcname) arglist = funcname ##__real; \
rv funcname ##__real arglist
+#define MOCK_DECL_ATTR(rv, funcname, arglist, attr) \
+ rv funcname ##__real arglist attr; \
+ extern rv(*funcname) arglist
+#define MOCK_IMPL(rv, funcname, arglist) \
+ rv(*funcname) arglist = funcname ##__real; \
+ rv funcname ##__real arglist
#define MOCK(func, replacement) \
do { \
(func) = (replacement); \
@@ -71,6 +79,8 @@
#else
#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
diff --git a/src/common/timers.c b/src/common/timers.c
new file mode 100644
index 0000000000..41b2008ac4
--- /dev/null
+++ b/src/common/timers.c
@@ -0,0 +1,293 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file timers.c
+ * \brief Wrapper around William Ahern's fast hierarchical timer wheel
+ * implementation, to tie it in with a libevent backend.
+ *
+ * Only use these functions from the main thread.
+ *
+ * The main advantage of tor_timer_t over using libevent's timers is that
+ * they're way more efficient if we need to have thousands or millions of
+ * them. For more information, see
+ * http://www.25thandclement.com/~william/projects/timeout.c.html
+ *
+ * Periodic timers are available in the backend, but I've turned them off.
+ * We can turn them back on if needed.
+ */
+
+/* Notes:
+ *
+ * Having a way to free all timers on shutdown would free people from the
+ * need to track them. Not sure if that's clever though.
+ *
+ * In an ideal world, Libevent would just switch to use this backend, and we
+ * could throw this file away. But even if Libevent does switch, we'll be
+ * stuck with legacy libevents for some time.
+ */
+
+#include "orconfig.h"
+
+#include "compat.h"
+#include "compat_libevent.h"
+#include "timers.h"
+#include "torlog.h"
+#include "util.h"
+
+#include <event2/event.h>
+
+struct timeout_cb {
+ timer_cb_fn_t cb;
+ void *arg;
+};
+
+/*
+ * These definitions are for timeouts.c and timeouts.h.
+ */
+#ifdef __GNUC__
+/* We're not exposing any of the functions outside this file. */
+#define TIMEOUT_PUBLIC __attribute__((__unused__)) static
+#else
+/* We're not exposing any of the functions outside this file. */
+#define TIMEOUT_PUBLIC static
+#endif
+/* We're not using periodic events. */
+#define TIMEOUT_DISABLE_INTERVALS
+/* We always know the global_timeouts object, so we don't need each timeout
+ * to keep a pointer to it. */
+#define TIMEOUT_DISABLE_RELATIVE_ACCESS
+/* We're providing our own struct timeout_cb. */
+#define TIMEOUT_CB_OVERRIDE
+/* We're going to support timers that are pretty far out in advance. Making
+ * this big can be inefficient, but having a significant number of timers
+ * above TIMEOUT_MAX can also be super-inefficent. Choosing 5 here sets
+ * timeout_max to 2^30 ticks, or 29 hours with our value for USEC_PER_TICK */
+#define WHEEL_NUM 5
+#include "src/ext/timeouts/timeout.c"
+
+static struct timeouts *global_timeouts = NULL;
+static struct event *global_timer_event = NULL;
+
+static monotime_t start_of_time;
+
+/** We need to choose this value carefully. Because we're using timer wheels,
+ * it actually costs us to have extra resolution we don't use. So for now,
+ * I'm going to define our resolution as .1 msec, and hope that's good enough.
+ *
+ * Note that two of the most popular libevent backends (epoll without timerfd,
+ * and windows select), simply can't support sub-millisecond resolution,
+ * do this is optimistic for a lot of users.
+ */
+#define USEC_PER_TICK 100
+
+/** One million microseconds in a second */
+#define USEC_PER_SEC 1000000
+
+/** Check at least once every N seconds. */
+#define MIN_CHECK_SECONDS 3600
+
+/** Check at least once every N ticks. */
+#define MIN_CHECK_TICKS \
+ (((timeout_t)MIN_CHECK_SECONDS) * (1000000 / USEC_PER_TICK))
+
+/**
+ * Convert the timeval in <b>tv</b> to a timeout_t, and return it.
+ *
+ * The output resolution is set by USEC_PER_TICK. Only use this to convert
+ * delays to number of ticks; the time represented by 0 is undefined.
+ */
+static timeout_t
+tv_to_timeout(const struct timeval *tv)
+{
+ uint64_t usec = tv->tv_usec;
+ usec += ((uint64_t)USEC_PER_SEC) * tv->tv_sec;
+ return usec / USEC_PER_TICK;
+}
+
+/**
+ * Convert the timeout in <b>t</b> to a timeval in <b>tv_out</b>. Only
+ * use this for delays, not absolute times.
+ */
+static void
+timeout_to_tv(timeout_t t, struct timeval *tv_out)
+{
+ t *= USEC_PER_TICK;
+ tv_out->tv_usec = (int)(t % USEC_PER_SEC);
+ tv_out->tv_sec = (time_t)(t / USEC_PER_SEC);
+}
+
+/**
+ * Update the timer <b>tv</b> to the current time in <b>tv</b>.
+ */
+static void
+timer_advance_to_cur_time(const monotime_t *now)
+{
+ timeout_t cur_tick = CEIL_DIV(monotime_diff_usec(&start_of_time, now),
+ USEC_PER_TICK);
+ timeouts_update(global_timeouts, cur_tick);
+}
+
+/**
+ * Adjust the time at which the libevent timer should fire based on
+ * the next-expiring time in <b>global_timeouts</b>
+ */
+static void
+libevent_timer_reschedule(void)
+{
+ monotime_t now;
+ monotime_get(&now);
+ timer_advance_to_cur_time(&now);
+
+ timeout_t delay = timeouts_timeout(global_timeouts);
+
+ struct timeval d;
+ if (delay > MIN_CHECK_TICKS)
+ delay = MIN_CHECK_TICKS;
+ timeout_to_tv(delay, &d);
+ event_add(global_timer_event, &d);
+}
+
+/**
+ * Invoked when the libevent timer has expired: see which tor_timer_t events
+ * have fired, activate their callbacks, and reschedule the libevent timer.
+ */
+static void
+libevent_timer_callback(evutil_socket_t fd, short what, void *arg)
+{
+ (void)fd;
+ (void)what;
+ (void)arg;
+
+ monotime_t now;
+ monotime_get(&now);
+ timer_advance_to_cur_time(&now);
+
+ tor_timer_t *t;
+ while ((t = timeouts_get(global_timeouts))) {
+ t->callback.cb(t, t->callback.arg, &now);
+ }
+
+ libevent_timer_reschedule();
+}
+
+/**
+ * Initialize the timers subsystem. Requires that libevent has already been
+ * initialized.
+ */
+void
+timers_initialize(void)
+{
+ if (BUG(global_timeouts))
+ return; // LCOV_EXCL_LINE
+
+ timeout_error_t err;
+ global_timeouts = timeouts_open(0, &err);
+ if (!global_timeouts) {
+ // LCOV_EXCL_START -- this can only fail on malloc failure.
+ log_err(LD_BUG, "Unable to open timer backend: %s", strerror(err));
+ tor_assert(0);
+ // LCOV_EXCL_STOP
+ }
+
+ monotime_init();
+ monotime_get(&start_of_time);
+
+ struct event *timer_event;
+ timer_event = tor_event_new(tor_libevent_get_base(),
+ -1, 0, libevent_timer_callback, NULL);
+ tor_assert(timer_event);
+ global_timer_event = timer_event;
+
+ libevent_timer_reschedule();
+}
+
+/**
+ * Release all storage held in the timers subsystem. Does not fire timers.
+ */
+void
+timers_shutdown(void)
+{
+ if (global_timer_event) {
+ tor_event_free(global_timer_event);
+ global_timer_event = NULL;
+ }
+ if (global_timeouts) {
+ timeouts_close(global_timeouts);
+ global_timeouts = NULL;
+ }
+}
+
+/**
+ * Allocate and return a new timer, with given callback and argument.
+ */
+tor_timer_t *
+timer_new(timer_cb_fn_t cb, void *arg)
+{
+ tor_timer_t *t = tor_malloc(sizeof(tor_timer_t));
+ timeout_init(t, 0);
+ timer_set_cb(t, cb, arg);
+ return t;
+}
+
+/**
+ * Release all storage held by <b>t</b>, and unschedule it if was already
+ * scheduled.
+ */
+void
+timer_free(tor_timer_t *t)
+{
+ if (! t)
+ return;
+
+ timeouts_del(global_timeouts, t);
+ tor_free(t);
+}
+
+/**
+ * Change the callback and argument associated with a timer <b>t</b>.
+ */
+void
+timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg)
+{
+ t->callback.cb = cb;
+ t->callback.arg = arg;
+}
+
+/**
+ * Schedule the timer t to fire at the current time plus a delay of
+ * <b>delay</b> microseconds. All times are relative to monotime_get().
+ */
+void
+timer_schedule(tor_timer_t *t, const struct timeval *tv)
+{
+ const timeout_t delay = tv_to_timeout(tv);
+
+ monotime_t now;
+ monotime_get(&now);
+ timer_advance_to_cur_time(&now);
+
+ /* Take the old timeout value. */
+ timeout_t to = timeouts_timeout(global_timeouts);
+
+ timeouts_add(global_timeouts, t, delay);
+
+ /* Should we update the libevent timer? */
+ if (to <= delay) {
+ return; /* we're already going to fire before this timer would trigger. */
+ }
+ libevent_timer_reschedule();
+}
+
+/**
+ * Cancel the timer <b>t</b> if it is currently scheduled. (It's okay to call
+ * this on an unscheduled timer.
+ */
+void
+timer_disable(tor_timer_t *t)
+{
+ timeouts_del(global_timeouts, t);
+ /* We don't reschedule the libevent timer here, since it's okay if it fires
+ * early. */
+}
+
diff --git a/src/common/timers.h b/src/common/timers.h
new file mode 100644
index 0000000000..5f918f8e15
--- /dev/null
+++ b/src/common/timers.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_TIMERS_H
+#define TOR_TIMERS_H
+
+#include "orconfig.h"
+#include "testsupport.h"
+
+struct monotime_t;
+typedef struct timeout tor_timer_t;
+typedef void (*timer_cb_fn_t)(tor_timer_t *, void *,
+ const struct monotime_t *);
+tor_timer_t *timer_new(timer_cb_fn_t cb, void *arg);
+void timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg);
+void timer_schedule(tor_timer_t *t, const struct timeval *delay);
+void timer_disable(tor_timer_t *t);
+void timer_free(tor_timer_t *t);
+
+void timers_initialize(void);
+void timers_shutdown(void);
+
+#endif
+
diff --git a/src/common/torgzip.c b/src/common/torgzip.c
index 71e55f8723..c44399aa74 100644
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@ -46,34 +46,16 @@
#include <zlib.h>
+#if defined ZLIB_VERNUM && ZLIB_VERNUM < 0x1200
+#error "We require zlib version 1.2 or later."
+#endif
+
static size_t tor_zlib_state_size_precalc(int inflate,
int windowbits, int memlevel);
/** Total number of bytes allocated for zlib state */
static size_t total_zlib_allocation = 0;
-/** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
- * set to -1 if we haven't checked yet. */
-static int gzip_is_supported = -1;
-
-/** Return true iff we support gzip-based compression. Otherwise, we need to
- * use zlib. */
-int
-is_gzip_supported(void)
-{
- if (gzip_is_supported >= 0)
- return gzip_is_supported;
-
- if (!strcmpstart(ZLIB_VERSION, "0.") ||
- !strcmpstart(ZLIB_VERSION, "1.0") ||
- !strcmpstart(ZLIB_VERSION, "1.1"))
- gzip_is_supported = 0;
- else
- gzip_is_supported = 1;
-
- return gzip_is_supported;
-}
-
/** Return a string representation of the version of the currently running
* version of zlib. */
const char *
@@ -165,12 +147,6 @@ tor_gzip_compress(char **out, size_t *out_len,
*out = NULL;
- if (method == GZIP_METHOD && !is_gzip_supported()) {
- /* Old zlib version don't support gzip in deflateInit2 */
- log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
- goto err;
- }
-
stream = tor_malloc_zero(sizeof(struct z_stream_s));
stream->zalloc = Z_NULL;
stream->zfree = Z_NULL;
@@ -182,9 +158,11 @@ tor_gzip_compress(char **out, size_t *out_len,
method_bits(method, HIGH_COMPRESSION),
get_memlevel(HIGH_COMPRESSION),
Z_DEFAULT_STRATEGY) != Z_OK) {
+ //LCOV_EXCL_START -- we can only provoke failure by giving junk arguments.
log_warn(LD_GENERAL, "Error from deflateInit2: %s",
stream->msg?stream->msg:"<no message>");
goto err;
+ //LCOV_EXCL_STOP
}
/* Guess 50% compression. */
@@ -203,6 +181,7 @@ tor_gzip_compress(char **out, size_t *out_len,
/* In case zlib doesn't work as I think .... */
if (stream->avail_out >= stream->avail_in+16)
break;
+ /* Falls through. */
case Z_BUF_ERROR:
offset = stream->next_out - ((unsigned char*)*out);
old_size = out_size;
@@ -237,13 +216,12 @@ tor_gzip_compress(char **out, size_t *out_len,
* the newly unsigned field isn't negative." */
tor_assert(stream->total_out >= 0);
#endif
- if (((size_t)stream->total_out) > out_size + 4097) {
- /* If we're wasting more than 4k, don't. */
- *out = tor_realloc(*out, stream->total_out + 1);
- }
if (deflateEnd(stream)!=Z_OK) {
+ // LCOV_EXCL_START -- unreachable if we handled the zlib structure right
+ tor_assert_nonfatal_unreached();
log_warn(LD_BUG, "Error freeing gzip structures");
goto err;
+ // LCOV_EXCL_STOP
}
tor_free(stream);
@@ -291,12 +269,6 @@ tor_gzip_uncompress(char **out, size_t *out_len,
tor_assert(in);
tor_assert(in_len < UINT_MAX);
- if (method == GZIP_METHOD && !is_gzip_supported()) {
- /* Old zlib version don't support gzip in inflateInit2 */
- log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
- return -1;
- }
-
*out = NULL;
stream = tor_malloc_zero(sizeof(struct z_stream_s));
@@ -308,9 +280,11 @@ tor_gzip_uncompress(char **out, size_t *out_len,
if (inflateInit2(stream,
method_bits(method, HIGH_COMPRESSION)) != Z_OK) {
+ // LCOV_EXCL_START -- can only hit this if we give bad inputs.
log_warn(LD_GENERAL, "Error from inflateInit2: %s",
stream->msg?stream->msg:"<no message>");
goto err;
+ // LCOV_EXCL_STOP
}
out_size = in_len * 2; /* guess 50% compression. */
@@ -346,6 +320,7 @@ tor_gzip_uncompress(char **out, size_t *out_len,
/* In case zlib doesn't work as I think.... */
if (stream->avail_out >= stream->avail_in+16)
break;
+ /* Falls through. */
case Z_BUF_ERROR:
if (stream->avail_out > 0) {
log_fn(protocol_warn_level, LD_PROTOCOL,
@@ -445,19 +420,13 @@ struct tor_zlib_state_t {
* <b>compress</b>, it's for compression; otherwise it's for
* decompression. */
tor_zlib_state_t *
-tor_zlib_new(int compress, compress_method_t method,
+tor_zlib_new(int compress_, compress_method_t method,
zlib_compression_level_t compression_level)
{
tor_zlib_state_t *out;
int bits, memlevel;
- if (method == GZIP_METHOD && !is_gzip_supported()) {
- /* Old zlib version don't support gzip in inflateInit2 */
- log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
- return NULL;
- }
-
- if (! compress) {
+ if (! compress_) {
/* use this setting for decompression, since we might have the
* max number of window bits */
compression_level = HIGH_COMPRESSION;
@@ -467,19 +436,19 @@ tor_zlib_new(int compress, compress_method_t method,
out->stream.zalloc = Z_NULL;
out->stream.zfree = Z_NULL;
out->stream.opaque = NULL;
- out->compress = compress;
+ out->compress = compress_;
bits = method_bits(method, compression_level);
memlevel = get_memlevel(compression_level);
- if (compress) {
+ if (compress_) {
if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED,
bits, memlevel,
Z_DEFAULT_STRATEGY) != Z_OK)
- goto err;
+ goto err; // LCOV_EXCL_LINE
} else {
if (inflateInit2(&out->stream, bits) != Z_OK)
- goto err;
+ goto err; // LCOV_EXCL_LINE
}
- out->allocation = tor_zlib_state_size_precalc(!compress, bits, memlevel);
+ out->allocation = tor_zlib_state_size_precalc(!compress_, bits, memlevel);
total_zlib_allocation += out->allocation;
@@ -573,13 +542,13 @@ tor_zlib_free(tor_zlib_state_t *state)
/** Return an approximate number of bytes used in RAM to hold a state with
* window bits <b>windowBits</b> and compression level 'memlevel' */
static size_t
-tor_zlib_state_size_precalc(int inflate, int windowbits, int memlevel)
+tor_zlib_state_size_precalc(int inflate_, int windowbits, int memlevel)
{
windowbits &= 15;
#define A_FEW_KILOBYTES 2048
- if (inflate) {
+ if (inflate_) {
/* From zconf.h:
"The memory requirements for inflate are (in bytes) 1 << windowBits
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 578af7caea..6732a42741 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -109,6 +109,11 @@
* would. Used as a flag, not a log domain. */
#define LD_NOFUNCNAME (1u<<30)
+#ifdef TOR_UNIT_TESTS
+/** This log message should not be intercepted by mock_saving_logv */
+#define LD_NO_MOCK (1u<<29)
+#endif
+
/** Mask of zero or more log domains, OR'd together. */
typedef uint32_t log_domain_mask_t;
@@ -176,7 +181,7 @@ void log_fn_ratelim_(struct ratelim_t *ratelim, int severity,
const char *format, ...)
CHECK_PRINTF(5,6);
-#if defined(__GNUC__)
+#if defined(__GNUC__) && __GNUC__ <= 3
/* These are the GCC varidaic macros, so that older versions of GCC don't
* break. */
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 89ad6af939..d61cc2e58a 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -24,18 +24,11 @@
#include <ws2tcpip.h>
#endif
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#endif
+#include "compat.h"
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic push
-#endif
/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
* srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#endif
+DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/opensslv.h>
#include "crypto.h"
@@ -53,20 +46,7 @@
#include <openssl/bn.h>
#include <openssl/rsa.h>
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic pop
-#else
-#pragma GCC diagnostic warning "-Wredundant-decls"
-#endif
-#endif
-
-#ifdef USE_BUFFEREVENTS
-#include <event2/bufferevent_ssl.h>
-#include <event2/buffer.h>
-#include <event2/event.h>
-#include "compat_libevent.h"
-#endif
+ENABLE_GCC_WARNING(redundant-decls)
#define TORTLS_PRIVATE
#include "tortls.h"
@@ -572,12 +552,11 @@ MOCK_IMPL(STATIC X509 *,
* claiming extra unsupported ciphers in order to avoid fingerprinting. */
#define SERVER_CIPHER_LIST \
(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
- SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA)
/** List of ciphers that servers should select from when we actually have
* our choice of what cipher to use. */
-const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
+static const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
/* This list is autogenerated with the gen_server_ciphers.py script;
* don't hand-edit it. */
#ifdef TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384
@@ -613,12 +592,8 @@ const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
/* Required */
TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":"
/* Required */
- TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":"
-#ifdef TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA
- TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":"
-#endif
- /* Required */
- SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA;
+ TLS1_TXT_DHE_RSA_WITH_AES_128_SHA
+ ;
/* Note: to set up your own private testing network with link crypto
* disabled, set your Tors' cipher list to
@@ -702,6 +677,15 @@ MOCK_IMPL(STATIC tor_x509_cert_t *,
return cert;
}
+/** Return a new copy of <b>cert</b>. */
+tor_x509_cert_t *
+tor_x509_cert_dup(const tor_x509_cert_t *cert)
+{
+ tor_assert(cert);
+ X509 *x509 = cert->cert;
+ return tor_x509_cert_new(X509_dup(x509));
+}
+
/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>,
* from a <b>certificate</b>. Return a newly allocated tor_x509_cert_t on
* success and NULL on failure. */
@@ -1509,6 +1493,10 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
tor_tls_t *tls;
(void) val;
+ IF_BUG_ONCE(ssl == NULL) {
+ return; // LCOV_EXCL_LINE
+ }
+
tor_tls_debug_state_callback(ssl, type, val);
if (type != SSL_CB_ACCEPT_LOOP)
@@ -2030,7 +2018,8 @@ tor_tls_peer_has_cert(tor_tls_t *tls)
return 1;
}
-/** Return the peer certificate, or NULL if there isn't one. */
+/** Return a newly allocated copy of the peer certificate, or NULL if there
+ * isn't one. */
MOCK_IMPL(tor_x509_cert_t *,
tor_tls_get_peer_cert,(tor_tls_t *tls))
{
@@ -2042,6 +2031,24 @@ tor_tls_get_peer_cert,(tor_tls_t *tls))
return tor_x509_cert_new(cert);
}
+/** Return a newly allocated copy of the cerficate we used on the connection,
+ * or NULL if somehow we didn't use one. */
+MOCK_IMPL(tor_x509_cert_t *,
+tor_tls_get_own_cert,(tor_tls_t *tls))
+{
+ X509 *cert = SSL_get_certificate(tls->ssl);
+ tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE,
+ "getting own-connection certificate");
+ if (!cert)
+ return NULL;
+ /* Fun inconsistency: SSL_get_peer_certificate increments the reference
+ * count, but SSL_get_certificate does not. */
+ X509 *duplicate = X509_dup(cert);
+ if (BUG(duplicate == NULL))
+ return NULL;
+ return tor_x509_cert_new(duplicate);
+}
+
/** Warn that a certificate lifetime extends through a certain range. */
static void
log_cert_lifetime(int severity, const X509 *cert, const char *problem)
@@ -2499,78 +2506,6 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
#endif
}
-#ifdef USE_BUFFEREVENTS
-/** Construct and return an TLS-encrypting bufferevent to send data over
- * <b>socket</b>, which must match the socket of the underlying bufferevent
- * <b>bufev_in</b>. The TLS object <b>tls</b> is used for encryption.
- *
- * This function will either create a filtering bufferevent that wraps around
- * <b>bufev_in</b>, or it will free bufev_in and return a new bufferevent that
- * uses the <b>tls</b> to talk to the network directly. Do not use
- * <b>bufev_in</b> after calling this function.
- *
- * The connection will start out doing a server handshake if <b>receiving</b>
- * is strue, and a client handshake otherwise.
- *
- * Returns NULL on failure.
- */
-struct bufferevent *
-tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in,
- evutil_socket_t socket, int receiving,
- int filter)
-{
- struct bufferevent *out;
- const enum bufferevent_ssl_state state = receiving ?
- BUFFEREVENT_SSL_ACCEPTING : BUFFEREVENT_SSL_CONNECTING;
-
- if (filter || tor_libevent_using_iocp_bufferevents()) {
- /* Grab an extra reference to the SSL, since BEV_OPT_CLOSE_ON_FREE
- means that the SSL will get freed too.
-
- This increment makes our SSL usage not-threadsafe, BTW. We should
- see if we're allowed to use CRYPTO_add from outside openssl. */
- tls->ssl->references += 1;
- out = bufferevent_openssl_filter_new(tor_libevent_get_base(),
- bufev_in,
- tls->ssl,
- state,
- BEV_OPT_DEFER_CALLBACKS|
- BEV_OPT_CLOSE_ON_FREE);
- /* Tell the underlying bufferevent when to accept more data from the SSL
- filter (only when it's got less than 32K to write), and when to notify
- the SSL filter that it could write more (when it drops under 24K). */
- bufferevent_setwatermark(bufev_in, EV_WRITE, 24*1024, 32*1024);
- } else {
- if (bufev_in) {
- evutil_socket_t s = bufferevent_getfd(bufev_in);
- tor_assert(s == -1 || s == socket);
- tor_assert(evbuffer_get_length(bufferevent_get_input(bufev_in)) == 0);
- tor_assert(evbuffer_get_length(bufferevent_get_output(bufev_in)) == 0);
- tor_assert(BIO_number_read(SSL_get_rbio(tls->ssl)) == 0);
- tor_assert(BIO_number_written(SSL_get_rbio(tls->ssl)) == 0);
- bufferevent_free(bufev_in);
- }
-
- /* Current versions (as of 2.0.x) of Libevent need to defer
- * bufferevent_openssl callbacks, or else our callback functions will
- * get called reentrantly, which is bad for us.
- */
- out = bufferevent_openssl_socket_new(tor_libevent_get_base(),
- socket,
- tls->ssl,
- state,
- BEV_OPT_DEFER_CALLBACKS);
- }
- tls->state = TOR_TLS_ST_BUFFEREVENT;
-
- /* Unblock _after_ creating the bufferevent, since accept/connect tend to
- * clear flags. */
- tor_tls_unblock_renegotiation(tls);
-
- return out;
-}
-#endif
-
/** Check whether the ECC group requested is supported by the current OpenSSL
* library instance. Return 1 if the group is supported, and 0 if not.
*/
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 1a59c67df3..f018c45c82 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -164,8 +164,18 @@ STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
int is_client);
STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain,
const char *doing);
+
+#ifdef TOR_UNIT_TESTS
+extern int tor_tls_object_ex_data_index;
+extern tor_tls_context_t *server_tls_context;
+extern tor_tls_context_t *client_tls_context;
+extern uint16_t v2_cipher_list[];
+extern uint64_t total_bytes_written_over_tls;
+extern uint64_t total_bytes_written_by_tls;
#endif
+#endif /* endif TORTLS_PRIVATE */
+
const char *tor_tls_err_to_string(int err);
void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
@@ -187,7 +197,9 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
int tor_tls_is_server(tor_tls_t *tls);
void tor_tls_free(tor_tls_t *tls);
int tor_tls_peer_has_cert(tor_tls_t *tls);
+tor_x509_cert_t *tor_x509_cert_dup(const tor_x509_cert_t *cert);
MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
+MOCK_DECL(tor_x509_cert_t *,tor_tls_get_own_cert,(tor_tls_t *tls));
int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity);
int tor_tls_check_lifetime(int severity,
tor_tls_t *tls, int past_tolerance,
@@ -225,14 +237,6 @@ void check_no_tls_errors_(const char *fname, int line);
void tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
int severity, int domain, const char *doing);
-#ifdef USE_BUFFEREVENTS
-int tor_tls_start_renegotiating(tor_tls_t *tls);
-struct bufferevent *tor_tls_init_bufferevent(tor_tls_t *tls,
- struct bufferevent *bufev_in,
- evutil_socket_t socket, int receiving,
- int filter);
-#endif
-
void tor_x509_cert_free(tor_x509_cert_t *cert);
tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
size_t certificate_len);
diff --git a/src/common/util.c b/src/common/util.c
index f3effe0957..d2cbacde31 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -9,10 +9,6 @@
* process control.
**/
-/* This is required on rh7 to make strptime not complain.
- */
-#define _GNU_SOURCE
-
#include "orconfig.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
@@ -105,23 +101,6 @@
#endif
/* =====
- * Assertion helper.
- * ===== */
-/** Helper for tor_assert: report the assertion failure. */
-void
-tor_assertion_failed_(const char *fname, unsigned int line,
- const char *func, const char *expr)
-{
- char buf[256];
- log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.",
- fname, line, func, expr);
- tor_snprintf(buf, sizeof(buf),
- "Assertion %s failed in %s at %s:%u",
- expr, func, fname, line);
- log_backtrace(LOG_ERR, LD_BUG, buf);
-}
-
-/* =====
* Memory management
* ===== */
#ifdef USE_DMALLOC
@@ -168,15 +147,17 @@ tor_malloc_(size_t size DMALLOC_PARAMS)
#ifdef USE_DMALLOC
result = dmalloc_malloc(file, line, size, DMALLOC_FUNC_MALLOC, 0, 0);
#else
- result = malloc(size);
+ result = raw_malloc(size);
#endif
if (PREDICT_UNLIKELY(result == NULL)) {
+ /* LCOV_EXCL_START */
log_err(LD_MM,"Out of memory on malloc(). Dying.");
/* If these functions die within a worker process, they won't call
* spawn_exit, but that's ok, since the parent will run out of memory soon
* anyway. */
exit(1);
+ /* LCOV_EXCL_STOP */
}
return result;
}
@@ -221,6 +202,15 @@ size_mul_check(const size_t x, const size_t y)
x <= SIZE_MAX / y);
}
+#ifdef TOR_UNIT_TESTS
+/** Exposed for unit tests only */
+int
+size_mul_check__(const size_t x, const size_t y)
+{
+ return size_mul_check(x,y);
+}
+#endif
+
/** Allocate a chunk of <b>nmemb</b>*<b>size</b> bytes of memory, fill
* the memory with zero bytes, and return a pointer to the result.
* Log and terminate the process on error. (Same as
@@ -256,12 +246,14 @@ tor_realloc_(void *ptr, size_t size DMALLOC_PARAMS)
#ifdef USE_DMALLOC
result = dmalloc_realloc(file, line, ptr, size, DMALLOC_FUNC_REALLOC, 0);
#else
- result = realloc(ptr, size);
+ result = raw_realloc(ptr, size);
#endif
if (PREDICT_UNLIKELY(result == NULL)) {
+ /* LCOV_EXCL_START */
log_err(LD_MM,"Out of memory on realloc(). Dying.");
exit(1);
+ /* LCOV_EXCL_STOP */
}
return result;
}
@@ -287,19 +279,21 @@ tor_reallocarray_(void *ptr, size_t sz1, size_t sz2 DMALLOC_PARAMS)
char *
tor_strdup_(const char *s DMALLOC_PARAMS)
{
- char *dup;
+ char *duplicate;
tor_assert(s);
#ifdef USE_DMALLOC
- dup = dmalloc_strdup(file, line, s, 0);
+ duplicate = dmalloc_strdup(file, line, s, 0);
#else
- dup = strdup(s);
+ duplicate = raw_strdup(s);
#endif
- if (PREDICT_UNLIKELY(dup == NULL)) {
+ if (PREDICT_UNLIKELY(duplicate == NULL)) {
+ /* LCOV_EXCL_START */
log_err(LD_MM,"Out of memory on strdup(). Dying.");
exit(1);
+ /* LCOV_EXCL_STOP */
}
- return dup;
+ return duplicate;
}
/** Allocate and return a new string containing the first <b>n</b>
@@ -311,17 +305,17 @@ tor_strdup_(const char *s DMALLOC_PARAMS)
char *
tor_strndup_(const char *s, size_t n DMALLOC_PARAMS)
{
- char *dup;
+ char *duplicate;
tor_assert(s);
tor_assert(n < SIZE_T_CEILING);
- dup = tor_malloc_((n+1) DMALLOC_FN_ARGS);
+ duplicate = tor_malloc_((n+1) DMALLOC_FN_ARGS);
/* Performance note: Ordinarily we prefer strlcpy to strncpy. But
* this function gets called a whole lot, and platform strncpy is
* much faster than strlcpy when strlen(s) is much longer than n.
*/
- strncpy(dup, s, n);
- dup[n]='\0';
- return dup;
+ strncpy(duplicate, s, n);
+ duplicate[n]='\0';
+ return duplicate;
}
/** Allocate a chunk of <b>len</b> bytes, with the same contents as the
@@ -329,12 +323,12 @@ tor_strndup_(const char *s, size_t n DMALLOC_PARAMS)
void *
tor_memdup_(const void *mem, size_t len DMALLOC_PARAMS)
{
- char *dup;
+ char *duplicate;
tor_assert(len < SIZE_T_CEILING);
tor_assert(mem);
- dup = tor_malloc_(len DMALLOC_FN_ARGS);
- memcpy(dup, mem, len);
- return dup;
+ duplicate = tor_malloc_(len DMALLOC_FN_ARGS);
+ memcpy(duplicate, mem, len);
+ return duplicate;
}
/** As tor_memdup(), but add an extra 0 byte at the end of the resulting
@@ -342,13 +336,13 @@ tor_memdup_(const void *mem, size_t len DMALLOC_PARAMS)
void *
tor_memdup_nulterm_(const void *mem, size_t len DMALLOC_PARAMS)
{
- char *dup;
+ char *duplicate;
tor_assert(len < SIZE_T_CEILING+1);
tor_assert(mem);
- dup = tor_malloc_(len+1 DMALLOC_FN_ARGS);
- memcpy(dup, mem, len);
- dup[len] = '\0';
- return dup;
+ duplicate = tor_malloc_(len+1 DMALLOC_FN_ARGS);
+ memcpy(duplicate, mem, len);
+ duplicate[len] = '\0';
+ return duplicate;
}
/** Helper for places that need to take a function pointer to the right
@@ -359,6 +353,7 @@ tor_free_(void *mem)
tor_free(mem);
}
+DISABLE_GCC_WARNING(aggregate-return)
/** Call the platform malloc info function, and dump the results to the log at
* level <b>severity</b>. If no such function exists, do nothing. */
void
@@ -386,6 +381,7 @@ tor_log_mallinfo(int severity)
);
#endif
}
+ENABLE_GCC_WARNING(aggregate-return)
/* =====
* Math
@@ -530,21 +526,6 @@ round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
return number;
}
-/** Return the lowest x in [INT64_MIN, INT64_MAX] 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 int64_t, return INT64_MAX */
-int64_t
-round_int64_to_next_multiple_of(int64_t number, int64_t divisor)
-{
- tor_assert(divisor > 0);
- if (INT64_MAX - divisor + 1 < number)
- return INT64_MAX;
- if (number >= 0)
- number += divisor - 1;
- number -= number % divisor;
- return number;
-}
-
/** Transform a random value <b>p</b> from the uniform distribution in
* [0.0, 1.0[ into a Laplace distributed value with location parameter
* <b>mu</b> and scale parameter <b>b</b>. Truncate the final result
@@ -575,7 +556,7 @@ sample_laplace_distribution(double mu, double b, double p)
* The epsilon value must be between ]0.0, 1.0]. delta_f must be greater
* than 0. */
int64_t
-add_laplace_noise(int64_t signal, double random, double delta_f,
+add_laplace_noise(int64_t signal_, double random_, double delta_f,
double epsilon)
{
int64_t noise;
@@ -588,15 +569,38 @@ add_laplace_noise(int64_t signal, double random, double delta_f,
/* Just add noise, no further signal */
noise = sample_laplace_distribution(0.0,
delta_f / epsilon,
- random);
+ random_);
/* Clip (signal + noise) to [INT64_MIN, INT64_MAX] */
- if (noise > 0 && INT64_MAX - noise < signal)
+ if (noise > 0 && INT64_MAX - noise < signal_)
return INT64_MAX;
- else if (noise < 0 && INT64_MIN - noise > signal)
+ else if (noise < 0 && INT64_MIN - noise > signal_)
return INT64_MIN;
else
- return signal + noise;
+ return signal_ + noise;
+}
+
+/* Helper: return greatest common divisor of a,b */
+static uint64_t
+gcd64(uint64_t a, uint64_t b)
+{
+ while (b) {
+ uint64_t t = b;
+ b = a % b;
+ a = t;
+ }
+ return a;
+}
+
+/* Given a fraction *<b>numer</b> / *<b>denom</b>, simplify it.
+ * Requires that the denominator is greater than 0. */
+void
+simplify_fraction64(uint64_t *numer, uint64_t *denom)
+{
+ tor_assert(denom);
+ uint64_t gcd = gcd64(*numer, *denom);
+ *numer /= gcd;
+ *denom /= gcd;
}
/** Return the number of bits set in <b>v</b>. */
@@ -634,12 +638,12 @@ n_bits_set_u8(uint8_t v)
void
tor_strstrip(char *s, const char *strip)
{
- char *read = s;
- while (*read) {
- if (strchr(strip, *read)) {
- ++read;
+ char *readp = s;
+ while (*readp) {
+ if (strchr(strip, *readp)) {
+ ++readp;
} else {
- *s++ = *read++;
+ *s++ = *readp++;
}
}
*s = '\0';
@@ -1130,6 +1134,9 @@ tor_digest256_is_zero(const char *digest)
/* Were there unexpected unconverted characters? */ \
if (!next && *endptr) \
goto err; \
+ /* Illogical (max, min) inputs? */ \
+ if (BUG(max < min)) \
+ goto err; \
/* Is r within limits? */ \
if (r < min || r > max) \
goto err; \
@@ -1402,42 +1409,138 @@ tor_escape_str_for_pt_args(const char *string, const char *chars_to_escape)
* Time
* ===== */
+#define TOR_USEC_PER_SEC 1000000
+
+/** Return the difference between start->tv_sec and end->tv_sec.
+ * Returns INT64_MAX on overflow and underflow.
+ */
+static int64_t
+tv_secdiff_impl(const struct timeval *start, const struct timeval *end)
+{
+ const int64_t s = (int64_t)start->tv_sec;
+ const int64_t e = (int64_t)end->tv_sec;
+
+ /* This may not be the most efficient way of implemeting this check,
+ * but it's easy to see that it's correct and doesn't overflow */
+
+ if (s > 0 && e < INT64_MIN + s) {
+ /* s is positive: equivalent to e - s < INT64_MIN, but without any
+ * overflow */
+ return INT64_MAX;
+ } else if (s < 0 && e > INT64_MAX + s) {
+ /* s is negative: equivalent to e - s > INT64_MAX, but without any
+ * overflow */
+ return INT64_MAX;
+ }
+
+ return e - s;
+}
+
/** Return the number of microseconds elapsed between *start and *end.
+ * Returns LONG_MAX on overflow and underflow.
*/
long
tv_udiff(const struct timeval *start, const struct timeval *end)
{
- long udiff;
- long secdiff = end->tv_sec - start->tv_sec;
+ /* Sanity check tv_usec */
+ if (start->tv_usec > TOR_USEC_PER_SEC || start->tv_usec < 0) {
+ log_warn(LD_GENERAL, "comparing times on microsecond detail with bad "
+ "start tv_usec: " I64_FORMAT " microseconds",
+ I64_PRINTF_ARG(start->tv_usec));
+ return LONG_MAX;
+ }
- if (labs(secdiff+1) > LONG_MAX/1000000) {
+ if (end->tv_usec > TOR_USEC_PER_SEC || end->tv_usec < 0) {
+ log_warn(LD_GENERAL, "comparing times on microsecond detail with bad "
+ "end tv_usec: " I64_FORMAT " microseconds",
+ I64_PRINTF_ARG(end->tv_usec));
+ return LONG_MAX;
+ }
+
+ /* Some BSDs have struct timeval.tv_sec 64-bit, but time_t (and long) 32-bit
+ */
+ int64_t udiff;
+ const int64_t secdiff = tv_secdiff_impl(start, end);
+
+ /* end->tv_usec - start->tv_usec can be up to 1 second either way */
+ if (secdiff > (int64_t)(LONG_MAX/1000000 - 1) ||
+ secdiff < (int64_t)(LONG_MIN/1000000 + 1)) {
log_warn(LD_GENERAL, "comparing times on microsecond detail too far "
- "apart: %ld seconds", secdiff);
+ "apart: " I64_FORMAT " seconds", I64_PRINTF_ARG(secdiff));
+ return LONG_MAX;
+ }
+
+ /* we'll never get an overflow here, because we check that both usecs are
+ * between 0 and TV_USEC_PER_SEC. */
+ udiff = secdiff*1000000 + ((int64_t)end->tv_usec - (int64_t)start->tv_usec);
+
+ /* Some compilers are smart enough to work out this is a no-op on L64 */
+#if SIZEOF_LONG < 8
+ if (udiff > (int64_t)LONG_MAX || udiff < (int64_t)LONG_MIN) {
return LONG_MAX;
}
+#endif
- udiff = secdiff*1000000L + (end->tv_usec - start->tv_usec);
- return udiff;
+ return (long)udiff;
}
/** Return the number of milliseconds elapsed between *start and *end.
+ * If the tv_usec difference is 500, rounds away from zero.
+ * Returns LONG_MAX on overflow and underflow.
*/
long
tv_mdiff(const struct timeval *start, const struct timeval *end)
{
- long mdiff;
- long secdiff = end->tv_sec - start->tv_sec;
+ /* Sanity check tv_usec */
+ if (start->tv_usec > TOR_USEC_PER_SEC || start->tv_usec < 0) {
+ log_warn(LD_GENERAL, "comparing times on millisecond detail with bad "
+ "start tv_usec: " I64_FORMAT " microseconds",
+ I64_PRINTF_ARG(start->tv_usec));
+ return LONG_MAX;
+ }
+
+ if (end->tv_usec > TOR_USEC_PER_SEC || end->tv_usec < 0) {
+ log_warn(LD_GENERAL, "comparing times on millisecond detail with bad "
+ "end tv_usec: " I64_FORMAT " microseconds",
+ I64_PRINTF_ARG(end->tv_usec));
+ return LONG_MAX;
+ }
- if (labs(secdiff+1) > LONG_MAX/1000) {
+ /* Some BSDs have struct timeval.tv_sec 64-bit, but time_t (and long) 32-bit
+ */
+ int64_t mdiff;
+ const int64_t secdiff = tv_secdiff_impl(start, end);
+
+ /* end->tv_usec - start->tv_usec can be up to 1 second either way, but the
+ * mdiff calculation may add another temporary second for rounding.
+ * Whether this actually causes overflow depends on the compiler's constant
+ * folding and order of operations. */
+ if (secdiff > (int64_t)(LONG_MAX/1000 - 2) ||
+ secdiff < (int64_t)(LONG_MIN/1000 + 1)) {
log_warn(LD_GENERAL, "comparing times on millisecond detail too far "
- "apart: %ld seconds", secdiff);
+ "apart: " I64_FORMAT " seconds", I64_PRINTF_ARG(secdiff));
return LONG_MAX;
}
/* Subtract and round */
- mdiff = secdiff*1000L +
- ((long)end->tv_usec - (long)start->tv_usec + 500L) / 1000L;
- return mdiff;
+ mdiff = secdiff*1000 +
+ /* We add a million usec here to ensure that the result is positive,
+ * so that the round-towards-zero behavior of the division will give
+ * the right result for rounding to the nearest msec. Later we subtract
+ * 1000 in order to get the correct result.
+ * We'll never get an overflow here, because we check that both usecs are
+ * between 0 and TV_USEC_PER_SEC. */
+ ((int64_t)end->tv_usec - (int64_t)start->tv_usec + 500 + 1000000) / 1000
+ - 1000;
+
+ /* Some compilers are smart enough to work out this is a no-op on L64 */
+#if SIZEOF_LONG < 8
+ if (mdiff > (int64_t)LONG_MAX || mdiff < (int64_t)LONG_MIN) {
+ return LONG_MAX;
+ }
+#endif
+
+ return (long)mdiff;
}
/**
@@ -1456,11 +1559,12 @@ tv_to_msec(const struct timeval *tv)
#define IS_LEAPYEAR(y) (!(y % 4) && ((y % 100) || !(y % 400)))
/** Helper: Return the number of leap-days between Jan 1, y1 and Jan 1, y2. */
static int
-n_leapdays(int y1, int y2)
+n_leapdays(int year1, int year2)
{
- --y1;
- --y2;
- return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
+ --year1;
+ --year2;
+ return (year2/4 - year1/4) - (year2/100 - year1/100)
+ + (year2/400 - year1/400);
}
/** Number of days per month in non-leap year; used by tor_timegm and
* parse_rfc1123_time. */
@@ -1638,11 +1742,16 @@ parse_rfc1123_time(const char *buf, time_t *t)
tm.tm_sec = (int)tm_sec;
if (tm.tm_year < 1970) {
+ /* LCOV_EXCL_START
+ * XXXX I think this is dead code; we already checked for
+ * invalid_year above. */
+ tor_assert_nonfatal_unreached();
char *esc = esc_for_log(buf);
log_warn(LD_GENERAL,
"Got invalid RFC1123 time %s. (Before 1970)", esc);
tor_free(esc);
return -1;
+ /* LCOV_EXCL_STOP */
}
tm.tm_year -= 1900;
@@ -1726,10 +1835,15 @@ parse_iso_time_(const char *cp, time_t *t, int strict)
st_tm.tm_wday = 0; /* Should be ignored. */
if (st_tm.tm_year < 70) {
+ /* LCOV_EXCL_START
+ * XXXX I think this is dead code; we already checked for
+ * year < 1970 above. */
+ tor_assert_nonfatal_unreached();
char *esc = esc_for_log(cp);
log_warn(LD_GENERAL, "Got invalid ISO time %s. (Before 1970)", esc);
tor_free(esc);
return -1;
+ /* LCOV_EXCL_STOP */
}
return tor_timegm(&st_tm, t);
}
@@ -1893,7 +2007,9 @@ update_approx_time(time_t now)
/** If the rate-limiter <b>lim</b> is ready at <b>now</b>, return the number
* of calls to rate_limit_is_ready (including this one!) since the last time
- * rate_limit_is_ready returned nonzero. Otherwise return 0. */
+ * rate_limit_is_ready returned nonzero. Otherwise return 0.
+ * If the call number hits <b>RATELIM_TOOMANY</b> limit, drop a warning
+ * about this event and stop counting. */
static int
rate_limit_is_ready(ratelim_t *lim, time_t now)
{
@@ -1903,7 +2019,10 @@ rate_limit_is_ready(ratelim_t *lim, time_t now)
lim->n_calls_since_last_time = 0;
return res;
} else {
- ++lim->n_calls_since_last_time;
+ if (lim->n_calls_since_last_time <= RATELIM_TOOMANY) {
+ ++lim->n_calls_since_last_time;
+ }
+
return 0;
}
}
@@ -1920,9 +2039,12 @@ rate_limit_log(ratelim_t *lim, time_t now)
return tor_strdup("");
} else {
char *cp=NULL;
+ const char *opt_over = (n >= RATELIM_TOOMANY) ? "over " : "";
+ /* XXXX this is not exactly correct: the messages could have occurred
+ * any time between the old value of lim->allowed and now. */
tor_asprintf(&cp,
- " [%d similar message(s) suppressed in last %d seconds]",
- n-1, lim->rate);
+ " [%s%d similar message(s) suppressed in last %d seconds]",
+ opt_over, n-1, lim->rate);
return cp;
}
} else {
@@ -2011,6 +2133,16 @@ clean_name_for_stat(char *name)
#endif
}
+/** Wrapper for unlink() to make it mockable for the test suite; returns 0
+ * if unlinking the file succeeded, -1 and sets errno if unlinking fails.
+ */
+
+MOCK_IMPL(int,
+tor_unlink,(const char *pathname))
+{
+ return unlink(pathname);
+}
+
/** Return:
* FN_ERROR if filename can't be read, is NULL, or is zero-length,
* FN_NOENT if it doesn't exist,
@@ -2059,11 +2191,13 @@ file_status(const char *fname)
}
}
-/** Check whether <b>dirname</b> exists and is private. If yes return 0. If
- * it does not exist, and <b>check</b>&CPD_CREATE is set, try to create it
- * and return 0 on success. If it does not exist, and
- * <b>check</b>&CPD_CHECK, and we think we can create it, return 0. Else
- * return -1. If CPD_GROUP_OK is set, then it's okay if the directory
+/** Check whether <b>dirname</b> exists and is private. If yes return 0.
+ * If <b>dirname</b> does not exist:
+ * - if <b>check</b>&CPD_CREATE, try to create it and return 0 on success.
+ * - if <b>check</b>&CPD_CHECK, and we think we can create it, return 0.
+ * - if <b>check</b>&CPD_CHECK is false, and the directory exists, return 0.
+ * - otherwise, return -1.
+ * If CPD_GROUP_OK is set, then it's okay if the directory
* is group-readable, but in all cases we create the directory mode 0700.
* If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and
* if the directory is created it will use mode 0750 with group read
@@ -2074,9 +2208,9 @@ file_status(const char *fname)
* When effective_user is not NULL, check permissions against the given user
* and its primary group.
*/
-int
-check_private_dir(const char *dirname, cpd_check_t check,
- const char *effective_user)
+MOCK_IMPL(int,
+check_private_dir,(const char *dirname, cpd_check_t check,
+ const char *effective_user))
{
int r;
struct stat st;
@@ -2189,13 +2323,14 @@ check_private_dir(const char *dirname, cpd_check_t check,
running_gid = getgid();
}
if (st.st_uid != running_uid) {
- const struct passwd *pw = NULL;
+ const struct passwd *pw_uid = NULL;
char *process_ownername = NULL;
- pw = tor_getpwuid(running_uid);
- process_ownername = pw ? tor_strdup(pw->pw_name) : tor_strdup("<unknown>");
+ pw_uid = tor_getpwuid(running_uid);
+ process_ownername = pw_uid ? tor_strdup(pw_uid->pw_name) :
+ tor_strdup("<unknown>");
- pw = tor_getpwuid(st.st_uid);
+ pw_uid = tor_getpwuid(st.st_uid);
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?",
@@ -2304,8 +2439,8 @@ check_private_dir(const char *dirname, cpd_check_t check,
* function, and all other functions in util.c that create files, create them
* with mode 0600.
*/
-int
-write_str_to_file(const char *fname, const char *str, int bin)
+MOCK_IMPL(int,
+write_str_to_file,(const char *fname, const char *str, int bin))
{
#ifdef _WIN32
if (!bin && strchr(str, '\r')) {
@@ -2664,8 +2799,8 @@ read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out)
* the call to stat and the call to read_all: the resulting string will
* be truncated.
*/
-char *
-read_file_to_str(const char *filename, int flags, struct stat *stat_out)
+MOCK_IMPL(char *,
+read_file_to_str, (const char *filename, int flags, struct stat *stat_out))
{
int fd; /* router file */
struct stat statbuf;
@@ -2773,7 +2908,7 @@ read_file_to_str(const char *filename, int flags, struct stat *stat_out)
* provided), and return a pointer to the position in <b>s</b> immediately
* after the string. On failure, return NULL.
*/
-static const char *
+const char *
unescape_string(const char *s, char **result, size_t *size_out)
{
const char *cp;
@@ -2820,9 +2955,11 @@ unescape_string(const char *s, char **result, size_t *size_out)
if (size_out) *size_out = out - *result;
return cp+1;
case '\0':
+ /* LCOV_EXCL_START -- we caught this in parse_config_from_line. */
tor_fragile_assert();
tor_free(*result);
return NULL;
+ /* LCOV_EXCL_STOP */
case '\\':
switch (cp[1])
{
@@ -2836,8 +2973,12 @@ unescape_string(const char *s, char **result, size_t *size_out)
x1 = hex_decode_digit(cp[2]);
x2 = hex_decode_digit(cp[3]);
if (x1 == -1 || x2 == -1) {
- tor_free(*result);
- return NULL;
+ /* LCOV_EXCL_START */
+ /* we caught this above in the initial loop. */
+ tor_assert_nonfatal_unreached();
+ tor_free(*result);
+ return NULL;
+ /* LCOV_EXCL_STOP */
}
*out++ = ((x1<<4) + x2);
@@ -2863,7 +3004,11 @@ unescape_string(const char *s, char **result, size_t *size_out)
cp += 2;
break;
default:
+ /* LCOV_EXCL_START */
+ /* we caught this above in the initial loop. */
+ tor_assert_nonfatal_unreached();
tor_free(*result); return NULL;
+ /* LCOV_EXCL_STOP */
}
break;
default:
@@ -2936,6 +3081,8 @@ parse_config_line_from_str_verbose(const char *line, char **key_out,
}
while (*line == ' ' || *line == '\t')
++line;
+ if (*line == '\r' && *(++line) == '\n')
+ ++line;
if (*line && *line != '#' && *line != '\n') {
if (err_out)
*err_out = "Excess data after quoted string";
@@ -3079,7 +3226,7 @@ digit_to_num(char d)
* success, store the result in <b>out</b>, advance bufp to the next
* character, and return 0. On failure, return -1. */
static int
-scan_unsigned(const char **bufp, unsigned long *out, int width, int base)
+scan_unsigned(const char **bufp, unsigned long *out, int width, unsigned base)
{
unsigned long result = 0;
int scanned_so_far = 0;
@@ -3092,7 +3239,7 @@ scan_unsigned(const char **bufp, unsigned long *out, int width, int base)
while (**bufp && (hex?TOR_ISXDIGIT(**bufp):TOR_ISDIGIT(**bufp))
&& scanned_so_far < width) {
- int digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++);
+ unsigned digit = hex?hex_decode_digit(*(*bufp)++):digit_to_num(*(*bufp)++);
// Check for overflow beforehand, without actually causing any overflow
// This preserves functionality on compilers that don't wrap overflow
// (i.e. that trap or optimise away overflow)
@@ -3138,14 +3285,15 @@ scan_signed(const char **bufp, long *out, int width)
if (neg && result > 0) {
if (result > ((unsigned long)LONG_MAX) + 1)
return -1; /* Underflow */
- // Avoid overflow on the cast to signed long when result is LONG_MIN
- // by subtracting 1 from the unsigned long positive value,
- // then, after it has been cast to signed and negated,
- // subtracting the original 1 (the double-subtraction is intentional).
- // Otherwise, the cast to signed could cause a temporary long
- // to equal LONG_MAX + 1, which is undefined.
- // We avoid underflow on the subtraction by treating -0 as positive.
- *out = (-(long)(result - 1)) - 1;
+ else if (result == ((unsigned long)LONG_MAX) + 1)
+ *out = LONG_MIN;
+ else {
+ /* We once had a far more clever no-overflow conversion here, but
+ * some versions of GCC apparently ran it into the ground. Now
+ * we just check for LONG_MIN explicitly.
+ */
+ *out = -(long)result;
+ }
} else {
if (result > LONG_MAX)
return -1; /* Overflow */
@@ -3291,8 +3439,10 @@ tor_vsscanf(const char *buf, const char *pattern, va_list ap)
*out = lng;
} else {
int *out = va_arg(ap, int *);
+#if LONG_MAX > INT_MAX
if (lng < INT_MIN || lng > INT_MAX)
return n_matched;
+#endif
*out = (int)lng;
}
++pattern;
@@ -3387,8 +3537,8 @@ smartlist_add_vasprintf(struct smartlist_t *sl, const char *pattern,
/** Return a new list containing the filenames in the directory <b>dirname</b>.
* Return NULL on error or if <b>dirname</b> is not a directory.
*/
-smartlist_t *
-tor_listdir(const char *dirname)
+MOCK_IMPL(smartlist_t *,
+tor_listdir, (const char *dirname))
{
smartlist_t *result;
#ifdef _WIN32
@@ -3495,13 +3645,17 @@ start_daemon(void)
start_daemon_called = 1;
if (pipe(daemon_filedes)) {
+ /* LCOV_EXCL_START */
log_err(LD_GENERAL,"pipe failed; exiting. Error was %s", strerror(errno));
exit(1);
+ /* LCOV_EXCL_STOP */
}
pid = fork();
if (pid < 0) {
+ /* LCOV_EXCL_START */
log_err(LD_GENERAL,"fork failed. Exiting.");
exit(1);
+ /* LCOV_EXCL_STOP */
}
if (pid) { /* Parent */
int ok;
@@ -3563,8 +3717,10 @@ finish_daemon(const char *desired_cwd)
nullfd = tor_open_cloexec("/dev/null", O_RDWR, 0);
if (nullfd < 0) {
+ /* LCOV_EXCL_START */
log_err(LD_GENERAL,"/dev/null can't be opened. Exiting.");
exit(1);
+ /* LCOV_EXCL_STOP */
}
/* close fds linking to invoking terminal, but
* close usual incoming fds, but redirect them somewhere
@@ -3573,8 +3729,10 @@ finish_daemon(const char *desired_cwd)
if (dup2(nullfd,0) < 0 ||
dup2(nullfd,1) < 0 ||
dup2(nullfd,2) < 0) {
+ /* LCOV_EXCL_START */
log_err(LD_GENERAL,"dup2 failed. Exiting.");
exit(1);
+ /* LCOV_EXCL_STOP */
}
if (nullfd > 2)
close(nullfd);
@@ -3688,9 +3846,9 @@ format_win_cmdline_argument(const char *arg)
formatted_arg[i++] = '"';
/* Add characters */
- SMARTLIST_FOREACH(arg_chars, char*, c,
+ SMARTLIST_FOREACH(arg_chars, char*, ch,
{
- formatted_arg[i++] = *c;
+ formatted_arg[i++] = *ch;
});
/* Add trailing quote */
@@ -3771,7 +3929,7 @@ format_number_sigsafe(unsigned long x, char *buf, int buf_len,
/* NOT tor_assert; see above. */
if (cp != buf) {
- abort();
+ abort(); // LCOV_EXCL_LINE
}
return len;
@@ -4349,7 +4507,7 @@ tor_spawn_background(const char *const filename, const char **argv,
_exit(255);
/* Never reached, but avoids compiler warning */
- return status;
+ return status; // LCOV_EXCL_LINE
}
/* In parent */
@@ -4548,13 +4706,13 @@ tor_get_exit_code(process_handle_t *process_handle,
return PROCESS_EXIT_RUNNING;
} else if (retval != process_handle->pid) {
log_warn(LD_GENERAL, "waitpid() failed for PID %d: %s",
- process_handle->pid, strerror(errno));
+ (int)process_handle->pid, strerror(errno));
return PROCESS_EXIT_ERROR;
}
if (!WIFEXITED(stat_loc)) {
log_warn(LD_GENERAL, "Process %d did not exit normally",
- process_handle->pid);
+ (int)process_handle->pid);
return PROCESS_EXIT_ERROR;
}
@@ -5535,7 +5693,29 @@ tor_weak_random_range(tor_weak_rng_t *rng, int32_t top)
int64_t
clamp_double_to_int64(double number)
{
- int exp;
+ int exponent;
+
+#if (defined(__MINGW32__) || defined(__MINGW64__)) && 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
+ versions of gcc, __builtin_choose_expr() can generate type warnings
+ even from branches that are not taken. So, suppress those warnings.
+*/
+#define PROBLEMATIC_FLOAT_CONVERSION_WARNING
+DISABLE_GCC_WARNING(float-conversion)
+#endif
+
+/*
+ With clang 4.0 we apparently run into "double promotion" warnings here,
+ since clang thinks we're promoting a double to a long double.
+ */
+#if defined(__clang__)
+#if __has_warning("-Wdouble-promotion")
+#define PROBLEMATIC_DOUBLE_PROMOTION_WARNING
+DISABLE_GCC_WARNING(double-promotion)
+#endif
+#endif
/* NaN is a special case that can't be used with the logic below. */
if (isnan(number)) {
@@ -5549,18 +5729,46 @@ clamp_double_to_int64(double number)
* magnitude of number is strictly less than 2^exp.
*
* If number is infinite, the call to frexp is legal but the contents of
- * exp are unspecified. */
- frexp(number, &exp);
+ * are exponent unspecified. */
+ frexp(number, &exponent);
/* If the magnitude of number is strictly less than 2^63, the truncated
* version of number is guaranteed to be representable. The only
* representable integer for which this is not the case is INT64_MIN, but
* it is covered by the logic below. */
- if (isfinite(number) && exp <= 63) {
- return number;
+ if (isfinite(number) && exponent <= 63) {
+ return (int64_t)number;
}
/* Handle infinities and finite numbers with magnitude >= 2^63. */
return signbit(number) ? INT64_MIN : INT64_MAX;
+
+#ifdef PROBLEMATIC_DOUBLE_PROMOTION_WARNING
+ENABLE_GCC_WARNING(double-promotion)
+#endif
+#ifdef PROBLEMATIC_FLOAT_CONVERSION_WARNING
+ENABLE_GCC_WARNING(float-conversion)
+#endif
+}
+
+/** Return a uint64_t value from <b>a</b> in network byte order. */
+uint64_t
+tor_htonll(uint64_t a)
+{
+#ifdef WORDS_BIGENDIAN
+ /* Big endian. */
+ return a;
+#else /* WORDS_BIGENDIAN */
+ /* Little endian. The worst... */
+ return htonl((uint32_t)(a>>32)) |
+ (((uint64_t)htonl((uint32_t)a))<<32);
+#endif /* WORDS_BIGENDIAN */
+}
+
+/** Return a uint64_t value from <b>a</b> in host byte order. */
+uint64_t
+tor_ntohll(uint64_t a)
+{
+ return tor_htonll(a);
}
diff --git a/src/common/util.h b/src/common/util.h
index ebcf88b32d..479fc8d610 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -22,6 +22,7 @@
/* for the correct alias to struct stat */
#include <sys/stat.h>
#endif
+#include "util_bug.h"
#ifndef O_BINARY
#define O_BINARY 0
@@ -33,41 +34,6 @@
#define O_NOFOLLOW 0
#endif
-/* Replace assert() with a variant that sends failures to the log before
- * calling assert() normally.
- */
-#ifdef NDEBUG
-/* Nobody should ever want to build with NDEBUG set. 99% of our asserts will
- * be outside the critical path anyway, so it's silly to disable bug-checking
- * throughout the entire program just because a few asserts are slowing you
- * down. Profile, optimize the critical path, and keep debugging on.
- *
- * And I'm not just saying that because some of our asserts check
- * security-critical properties.
- */
-#error "Sorry; we don't support building with NDEBUG."
-#endif
-
-/* 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
- * didn't hit. */
-#if defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS)
-#define tor_assert(a) STMT_BEGIN \
- (void)(a); \
- STMT_END
-#else
-/** Like assert(3), but send assertion failures to the log as well as to
- * stderr. */
-#define tor_assert(expr) STMT_BEGIN \
- if (PREDICT_UNLIKELY(!(expr))) { \
- tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
- abort(); \
- } STMT_END
-#endif
-
-void tor_assertion_failed_(const char *fname, unsigned int line,
- const char *func, const char *expr);
-
/* If we're building with dmalloc, we want all of our memory allocation
* functions to take an extra file/line pair of arguments. If not, not.
* We define DMALLOC_PARAMS to the extra parameters to insert in the
@@ -81,11 +47,6 @@ void tor_assertion_failed_(const char *fname, unsigned int line,
#define DMALLOC_ARGS
#endif
-/** Define this if you want Tor to crash when any problem comes up,
- * so you can get a coredump and track things down. */
-// #define tor_fragile_assert() tor_assert(0)
-#define tor_fragile_assert()
-
/* Memory management */
void *tor_malloc_(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
void *tor_malloc_zero_(size_t size DMALLOC_PARAMS) ATTR_MALLOC;
@@ -100,6 +61,8 @@ void *tor_memdup_(const void *mem, size_t len DMALLOC_PARAMS)
void *tor_memdup_nulterm_(const void *mem, size_t len DMALLOC_PARAMS)
ATTR_MALLOC ATTR_NONNULL((1));
void tor_free_(void *mem);
+uint64_t tor_htonll(uint64_t a);
+uint64_t tor_ntohll(uint64_t a);
#ifdef USE_DMALLOC
extern int dmalloc_free(const char *file, const int line, void *pnt,
const int func_id);
@@ -119,7 +82,7 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
*/
#define tor_free(p) STMT_BEGIN \
if (PREDICT_LIKELY((p)!=NULL)) { \
- free(p); \
+ raw_free(p); \
(p)=NULL; \
} \
STMT_END
@@ -136,6 +99,14 @@ extern int dmalloc_free(const char *file, const int line, void *pnt,
#define tor_memdup(s, n) tor_memdup_(s, n DMALLOC_ARGS)
#define tor_memdup_nulterm(s, n) tor_memdup_nulterm_(s, n DMALLOC_ARGS)
+/* Aliases for the underlying system malloc/realloc/free. Only use
+ * them to indicate "I really want the underlying system function, I know
+ * what I'm doing." */
+#define raw_malloc malloc
+#define raw_realloc realloc
+#define raw_free free
+#define raw_strdup strdup
+
void tor_log_mallinfo(int severity);
/** Return the offset of <b>member</b> within the type <b>tp</b>, in bytes */
@@ -184,12 +155,12 @@ uint64_t round_to_power_of_2(uint64_t u64);
unsigned round_to_next_multiple_of(unsigned number, unsigned divisor);
uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor);
uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor);
-int64_t round_int64_to_next_multiple_of(int64_t number, int64_t divisor);
int64_t sample_laplace_distribution(double mu, double b, double p);
int64_t add_laplace_noise(int64_t signal, double random, double delta_f,
double epsilon);
int n_bits_set_u8(uint8_t v);
int64_t clamp_double_to_int64(double number);
+void simplify_fraction64(uint64_t *numer, uint64_t *denom);
/* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
* and positive <b>b</b>. Works on integer types only. Not defined if a+b can
@@ -330,6 +301,7 @@ typedef struct ratelim_t {
} ratelim_t;
#define RATELIM_INIT(r) { (r), 0, 0 }
+#define RATELIM_TOOMANY (16*1000*1000)
char *rate_limit_log(ratelim_t *lim, time_t now);
@@ -349,6 +321,8 @@ const char *stream_status_to_string(enum stream_status stream_status);
enum stream_status get_string_from_pipe(FILE *stream, char *buf, size_t count);
+MOCK_DECL(int,tor_unlink,(const char *pathname));
+
/** Return values from file_status(); see that function's documentation
* for details. */
typedef enum { FN_ERROR, FN_NOENT, FN_FILE, FN_DIR, FN_EMPTY } file_status_t;
@@ -364,8 +338,9 @@ typedef unsigned int cpd_check_t;
#define CPD_GROUP_READ (1u << 3)
#define CPD_CHECK_MODE_ONLY (1u << 4)
#define CPD_RELAX_DIRMODE_CHECK (1u << 5)
-int check_private_dir(const char *dirname, cpd_check_t check,
- const char *effective_user);
+MOCK_DECL(int, check_private_dir,
+ (const char *dirname, cpd_check_t check,
+ const char *effective_user));
#define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC)
#define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND)
@@ -378,7 +353,8 @@ FILE *start_writing_to_stdio_file(const char *fname, int open_flags, int mode,
FILE *fdopen_file(open_file_t *file_data);
int finish_writing_to_file(open_file_t *file_data);
int abort_writing_to_file(open_file_t *file_data);
-int write_str_to_file(const char *fname, const char *str, int bin);
+MOCK_DECL(int,
+write_str_to_file,(const char *fname, const char *str, int bin));
MOCK_DECL(int,
write_bytes_to_file,(const char *fname, const char *str, size_t len,
int bin));
@@ -403,18 +379,18 @@ int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
#ifndef _WIN32
struct stat;
#endif
-char *read_file_to_str(const char *filename, int flags, struct stat *stat_out)
- ATTR_MALLOC;
+MOCK_DECL_ATTR(char *, read_file_to_str,
+ (const char *filename, int flags, struct stat *stat_out),
+ ATTR_MALLOC);
char *read_file_to_str_until_eof(int fd, size_t max_bytes_to_read,
size_t *sz_out)
ATTR_MALLOC;
+const char *unescape_string(const char *s, char **result, size_t *size_out);
const char *parse_config_line_from_str_verbose(const char *line,
char **key_out, char **value_out,
const char **err_out);
-#define parse_config_line_from_str(line,key_out,value_out) \
- parse_config_line_from_str_verbose((line),(key_out),(value_out),NULL)
char *expand_filename(const char *filename);
-struct smartlist_t *tor_listdir(const char *dirname);
+MOCK_DECL(struct smartlist_t *, tor_listdir, (const char *dirname));
int path_is_relative(const char *filename);
/* Process helpers */
@@ -575,6 +551,10 @@ STATIC int format_helper_exit_status(unsigned char child_state,
#endif
+#ifdef TOR_UNIT_TESTS
+int size_mul_check__(const size_t x, const size_t y);
+#endif
+
#define ARRAY_LENGTH(x) ((sizeof(x)) / sizeof(x[0]))
#endif
diff --git a/src/common/util_bug.c b/src/common/util_bug.c
new file mode 100644
index 0000000000..08aba47974
--- /dev/null
+++ b/src/common/util_bug.c
@@ -0,0 +1,115 @@
+/* Copyright (c) 2003, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_bug.c
+ **/
+
+#include "orconfig.h"
+#include "util_bug.h"
+#include "torlog.h"
+#include "backtrace.h"
+#include "container.h"
+
+#ifdef TOR_UNIT_TESTS
+static void (*failed_assertion_cb)(void) = NULL;
+static int n_bugs_to_capture = 0;
+static smartlist_t *bug_messages = NULL;
+#define capturing_bugs() (bug_messages != NULL && n_bugs_to_capture)
+void
+tor_capture_bugs_(int n)
+{
+ tor_end_capture_bugs_();
+ bug_messages = smartlist_new();
+ n_bugs_to_capture = n;
+}
+void
+tor_end_capture_bugs_(void)
+{
+ n_bugs_to_capture = 0;
+ if (!bug_messages)
+ return;
+ SMARTLIST_FOREACH(bug_messages, char *, cp, tor_free(cp));
+ smartlist_free(bug_messages);
+ bug_messages = NULL;
+}
+const smartlist_t *
+tor_get_captured_bug_log_(void)
+{
+ return bug_messages;
+}
+static void
+add_captured_bug(const char *s)
+{
+ --n_bugs_to_capture;
+ smartlist_add(bug_messages, tor_strdup(s));
+}
+/** Set a callback to be invoked when we get any tor_bug_occurred_
+ * invocation. We use this in the unit tests so that a nonfatal
+ * assertion failure can also count as a test failure.
+ */
+void
+tor_set_failed_assertion_callback(void (*fn)(void))
+{
+ failed_assertion_cb = fn;
+}
+#else
+#define capturing_bugs() (0)
+#define add_captured_bug(s) do { } while (0)
+#endif
+
+/** Helper for tor_assert: report the assertion failure. */
+void
+tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr)
+{
+ char buf[256];
+ log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.",
+ fname, line, func, expr);
+ tor_snprintf(buf, sizeof(buf),
+ "Assertion %s failed in %s at %s:%u",
+ expr, func, fname, line);
+ log_backtrace(LOG_ERR, LD_BUG, buf);
+}
+
+/** Helper for tor_assert_nonfatal: report the assertion failure. */
+void
+tor_bug_occurred_(const char *fname, unsigned int line,
+ const char *func, const char *expr,
+ int once)
+{
+ char buf[256];
+ const char *once_str = once ?
+ " (Future instances of this warning will be silenced.)": "";
+ if (! expr) {
+ if (capturing_bugs()) {
+ add_captured_bug("This line should not have been reached.");
+ return;
+ }
+ log_warn(LD_BUG, "%s:%u: %s: This line should not have been reached.%s",
+ fname, line, func, once_str);
+ tor_snprintf(buf, sizeof(buf),
+ "Line unexpectedly reached at %s at %s:%u",
+ func, fname, line);
+ } else {
+ if (capturing_bugs()) {
+ add_captured_bug(expr);
+ return;
+ }
+ log_warn(LD_BUG, "%s:%u: %s: Non-fatal assertion %s failed.%s",
+ fname, line, func, expr, once_str);
+ tor_snprintf(buf, sizeof(buf),
+ "Non-fatal assertion %s failed in %s at %s:%u",
+ expr, func, fname, line);
+ }
+ log_backtrace(LOG_WARN, LD_BUG, buf);
+
+#ifdef TOR_UNIT_TESTS
+ if (failed_assertion_cb) {
+ failed_assertion_cb();
+ }
+#endif
+}
+
diff --git a/src/common/util_bug.h b/src/common/util_bug.h
new file mode 100644
index 0000000000..0695806911
--- /dev/null
+++ b/src/common/util_bug.h
@@ -0,0 +1,173 @@
+/* Copyright (c) 2003-2004, Roger Dingledine
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file util_bug.h
+ **/
+
+#ifndef TOR_UTIL_BUG_H
+#define TOR_UTIL_BUG_H
+
+#include "orconfig.h"
+#include "compat.h"
+#include "testsupport.h"
+
+/* Replace assert() with a variant that sends failures to the log before
+ * calling assert() normally.
+ */
+#ifdef NDEBUG
+/* Nobody should ever want to build with NDEBUG set. 99% of our asserts will
+ * be outside the critical path anyway, so it's silly to disable bug-checking
+ * throughout the entire program just because a few asserts are slowing you
+ * down. Profile, optimize the critical path, and keep debugging on.
+ *
+ * And I'm not just saying that because some of our asserts check
+ * security-critical properties.
+ */
+#error "Sorry; we don't support building with NDEBUG."
+#endif
+
+/* 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
+ * didn't hit. */
+#if defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS)
+#define tor_assert(a) STMT_BEGIN \
+ (void)(a); \
+ STMT_END
+#else
+/** Like assert(3), but send assertion failures to the log as well as to
+ * stderr. */
+#define tor_assert(expr) STMT_BEGIN \
+ if (PREDICT_UNLIKELY(!(expr))) { \
+ tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
+ abort(); \
+ } STMT_END
+#endif
+
+#define tor_assert_unreached() tor_assert(0)
+
+/* Non-fatal bug assertions. The "unreached" variants mean "this line should
+ * never be reached." The "once" variants mean "Don't log a warning more than
+ * once".
+ *
+ * The 'BUG' macro checks a boolean condition and logs an error message if it
+ * is true. Example usage:
+ * if (BUG(x == NULL))
+ * return -1;
+ */
+
+#ifdef __COVERITY__
+#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
+
+#if defined(__COVERITY__) || defined(__clang_analyzer__)
+// We're running with a static analysis tool: let's treat even nonfatal
+// assertion failures as something that we need to avoid.
+#define ALL_BUGS_ARE_FATAL
+#endif
+
+#ifdef ALL_BUGS_ARE_FATAL
+#define tor_assert_nonfatal_unreached() tor_assert(0)
+#define tor_assert_nonfatal(cond) tor_assert((cond))
+#define tor_assert_nonfatal_unreached_once() tor_assert(0)
+#define tor_assert_nonfatal_once(cond) tor_assert((cond))
+#define BUG(cond) \
+ (PREDICT_UNLIKELY(cond) ? \
+ (tor_assertion_failed_(SHORT_FILE__,__LINE__,__func__,"!("#cond")"), \
+ abort(), 1) \
+ : 0)
+#elif defined(TOR_UNIT_TESTS) && defined(DISABLE_ASSERTS_IN_UNIT_TESTS)
+#define tor_assert_nonfatal_unreached() STMT_NIL
+#define tor_assert_nonfatal(cond) ((void)(cond))
+#define tor_assert_nonfatal_unreached_once() STMT_NIL
+#define tor_assert_nonfatal_once(cond) ((void)(cond))
+#define BUG(cond) (PREDICT_UNLIKELY(cond) ? 1 : 0)
+#else /* Normal case, !ALL_BUGS_ARE_FATAL, !DISABLE_ASSERTS_IN_UNIT_TESTS */
+#define tor_assert_nonfatal_unreached() STMT_BEGIN \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 0); \
+ STMT_END
+#define tor_assert_nonfatal(cond) STMT_BEGIN \
+ if (PREDICT_UNLIKELY(!(cond))) { \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 0); \
+ } \
+ STMT_END
+#define tor_assert_nonfatal_unreached_once() STMT_BEGIN \
+ static int warning_logged__ = 0; \
+ if (!warning_logged__) { \
+ warning_logged__ = 1; \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 1); \
+ } \
+ STMT_END
+#define tor_assert_nonfatal_once(cond) STMT_BEGIN \
+ static int warning_logged__ = 0; \
+ if (!warning_logged__ && PREDICT_UNLIKELY(!(cond))) { \
+ warning_logged__ = 1; \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1); \
+ } \
+ STMT_END
+#define BUG(cond) \
+ (PREDICT_UNLIKELY(cond) ? \
+ (tor_bug_occurred_(SHORT_FILE__,__LINE__,__func__,"!("#cond")",0), 1) \
+ : 0)
+#endif
+
+#ifdef __GNUC__
+#define IF_BUG_ONCE__(cond,var) \
+ if (( { \
+ static int var = 0; \
+ int bool_result = (cond); \
+ if (PREDICT_UNLIKELY(bool_result) && !var) { \
+ var = 1; \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \
+ "!("#cond")", 1); \
+ } \
+ PREDICT_UNLIKELY(bool_result); } ))
+#else
+#define IF_BUG_ONCE__(cond,var) \
+ static int var = 0; \
+ if (PREDICT_UNLIKELY(cond) ? \
+ (var ? 1 : \
+ (var=1, \
+ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \
+ "!("#cond")", 1), \
+ 1)) \
+ : 0)
+#endif
+#define IF_BUG_ONCE_VARNAME_(a) \
+ warning_logged_on_ ## a ## __
+#define IF_BUG_ONCE_VARNAME__(a) \
+ IF_BUG_ONCE_VARNAME_(a)
+
+/** This macro behaves as 'if (bug(x))', except that it only logs its
+ * warning once, no matter how many times it triggers.
+ */
+
+#define IF_BUG_ONCE(cond) \
+ IF_BUG_ONCE__((cond), \
+ IF_BUG_ONCE_VARNAME__(__LINE__))
+
+/** Define this if you want Tor to crash when any problem comes up,
+ * so you can get a coredump and track things down. */
+// #define tor_fragile_assert() tor_assert_unreached(0)
+#define tor_fragile_assert() tor_assert_nonfatal_unreached_once()
+
+void tor_assertion_failed_(const char *fname, unsigned int line,
+ const char *func, const char *expr);
+void tor_bug_occurred_(const char *fname, unsigned int line,
+ const char *func, const char *expr,
+ int once);
+
+#ifdef TOR_UNIT_TESTS
+void tor_capture_bugs_(int n);
+void tor_end_capture_bugs_(void);
+const struct smartlist_t *tor_get_captured_bug_log_(void);
+void tor_set_failed_assertion_callback(void (*fn)(void));
+#endif
+
+#endif
+
diff --git a/src/common/util_format.c b/src/common/util_format.c
index 8aae9e8771..aef9db85c8 100644
--- a/src/common/util_format.c
+++ b/src/common/util_format.c
@@ -21,33 +21,48 @@
#include <string.h>
#include <stdlib.h>
-/** Implements base32 encoding as in RFC 4648. Limitation: Requires
- * that srclen*8 is a multiple of 5.
- */
+/* Return the base32 encoded size in bytes using the source length srclen.
+ * The NUL terminated byte is added as well since every base32 encoding
+ * requires enough space for it. */
+size_t
+base32_encoded_size(size_t srclen)
+{
+ size_t enclen;
+ enclen = CEIL_DIV(srclen*8, 5) + 1;
+ tor_assert(enclen < INT_MAX && enclen > srclen);
+ return enclen;
+}
+
+/** Implements base32 encoding as in RFC 4648. */
void
base32_encode(char *dest, size_t destlen, const char *src, size_t srclen)
{
unsigned int i, v, u;
- size_t nbits = srclen * 8, bit;
+ size_t nbits = srclen * 8;
+ size_t bit;
tor_assert(srclen < SIZE_T_CEILING/8);
- tor_assert((nbits%5) == 0); /* We need an even multiple of 5 bits. */
- tor_assert((nbits/5)+1 <= destlen); /* We need enough space. */
+ /* We need enough space for the encoded data and the extra NUL byte. */
+ tor_assert(base32_encoded_size(srclen) <= destlen);
tor_assert(destlen < SIZE_T_CEILING);
+ /* Make sure we leave no uninitialized data in the destination buffer. */
+ memset(dest, 0, destlen);
+
for (i=0,bit=0; bit < nbits; ++i, bit+=5) {
/* set v to the 16-bit value starting at src[bits/8], 0-padded. */
v = ((uint8_t)src[bit/8]) << 8;
- if (bit+5<nbits) v += (uint8_t)src[(bit/8)+1];
- /* set u to the 5-bit value at the bit'th bit of src. */
+ if (bit+5<nbits)
+ v += (uint8_t)src[(bit/8)+1];
+ /* set u to the 5-bit value at the bit'th bit of buf. */
u = (v >> (11-(bit%8))) & 0x1F;
dest[i] = BASE32_CHARS[u];
}
dest[i] = '\0';
}
-/** Implements base32 decoding as in RFC 4648. Limitation: Requires
- * that srclen*5 is a multiple of 8. Returns 0 if successful, -1 otherwise.
+/** Implements base32 decoding as in RFC 4648.
+ * Returns 0 if successful, -1 otherwise.
*/
int
base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
@@ -57,13 +72,13 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
unsigned int i;
size_t nbits, j, bit;
char *tmp;
- nbits = srclen * 5;
+ nbits = ((srclen * 5) / 8) * 8;
tor_assert(srclen < SIZE_T_CEILING / 5);
- tor_assert((nbits%8) == 0); /* We need an even multiple of 8 bits. */
tor_assert((nbits/8) <= destlen); /* We need enough space. */
tor_assert(destlen < SIZE_T_CEILING);
+ /* Make sure we leave no uninitialized data in the destination buffer. */
memset(dest, 0, destlen);
/* Convert base32 encoded chars to the 5-bit values that they represent. */
@@ -73,7 +88,7 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
else if (src[j] > 0x31 && src[j] < 0x38) tmp[j] = src[j] - 0x18;
else if (src[j] > 0x40 && src[j] < 0x5B) tmp[j] = src[j] - 0x41;
else {
- log_warn(LD_BUG, "illegal character in base32 encoded string");
+ log_warn(LD_GENERAL, "illegal character in base32 encoded string");
tor_free(tmp);
return -1;
}
@@ -186,7 +201,8 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
if (enclen > INT_MAX)
return -1;
- memset(dest, 0, enclen);
+ /* Make sure we leave no uninitialized data in the destination buffer. */
+ memset(dest, 0, destlen);
/* XXX/Yawning: If this ends up being too slow, this can be sped up
* by separating the multiline format case and the normal case, and
@@ -249,7 +265,7 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
break;
default:
/* Something went catastrophically wrong. */
- tor_fragile_assert();
+ tor_fragile_assert(); // LCOV_EXCL_LINE
return -1;
}
@@ -387,6 +403,7 @@ base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
if (destlen > SIZE_T_CEILING)
return -1;
+ /* Make sure we leave no uninitialized data in the destination buffer. */
memset(dest, 0, destlen);
/* Iterate over all the bytes in src. Each one will add 0 or 6 bits to the
@@ -461,6 +478,9 @@ base16_encode(char *dest, size_t destlen, const char *src, size_t srclen)
tor_assert(destlen >= srclen*2+1);
tor_assert(destlen < SIZE_T_CEILING);
+ /* Make sure we leave no uninitialized data in the destination buffer. */
+ memset(dest, 0, destlen);
+
cp = dest;
end = src+srclen;
while (src<end) {
@@ -504,20 +524,24 @@ hex_decode_digit(char c)
return hex_decode_digit_(c);
}
-/** Given a hexadecimal string of <b>srclen</b> bytes in <b>src</b>, decode it
- * and store the result in the <b>destlen</b>-byte buffer at <b>dest</b>.
- * Return 0 on success, -1 on failure. */
+/** Given a hexadecimal string of <b>srclen</b> bytes in <b>src</b>, decode
+ * it and store the result in the <b>destlen</b>-byte buffer at <b>dest</b>.
+ * Return the number of bytes decoded on success, -1 on failure. If
+ * <b>destlen</b> is greater than INT_MAX or less than half of
+ * <b>srclen</b>, -1 is returned. */
int
base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
{
const char *end;
-
+ char *dest_orig = dest;
int v1,v2;
+
if ((srclen % 2) != 0)
return -1;
- if (destlen < srclen/2 || destlen > SIZE_T_CEILING)
+ if (destlen < srclen/2 || destlen > INT_MAX)
return -1;
+ /* Make sure we leave no uninitialized data in the destination buffer. */
memset(dest, 0, destlen);
end = src+srclen;
@@ -530,6 +554,9 @@ base16_decode(char *dest, size_t destlen, const char *src, size_t srclen)
++dest;
src+=2;
}
- return 0;
+
+ tor_assert((dest-dest_orig) <= (ptrdiff_t) destlen);
+
+ return (int) (dest-dest_orig);
}
diff --git a/src/common/util_format.h b/src/common/util_format.h
index a748a4f3cf..20ac711d10 100644
--- a/src/common/util_format.h
+++ b/src/common/util_format.h
@@ -24,6 +24,7 @@ int base64_decode_nopad(uint8_t *dest, size_t destlen,
#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);
int base32_decode(char *dest, size_t destlen, const char *src, size_t srclen);
+size_t base32_encoded_size(size_t srclen);
int hex_decode_digit(char c);
void base16_encode(char *dest, size_t destlen, const char *src, size_t srclen);
diff --git a/src/common/util_process.c b/src/common/util_process.c
index 848b238318..abda63720c 100644
--- a/src/common/util_process.c
+++ b/src/common/util_process.c
@@ -61,9 +61,9 @@ process_map_entries_eq_(const waitpid_callback_t *a,
static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
- process_map_entries_eq_);
+ process_map_entries_eq_)
HT_GENERATE2(process_map, waitpid_callback_t, node, process_map_entry_hash_,
- process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_);
+ process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_)
/**
* Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for
diff --git a/src/common/workqueue.c b/src/common/workqueue.c
index 0a38550de0..e1fb663a2a 100644
--- a/src/common/workqueue.c
+++ b/src/common/workqueue.c
@@ -6,6 +6,20 @@
*
* \brief Implements worker threads, queues of work for them, and mechanisms
* for them to send answers back to the main thread.
+ *
+ * The main structure here is a threadpool_t : it manages a set of worker
+ * threads, a queue of pending work, and a reply queue. Every piece of work
+ * is a workqueue_entry_t, containing data to process and a function to
+ * process it with.
+ *
+ * The main thread informs the worker threads of pending work by using a
+ * condition variable. The workers inform the main process of completed work
+ * by using an alert_sockets_t object, as implemented in compat_threads.c.
+ *
+ * The main thread can also queue an "update" that will be handled by all the
+ * workers. This is useful for updating state that all the workers share.
+ *
+ * In Tor today, there is currently only one thread pool, used in cpuworker.c.
*/
#include "orconfig.h"
@@ -262,9 +276,12 @@ workerthread_new(void *state, threadpool_t *pool, replyqueue_t *replyqueue)
thr->in_pool = pool;
if (spawn_func(worker_thread_main, thr) < 0) {
+ //LCOV_EXCL_START
+ tor_assert_nonfatal_unreached();
log_err(LD_GENERAL, "Can't launch worker thread.");
tor_free(thr);
return NULL;
+ //LCOV_EXCL_STOP
}
return thr;
@@ -375,8 +392,8 @@ threadpool_queue_update(threadpool_t *pool,
static int
threadpool_start_threads(threadpool_t *pool, int n)
{
- if (n < 0)
- return -1;
+ if (BUG(n < 0))
+ return -1; // LCOV_EXCL_LINE
if (n > MAX_THREADS)
n = MAX_THREADS;
@@ -391,9 +408,12 @@ threadpool_start_threads(threadpool_t *pool, int n)
workerthread_t *thr = workerthread_new(state, pool, pool->reply_queue);
if (!thr) {
+ //LCOV_EXCL_START
+ tor_assert_nonfatal_unreached();
pool->free_thread_state_fn(state);
tor_mutex_release(&pool->lock);
return -1;
+ //LCOV_EXCL_STOP
}
thr->index = pool->n_threads;
pool->threads[pool->n_threads++] = thr;
@@ -429,10 +449,13 @@ threadpool_new(int n_threads,
pool->reply_queue = replyqueue;
if (threadpool_start_threads(pool, n_threads) < 0) {
+ //LCOV_EXCL_START
+ tor_assert_nonfatal_unreached();
tor_cond_uninit(&pool->condition);
tor_mutex_uninit(&pool->lock);
tor_free(pool);
return NULL;
+ //LCOV_EXCL_STOP
}
return pool;
@@ -456,8 +479,10 @@ replyqueue_new(uint32_t alertsocks_flags)
rq = tor_malloc_zero(sizeof(replyqueue_t));
if (alert_sockets_create(&rq->alert, alertsocks_flags) < 0) {
+ //LCOV_EXCL_START
tor_free(rq);
return NULL;
+ //LCOV_EXCL_STOP
}
tor_mutex_init(&rq->lock);
@@ -486,10 +511,12 @@ void
replyqueue_process(replyqueue_t *queue)
{
if (queue->alert.drain_fn(queue->alert.read_fd) < 0) {
+ //LCOV_EXCL_START
static ratelim_t warn_limit = RATELIM_INIT(7200);
log_fn_ratelim(&warn_limit, LOG_WARN, LD_GENERAL,
"Failure from drain_fd: %s",
tor_socket_strerror(tor_socket_errno(queue->alert.read_fd)));
+ //LCOV_EXCL_STOP
}
tor_mutex_acquire(&queue->lock);
diff --git a/src/common/workqueue.h b/src/common/workqueue.h
index 89282e6f21..54276767b0 100644
--- a/src/common/workqueue.h
+++ b/src/common/workqueue.h
@@ -7,7 +7,7 @@
#include "compat.h"
/** A replyqueue is used to tell the main thread about the outcome of
- * work that we queued for the the workers. */
+ * work that we queued for the workers. */
typedef struct replyqueue_s replyqueue_t;
/** A thread-pool manages starting threads and passing work to them. */
typedef struct threadpool_s threadpool_t;
diff --git a/src/config/mmdb-convert.py b/src/config/mmdb-convert.py
index cbe9acdc5d..3a454a3fc1 100644
--- a/src/config/mmdb-convert.py
+++ b/src/config/mmdb-convert.py
@@ -20,7 +20,7 @@
When given a simplicity/speed tradeoff, it opts for simplicity.
- You will not understand the code without undestanding the MaxMind-DB
+ You will not understand the code without understanding the MaxMind-DB
file format. It is specified at:
https://github.com/maxmind/MaxMind-DB/blob/master/MaxMind-DB-spec.md.
@@ -257,7 +257,7 @@ IGNORE_LEN_TYPES = set([
TP_ARRAY, # Length is number of members that follow.
TP_PTR, # Length is index to pointed-to data element.
TP_BOOL, # Length is 0 or 1.
- TP_DCACHE, # Length isnumber of members that follow
+ TP_DCACHE, # Length is number of members that follow
])
def parse_data_section(s):
@@ -265,7 +265,7 @@ def parse_data_section(s):
Datum items."""
# Stack of possibly nested containers. We use the 'nChildren' member of
- # the last one to tell how many moreitems nest directly inside.
+ # the last one to tell how many more items nest directly inside.
stack = []
# List of all items, including nested ones.
diff --git a/src/config/torrc.minimal.in-staging b/src/config/torrc.minimal.in-staging
index 248cb5cf02..c537c51f9b 100644
--- a/src/config/torrc.minimal.in-staging
+++ b/src/config/torrc.minimal.in-staging
@@ -98,11 +98,14 @@
# OutboundBindAddress 10.0.0.5
## A handle for your relay, so people don't have to refer to it by key.
+## Nicknames must be between 1 and 19 characters inclusive, and must
+## contain only the alphanumeric characters (a-z, A-Z, 0-9). No unicode,
+## no emoji.
#Nickname ididnteditheconfig
## Define these to limit how much relayed traffic you will allow. Your
## own traffic is still unthrottled. Note that RelayBandwidthRate must
-## be at least 20 kilobytes per second.
+## be at least 75 kilobytes per second.
## Note that units for these config options are bytes (per second), not
## bits (per second), and that prefixes are binary prefixes, i.e. 2^10,
## 2^20, etc.
@@ -128,9 +131,12 @@
## 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.
+## Notice that "<" and ">" are recommended.
#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>
+## 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
+## to forge.
+#ContactInfo FFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFF Random Person <nobody AT example dot com>
## Uncomment this to mirror directory information for others. Please do
## if you have enough bandwidth.
@@ -147,11 +153,12 @@
## distribution for a sample.
#DirPortFrontPage @CONFDIR@/tor-exit-notice.html
-## Uncomment this if you run more than one Tor relay, and add the identity
-## key fingerprint of each Tor relay you control, even if they're on
-## different networks. You declare it here so Tor clients can avoid
-## using more than one of your relays in a single circuit. See
-## https://www.torproject.org/docs/faq#MultipleRelays
+## Uncomment this if you run more than one Tor relay, and add the
+## identity key fingerprint of each Tor relay you control, even if
+## they're on different networks. Include "$" with each key id. You
+## declare it here so Tor clients can avoid using more than one of
+## your relays in a single circuit.
+## 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.
#MyFamily $keyid,$keyid,...
@@ -201,4 +208,3 @@
## a private bridge, for example because you'll give out your bridge
## address manually to your friends, uncomment this line:
#PublishServerDescriptor 0
-
diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in
index 248cb5cf02..5328206da9 100644
--- a/src/config/torrc.sample.in
+++ b/src/config/torrc.sample.in
@@ -98,11 +98,13 @@
# OutboundBindAddress 10.0.0.5
## A handle for your relay, so people don't have to refer to it by key.
+## Nicknames must be between 1 and 19 characters inclusive, and must
+## contain only the characters [a-zA-Z0-9].
#Nickname ididnteditheconfig
## Define these to limit how much relayed traffic you will allow. Your
## own traffic is still unthrottled. Note that RelayBandwidthRate must
-## be at least 20 kilobytes per second.
+## be at least 75 kilobytes per second.
## Note that units for these config options are bytes (per second), not
## bits (per second), and that prefixes are binary prefixes, i.e. 2^10,
## 2^20, etc.
diff --git a/src/ext/README b/src/ext/README
index 7ce1bc3b74..d7e5439c71 100644
--- a/src/ext/README
+++ b/src/ext/README
@@ -11,13 +11,6 @@ strlcpy.c
for strcat and strcpy. These are nonstandard, and some libc
implementations refuse to add them for religious reasons.
-eventdns.[ch]
-
- A fork of Libevent's DNS implementation, used by Tor when Libevent
- 2.0 or later is not available. Once Libevent 2.0 is required, we
- should throw this away; it has diverged from evdns.[ch], and is
- no longer easily mergeable.
-
ht.h
An implementation of a hash table in the style of Niels Provos's
@@ -73,3 +66,14 @@ readpassphrase.[ch]
Portable readpassphrase implementation from OpenSSH portable, version
6.8p1.
+
+timeouts/
+
+ William Ahern's hierarchical timer-wheel implementation. MIT license.
+
+mulodi/
+
+ Contains an overflow-checking 64-bit signed integer multiply
+ from LLVM's compiler_rt. For some reason, this is missing from
+ 32-bit libclang in many places. Dual licensed MIT-license and
+ BSD-like license; see mulodi/LICENSE.TXT.
diff --git a/src/ext/csiphash.c b/src/ext/csiphash.c
index b60f73a7ff..49a6dc4778 100644
--- a/src/ext/csiphash.c
+++ b/src/ext/csiphash.c
@@ -122,13 +122,13 @@ uint64_t siphash24(const void *src, unsigned long src_sz, const struct sipkey *k
}
switch (src_sz - blocks) {
- case 7: last7 |= (uint64_t)m[i + 6] << 48;
- case 6: last7 |= (uint64_t)m[i + 5] << 40;
- case 5: last7 |= (uint64_t)m[i + 4] << 32;
- case 4: last7 |= (uint64_t)m[i + 3] << 24;
- case 3: last7 |= (uint64_t)m[i + 2] << 16;
- case 2: last7 |= (uint64_t)m[i + 1] << 8;
- case 1: last7 |= (uint64_t)m[i + 0] ;
+ case 7: last7 |= (uint64_t)m[i + 6] << 48; /* Falls through. */
+ case 6: last7 |= (uint64_t)m[i + 5] << 40; /* Falls through. */
+ case 5: last7 |= (uint64_t)m[i + 4] << 32; /* Falls through. */
+ case 4: last7 |= (uint64_t)m[i + 3] << 24; /* Falls through. */
+ case 3: last7 |= (uint64_t)m[i + 2] << 16; /* Falls through. */
+ case 2: last7 |= (uint64_t)m[i + 1] << 8; /* Falls through. */
+ case 1: last7 |= (uint64_t)m[i + 0] ; /* Falls through. */
case 0:
default:;
}
diff --git a/src/ext/curve25519_donna/curve25519-donna.c b/src/ext/curve25519_donna/curve25519-donna.c
index 5a0c3401dd..1c5a27ab8a 100644
--- a/src/ext/curve25519_donna/curve25519-donna.c
+++ b/src/ext/curve25519_donna/curve25519-donna.c
@@ -483,7 +483,6 @@ fcontract(u8 *output, limb *input_limbs) {
int i;
int j;
s32 input[10];
- s32 mask;
/* |input_limbs[i]| < 2^26, so it's valid to convert to an s32. */
for (i = 0; i < 10; i++) {
@@ -572,7 +571,7 @@ fcontract(u8 *output, limb *input_limbs) {
/* It still remains the case that input might be between 2^255-19 and 2^255.
* In this case, input[1..9] must take their maximum value and input[0] must
* be >= (2^255-19) & 0x3ffffff, which is 0x3ffffed. */
- mask = s32_gte(input[0], 0x3ffffed);
+ s32 mask = s32_gte(input[0], 0x3ffffed);
for (i = 1; i < 10; i++) {
if ((i & 1) == 1) {
mask &= s32_eq(input[i], 0x1ffffff);
diff --git a/src/ext/ed25519/donna/curve25519-donna-64bit.h b/src/ext/ed25519/donna/curve25519-donna-64bit.h
index 2941d1bcdc..50c9916768 100644
--- a/src/ext/ed25519/donna/curve25519-donna-64bit.h
+++ b/src/ext/ed25519/donna/curve25519-donna-64bit.h
@@ -8,9 +8,9 @@
typedef uint64_t bignum25519[5];
-static const uint64_t reduce_mask_40 = ((uint64_t)1 << 40) - 1;
+//static const uint64_t reduce_mask_40 = ((uint64_t)1 << 40) - 1;
static const uint64_t reduce_mask_51 = ((uint64_t)1 << 51) - 1;
-static const uint64_t reduce_mask_56 = ((uint64_t)1 << 56) - 1;
+//static const uint64_t reduce_mask_56 = ((uint64_t)1 << 56) - 1;
/* out = in */
DONNA_INLINE static void
diff --git a/src/ext/ed25519/donna/curve25519-donna-sse2.h b/src/ext/ed25519/donna/curve25519-donna-sse2.h
index 1dbfd44d8b..1123cb1e91 100644
--- a/src/ext/ed25519/donna/curve25519-donna-sse2.h
+++ b/src/ext/ed25519/donna/curve25519-donna-sse2.h
@@ -29,26 +29,19 @@ typedef packedelem32 packed32bignum25519[5];
typedef packedelem64 packed64bignum25519[10];
static const packedelem32 bot32bitmask = {{0xffffffff, 0x00000000, 0xffffffff, 0x00000000}};
-static const packedelem32 top32bitmask = {{0x00000000, 0xffffffff, 0x00000000, 0xffffffff}};
static const packedelem32 top64bitmask = {{0x00000000, 0x00000000, 0xffffffff, 0xffffffff}};
static const packedelem32 bot64bitmask = {{0xffffffff, 0xffffffff, 0x00000000, 0x00000000}};
/* reduction masks */
static const packedelem64 packedmask26 = {{0x03ffffff, 0x03ffffff}};
static const packedelem64 packedmask25 = {{0x01ffffff, 0x01ffffff}};
-static const packedelem32 packedmask2625 = {{0x3ffffff,0,0x1ffffff,0}};
static const packedelem32 packedmask26262626 = {{0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff}};
static const packedelem32 packedmask25252525 = {{0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff}};
/* multipliers */
static const packedelem64 packednineteen = {{19, 19}};
-static const packedelem64 packednineteenone = {{19, 1}};
static const packedelem64 packedthirtyeight = {{38, 38}};
static const packedelem64 packed3819 = {{19*2,19}};
-static const packedelem64 packed9638 = {{19*4,19*2}};
-
-/* 121666,121665 */
-static const packedelem64 packed121666121665 = {{121666, 121665}};
/* 2*(2^255 - 19) = 0 mod p */
static const packedelem32 packed2p0 = {{0x7ffffda,0x3fffffe,0x7fffffe,0x3fffffe}};
diff --git a/src/ext/ed25519/donna/ed25519-donna-64bit-x86.h b/src/ext/ed25519/donna/ed25519-donna-64bit-x86.h
index 30bd472762..f6b5570298 100644
--- a/src/ext/ed25519/donna/ed25519-donna-64bit-x86.h
+++ b/src/ext/ed25519/donna/ed25519-donna-64bit-x86.h
@@ -2,6 +2,11 @@
#define HAVE_GE25519_SCALARMULT_BASE_CHOOSE_NIELS
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Woverlength-strings"
+#endif
+
DONNA_NOINLINE static void
ge25519_scalarmult_base_choose_niels(ge25519_niels *t, const uint8_t table[256][96], uint32_t pos, signed char b) {
int64_t breg = (int64_t)b;
@@ -347,5 +352,9 @@ ge25519_scalarmult_base_choose_niels(ge25519_niels *t, const uint8_t table[256][
);
}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
#endif /* defined(ED25519_GCC_64BIT_X86_CHOOSE) */
diff --git a/src/ext/ed25519/donna/ed25519-donna-batchverify.h b/src/ext/ed25519/donna/ed25519-donna-batchverify.h
index 43c4923b3e..7c64cce787 100644
--- a/src/ext/ed25519/donna/ed25519-donna-batchverify.h
+++ b/src/ext/ed25519/donna/ed25519-donna-batchverify.h
@@ -188,7 +188,7 @@ ge25519_multi_scalarmult_vartime(ge25519 *r, batch_heap *heap, size_t count) {
}
/* not actually used for anything other than testing */
-unsigned char batch_point_buffer[3][32];
+static unsigned char batch_point_buffer[3][32];
static int
ge25519_is_neutral_vartime(const ge25519 *p) {
diff --git a/src/ext/ed25519/donna/ed25519-donna.h b/src/ext/ed25519/donna/ed25519-donna.h
index 64561d3288..299c8d90fd 100644
--- a/src/ext/ed25519/donna/ed25519-donna.h
+++ b/src/ext/ed25519/donna/ed25519-donna.h
@@ -10,6 +10,16 @@
#include "ed25519-donna-portable.h"
+#include "orconfig.h"
+
+#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
+/* Some of the ASM here is very long strings. */
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Woverlength-strings"
+#else
+#pragma GCC diagnostic ignored "-Woverlength-strings"
+#endif
+#endif
#if defined(ED25519_SSE2)
#else
diff --git a/src/ext/ed25519/donna/ed25519_tor.c b/src/ext/ed25519/donna/ed25519_tor.c
index 52b259dfe1..9537ae66a1 100644
--- a/src/ext/ed25519/donna/ed25519_tor.c
+++ b/src/ext/ed25519/donna/ed25519_tor.c
@@ -34,7 +34,7 @@
#define ED25519_FN2(fn,suffix) ED25519_FN3(fn,suffix)
#define ED25519_FN(fn) ED25519_FN2(fn,ED25519_SUFFIX)
-
+#include "orconfig.h"
#include "ed25519-donna.h"
#include "ed25519_donna_tor.h"
#include "ed25519-randombytes.h"
@@ -44,7 +44,8 @@ typedef unsigned char ed25519_signature[64];
typedef unsigned char ed25519_public_key[32];
typedef unsigned char ed25519_secret_key[32];
-static void gettweak(unsigned char *out, const unsigned char *param);
+static void ed25519_donna_gettweak(unsigned char *out,
+ const unsigned char *param);
static int ED25519_FN(ed25519_sign_open) (const unsigned char *m, size_t mlen,
const ed25519_public_key pk, const ed25519_signature RS);
@@ -242,7 +243,7 @@ ed25519_donna_sign(unsigned char *sig, const unsigned char *m, size_t mlen,
}
static void
-gettweak(unsigned char *out, const unsigned char *param)
+ed25519_donna_gettweak(unsigned char *out, const unsigned char *param)
{
static const char str[] = "Derive temporary signing key";
ed25519_hash_context ctx;
@@ -266,7 +267,7 @@ ed25519_donna_blind_secret_key(unsigned char *out, const unsigned char *inp,
ed25519_hash_context ctx;
bignum256modm ALIGN(16) sk, t;
- gettweak(tweak, param);
+ ed25519_donna_gettweak(tweak, param);
expand256_modm(t, tweak, 32);
expand256_modm(sk, inp, 32);
@@ -297,7 +298,7 @@ ed25519_donna_blind_public_key(unsigned char *out, const unsigned char *inp,
ge25519 ALIGN(16) A, Aprime;
bignum256modm ALIGN(16) t;
- gettweak(tweak, param);
+ ed25519_donna_gettweak(tweak, param);
expand256_modm(t, tweak, 32);
/* No "ge25519_unpack", negate the public key. */
diff --git a/src/ext/ed25519/donna/modm-donna-32bit.h b/src/ext/ed25519/donna/modm-donna-32bit.h
index 5f36df655d..0ef9e58fa1 100644
--- a/src/ext/ed25519/donna/modm-donna-32bit.h
+++ b/src/ext/ed25519/donna/modm-donna-32bit.h
@@ -385,14 +385,14 @@ sub256_modm_batch(bignum256modm out, const bignum256modm a, const bignum256modm
size_t i = 0;
bignum256modm_element_t carry = 0;
switch (limbsize) {
- case 8: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
- case 7: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
- case 6: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
- case 5: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
- case 4: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
- case 3: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
- case 2: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
- case 1: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++;
+ case 8: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
+ case 7: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
+ case 6: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
+ case 5: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
+ case 4: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
+ case 3: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
+ case 2: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
+ case 1: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 31); out[i] &= 0x3fffffff; i++; /* Falls through. */
case 0:
default: out[i] = (a[i] - b[i]) - carry;
}
@@ -403,14 +403,14 @@ sub256_modm_batch(bignum256modm out, const bignum256modm a, const bignum256modm
static int
lt256_modm_batch(const bignum256modm a, const bignum256modm b, size_t limbsize) {
switch (limbsize) {
- case 8: if (a[8] > b[8]) return 0; if (a[8] < b[8]) return 1;
- case 7: if (a[7] > b[7]) return 0; if (a[7] < b[7]) return 1;
- case 6: if (a[6] > b[6]) return 0; if (a[6] < b[6]) return 1;
- case 5: if (a[5] > b[5]) return 0; if (a[5] < b[5]) return 1;
- case 4: if (a[4] > b[4]) return 0; if (a[4] < b[4]) return 1;
- case 3: if (a[3] > b[3]) return 0; if (a[3] < b[3]) return 1;
- case 2: if (a[2] > b[2]) return 0; if (a[2] < b[2]) return 1;
- case 1: if (a[1] > b[1]) return 0; if (a[1] < b[1]) return 1;
+ case 8: if (a[8] > b[8]) return 0; if (a[8] < b[8]) return 1; /* Falls through. */
+ case 7: if (a[7] > b[7]) return 0; if (a[7] < b[7]) return 1; /* Falls through. */
+ case 6: if (a[6] > b[6]) return 0; if (a[6] < b[6]) return 1; /* Falls through. */
+ case 5: if (a[5] > b[5]) return 0; if (a[5] < b[5]) return 1; /* Falls through. */
+ case 4: if (a[4] > b[4]) return 0; if (a[4] < b[4]) return 1; /* Falls through. */
+ case 3: if (a[3] > b[3]) return 0; if (a[3] < b[3]) return 1; /* Falls through. */
+ case 2: if (a[2] > b[2]) return 0; if (a[2] < b[2]) return 1; /* Falls through. */
+ case 1: if (a[1] > b[1]) return 0; if (a[1] < b[1]) return 1; /* Falls through. */
case 0: if (a[0] > b[0]) return 0; if (a[0] < b[0]) return 1;
}
return 0;
@@ -420,14 +420,14 @@ lt256_modm_batch(const bignum256modm a, const bignum256modm b, size_t limbsize)
static int
lte256_modm_batch(const bignum256modm a, const bignum256modm b, size_t limbsize) {
switch (limbsize) {
- case 8: if (a[8] > b[8]) return 0; if (a[8] < b[8]) return 1;
- case 7: if (a[7] > b[7]) return 0; if (a[7] < b[7]) return 1;
- case 6: if (a[6] > b[6]) return 0; if (a[6] < b[6]) return 1;
- case 5: if (a[5] > b[5]) return 0; if (a[5] < b[5]) return 1;
- case 4: if (a[4] > b[4]) return 0; if (a[4] < b[4]) return 1;
- case 3: if (a[3] > b[3]) return 0; if (a[3] < b[3]) return 1;
- case 2: if (a[2] > b[2]) return 0; if (a[2] < b[2]) return 1;
- case 1: if (a[1] > b[1]) return 0; if (a[1] < b[1]) return 1;
+ case 8: if (a[8] > b[8]) return 0; if (a[8] < b[8]) return 1; /* Falls through. */
+ case 7: if (a[7] > b[7]) return 0; if (a[7] < b[7]) return 1; /* Falls through. */
+ case 6: if (a[6] > b[6]) return 0; if (a[6] < b[6]) return 1; /* Falls through. */
+ case 5: if (a[5] > b[5]) return 0; if (a[5] < b[5]) return 1; /* Falls through. */
+ case 4: if (a[4] > b[4]) return 0; if (a[4] < b[4]) return 1; /* Falls through. */
+ case 3: if (a[3] > b[3]) return 0; if (a[3] < b[3]) return 1; /* Falls through. */
+ case 2: if (a[2] > b[2]) return 0; if (a[2] < b[2]) return 1; /* Falls through. */
+ case 1: if (a[1] > b[1]) return 0; if (a[1] < b[1]) return 1; /* Falls through. */
case 0: if (a[0] > b[0]) return 0; if (a[0] < b[0]) return 1;
}
return 1;
diff --git a/src/ext/ed25519/donna/modm-donna-64bit.h b/src/ext/ed25519/donna/modm-donna-64bit.h
index 012ea9ea08..06c98e3039 100644
--- a/src/ext/ed25519/donna/modm-donna-64bit.h
+++ b/src/ext/ed25519/donna/modm-donna-64bit.h
@@ -294,10 +294,10 @@ sub256_modm_batch(bignum256modm out, const bignum256modm a, const bignum256modm
size_t i = 0;
bignum256modm_element_t carry = 0;
switch (limbsize) {
- case 4: out[i] = (a[i] - b[i]) ; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++;
- case 3: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++;
- case 2: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++;
- case 1: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++;
+ case 4: out[i] = (a[i] - b[i]) ; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++; /* Falls through. */
+ case 3: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++; /* Falls through. */
+ case 2: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++; /* Falls through. */
+ case 1: out[i] = (a[i] - b[i]) - carry; carry = (out[i] >> 63); out[i] &= 0xffffffffffffff; i++; /* Falls through. */
case 0:
default: out[i] = (a[i] - b[i]) - carry;
}
@@ -310,10 +310,10 @@ lt256_modm_batch(const bignum256modm a, const bignum256modm b, size_t limbsize)
size_t i = 0;
bignum256modm_element_t t, carry = 0;
switch (limbsize) {
- case 4: t = (a[i] - b[i]) ; carry = (t >> 63); i++;
- case 3: t = (a[i] - b[i]) - carry; carry = (t >> 63); i++;
- case 2: t = (a[i] - b[i]) - carry; carry = (t >> 63); i++;
- case 1: t = (a[i] - b[i]) - carry; carry = (t >> 63); i++;
+ case 4: t = (a[i] - b[i]) ; carry = (t >> 63); i++; /* Falls through. */
+ case 3: t = (a[i] - b[i]) - carry; carry = (t >> 63); i++; /* Falls through. */
+ case 2: t = (a[i] - b[i]) - carry; carry = (t >> 63); i++; /* Falls through. */
+ case 1: t = (a[i] - b[i]) - carry; carry = (t >> 63); i++; /* Falls through. */
case 0: t = (a[i] - b[i]) - carry; carry = (t >> 63);
}
return (int)carry;
@@ -325,10 +325,10 @@ lte256_modm_batch(const bignum256modm a, const bignum256modm b, size_t limbsize)
size_t i = 0;
bignum256modm_element_t t, carry = 0;
switch (limbsize) {
- case 4: t = (b[i] - a[i]) ; carry = (t >> 63); i++;
- case 3: t = (b[i] - a[i]) - carry; carry = (t >> 63); i++;
- case 2: t = (b[i] - a[i]) - carry; carry = (t >> 63); i++;
- case 1: t = (b[i] - a[i]) - carry; carry = (t >> 63); i++;
+ case 4: t = (b[i] - a[i]) ; carry = (t >> 63); i++; /* Falls through. */
+ case 3: t = (b[i] - a[i]) - carry; carry = (t >> 63); i++; /* Falls through. */
+ case 2: t = (b[i] - a[i]) - carry; carry = (t >> 63); i++; /* Falls through. */
+ case 1: t = (b[i] - a[i]) - carry; carry = (t >> 63); i++; /* Falls through. */
case 0: t = (b[i] - a[i]) - carry; carry = (t >> 63);
}
return (int)!carry;
diff --git a/src/ext/ed25519/ref10/blinding.c b/src/ext/ed25519/ref10/blinding.c
index 4d9a9cbbe7..ee3e8666fa 100644
--- a/src/ext/ed25519/ref10/blinding.c
+++ b/src/ext/ed25519/ref10/blinding.c
@@ -10,7 +10,7 @@
#include "crypto.h"
static void
-gettweak(unsigned char *out, const unsigned char *param)
+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);
@@ -26,7 +26,7 @@ int ed25519_ref10_blind_secret_key(unsigned char *out,
const char str[] = "Derive temporary signing key hash input";
unsigned char tweak[64];
unsigned char zero[32];
- gettweak(tweak, param);
+ ed25519_ref10_gettweak(tweak, param);
memset(zero, 0, 32);
sc_muladd(out, inp, tweak, zero);
@@ -50,7 +50,7 @@ int ed25519_ref10_blind_public_key(unsigned char *out,
ge_p3 A;
ge_p2 Aprime;
- gettweak(tweak, param);
+ ed25519_ref10_gettweak(tweak, param);
memset(zero, 0, sizeof(zero));
/* Not the greatest implementation of all of this. I wish I had
diff --git a/src/ext/ed25519/ref10/keypair.c b/src/ext/ed25519/ref10/keypair.c
index 7ddbaa971e..68a88f9adc 100644
--- a/src/ext/ed25519/ref10/keypair.c
+++ b/src/ext/ed25519/ref10/keypair.c
@@ -1,6 +1,7 @@
/* Modified for Tor: new API, 64-byte secret keys. */
-#include <string.h>
+
#include "randombytes.h"
+#include <string.h>
#include "crypto_sign.h"
#include "crypto_hash_sha512.h"
#include "ge.h"
diff --git a/src/ext/ed25519/ref10/open.c b/src/ext/ed25519/ref10/open.c
index 9dbeb4cdd0..3ab7b7d6e7 100644
--- a/src/ext/ed25519/ref10/open.c
+++ b/src/ext/ed25519/ref10/open.c
@@ -1,6 +1,7 @@
/* (Modified by Tor to verify signature separately from message) */
-#include <string.h>
+
#include "crypto_sign.h"
+#include <string.h>
#include "crypto_hash_sha512.h"
#include "crypto_verify_32.h"
#include "ge.h"
diff --git a/src/ext/eventdns.c b/src/ext/eventdns.c
deleted file mode 100644
index fc5657cbb4..0000000000
--- a/src/ext/eventdns.c
+++ /dev/null
@@ -1,3518 +0,0 @@
-/* READ THIS COMMENT BEFORE HACKING THIS FILE.
- *
- * This eventdns.c copy has diverged a bit from Libevent's version, and it's
- * no longer easy to resynchronize them. Once Tor requires Libevent 2.0, we
- * will just dump this file and use Libevent's evdns code.
- *
- * Therefore, you probably shouldn't make any change here without making it to
- * Libevent as well: it's not good for the implementation to diverge even
- * more. Also, we can't shouldn't wantonly the API here (since Libevent APIs
- * can't change in ways that break user behavior). Also, we shouldn't bother
- * with cosmetic changes: the whole module is slated for demolition, so
- * there's no point dusting the linebreaks or re-painting the parser.
- *
- * (We can't just drop the Libevent 2.0 evdns implementation in here instead,
- * since it depends pretty heavily on parts of Libevent 2.0.)
- */
-
-/* Async DNS Library
- * Adam Langley <agl@imperialviolet.org>
- * Public Domain code
- *
- * This software is Public Domain. To view a copy of the public domain dedication,
- * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
- * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
- *
- * I ask and expect, but do not require, that all derivative works contain an
- * attribution similar to:
- * Parts developed by Adam Langley <agl@imperialviolet.org>
- *
- * You may wish to replace the word "Parts" with something else depending on
- * the amount of original code.
- *
- * (Derivative works does not include programs which link against, run or include
- * the source verbatim in their source distributions)
- *
- * Version: 0.1b
- */
-
-#include "eventdns_tor.h"
-#include "util.h"
-#include <sys/types.h>
-/* #define NDEBUG */
-
-#ifndef DNS_USE_CPU_CLOCK_FOR_ID
-#ifndef DNS_USE_GETTIMEOFDAY_FOR_ID
-#ifndef DNS_USE_OPENSSL_FOR_ID
-#error Must configure at least one id generation method.
-#error Please see the documentation.
-#endif
-#endif
-#endif
-
-/* #define _POSIX_C_SOURCE 200507 */
-#define _GNU_SOURCE
-
-#ifdef DNS_USE_CPU_CLOCK_FOR_ID
-#ifdef DNS_USE_OPENSSL_FOR_ID
-#error Multiple id options selected
-#endif
-#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
-#error Multiple id options selected
-#endif
-#include <time.h>
-#endif
-
-#ifdef DNS_USE_OPENSSL_FOR_ID
-#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
-#error Multiple id options selected
-#endif
-#include <openssl/rand.h>
-#endif
-
-#include <string.h>
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#include <stdlib.h>
-#include <errno.h>
-#include <assert.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#include <sys/stat.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "eventdns.h"
-
-#ifdef _WIN32
-#include <windows.h>
-#include <winsock2.h>
-#include <iphlpapi.h>
-#else
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif
-
-#ifdef HAVE_NETINET_IN6_H
-#include <netinet/in6.h>
-#endif
-
-#ifdef _WIN32
-typedef int socklen_t;
-#endif
-
-#define EVDNS_LOG_DEBUG 0
-#define EVDNS_LOG_WARN 1
-
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX 255
-#endif
-
-#ifndef NDEBUG
-#include <stdio.h>
-#endif
-
-/* for debugging possible memory leaks. */
-#define mm_malloc(x) tor_malloc(x)
-#define mm_realloc(x,y) tor_realloc((x),(y))
-#define mm_free(x) tor_free(x)
-#define mm_strdup(x) tor_strdup(x)
-#define _mm_free(x) tor_free_(x)
-
-#undef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
-
-#if 0
-#ifdef __USE_ISOC99B
-/* libevent doesn't work without this */
-typedef uint8_t u_char;
-typedef unsigned int uint;
-#endif
-#endif
-#include <event.h>
-
-#define u64 uint64_t
-#define u32 uint32_t
-#define u16 uint16_t
-#define u8 uint8_t
-
-#define MAX_ADDRS 4 /* maximum number of addresses from a single packet */
-/* which we bother recording */
-
-#define TYPE_A EVDNS_TYPE_A
-#define TYPE_CNAME 5
-#define TYPE_PTR EVDNS_TYPE_PTR
-#define TYPE_AAAA EVDNS_TYPE_AAAA
-
-#define CLASS_INET EVDNS_CLASS_INET
-
-#define CLEAR(x) do { memset((x), 0xF0, sizeof(*(x))); } while(0)
-
-struct evdns_request {
- u8 *request; /* the dns packet data */
- unsigned int request_len;
- int reissue_count;
- int tx_count; /* the number of times that this packet has been sent */
- unsigned int request_type; /* TYPE_PTR or TYPE_A */
- void *user_pointer; /* the pointer given to us for this request */
- evdns_callback_type user_callback;
- struct nameserver *ns; /* the server which we last sent it */
-
- /* elements used by the searching code */
- int search_index;
- struct search_state *search_state;
- char *search_origname; /* needs to be mm_free()ed */
- int search_flags;
-
- /* these objects are kept in a circular list */
- struct evdns_request *next, *prev;
-
- struct event timeout_event;
-
- u16 trans_id; /* the transaction id */
- char request_appended; /* true if the request pointer is data which follows this struct */
- char transmit_me; /* needs to be transmitted */
-};
-
-#ifndef HAVE_STRUCT_IN6_ADDR
-struct in6_addr {
- u8 s6_addr[16];
-};
-#endif
-
-struct reply {
- unsigned int type;
- unsigned int have_answer;
- union {
- struct {
- u32 addrcount;
- u32 addresses[MAX_ADDRS];
- } a;
- struct {
- u32 addrcount;
- struct in6_addr addresses[MAX_ADDRS];
- } aaaa;
- struct {
- char name[HOST_NAME_MAX];
- } ptr;
- } data;
-};
-
-struct nameserver {
- int socket; /* a connected UDP socket */
- struct sockaddr_storage address;
- int failed_times; /* number of times which we have given this server a chance */
- int timedout; /* number of times in a row a request has timed out */
- struct event event;
- /* these objects are kept in a circular list */
- struct nameserver *next, *prev;
- struct event timeout_event; /* used to keep the timeout for */
- /* when we next probe this server. */
- /* Valid if state == 0 */
- char state; /* zero if we think that this server is down */
- char choked; /* true if we have an EAGAIN from this server's socket */
- char write_waiting; /* true if we are waiting for EV_WRITE events */
-};
-
-static struct evdns_request *req_head = NULL, *req_waiting_head = NULL;
-static struct nameserver *server_head = NULL;
-
-/* Represents a local port where we're listening for DNS requests. Right now, */
-/* only UDP is supported. */
-struct evdns_server_port {
- int socket; /* socket we use to read queries and write replies. */
- int refcnt; /* reference count. */
- char choked; /* Are we currently blocked from writing? */
- char closing; /* Are we trying to close this port, pending writes? */
- evdns_request_callback_fn_type user_callback; /* Fn to handle requests */
- void *user_data; /* Opaque pointer passed to user_callback */
- struct event event; /* Read/write event */
- /* circular list of replies that we want to write. */
- struct server_request *pending_replies;
-};
-
-/* Represents part of a reply being built. (That is, a single RR.) */
-struct server_reply_item {
- struct server_reply_item *next; /* next item in sequence. */
- char *name; /* name part of the RR */
- u16 type : 16; /* The RR type */
- u16 class : 16; /* The RR class (usually CLASS_INET) */
- u32 ttl; /* The RR TTL */
- char is_name; /* True iff data is a label */
- u16 datalen; /* Length of data; -1 if data is a label */
- void *data; /* The contents of the RR */
-};
-
-/* Represents a request that we've received as a DNS server, and holds */
-/* the components of the reply as we're constructing it. */
-struct server_request {
- /* Pointers to the next and previous entries on the list of replies */
- /* that we're waiting to write. Only set if we have tried to respond */
- /* and gotten EAGAIN. */
- struct server_request *next_pending;
- struct server_request *prev_pending;
-
- u16 trans_id; /* Transaction id. */
- struct evdns_server_port *port; /* Which port received this request on? */
- struct sockaddr_storage addr; /* Where to send the response */
- socklen_t addrlen; /* length of addr */
-
- int n_answer; /* how many answer RRs have been set? */
- int n_authority; /* how many authority RRs have been set? */
- int n_additional; /* how many additional RRs have been set? */
-
- struct server_reply_item *answer; /* linked list of answer RRs */
- struct server_reply_item *authority; /* linked list of authority RRs */
- struct server_reply_item *additional; /* linked list of additional RRs */
-
- /* Constructed response. Only set once we're ready to send a reply. */
- /* Once this is set, the RR fields are cleared, and no more should be set. */
- char *response;
- size_t response_len;
-
- /* Caller-visible fields: flags, questions. */
- struct evdns_server_request base;
-};
-
-/* helper macro */
-#define OFFSET_OF(st, member) ((off_t) (((char*)&((st*)0)->member)-(char*)0))
-
-/* Given a pointer to an evdns_server_request, get the corresponding */
-/* server_request. */
-#define TO_SERVER_REQUEST(base_ptr) \
- ((struct server_request*) \
- (((char*)(base_ptr) - OFFSET_OF(struct server_request, base))))
-
-/* The number of good nameservers that we have */
-static int global_good_nameservers = 0;
-
-/* inflight requests are contained in the req_head list */
-/* and are actually going out across the network */
-static int global_requests_inflight = 0;
-/* requests which aren't inflight are in the waiting list */
-/* and are counted here */
-static int global_requests_waiting = 0;
-
-static int global_max_requests_inflight = 64;
-
-static struct timeval global_timeout = {5, 0}; /* 5 seconds */
-static int global_max_reissues = 1; /* a reissue occurs when we get some errors from the server */
-static int global_max_retransmits = 3; /* number of times we'll retransmit a request which timed out */
-/* number of timeouts in a row before we consider this server to be down */
-static int global_max_nameserver_timeout = 3;
-
-/* true iff we should use the 0x20 hack. */
-static int global_randomize_case = 1;
-
-/* These are the timeout values for nameservers. If we find a nameserver is down */
-/* we try to probe it at intervals as given below. Values are in seconds. */
-static const struct timeval global_nameserver_timeouts[] = {{10, 0}, {60, 0}, {300, 0}, {900, 0}, {3600, 0}};
-static const int global_nameserver_timeouts_length = (int)(sizeof(global_nameserver_timeouts)/sizeof(struct timeval));
-
-static struct nameserver *nameserver_pick(void);
-static void evdns_request_insert(struct evdns_request *req, struct evdns_request **head);
-static void nameserver_ready_callback(int fd, short events, void *arg);
-static int evdns_transmit(void);
-static int evdns_request_transmit(struct evdns_request *req);
-static void nameserver_send_probe(struct nameserver *const ns);
-static void search_request_finished(struct evdns_request *const);
-static int search_try_next(struct evdns_request *const req);
-static int search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg);
-static void evdns_requests_pump_waiting_queue(void);
-static u16 transaction_id_pick(void);
-static struct evdns_request *request_new(int type, const char *name, int flags, evdns_callback_type callback, void *ptr);
-static void request_submit(struct evdns_request *req);
-
-static int server_request_free(struct server_request *req);
-static void server_request_free_answers(struct server_request *req);
-static void server_port_free(struct evdns_server_port *port);
-static void server_port_ready_callback(int fd, short events, void *arg);
-
-static int strtoint(const char *const str);
-
-#ifdef _WIN32
-static int
-last_error(int sock)
-{
- int optval, optvallen=sizeof(optval);
- int err = WSAGetLastError();
- if (err == WSAEWOULDBLOCK && sock >= 0) {
- if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&optval,
- &optvallen))
- return err;
- if (optval)
- return optval;
- }
- return err;
-
-}
-static int
-error_is_eagain(int err)
-{
- return err == EAGAIN || err == WSAEWOULDBLOCK;
-}
-#define inet_aton(c, addr) tor_inet_aton((c), (addr))
-#define CLOSE_SOCKET(x) closesocket(x)
-#else
-#define last_error(sock) (errno)
-#if EAGAIN != EWOULDBLOCK
-#define error_is_eagain(err) ((err) == EAGAIN || (err) == EWOULDBLOCK)
-#else
-#define error_is_eagain(err) ((err) == EAGAIN)
-#endif
-#define CLOSE_SOCKET(x) close(x)
-#endif
-
-#define ISSPACE(c) TOR_ISSPACE(c)
-#define ISDIGIT(c) TOR_ISDIGIT(c)
-#define ISALPHA(c) TOR_ISALPHA(c)
-#define TOLOWER(c) TOR_TOLOWER(c)
-#define TOUPPER(c) TOR_TOUPPER(c)
-
-#ifndef NDEBUG
-static const char *
-debug_ntoa(u32 address)
-{
- static char buf[32];
- u32 a = ntohl(address);
- tor_snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
- (int)(u8)((a>>24)&0xff),
- (int)(u8)((a>>16)&0xff),
- (int)(u8)((a>>8 )&0xff),
- (int)(u8)((a )&0xff));
- return buf;
-}
-static const char *
-debug_ntop(const struct sockaddr *sa)
-{
- if (sa->sa_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in *) sa;
- return debug_ntoa(sin->sin_addr.s_addr);
- }
- if (sa->sa_family == AF_INET6) {
- /* Tor-specific. In libevent, add more check code. */
- static char buf[128];
- struct sockaddr_in6 *sin = (struct sockaddr_in6 *) sa;
- tor_inet_ntop(AF_INET6, &sin->sin6_addr, buf, sizeof(buf));
- return buf;
- }
- return "<unknown>";
-}
-#endif
-
-static evdns_debug_log_fn_type evdns_log_fn = NULL;
-
-void
-evdns_set_log_fn(evdns_debug_log_fn_type fn)
-{
- evdns_log_fn = fn;
-}
-
-#ifdef __GNUC__
-#define EVDNS_LOG_CHECK __attribute__ ((format(printf, 2, 3)))
-#else
-#define EVDNS_LOG_CHECK
-#endif
-
-static void evdns_log(int warn, const char *fmt, ...) EVDNS_LOG_CHECK;
-static void
-evdns_log(int warn, const char *fmt, ...)
-{
- va_list args;
- static char buf[512];
- if (!evdns_log_fn)
- return;
- va_start(args,fmt);
- tor_vsnprintf(buf, sizeof(buf), fmt, args);
- evdns_log_fn(warn, buf);
- va_end(args);
-}
-
-static int
-sockaddr_eq(const struct sockaddr *sa1, const struct sockaddr *sa2,
- int include_port)
-{
- if (sa1->sa_family != sa2->sa_family)
- return 0;
- if (sa1->sa_family == AF_INET) {
- const struct sockaddr_in *sin1, *sin2;
- sin1 = (const struct sockaddr_in *)sa1;
- sin2 = (const struct sockaddr_in *)sa2;
- if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr)
- return 0;
- else if (include_port && sin1->sin_port != sin2->sin_port)
- return 0;
- else
- return 1;
- }
-#ifdef AF_INET6
- if (sa1->sa_family == AF_INET6) {
- const struct sockaddr_in6 *sin1, *sin2;
- sin1 = (const struct sockaddr_in6 *)sa1;
- sin2 = (const struct sockaddr_in6 *)sa2;
- if (tor_memneq(sin1->sin6_addr.s6_addr, sin2->sin6_addr.s6_addr, 16))
- return 0;
- else if (include_port && sin1->sin6_port != sin2->sin6_port)
- return 0;
- else
- return 1;
- }
-#endif
- return 1;
-}
-
-#define add_timeout_event(s, to) \
- (event_add(&(s)->timeout_event, (to)))
-#define del_timeout_event(s) \
- (event_del(&(s)->timeout_event))
-
-/* This walks the list of inflight requests to find the */
-/* one with a matching transaction id. Returns NULL on */
-/* failure */
-static struct evdns_request *
-request_find_from_trans_id(u16 trans_id) {
- struct evdns_request *req = req_head, *const started_at = req_head;
-
- if (req) {
- do {
- if (req->trans_id == trans_id) return req;
- req = req->next;
- } while (req != started_at);
- }
-
- return NULL;
-}
-
-/* a libevent callback function which is called when a nameserver */
-/* has gone down and we want to test if it has came back to life yet */
-static void
-nameserver_prod_callback(int fd, short events, void *arg) {
- struct nameserver *const ns = (struct nameserver *) arg;
- (void)fd;
- (void)events;
-
- nameserver_send_probe(ns);
-}
-
-/* a libevent callback which is called when a nameserver probe (to see if */
-/* it has come back to life) times out. We increment the count of failed_times */
-/* and wait longer to send the next probe packet. */
-static void
-nameserver_probe_failed(struct nameserver *const ns) {
- const struct timeval * timeout;
- del_timeout_event(ns);
-
- if (ns->state == 1) {
- /* This can happen if the nameserver acts in a way which makes us mark */
- /* it as bad and then starts sending good replies. */
- return;
- }
-
- timeout =
- &global_nameserver_timeouts[MIN(ns->failed_times,
- global_nameserver_timeouts_length - 1)];
- ns->failed_times++;
-
- if (add_timeout_event(ns, (struct timeval *) timeout) < 0) {
- evdns_log(EVDNS_LOG_WARN,
- "Error from libevent when adding timer event for %s",
- debug_ntop((struct sockaddr *)&ns->address));
- /* ???? Do more? */
- }
-}
-
-/* called when a nameserver has been deemed to have failed. For example, too */
-/* many packets have timed out etc */
-static void
-nameserver_failed(struct nameserver *const ns, const char *msg) {
- struct evdns_request *req, *started_at;
- /* if this nameserver has already been marked as failed */
- /* then don't do anything */
- if (!ns->state) return;
-
- evdns_log(EVDNS_LOG_WARN, "Nameserver %s has failed: %s",
- debug_ntop((struct sockaddr *)&ns->address), msg);
- global_good_nameservers--;
- assert(global_good_nameservers >= 0);
- if (global_good_nameservers == 0) {
- evdns_log(EVDNS_LOG_WARN, "All nameservers have failed");
- }
-
- ns->state = 0;
- ns->failed_times = 1;
-
- if (add_timeout_event(ns, (struct timeval *) &global_nameserver_timeouts[0]) < 0) {
- evdns_log(EVDNS_LOG_WARN,
- "Error from libevent when adding timer event for %s",
- debug_ntop((struct sockaddr *)&ns->address));
- /* ???? Do more? */
- }
-
- /* walk the list of inflight requests to see if any can be reassigned to */
- /* a different server. Requests in the waiting queue don't have a */
- /* nameserver assigned yet */
-
- /* if we don't have *any* good nameservers then there's no point */
- /* trying to reassign requests to one */
- if (!global_good_nameservers) return;
-
- req = req_head;
- started_at = req_head;
- if (req) {
- do {
- if (req->tx_count == 0 && req->ns == ns) {
- /* still waiting to go out, can be moved */
- /* to another server */
- req->ns = nameserver_pick();
- }
- req = req->next;
- } while (req != started_at);
- }
-}
-
-static void
-nameserver_up(struct nameserver *const ns) {
- if (ns->state) return;
- evdns_log(EVDNS_LOG_WARN, "Nameserver %s is back up",
- debug_ntop((struct sockaddr *)&ns->address));
- del_timeout_event(ns);
- ns->state = 1;
- ns->failed_times = 0;
- ns->timedout = 0;
- global_good_nameservers++;
-}
-
-static void
-request_trans_id_set(struct evdns_request *const req, const u16 trans_id) {
- req->trans_id = trans_id;
- *((u16 *) req->request) = htons(trans_id);
-}
-
-/* Called to remove a request from a list and dealloc it. */
-/* head is a pointer to the head of the list it should be */
-/* removed from or NULL if the request isn't in a list. */
-static void
-request_finished(struct evdns_request *const req, struct evdns_request **head) {
- if (head) {
- if (req->next == req) {
- /* only item in the list */
- *head = NULL;
- } else {
- req->next->prev = req->prev;
- req->prev->next = req->next;
- if (*head == req) *head = req->next;
- }
- }
-
- evdns_log(EVDNS_LOG_DEBUG, "Removing timeout for request %lx",
- (unsigned long) req);
- del_timeout_event(req);
-
- search_request_finished(req);
- global_requests_inflight--;
-
- if (!req->request_appended) {
- /* need to free the request data on it's own */
- mm_free(req->request);
- } else {
- /* the request data is appended onto the header */
- /* so everything gets mm_free()ed when we: */
- }
-
- CLEAR(req);
- _mm_free(req);
-
- evdns_requests_pump_waiting_queue();
-}
-
-/* This is called when a server returns a funny error code. */
-/* We try the request again with another server. */
-/* */
-/* return: */
-/* 0 ok */
-/* 1 failed/reissue is pointless */
-static int
-request_reissue(struct evdns_request *req) {
- const struct nameserver *const last_ns = req->ns;
- /* the last nameserver should have been marked as failing */
- /* by the caller of this function, therefore pick will try */
- /* not to return it */
- req->ns = nameserver_pick();
- if (req->ns == last_ns) {
- /* ... but pick did return it */
- /* not a lot of point in trying again with the */
- /* same server */
- return 1;
- }
-
- req->reissue_count++;
- req->tx_count = 0;
- req->transmit_me = 1;
-
- return 0;
-}
-
-/* this function looks for space on the inflight queue and promotes */
-/* requests from the waiting queue if it can. */
-static void
-evdns_requests_pump_waiting_queue(void) {
- while (global_requests_inflight < global_max_requests_inflight &&
- global_requests_waiting) {
- struct evdns_request *req;
- /* move a request from the waiting queue to the inflight queue */
- assert(req_waiting_head);
- if (req_waiting_head->next == req_waiting_head) {
- /* only one item in the queue */
- req = req_waiting_head;
- req_waiting_head = NULL;
- } else {
- req = req_waiting_head;
- req->next->prev = req->prev;
- req->prev->next = req->next;
- req_waiting_head = req->next;
- }
-
- global_requests_waiting--;
- global_requests_inflight++;
-
- req->ns = nameserver_pick();
- request_trans_id_set(req, transaction_id_pick());
-
- evdns_request_insert(req, &req_head);
- evdns_request_transmit(req);
- evdns_transmit();
- }
-}
-
-static void
-reply_callback(struct evdns_request *const req, u32 ttl, u32 err, struct reply *reply) {
- switch (req->request_type) {
- case TYPE_A:
- if (reply)
- req->user_callback(DNS_ERR_NONE, DNS_IPv4_A,
- reply->data.a.addrcount, ttl,
- reply->data.a.addresses,
- req->user_pointer);
- else
- req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
- return;
- case TYPE_PTR:
- if (reply) {
- char *name = reply->data.ptr.name;
- req->user_callback(DNS_ERR_NONE, DNS_PTR, 1, ttl,
- &name, req->user_pointer);
- } else {
- req->user_callback(err, 0, 0, 0, NULL,
- req->user_pointer);
- }
- return;
- case TYPE_AAAA:
- if (reply)
- req->user_callback(DNS_ERR_NONE, DNS_IPv6_AAAA,
- reply->data.aaaa.addrcount, ttl,
- reply->data.aaaa.addresses,
- req->user_pointer);
- else
- req->user_callback(err, 0, 0, 0, NULL, req->user_pointer);
- return;
- }
- assert(0);
-}
-
-/* this processes a parsed reply packet */
-static void
-reply_handle(struct evdns_request *const req, u16 flags, u32 ttl, struct reply *reply) {
- int error;
- static const int error_codes[] = {DNS_ERR_FORMAT, DNS_ERR_SERVERFAILED, DNS_ERR_NOTEXIST, DNS_ERR_NOTIMPL, DNS_ERR_REFUSED};
-
- if (flags & 0x020f || !reply || !reply->have_answer) {
- /* there was an error */
- if (flags & 0x0200) {
- error = DNS_ERR_TRUNCATED;
- } else {
- u16 error_code = (flags & 0x000f) - 1;
- if (error_code > 4) {
- error = DNS_ERR_UNKNOWN;
- } else {
- error = error_codes[error_code];
- }
- }
-
- switch(error) {
- case DNS_ERR_NOTIMPL:
- case DNS_ERR_REFUSED:
- /* we regard these errors as marking a bad nameserver */
- if (req->reissue_count < global_max_reissues) {
- char msg[64];
- tor_snprintf(msg, sizeof(msg), "Bad response %d (%s)",
- error, evdns_err_to_string(error));
- nameserver_failed(req->ns, msg);
- if (!request_reissue(req)) return;
- }
- break;
- case DNS_ERR_SERVERFAILED:
- /* rcode 2 (servfailed) sometimes means "we are broken" and
- * sometimes (with some binds) means "that request was very
- * confusing." Treat this as a timeout, not a failure.
- */
- /*XXXX refactor the parts of */
- evdns_log(EVDNS_LOG_DEBUG, "Got a SERVERFAILED from nameserver %s; "
- "will allow the request to time out.",
- debug_ntop((struct sockaddr *)&req->ns->address));
- break;
- default:
- /* we got a good reply from the nameserver */
- nameserver_up(req->ns);
- }
-
- if (req->search_state && req->request_type != TYPE_PTR) {
- /* if we have a list of domains to search in, try the next one */
- if (!search_try_next(req)) {
- /* a new request was issued so this request is finished and */
- /* the user callback will be made when that request (or a */
- /* child of it) finishes. */
- request_finished(req, &req_head);
- return;
- }
- }
-
- /* all else failed. Pass the failure up */
- reply_callback(req, 0, error, NULL);
- request_finished(req, &req_head);
- } else {
- /* all ok, tell the user */
- reply_callback(req, ttl, 0, reply);
- nameserver_up(req->ns);
- request_finished(req, &req_head);
- }
-}
-
-static inline int
-name_parse(u8 *packet, int length, int *idx, char *name_out, size_t name_out_len) {
- int name_end = -1;
- int j = *idx;
- int ptr_count = 0;
-#define GET32(x) do { if (j + 4 > length) goto err; memcpy(&_t32, packet + j, 4); j += 4; x = ntohl(_t32); } while(0)
-#define GET16(x) do { if (j + 2 > length) goto err; memcpy(&_t, packet + j, 2); j += 2; x = ntohs(_t); } while(0)
-#define GET8(x) do { if (j >= length) goto err; x = packet[j++]; } while(0)
-
- char *cp = name_out;
- const char *const end = name_out + name_out_len;
-
- /* Normally, names are a series of length prefixed strings terminated */
- /* with a length of 0 (the lengths are u8's < 63). */
- /* However, the length can start with a pair of 1 bits and that */
- /* means that the next 14 bits are a pointer within the current */
- /* packet. */
-
- for(;;) {
- u8 label_len;
- if (j >= length) return -1;
- GET8(label_len);
- if (!label_len) break;
- if (label_len & 0xc0) {
- u8 ptr_low;
- GET8(ptr_low);
- if (name_end < 0) name_end = j;
- j = (((int)label_len & 0x3f) << 8) + ptr_low;
- /* Make sure that the target offset is in-bounds. */
- if (j < 0 || j >= length) return -1;
- /* If we've jumped more times than there are characters in the
- * message, we must have a loop. */
- if (++ptr_count > length) return -1;
- continue;
- }
- if (label_len > 63) return -1;
- if (cp != name_out) {
- if (cp >= name_out + name_out_len - 1) return -1;
- *cp++ = '.';
- }
- if (label_len > name_out_len ||
- cp >= name_out + name_out_len - label_len) return -1;
- memcpy(cp, packet + j, label_len);
- cp += label_len;
- j += label_len;
- }
- if (cp >= end) return -1;
- *cp = '\0';
- if (name_end < 0)
- *idx = j;
- else
- *idx = name_end;
- return 0;
- err:
- return -1;
-}
-
-/* parses a raw reply from a nameserver. */
-static int
-reply_parse(u8 *packet, int length) {
- int j = 0; /* index into packet */
- int k;
- u16 _t; /* used by the macros */
- u32 _t32; /* used by the macros */
- char tmp_name[256], cmp_name[256]; /* used by the macros */
-
- u16 trans_id, questions, answers, authority, additional, datalength;
- u16 flags = 0;
- u32 ttl, ttl_r = 0xffffffff;
- struct reply reply;
- struct evdns_request *req = NULL;
- unsigned int i;
- int name_matches = 0;
-
- GET16(trans_id);
- GET16(flags);
- GET16(questions);
- GET16(answers);
- GET16(authority);
- GET16(additional);
- (void) authority; /* suppress "unused variable" warnings. */
- (void) additional; /* suppress "unused variable" warnings. */
-
- req = request_find_from_trans_id(trans_id);
- /* if no request, can't do anything. */
- if (!req) return -1;
-
- memset(&reply, 0, sizeof(reply));
-
- /* If it's not an answer, it doesn't go with any of our requests. */
- if (!(flags & 0x8000)) return -1; /* must be an answer */
- if (flags & 0x020f) {
- /* there was an error */
- goto err;
- }
- /* if (!answers) return; */ /* must have an answer of some form */
-
- /* This macro skips a name in the DNS reply. */
-#define GET_NAME \
- do { tmp_name[0] = '\0'; \
- if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0)\
- goto err; \
- } while(0)
-#define TEST_NAME \
- do { tmp_name[0] = '\0'; \
- cmp_name[0] = '\0'; \
- k = j; \
- if (name_parse(packet, length, &j, tmp_name, sizeof(tmp_name))<0)\
- goto err; \
- if (name_parse(req->request, req->request_len, &k, cmp_name, sizeof(cmp_name))<0) \
- goto err; \
- if (global_randomize_case) { \
- if (strcmp(tmp_name, cmp_name) == 0) \
- name_matches = 1; /* we ignore mismatching names */ \
- } else { \
- if (strcasecmp(tmp_name, cmp_name) == 0) \
- name_matches = 1; \
- } \
- } while(0)
-
- reply.type = req->request_type;
-
- /* skip over each question in the reply */
- for (i = 0; i < questions; ++i) {
- /* the question looks like
- * <label:name><u16:type><u16:class>
- */
- TEST_NAME;
- j += 4;
- if (j >= length) goto err;
- }
-
- if (!name_matches)
- goto err;
-
- /* now we have the answer section which looks like
- * <label:name><u16:type><u16:class><u32:ttl><u16:len><data...>
- */
-
- for (i = 0; i < answers; ++i) {
- u16 type, class;
-
- GET_NAME;
- GET16(type);
- GET16(class);
- GET32(ttl);
- GET16(datalength);
-
- if (type == TYPE_A && class == CLASS_INET) {
- int addrcount, addrtocopy;
- if (req->request_type != TYPE_A) {
- j += datalength; continue;
- }
- if ((datalength & 3) != 0) /* not an even number of As. */
- goto err;
- addrcount = datalength >> 2;
- addrtocopy = MIN(MAX_ADDRS - reply.data.a.addrcount, (unsigned)addrcount);
-
- ttl_r = MIN(ttl_r, ttl);
- /* we only bother with the first four addresses. */
- if (j + 4*addrtocopy > length) goto err;
- memcpy(&reply.data.a.addresses[reply.data.a.addrcount],
- packet + j, 4*addrtocopy);
- reply.data.a.addrcount += addrtocopy;
- reply.have_answer = 1;
- if (reply.data.a.addrcount == MAX_ADDRS) break;
- j += 4*addrtocopy;
- } else if (type == TYPE_PTR && class == CLASS_INET) {
- if (req->request_type != TYPE_PTR) {
- j += datalength; continue;
- }
- GET_NAME;
- strlcpy(reply.data.ptr.name, tmp_name,
- sizeof(reply.data.ptr.name));
- ttl_r = MIN(ttl_r, ttl);
- reply.have_answer = 1;
- break;
- } else if (type == TYPE_AAAA && class == CLASS_INET) {
- int addrcount, addrtocopy;
- if (req->request_type != TYPE_AAAA) {
- j += datalength; continue;
- }
- if ((datalength & 15) != 0) /* not an even number of AAAAs. */
- goto err;
- addrcount = datalength >> 4; /* each address is 16 bytes long */
- addrtocopy = MIN(MAX_ADDRS - reply.data.aaaa.addrcount, (unsigned)addrcount);
- ttl_r = MIN(ttl_r, ttl);
-
- /* we only bother with the first four addresses. */
- if (j + 16*addrtocopy > length) goto err;
- memcpy(&reply.data.aaaa.addresses[reply.data.aaaa.addrcount],
- packet + j, 16*addrtocopy);
- reply.data.aaaa.addrcount += addrtocopy;
- reply.have_answer = 1;
- if (reply.data.aaaa.addrcount == MAX_ADDRS) break;
- j += 16*addrtocopy;
- } else {
- /* skip over any other type of resource */
- j += datalength;
- }
- }
-
- reply_handle(req, flags, ttl_r, &reply);
- return 0;
- err:
- if (req)
- reply_handle(req, flags, 0, NULL);
- return -1;
-}
-
-/* Parse a raw request (packet,length) sent to a nameserver port (port) from */
-/* a DNS client (addr,addrlen), and if it's well-formed, call the corresponding */
-/* callback. */
-static int
-request_parse(u8 *packet, ssize_t length, struct evdns_server_port *port, struct sockaddr *addr, socklen_t addrlen)
-{
- int j = 0; /* index into packet */
- u16 _t; /* used by the macros */
- char tmp_name[256]; /* used by the macros */
-
- int i;
- u16 trans_id, flags, questions, answers, authority, additional;
- struct server_request *server_req = NULL;
-
- /* Get the header fields */
- GET16(trans_id);
- GET16(flags);
- GET16(questions);
- GET16(answers);
- GET16(authority);
- GET16(additional);
- (void)additional;
- (void)authority;
- (void)answers;
-
- if (flags & 0x8000) return -1; /* Must not be an answer. */
- flags &= 0x0110; /* Only RD and CD get preserved. */
-
- if (length > INT_MAX)
- return -1;
-
- server_req = mm_malloc(sizeof(struct server_request));
- if (server_req == NULL) return -1;
- memset(server_req, 0, sizeof(struct server_request));
-
- server_req->trans_id = trans_id;
- memcpy(&server_req->addr, addr, addrlen);
- server_req->addrlen = addrlen;
-
- server_req->base.flags = flags;
- server_req->base.nquestions = 0;
- server_req->base.questions = mm_malloc(sizeof(struct evdns_server_question *) * questions);
- if (server_req->base.questions == NULL)
- goto err;
-
- for (i = 0; i < questions; ++i) {
- u16 type, class;
- struct evdns_server_question *q;
- size_t namelen;
- if (name_parse(packet, (int)length, &j, tmp_name, sizeof(tmp_name))<0)
- goto err;
- GET16(type);
- GET16(class);
- namelen = strlen(tmp_name);
- q = mm_malloc(sizeof(struct evdns_server_question) + namelen);
- if (!q)
- goto err;
- q->type = type;
- q->dns_question_class = class;
- memcpy(q->name, tmp_name, namelen+1);
- server_req->base.questions[server_req->base.nquestions++] = q;
- }
-
- /* Ignore answers, authority, and additional. */
-
- server_req->port = port;
- port->refcnt++;
-
- /* Only standard queries are supported. */
- if (flags & 0x7800) {
- evdns_server_request_respond(&(server_req->base), DNS_ERR_NOTIMPL);
- return -1;
- }
-
- port->user_callback(&(server_req->base), port->user_data);
-
- return 0;
-err:
- if (server_req) {
- if (server_req->base.questions) {
- for (i = 0; i < server_req->base.nquestions; ++i)
- mm_free(server_req->base.questions[i]);
- mm_free(server_req->base.questions);
- }
- CLEAR(server_req);
- mm_free(server_req);
- }
- return -1;
-
-#undef SKIP_NAME
-#undef GET32
-#undef GET16
-#undef GET8
-}
-
-static uint16_t
-default_transaction_id_fn(void)
-{
- u16 trans_id;
-#ifdef DNS_USE_CPU_CLOCK_FOR_ID
- struct timespec ts;
-#ifdef CLOCK_MONOTONIC
- if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
-#else
- if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
-#endif
- event_err(1, "clock_gettime");
- trans_id = ts.tv_nsec & 0xffff;
-#endif
-
-#ifdef DNS_USE_GETTIMEOFDAY_FOR_ID
- struct timeval tv;
- gettimeofday(&tv, NULL);
- trans_id = tv.tv_usec & 0xffff;
-#endif
-
-#ifdef DNS_USE_OPENSSL_FOR_ID
- if (RAND_pseudo_bytes((u8 *) &trans_id, 2) == -1) {
- /* in the case that the RAND call fails we back */
- /* down to using gettimeofday. */
- /*
- struct timeval tv;
- gettimeofday(&tv, NULL);
- trans_id = tv.tv_usec & 0xffff;
- */
- abort();
- }
-#endif
- return (unsigned short) trans_id;
-}
-
-static uint16_t (*trans_id_function)(void) = default_transaction_id_fn;
-
-static void
-default_random_bytes_fn(char *buf, size_t n)
-{
- unsigned i;
- for (i = 0; i < n; i += 2) {
- u16 tid = trans_id_function();
- buf[i] = (tid >> 8) & 0xff;
- if (i+1<n)
- buf[i+1] = tid & 0xff;
- }
-}
-
-static void (*rand_bytes_function)(char *buf, size_t n) =
- default_random_bytes_fn;
-
-static u16
-trans_id_from_random_bytes_fn(void)
-{
- u16 tid;
- rand_bytes_function((char*) &tid, sizeof(tid));
- return tid;
-}
-
-void
-evdns_set_transaction_id_fn(uint16_t (*fn)(void))
-{
- if (fn)
- trans_id_function = fn;
- else
- trans_id_function = default_transaction_id_fn;
- rand_bytes_function = default_random_bytes_fn;
-}
-
-void
-evdns_set_random_bytes_fn(void (*fn)(char *, size_t))
-{
- rand_bytes_function = fn;
- trans_id_function = trans_id_from_random_bytes_fn;
-}
-
-/* Try to choose a strong transaction id which isn't already in flight */
-static u16
-transaction_id_pick(void) {
- for (;;) {
- const struct evdns_request *req = req_head, *started_at;
- u16 trans_id = trans_id_function();
-
- if (trans_id == 0xffff) continue;
- /* now check to see if that id is already inflight */
- req = started_at = req_head;
- if (req) {
- do {
- if (req->trans_id == trans_id) break;
- req = req->next;
- } while (req != started_at);
- }
- /* we didn't find it, so this is a good id */
- if (req == started_at) return trans_id;
- }
-}
-
-/* choose a namesever to use. This function will try to ignore */
-/* nameservers which we think are down and load balance across the rest */
-/* by updating the server_head global each time. */
-static struct nameserver *
-nameserver_pick(void) {
- struct nameserver *started_at = server_head, *picked;
- if (!server_head) return NULL;
-
- /* if we don't have any good nameservers then there's no */
- /* point in trying to find one. */
- if (!global_good_nameservers) {
- server_head = server_head->next;
- return server_head;
- }
-
- /* remember that nameservers are in a circular list */
- for (;;) {
- if (server_head->state) {
- /* we think this server is currently good */
- picked = server_head;
- server_head = server_head->next;
- return picked;
- }
-
- server_head = server_head->next;
- if (server_head == started_at) {
- /* all the nameservers seem to be down */
- /* so we just return this one and hope for the */
- /* best */
- assert(global_good_nameservers == 0);
- picked = server_head;
- server_head = server_head->next;
- return picked;
- }
- }
-}
-
-/* this is called when a namesever socket is ready for reading */
-static void
-nameserver_read(struct nameserver *ns) {
- struct sockaddr_storage ss;
- struct sockaddr *sa = (struct sockaddr *) &ss;
- socklen_t addrlen = sizeof(ss);
- u8 packet[1500];
-
- for (;;) {
- const int r =
- (int)recvfrom(ns->socket, (void*)packet,
- (socklen_t)sizeof(packet), 0,
- sa, &addrlen);
- if (r < 0) {
- int err = last_error(ns->socket);
- if (error_is_eagain(err)) return;
- nameserver_failed(ns, tor_socket_strerror(err));
- return;
- }
- /* XXX Match port too? */
- if (!sockaddr_eq(sa, (struct sockaddr*)&ns->address, 0)) {
- evdns_log(EVDNS_LOG_WARN,
- "Address mismatch on received DNS packet. Address was %s",
- debug_ntop(sa));
- return;
- }
- ns->timedout = 0;
- reply_parse(packet, r);
- }
-}
-
-/* Read a packet from a DNS client on a server port s, parse it, and */
-/* act accordingly. */
-static void
-server_port_read(struct evdns_server_port *s) {
- u8 packet[1500];
- struct sockaddr_storage addr;
- socklen_t addrlen;
- ssize_t r;
-
- for (;;) {
- addrlen = (socklen_t)sizeof(struct sockaddr_storage);
- r = recvfrom(s->socket, (void*)packet, sizeof(packet), 0,
- (struct sockaddr*) &addr, &addrlen);
- if (r < 0) {
- int err = last_error(s->socket);
- if (error_is_eagain(err)) return;
- evdns_log(EVDNS_LOG_WARN, "Error %s (%d) while reading request.",
- tor_socket_strerror(err), err);
- return;
- }
- request_parse(packet, r, s, (struct sockaddr*) &addr, addrlen);
- }
-}
-
-/* Try to write all pending replies on a given DNS server port. */
-static void
-server_port_flush(struct evdns_server_port *port)
-{
- struct server_request *req = port->pending_replies;
- while (req) {
- ssize_t r = sendto(port->socket, req->response, req->response_len, 0,
- (struct sockaddr*) &req->addr, (socklen_t)req->addrlen);
- if (r < 0) {
- int err = last_error(port->socket);
- if (error_is_eagain(err))
- return;
- evdns_log(EVDNS_LOG_WARN, "Error %s (%d) while writing response to port; dropping", tor_socket_strerror(err), err);
- }
- if (server_request_free(req)) {
- /* we released the last reference to req->port. */
- return;
- } else {
- assert(port->pending_replies != req);
- req = port->pending_replies;
- }
- }
-
- /* We have no more pending requests; stop listening for 'writeable' events. */
- (void) event_del(&port->event);
- CLEAR(&port->event);
- event_set(&port->event, port->socket, EV_READ | EV_PERSIST,
- server_port_ready_callback, port);
- if (event_add(&port->event, NULL) < 0) {
- evdns_log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server.");
- /* ???? Do more? */
- }
-}
-
-/* set if we are waiting for the ability to write to this server. */
-/* if waiting is true then we ask libevent for EV_WRITE events, otherwise */
-/* we stop these events. */
-static void
-nameserver_write_waiting(struct nameserver *ns, char waiting) {
- if (ns->write_waiting == waiting) return;
-
- ns->write_waiting = waiting;
- (void) event_del(&ns->event);
- CLEAR(&ns->event);
- event_set(&ns->event, ns->socket, EV_READ | (waiting ? EV_WRITE : 0) | EV_PERSIST,
- nameserver_ready_callback, ns);
- if (event_add(&ns->event, NULL) < 0) {
- evdns_log(EVDNS_LOG_WARN, "Error from libevent when adding event for %s",
- debug_ntop((struct sockaddr *)&ns->address));
- /* ???? Do more? */
- }
-}
-
-/* a callback function. Called by libevent when the kernel says that */
-/* a nameserver socket is ready for writing or reading */
-static void
-nameserver_ready_callback(int fd, short events, void *arg) {
- struct nameserver *ns = (struct nameserver *) arg;
- (void)fd;
-
- if (events & EV_WRITE) {
- ns->choked = 0;
- if (!evdns_transmit()) {
- nameserver_write_waiting(ns, 0);
- }
- }
- if (events & EV_READ) {
- nameserver_read(ns);
- }
-}
-
-/* a callback function. Called by libevent when the kernel says that */
-/* a server socket is ready for writing or reading. */
-static void
-server_port_ready_callback(int fd, short events, void *arg) {
- struct evdns_server_port *port = (struct evdns_server_port *) arg;
- (void) fd;
-
- if (events & EV_WRITE) {
- port->choked = 0;
- server_port_flush(port);
- }
- if (events & EV_READ) {
- server_port_read(port);
- }
-}
-
-/* This is an inefficient representation; only use it via the dnslabel_table_*
- * functions, so that is can be safely replaced with something smarter later. */
-#define MAX_LABELS 128
-/* Structures used to implement name compression */
-struct dnslabel_entry { char *v; off_t pos; };
-struct dnslabel_table {
- int n_labels; /* number of current entries */
- /* map from name to position in message */
- struct dnslabel_entry labels[MAX_LABELS];
-};
-
-/* Initialize dnslabel_table. */
-static void
-dnslabel_table_init(struct dnslabel_table *table)
-{
- table->n_labels = 0;
-}
-
-/* Free all storage held by table, but not the table itself. */
-static void
-dnslabel_clear(struct dnslabel_table *table)
-{
- int i;
- for (i = 0; i < table->n_labels; ++i)
- mm_free(table->labels[i].v);
- table->n_labels = 0;
-}
-
-/* return the position of the label in the current message, or -1 if the label */
-/* hasn't been used yet. */
-static int
-dnslabel_table_get_pos(const struct dnslabel_table *table, const char *label)
-{
- int i;
- for (i = 0; i < table->n_labels; ++i) {
- if (!strcmp(label, table->labels[i].v)) {
- off_t pos = table->labels[i].pos;
- if (pos > 65535)
- return -1;
- return (int)pos;
- }
- }
- return -1;
-}
-
-/* remember that we've used the label at position pos */
-static int
-dnslabel_table_add(struct dnslabel_table *table, const char *label, off_t pos)
-{
- char *v;
- int p;
- if (table->n_labels == MAX_LABELS)
- return (-1);
- v = mm_strdup(label);
- if (v == NULL)
- return (-1);
- p = table->n_labels++;
- table->labels[p].v = v;
- table->labels[p].pos = pos;
-
- return (0);
-}
-
-/* Converts a string to a length-prefixed set of DNS labels, starting */
-/* at buf[j]. name and buf must not overlap. name_len should be the length */
-/* of name. table is optional, and is used for compression. */
-/* */
-/* Input: abc.def */
-/* Output: <3>abc<3>def<0> */
-/* */
-/* Returns the first index after the encoded name, or negative on error. */
-/* -1 label was > 63 bytes */
-/* -2 name too long to fit in buffer. */
-/* */
-static off_t
-dnsname_to_labels(u8 *const buf, size_t buf_len, off_t j,
- const char *name, const size_t name_len,
- struct dnslabel_table *table) {
- const char *end = name + name_len;
- int ref = 0;
- u16 _t;
-
-#define APPEND16(x) do { \
- if (j + 2 > (off_t)buf_len) \
- goto overflow; \
- _t = htons(x); \
- memcpy(buf + j, &_t, 2); \
- j += 2; \
- } while (0)
-#define APPEND32(x) do { \
- if (j + 4 > (off_t)buf_len) \
- goto overflow; \
- _t32 = htonl(x); \
- memcpy(buf + j, &_t32, 4); \
- j += 4; \
- } while (0)
-
- if (name_len > 255) return -2;
-
- for (;;) {
- const char *const start = name;
- if (table && (ref = dnslabel_table_get_pos(table, name)) >= 0) {
- APPEND16(ref | 0xc000);
- return j;
- }
- name = strchr(name, '.');
- if (!name) {
- const size_t label_len = end - start;
- if (label_len > 63) return -1;
- if ((size_t)(j+label_len+1) > buf_len) return -2;
- if (table) dnslabel_table_add(table, start, j);
- buf[j++] = (uint8_t)label_len;
-
- memcpy(buf + j, start, label_len);
- j += end - start;
- break;
- } else {
- /* append length of the label. */
- const size_t label_len = name - start;
- if (label_len > 63) return -1;
- if ((size_t)(j+label_len+1) > buf_len) return -2;
- if (table) dnslabel_table_add(table, start, j);
- buf[j++] = (uint8_t)label_len;
-
- memcpy(buf + j, start, name - start);
- j += name - start;
- /* hop over the '.' */
- name++;
- }
- }
-
- /* the labels must be terminated by a 0. */
- /* It's possible that the name ended in a . */
- /* in which case the zero is already there */
- if (!j || buf[j-1]) buf[j++] = 0;
- return j;
- overflow:
- return (-2);
-}
-
-/* Finds the length of a dns request for a DNS name of the given */
-/* length. The actual request may be smaller than the value returned */
-/* here */
-static size_t
-evdns_request_len(const size_t name_len) {
- return 96 + /* length of the DNS standard header */
- name_len + 2 +
- 4; /* space for the resource type */
-}
-
-/* build a dns request packet into buf. buf should be at least as long */
-/* as evdns_request_len told you it should be. */
-/* */
-/* Returns the amount of space used. Negative on error. */
-static int
-evdns_request_data_build(const char *const name, const size_t name_len,
- const u16 trans_id, const u16 type, const u16 class,
- u8 *const buf, size_t buf_len) {
- off_t j = 0; /* current offset into buf */
- u16 _t; /* used by the macros */
-
- APPEND16(trans_id);
- APPEND16(0x0100); /* standard query, recusion needed */
- APPEND16(1); /* one question */
- APPEND16(0); /* no answers */
- APPEND16(0); /* no authority */
- APPEND16(0); /* no additional */
-
- j = dnsname_to_labels(buf, buf_len, j, name, name_len, NULL);
- if (j < 0) {
- return (int)j;
- }
-
- APPEND16(type);
- APPEND16(class);
-
- return (int)j;
- overflow:
- return (-1);
-}
-
-/* exported function */
-struct evdns_server_port *
-evdns_add_server_port(tor_socket_t socket, int is_tcp, evdns_request_callback_fn_type cb, void *user_data)
-{
- struct evdns_server_port *port;
- if (!(port = mm_malloc(sizeof(struct evdns_server_port))))
- return NULL;
- memset(port, 0, sizeof(struct evdns_server_port));
-
- assert(!is_tcp); /* TCP sockets not yet implemented */
- port->socket = socket;
- port->refcnt = 1;
- port->choked = 0;
- port->closing = 0;
- port->user_callback = cb;
- port->user_data = user_data;
- port->pending_replies = NULL;
-
- event_set(&port->event, port->socket, EV_READ | EV_PERSIST,
- server_port_ready_callback, port);
- if (event_add(&port->event, NULL)<0) {
- mm_free(port);
- return NULL;
- }
- return port;
-}
-
-/* exported function */
-void
-evdns_close_server_port(struct evdns_server_port *port)
-{
- port->closing = 1;
- if (--port->refcnt == 0)
- server_port_free(port);
-}
-
-/* exported function */
-int
-evdns_server_request_add_reply(struct evdns_server_request *_req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data)
-{
- struct server_request *req = TO_SERVER_REQUEST(_req);
- struct server_reply_item **itemp, *item;
- int *countp;
-
- if (req->response) /* have we already answered? */
- return (-1);
-
- switch (section) {
- case EVDNS_ANSWER_SECTION:
- itemp = &req->answer;
- countp = &req->n_answer;
- break;
- case EVDNS_AUTHORITY_SECTION:
- itemp = &req->authority;
- countp = &req->n_authority;
- break;
- case EVDNS_ADDITIONAL_SECTION:
- itemp = &req->additional;
- countp = &req->n_additional;
- break;
- default:
- return (-1);
- }
- while (*itemp) {
- itemp = &((*itemp)->next);
- }
- item = mm_malloc(sizeof(struct server_reply_item));
- if (!item)
- return -1;
- CLEAR(item);
- item->next = NULL;
- if (!(item->name = mm_strdup(name))) {
- CLEAR(item);
- mm_free(item);
- return -1;
- }
- item->type = type;
- item->class = class;
- item->ttl = ttl;
- item->is_name = is_name != 0;
- item->datalen = 0;
- item->data = NULL;
- if (data) {
- if (item->is_name) {
- if (!(item->data = mm_strdup(data))) {
- mm_free(item->name);
- CLEAR(item);
- mm_free(item);
- return -1;
- }
- item->datalen = (u16)-1;
- } else {
- if (!(item->data = mm_malloc(datalen))) {
- mm_free(item->name);
- CLEAR(item);
- mm_free(item);
- return -1;
- }
- item->datalen = datalen;
- memcpy(item->data, data, datalen);
- }
- }
-
- *itemp = item;
- ++(*countp);
- return 0;
-}
-
-/* exported function */
-int
-evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl)
-{
- return evdns_server_request_add_reply(
- req, EVDNS_ANSWER_SECTION, name, TYPE_A, CLASS_INET,
- ttl, n*4, 0, addrs);
-}
-
-/* exported function */
-int
-evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl)
-{
- return evdns_server_request_add_reply(
- req, EVDNS_ANSWER_SECTION, name, TYPE_AAAA, CLASS_INET,
- ttl, n*16, 0, addrs);
-}
-
-/* exported function */
-int
-evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl)
-{
- u32 a;
- char buf[32];
- assert(in || inaddr_name);
- assert(!(in && inaddr_name));
- if (in) {
- a = ntohl(in->s_addr);
- tor_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
- (int)(u8)((a )&0xff),
- (int)(u8)((a>>8 )&0xff),
- (int)(u8)((a>>16)&0xff),
- (int)(u8)((a>>24)&0xff));
- inaddr_name = buf;
- }
- return evdns_server_request_add_reply(
- req, EVDNS_ANSWER_SECTION, inaddr_name, TYPE_PTR, CLASS_INET,
- ttl, -1, 1, hostname);
-}
-
-/* exported function */
-int
-evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl)
-{
- return evdns_server_request_add_reply(
- req, EVDNS_ANSWER_SECTION, name, TYPE_CNAME, CLASS_INET,
- ttl, -1, 1, cname);
-}
-
-
-static int
-evdns_server_request_format_response(struct server_request *req, int err)
-{
- unsigned char buf[1500];
- size_t buf_len = sizeof(buf);
- off_t j = 0, r;
- u16 _t;
- u32 _t32;
- int i;
- u16 flags;
- struct dnslabel_table table;
-
- if (err < 0 || err > 15) return -1;
-
- /* Set response bit and error code; copy OPCODE and RD fields from
- * question; copy RA and AA if set by caller. */
- flags = req->base.flags;
- flags |= (0x8000 | err);
-
- dnslabel_table_init(&table);
- APPEND16(req->trans_id);
- APPEND16(flags);
- APPEND16(req->base.nquestions);
- APPEND16(req->n_answer);
- APPEND16(req->n_authority);
- APPEND16(req->n_additional);
-
- /* Add questions. */
- for (i=0; i < req->base.nquestions; ++i) {
- const char *s = req->base.questions[i]->name;
- j = dnsname_to_labels(buf, buf_len, j, s, strlen(s), &table);
- if (j < 0) {
- dnslabel_clear(&table);
- return (int) j;
- }
- APPEND16(req->base.questions[i]->type);
- APPEND16(req->base.questions[i]->dns_question_class);
- }
-
- /* Add answer, authority, and additional sections. */
- for (i=0; i<3; ++i) {
- struct server_reply_item *item;
- if (i==0)
- item = req->answer;
- else if (i==1)
- item = req->authority;
- else
- item = req->additional;
- while (item) {
- r = dnsname_to_labels(buf, buf_len, j, item->name, strlen(item->name), &table);
- if (r < 0)
- goto overflow;
- j = r;
-
- APPEND16(item->type);
- APPEND16(item->class);
- APPEND32(item->ttl);
- if (item->is_name) {
- off_t len_idx = j, name_start;
- j += 2;
- name_start = j;
- r = dnsname_to_labels(buf, buf_len, j, item->data, strlen(item->data), &table);
- if (r < 0)
- goto overflow;
- j = r;
- _t = htons( (j-name_start) );
- memcpy(buf+len_idx, &_t, 2);
- } else {
- APPEND16(item->datalen);
- if (j+item->datalen > (off_t)buf_len)
- goto overflow;
- memcpy(buf+j, item->data, item->datalen);
- j += item->datalen;
- }
- item = item->next;
- }
- }
-
- if (j > 512) {
-overflow:
- j = 512;
- buf[2] |= 0x02; /* set the truncated bit. */
- }
-
- req->response_len = (size_t)j;
-
- if (!(req->response = mm_malloc(req->response_len))) {
- server_request_free_answers(req);
- dnslabel_clear(&table);
- return (-1);
- }
- memcpy(req->response, buf, req->response_len);
- server_request_free_answers(req);
- dnslabel_clear(&table);
- return (0);
-}
-
-/* exported function */
-int
-evdns_server_request_respond(struct evdns_server_request *_req, int err)
-{
- struct server_request *req = TO_SERVER_REQUEST(_req);
- struct evdns_server_port *port = req->port;
- ssize_t r;
- if (!req->response) {
- if ((r = evdns_server_request_format_response(req, err))<0)
- return (int)r;
- }
-
- r = sendto(port->socket, req->response, req->response_len, 0,
- (struct sockaddr*) &req->addr, req->addrlen);
- if (r<0) {
- int error = last_error(port->socket);
- if (! error_is_eagain(error))
- return -1;
-
- if (port->pending_replies) {
- req->prev_pending = port->pending_replies->prev_pending;
- req->next_pending = port->pending_replies;
- req->prev_pending->next_pending =
- req->next_pending->prev_pending = req;
- } else {
- req->prev_pending = req->next_pending = req;
- port->pending_replies = req;
- port->choked = 1;
-
- (void) event_del(&port->event);
- CLEAR(&port->event);
- event_set(&port->event, port->socket, (port->closing?0:EV_READ) | EV_WRITE | EV_PERSIST, server_port_ready_callback, port);
-
- if (event_add(&port->event, NULL) < 0) {
- evdns_log(EVDNS_LOG_WARN, "Error from libevent when adding event for DNS server");
- }
-
- }
-
- return 1;
- }
- if (server_request_free(req))
- return 0;
-
- if (port->pending_replies)
- server_port_flush(port);
-
- return 0;
-}
-
-/* Free all storage held by RRs in req. */
-static void
-server_request_free_answers(struct server_request *req)
-{
- struct server_reply_item *victim, *next, **list;
- int i;
- for (i = 0; i < 3; ++i) {
- if (i==0)
- list = &req->answer;
- else if (i==1)
- list = &req->authority;
- else
- list = &req->additional;
-
- victim = *list;
- while (victim) {
- next = victim->next;
- mm_free(victim->name);
- if (victim->data)
- mm_free(victim->data);
- mm_free(victim);
- victim = next;
- }
- *list = NULL;
- }
-}
-
-/* Free all storage held by req, and remove links to it. */
-/* return true iff we just wound up freeing the server_port. */
-static int
-server_request_free(struct server_request *req)
-{
- int i, rc=1;
- if (req->base.questions) {
- for (i = 0; i < req->base.nquestions; ++i)
- mm_free(req->base.questions[i]);
- mm_free(req->base.questions);
- }
-
- if (req->port) {
- if (req->port->pending_replies == req) {
- if (req->next_pending && req->next_pending != req)
- req->port->pending_replies = req->next_pending;
- else
- req->port->pending_replies = NULL;
- }
- rc = --req->port->refcnt;
- }
-
- if (req->response) {
- mm_free(req->response);
- }
-
- server_request_free_answers(req);
-
- if (req->next_pending && req->next_pending != req) {
- req->next_pending->prev_pending = req->prev_pending;
- req->prev_pending->next_pending = req->next_pending;
- }
-
- if (rc == 0) {
- server_port_free(req->port);
- CLEAR(req);
- mm_free(req);
- return (1);
- }
- CLEAR(req);
- mm_free(req);
- return (0);
-}
-
-/* Free all storage held by an evdns_server_port. Only called when the
- * reference count is down to 0. */
-static void
-server_port_free(struct evdns_server_port *port)
-{
- assert(port);
- assert(!port->refcnt);
- assert(!port->pending_replies);
- if (port->socket > 0) {
- CLOSE_SOCKET(port->socket);
- port->socket = -1;
- }
- (void) event_del(&port->event);
- CLEAR(&port->event);
- CLEAR(port);
- mm_free(port);
-}
-
-/* exported function */
-int
-evdns_server_request_drop(struct evdns_server_request *_req)
-{
- struct server_request *req = TO_SERVER_REQUEST(_req);
- server_request_free(req);
- return 0;
-}
-
-/* exported function */
-int
-evdns_server_request_get_requesting_addr(struct evdns_server_request *_req, struct sockaddr *sa, int addr_len)
-{
- struct server_request *req = TO_SERVER_REQUEST(_req);
- if (addr_len < (int)req->addrlen)
- return -1;
- memcpy(sa, &(req->addr), req->addrlen);
- return req->addrlen;
-}
-
-#undef APPEND16
-#undef APPEND32
-
-/* this is a libevent callback function which is called when a request */
-/* has timed out. */
-static void
-evdns_request_timeout_callback(int fd, short events, void *arg) {
- struct evdns_request *const req = (struct evdns_request *) arg;
- (void) fd;
- (void) events;
-
- evdns_log(EVDNS_LOG_DEBUG, "Request %lx timed out", (unsigned long) arg);
-
- req->ns->timedout++;
- if (req->ns->timedout > global_max_nameserver_timeout) {
- req->ns->timedout = 0;
- nameserver_failed(req->ns, "request timed out.");
- }
-
- if (req->tx_count >= global_max_retransmits) {
- /* this request has failed */
- reply_callback(req, 0, DNS_ERR_TIMEOUT, NULL);
- request_finished(req, &req_head);
- } else {
- /* retransmit it */
- /* Stop waiting for the timeout. No need to do this in
- * request_finished; that one already deletes the timeout event.
- * XXXX023 port this change to libevent. */
- del_timeout_event(req);
- evdns_request_transmit(req);
- }
-}
-
-/* try to send a request to a given server. */
-/* */
-/* return: */
-/* 0 ok */
-/* 1 temporary failure */
-/* 2 other failure */
-static int
-evdns_request_transmit_to(struct evdns_request *req, struct nameserver *server) {
- const ssize_t r = send(server->socket, (void*)req->request,
- req->request_len, 0);
- if (r < 0) {
- int err = last_error(server->socket);
- if (error_is_eagain(err)) return 1;
- nameserver_failed(req->ns, tor_socket_strerror(err));
- return 2;
- } else if (r != (ssize_t)req->request_len) {
- return 1; /* short write */
- } else {
- return 0;
- }
-}
-
-/* try to send a request, updating the fields of the request */
-/* as needed */
-/* */
-/* return: */
-/* 0 ok */
-/* 1 failed */
-static int
-evdns_request_transmit(struct evdns_request *req) {
- int retcode = 0, r;
-
- /* if we fail to send this packet then this flag marks it */
- /* for evdns_transmit */
- req->transmit_me = 1;
- if (req->trans_id == 0xffff) abort();
-
- if (req->ns->choked) {
- /* don't bother trying to write to a socket */
- /* which we have had EAGAIN from */
- return 1;
- }
-
- r = evdns_request_transmit_to(req, req->ns);
- switch (r) {
- case 1:
- /* temp failure */
- req->ns->choked = 1;
- nameserver_write_waiting(req->ns, 1);
- return 1;
- case 2:
- /* failed to transmit the request entirely. */
- retcode = 1;
- /* fall through: we'll set a timeout, which will time out,
- * and make us retransmit the request anyway. */
- default:
- /* transmitted; we need to check for timeout. */
- evdns_log(EVDNS_LOG_DEBUG,
- "Setting timeout for request %lx", (unsigned long) req);
-
- if (add_timeout_event(req, &global_timeout) < 0) {
- evdns_log(EVDNS_LOG_WARN,
- "Error from libevent when adding timer for request %lx",
- (unsigned long) req);
- /* ???? Do more? */
- }
- req->tx_count++;
- req->transmit_me = 0;
- return retcode;
- }
-}
-
-static void
-nameserver_probe_callback(int result, char type, int count, int ttl, void *addresses, void *arg) {
- struct sockaddr *addr = arg;
- struct nameserver *server;
- (void) type;
- (void) count;
- (void) ttl;
- (void) addresses;
-
- for (server = server_head; server; server = server->next) {
- if (sockaddr_eq(addr, (struct sockaddr*) &server->address, 1)) {
- if (result == DNS_ERR_NONE || result == DNS_ERR_NOTEXIST) {
- /* this is a good reply */
- nameserver_up(server);
- } else {
- nameserver_probe_failed(server);
- }
- }
- if (server->next == server_head)
- break;
- }
-
- mm_free(addr);
-}
-
-static void
-nameserver_send_probe(struct nameserver *const ns) {
- struct evdns_request *req;
- struct sockaddr_storage *addr;
- /* here we need to send a probe to a given nameserver */
- /* in the hope that it is up now. */
-
- /* We identify the nameserver by its address, in case it is removed before
- * our probe comes back. */
- addr = mm_malloc(sizeof(struct sockaddr_storage));
- memcpy(addr, &ns->address, sizeof(struct sockaddr_storage));
-
- evdns_log(EVDNS_LOG_DEBUG, "Sending probe to %s", debug_ntop((struct sockaddr *)&ns->address));
-
- req = request_new(TYPE_A, "www.google.com", DNS_QUERY_NO_SEARCH, nameserver_probe_callback, addr);
- if (!req) {
- mm_free(addr);
- return;
- }
- /* we force this into the inflight queue no matter what */
- request_trans_id_set(req, transaction_id_pick());
- req->ns = ns;
- request_submit(req);
-}
-
-/* returns: */
-/* 0 didn't try to transmit anything */
-/* 1 tried to transmit something */
-static int
-evdns_transmit(void) {
- char did_try_to_transmit = 0;
-
- if (req_head) {
- struct evdns_request *const started_at = req_head, *req = req_head;
- /* first transmit all the requests which are currently waiting */
- do {
- if (req->transmit_me) {
- did_try_to_transmit = 1;
- evdns_request_transmit(req);
- }
-
- req = req->next;
- } while (req != started_at);
- }
-
- return did_try_to_transmit;
-}
-
-/* exported function */
-int
-evdns_count_nameservers(void)
-{
- const struct nameserver *server = server_head;
- int n = 0;
- if (!server)
- return 0;
- do {
- ++n;
- server = server->next;
- } while (server != server_head);
- return n;
-}
-
-/* exported function */
-int
-evdns_clear_nameservers_and_suspend(void)
-{
- struct nameserver *server = server_head, *started_at = server_head;
- struct evdns_request *req = req_head, *req_started_at = req_head;
-
- if (!server)
- return 0;
- while (1) {
- struct nameserver *next = server->next;
- (void) event_del(&server->event);
- CLEAR(&server->event);
- del_timeout_event(server);
- if (server->socket >= 0)
- CLOSE_SOCKET(server->socket);
- CLEAR(server);
- mm_free(server);
- if (next == started_at)
- break;
- server = next;
- }
- server_head = NULL;
- global_good_nameservers = 0;
-
- while (req) {
- struct evdns_request *next = req->next;
- req->tx_count = req->reissue_count = 0;
- req->ns = NULL;
- /* ???? What to do about searches? */
- del_timeout_event(req);
- req->trans_id = 0;
- req->transmit_me = 0;
-
- global_requests_waiting++;
- evdns_request_insert(req, &req_waiting_head);
- /* We want to insert these suspended elements at the front of
- * the waiting queue, since they were pending before any of
- * the waiting entries were added. This is a circular list,
- * so we can just shift the start back by one.*/
- req_waiting_head = req_waiting_head->prev;
-
- if (next == req_started_at)
- break;
- req = next;
- }
- req_head = NULL;
- global_requests_inflight = 0;
-
- return 0;
-}
-
-static struct sockaddr_storage global_bind_address;
-static socklen_t global_bind_addrlen = 0;
-static int global_bind_addr_is_set = 0;
-void
-evdns_set_default_outgoing_bind_address(const struct sockaddr *addr,
- socklen_t addrlen)
-{
- memset(&global_bind_address, 0, sizeof(global_bind_address));
- if (addr) {
- assert(addrlen <= (socklen_t)sizeof(global_bind_address));
- memcpy(&global_bind_address, addr, addrlen);
- global_bind_addrlen = addrlen;
- global_bind_addr_is_set = 1;
- } else {
- global_bind_addr_is_set = 0;
- }
-}
-
-/* exported function */
-int
-evdns_resume(void)
-{
- evdns_requests_pump_waiting_queue();
- return 0;
-}
-
-static int
-sockaddr_is_loopback(const struct sockaddr *addr)
-{
- static const char LOOPBACK_S6[16] =
- "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1";
- if (addr->sa_family == AF_INET) {
- struct sockaddr_in *sin = (struct sockaddr_in *)addr;
- return (ntohl(sin->sin_addr.s_addr) & 0xff000000) == 0x7f000000;
- } else if (addr->sa_family == AF_INET6) {
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr;
- return fast_memeq(sin6->sin6_addr.s6_addr, LOOPBACK_S6, 16);
- }
- return 0;
-}
-
-static int
-_evdns_nameserver_add_impl(const struct sockaddr *address,
- socklen_t addrlen) {
- /* first check to see if we already have this nameserver */
-
- const struct nameserver *server = server_head, *const started_at = server_head;
- struct nameserver *ns;
-
- int err = 0;
- if (server) {
- do {
- if (sockaddr_eq(address, (struct sockaddr *)&server->address, 1)) {
- evdns_log(EVDNS_LOG_DEBUG, "Duplicate nameserver.");
- return 3;
- }
- server = server->next;
- } while (server != started_at);
- }
- if (addrlen > (int)sizeof(ns->address)) {
- evdns_log(EVDNS_LOG_DEBUG, "Addrlen %d too long.", (int)addrlen);
- return 2;
- }
-
- ns = (struct nameserver *) mm_malloc(sizeof(struct nameserver));
- if (!ns) return -1;
-
- memset(ns, 0, sizeof(struct nameserver));
-
- evtimer_set(&ns->timeout_event, nameserver_prod_callback, ns);
-
-#if 1
- ns->socket = tor_open_socket_nonblocking(address->sa_family, SOCK_DGRAM, 0);
- if (!SOCKET_OK(ns->socket)) { err = 1; goto out1; }
-#else
- ns->socket = tor_open_socket(address->sa_family, SOCK_DGRAM, 0);
- if (ns->socket < 0) { err = 1; goto out1; }
-#ifdef _WIN32
- {
- u_long nonblocking = 1;
- ioctlsocket(ns->socket, FIONBIO, &nonblocking);
- }
-#else
- if (fcntl(ns->socket, F_SETFL, O_NONBLOCK) == -1) {
- evdns_log(EVDNS_LOG_WARN, "Error %s (%d) while settings file status flags.",
- tor_socket_strerror(errno), errno);
- err = 2;
- goto out2;
- }
-#endif
-
-#endif /* 1 */
- if (global_bind_addr_is_set &&
- !sockaddr_is_loopback((struct sockaddr*)&global_bind_address)) {
- if (bind(ns->socket, (struct sockaddr *)&global_bind_address,
- global_bind_addrlen) < 0) {
- evdns_log(EVDNS_LOG_DEBUG, "Couldn't bind to outgoing address.");
- err = 2;
- goto out2;
- }
- }
-
- if (connect(ns->socket, address, addrlen) != 0) {
- evdns_log(EVDNS_LOG_DEBUG, "Couldn't open socket to nameserver.");
- err = 2;
- goto out2;
- }
-
- memcpy(&ns->address, address, addrlen);
- ns->state = 1;
- event_set(&ns->event, ns->socket, EV_READ | EV_PERSIST, nameserver_ready_callback, ns);
- if (event_add(&ns->event, NULL) < 0) {
- evdns_log(EVDNS_LOG_DEBUG, "Couldn't add event for nameserver.");
- err = 2;
- goto out2;
- }
-
- evdns_log(EVDNS_LOG_DEBUG, "Added nameserver %s", debug_ntop(address));
-
- /* insert this nameserver into the list of them */
- if (!server_head) {
- ns->next = ns->prev = ns;
- server_head = ns;
- } else {
- ns->next = server_head->next;
- ns->prev = server_head;
- server_head->next = ns;
- if (server_head->prev == server_head) {
- server_head->prev = ns;
- }
- }
-
- global_good_nameservers++;
-
- return 0;
-
-out2:
- CLOSE_SOCKET(ns->socket);
-out1:
- CLEAR(ns);
- mm_free(ns);
- evdns_log(EVDNS_LOG_WARN, "Unable to add nameserver %s: error %d", debug_ntop(address), err);
- return err;
-}
-
-/* exported function */
-int
-evdns_nameserver_add(uint32_t address) {
- struct sockaddr_in sin;
- memset(&sin, 0, sizeof(sin));
- sin.sin_family = AF_INET;
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sin.sin_len = sizeof(sin);
-#endif
- sin.sin_addr.s_addr = htonl(address);
- sin.sin_port = 53;
- return _evdns_nameserver_add_impl((struct sockaddr*) &sin, sizeof(sin));
-}
-
-/* exported function */
-int
-evdns_nameserver_ip_add(const char *ip_as_string) {
- int port;
- char buf[128];
- const char *cp, *addr_part, *port_part;
- int is_ipv6;
- /* recognized formats are:
- * [ipv6]:port
- * ipv6
- * [ipv6]
- * ipv4:port
- * ipv4
- */
-
- evdns_log(EVDNS_LOG_DEBUG, "Trying to add nameserver <%s>", ip_as_string);
-
- cp = strchr(ip_as_string, ':');
- if (*ip_as_string == '[') {
- size_t len;
- if (!(cp = strchr(ip_as_string, ']'))) {
- evdns_log(EVDNS_LOG_DEBUG, "Nameserver missing closing ]");
- return 4;
- }
- len = cp-(ip_as_string + 1);
- if (len > sizeof(buf)-1) {
- evdns_log(EVDNS_LOG_DEBUG, "[Nameserver] does not fit in buffer.");
- return 4;
- }
- memcpy(buf, ip_as_string+1, len);
- buf[len] = '\0';
- addr_part = buf;
- if (cp[1] == ':')
- port_part = cp+2;
- else
- port_part = NULL;
- is_ipv6 = 1;
- } else if (cp && strchr(cp+1, ':')) {
- is_ipv6 = 1;
- addr_part = ip_as_string;
- port_part = NULL;
- } else if (cp) {
- is_ipv6 = 0;
- if (cp - ip_as_string > (int)sizeof(buf)-1) {
- evdns_log(EVDNS_LOG_DEBUG, "Nameserver does not fit in buffer.");
- return 4;
- }
- memcpy(buf, ip_as_string, cp-ip_as_string);
- buf[cp-ip_as_string] = '\0';
- addr_part = buf;
- port_part = cp+1;
- } else {
- addr_part = ip_as_string;
- port_part = NULL;
- is_ipv6 = 0;
- }
-
- if (port_part == NULL) {
- port = 53;
- } else {
- port = strtoint(port_part);
- if (port <= 0 || port > 65535) {
- evdns_log(EVDNS_LOG_DEBUG, "Nameserver port <%s> out of range",
- port_part);
- return 4;
- }
- }
-
- /* Tor-only. needs a more general fix. */
- assert(addr_part);
- if (is_ipv6) {
- struct sockaddr_in6 sin6;
- memset(&sin6, 0, sizeof(sin6));
-#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN
- sin6.sin6_len = sizeof(sin6);
-#endif
- sin6.sin6_family = AF_INET6;
- sin6.sin6_port = htons(port);
- if (1 != tor_inet_pton(AF_INET6, addr_part, &sin6.sin6_addr)) {
- evdns_log(EVDNS_LOG_DEBUG, "inet_pton(%s) failed", addr_part);
- return 4;
- }
- return _evdns_nameserver_add_impl((struct sockaddr*)&sin6,
- sizeof(sin6));
- } else {
- struct sockaddr_in sin;
- memset(&sin, 0, sizeof(sin));
-#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
- sin.sin_len = sizeof(sin);
-#endif
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
- if (!inet_aton(addr_part, &sin.sin_addr)) {
- evdns_log(EVDNS_LOG_DEBUG, "inet_pton(%s) failed", addr_part);
- return 4;
- }
- return _evdns_nameserver_add_impl((struct sockaddr*)&sin,
- sizeof(sin));
- }
-}
-
-int
-evdns_nameserver_sockaddr_add(const struct sockaddr *sa, socklen_t len)
-{
- return _evdns_nameserver_add_impl(sa, len);
-}
-
-/* insert into the tail of the queue */
-static void
-evdns_request_insert(struct evdns_request *req, struct evdns_request **head) {
- if (!*head) {
- *head = req;
- req->next = req->prev = req;
- return;
- }
-
- req->prev = (*head)->prev;
- req->prev->next = req;
- req->next = *head;
- (*head)->prev = req;
-}
-
-static int
-string_num_dots(const char *s) {
- int count = 0;
- while ((s = strchr(s, '.'))) {
- s++;
- count++;
- }
- return count;
-}
-
-static struct evdns_request *
-request_new(int type, const char *name, int flags,
- evdns_callback_type callback, void *user_ptr) {
- const char issuing_now =
- (global_requests_inflight < global_max_requests_inflight) ? 1 : 0;
-
- const size_t name_len = strlen(name);
- const size_t request_max_len = evdns_request_len(name_len);
- const u16 trans_id = issuing_now ? transaction_id_pick() : 0xffff;
- /* the request data is alloced in a single block with the header */
- struct evdns_request *const req =
- (struct evdns_request *) mm_malloc(sizeof(struct evdns_request) + request_max_len);
- char namebuf[256];
- int rlen;
- (void) flags;
-
- if (!req) return NULL;
-
- if (name_len >= sizeof(namebuf)) {
- _mm_free(req);
- return NULL;
- }
-
- memset(req, 0, sizeof(struct evdns_request));
-
- evtimer_set(&req->timeout_event, evdns_request_timeout_callback, req);
-
- if (global_randomize_case) {
- unsigned i;
- char randbits[32];
- strlcpy(namebuf, name, sizeof(namebuf));
- rand_bytes_function(randbits, (name_len+7)/8);
- for (i = 0; i < name_len; ++i) {
- if (ISALPHA(namebuf[i])) {
- if ((randbits[i >> 3] & (1<<(i%7))))
- namebuf[i] = TOLOWER(namebuf[i]);
- else
- namebuf[i] = TOUPPER(namebuf[i]);
- }
- }
- name = namebuf;
- }
-
- /* request data lives just after the header */
- req->request = ((u8 *) req) + sizeof(struct evdns_request);
- /* denotes that the request data shouldn't be mm_free()ed */
- req->request_appended = 1;
- rlen = evdns_request_data_build(name, name_len, trans_id,
- type, CLASS_INET, req->request, request_max_len);
- if (rlen < 0)
- goto err1;
- req->request_len = rlen;
- req->trans_id = trans_id;
- req->tx_count = 0;
- req->request_type = type;
- req->user_pointer = user_ptr;
- req->user_callback = callback;
- req->ns = issuing_now ? nameserver_pick() : NULL;
- req->next = req->prev = NULL;
-
- return req;
-err1:
- CLEAR(req);
- _mm_free(req);
- return NULL;
-}
-
-static void
-request_submit(struct evdns_request *const req) {
- if (req->ns) {
- /* if it has a nameserver assigned then this is going */
- /* straight into the inflight queue */
- evdns_request_insert(req, &req_head);
- global_requests_inflight++;
- evdns_request_transmit(req);
- } else {
- evdns_request_insert(req, &req_waiting_head);
- global_requests_waiting++;
- }
-}
-
-/* exported function */
-int evdns_resolve_ipv4(const char *name, int flags,
- evdns_callback_type callback, void *ptr) {
- evdns_log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
- if (flags & DNS_QUERY_NO_SEARCH) {
- struct evdns_request *const req =
- request_new(TYPE_A, name, flags, callback, ptr);
- if (req == NULL)
- return (1);
- request_submit(req);
- return (0);
- } else {
- return (search_request_new(TYPE_A, name, flags, callback, ptr));
- }
-}
-
-/* exported function */
-int evdns_resolve_ipv6(const char *name, int flags,
- evdns_callback_type callback, void *ptr) {
- evdns_log(EVDNS_LOG_DEBUG, "Resolve requested for %s", name);
- if (flags & DNS_QUERY_NO_SEARCH) {
- struct evdns_request *const req =
- request_new(TYPE_AAAA, name, flags, callback, ptr);
- if (req == NULL)
- return (1);
- request_submit(req);
- return (0);
- } else {
- return (search_request_new(TYPE_AAAA, name, flags, callback, ptr));
- }
-}
-
-int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr) {
- char buf[32];
- struct evdns_request *req;
- u32 a;
- assert(in);
- a = ntohl(in->s_addr);
- tor_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa",
- (int)(u8)((a )&0xff),
- (int)(u8)((a>>8 )&0xff),
- (int)(u8)((a>>16)&0xff),
- (int)(u8)((a>>24)&0xff));
- evdns_log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
- req = request_new(TYPE_PTR, buf, flags, callback, ptr);
- if (!req) return 1;
- request_submit(req);
- return 0;
-}
-
-int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr) {
- /* 32 nybbles, 32 periods, "ip6.arpa", NUL. */
- char buf[73];
- char *cp;
- struct evdns_request *req;
- int i;
- assert(in);
- cp = buf;
- for (i=15; i >= 0; --i) {
- u8 byte = in->s6_addr[i];
- *cp++ = "0123456789abcdef"[byte & 0x0f];
- *cp++ = '.';
- *cp++ = "0123456789abcdef"[byte >> 4];
- *cp++ = '.';
- }
- assert(cp + strlen("ip6.arpa") < buf+sizeof(buf));
- memcpy(cp, "ip6.arpa", strlen("ip6.arpa")+1);
- evdns_log(EVDNS_LOG_DEBUG, "Resolve requested for %s (reverse)", buf);
- req = request_new(TYPE_PTR, buf, flags, callback, ptr);
- if (!req) return 1;
- request_submit(req);
- return 0;
-}
-
-/*/////////////////////////////////////////////////////////////////// */
-/* Search support */
-/* */
-/* the libc resolver has support for searching a number of domains */
-/* to find a name. If nothing else then it takes the single domain */
-/* from the gethostname() call. */
-/* */
-/* It can also be configured via the domain and search options in a */
-/* resolv.conf. */
-/* */
-/* The ndots option controls how many dots it takes for the resolver */
-/* to decide that a name is non-local and so try a raw lookup first. */
-
-struct search_domain {
- size_t len;
- struct search_domain *next;
- /* the text string is appended to this structure */
-};
-
-struct search_state {
- int refcount;
- int ndots;
- int num_domains;
- struct search_domain *head;
-};
-
-static struct search_state *global_search_state = NULL;
-
-static void
-search_state_decref(struct search_state *const state) {
- if (!state) return;
- state->refcount--;
- if (!state->refcount) {
- struct search_domain *next, *dom;
- for (dom = state->head; dom; dom = next) {
- next = dom->next;
- CLEAR(dom);
- _mm_free(dom);
- }
- CLEAR(state);
- _mm_free(state);
- }
-}
-
-static struct search_state *
-search_state_new(void) {
- struct search_state *state = (struct search_state *) mm_malloc(sizeof(struct search_state));
- if (!state) return NULL;
- memset(state, 0, sizeof(struct search_state));
- state->refcount = 1;
- state->ndots = 1;
-
- return state;
-}
-
-static void
-search_postfix_clear(void) {
- search_state_decref(global_search_state);
-
- global_search_state = search_state_new();
-}
-
-/* exported function */
-void
-evdns_search_clear(void) {
- search_postfix_clear();
-}
-
-static void
-search_postfix_add(const char *domain) {
- size_t domain_len;
- struct search_domain *sdomain;
- while (domain[0] == '.') domain++;
- domain_len = strlen(domain);
-
- if (!global_search_state) global_search_state = search_state_new();
- if (!global_search_state) return;
- global_search_state->num_domains++;
-
- sdomain = (struct search_domain *) mm_malloc(sizeof(struct search_domain) + domain_len);
- if (!sdomain) return;
- memcpy( ((u8 *) sdomain) + sizeof(struct search_domain), domain, domain_len);
- sdomain->next = global_search_state->head;
- sdomain->len = domain_len;
-
- global_search_state->head = sdomain;
-}
-
-/* reverse the order of members in the postfix list. This is needed because, */
-/* when parsing resolv.conf we push elements in the wrong order */
-static void
-search_reverse(void) {
- struct search_domain *cur, *prev = NULL, *next;
- cur = global_search_state->head;
- while (cur) {
- next = cur->next;
- cur->next = prev;
- prev = cur;
- cur = next;
- }
-
- global_search_state->head = prev;
-}
-
-/* exported function */
-void
-evdns_search_add(const char *domain) {
- search_postfix_add(domain);
-}
-
-/* exported function */
-void
-evdns_search_ndots_set(const int ndots) {
- if (!global_search_state) global_search_state = search_state_new();
- if (!global_search_state) return;
- global_search_state->ndots = ndots;
-}
-
-static void
-search_set_from_hostname(void) {
- char hostname[HOST_NAME_MAX + 1], *domainname;
-
- search_postfix_clear();
- if (gethostname(hostname, sizeof(hostname))) return;
- domainname = strchr(hostname, '.');
- if (!domainname) return;
- search_postfix_add(domainname);
-}
-
-/* warning: returns malloced string */
-static char *
-search_make_new(const struct search_state *const state, int n, const char *const base_name) {
- const size_t base_len = strlen(base_name);
- const char need_to_append_dot = base_name[base_len - 1] == '.' ? 0 : 1;
- struct search_domain *dom;
-
- for (dom = state->head; dom; dom = dom->next) {
- if (!n--) {
- /* this is the postfix we want */
- /* the actual postfix string is kept at the end of the structure */
- const u8 *const postfix = ((u8 *) dom) + sizeof(struct search_domain);
- const size_t postfix_len = dom->len;
- char *const newname = (char *) mm_malloc(base_len + need_to_append_dot + postfix_len + 1);
- if (!newname) return NULL;
- memcpy(newname, base_name, base_len);
- if (need_to_append_dot) newname[base_len] = '.';
- memcpy(newname + base_len + need_to_append_dot, postfix, postfix_len);
- newname[base_len + need_to_append_dot + postfix_len] = 0;
- return newname;
- }
- }
-
- /* we ran off the end of the list and still didn't find the requested string */
- abort();
- return NULL; /* unreachable; stops warnings in some compilers. */
-}
-
-static int
-search_request_new(int type, const char *const name, int flags, evdns_callback_type user_callback, void *user_arg) {
- assert(type == TYPE_A || type == TYPE_AAAA);
- if ( ((flags & DNS_QUERY_NO_SEARCH) == 0) &&
- global_search_state &&
- global_search_state->num_domains) {
- /* we have some domains to search */
- struct evdns_request *req;
- if (string_num_dots(name) >= global_search_state->ndots) {
- req = request_new(type, name, flags, user_callback, user_arg);
- if (!req) return 1;
- req->search_index = -1;
- } else {
- char *const new_name = search_make_new(global_search_state, 0, name);
- if (!new_name) return 1;
- req = request_new(type, new_name, flags, user_callback, user_arg);
- _mm_free(new_name);
- if (!req) return 1;
- req->search_index = 0;
- }
- req->search_origname = mm_strdup(name);
- req->search_state = global_search_state;
- req->search_flags = flags;
- global_search_state->refcount++;
- request_submit(req);
- return 0;
- } else {
- struct evdns_request *const req = request_new(type, name, flags, user_callback, user_arg);
- if (!req) return 1;
- request_submit(req);
- return 0;
- }
-}
-
-/* this is called when a request has failed to find a name. We need to check */
-/* if it is part of a search and, if so, try the next name in the list */
-/* returns: */
-/* 0 another request has been submitted */
-/* 1 no more requests needed */
-static int
-search_try_next(struct evdns_request *const req) {
- if (req->search_state) {
- /* it is part of a search */
- char *new_name;
- struct evdns_request *newreq;
- req->search_index++;
- if (req->search_index >= req->search_state->num_domains) {
- /* no more postfixes to try, however we may need to try */
- /* this name without a postfix */
- if (string_num_dots(req->search_origname) < req->search_state->ndots) {
- /* yep, we need to try it raw */
- struct evdns_request *const newreq = request_new(req->request_type, req->search_origname, req->search_flags, req->user_callback, req->user_pointer);
- evdns_log(EVDNS_LOG_DEBUG, "Search: trying raw query %s", req->search_origname);
- if (newreq) {
- request_submit(newreq);
- return 0;
- }
- }
- return 1;
- }
-
- new_name = search_make_new(req->search_state, req->search_index, req->search_origname);
- if (!new_name) return 1;
- evdns_log(EVDNS_LOG_DEBUG, "Search: now trying %s (%d)", new_name, req->search_index);
- newreq = request_new(req->request_type, new_name, req->search_flags, req->user_callback, req->user_pointer);
- mm_free(new_name);
- if (!newreq) return 1;
- newreq->search_origname = req->search_origname;
- req->search_origname = NULL;
- newreq->search_state = req->search_state;
- newreq->search_flags = req->search_flags;
- newreq->search_index = req->search_index;
- newreq->search_state->refcount++;
- request_submit(newreq);
- return 0;
- }
- return 1;
-}
-
-static void
-search_request_finished(struct evdns_request *const req) {
- if (req->search_state) {
- search_state_decref(req->search_state);
- req->search_state = NULL;
- }
- if (req->search_origname) {
- mm_free(req->search_origname);
- req->search_origname = NULL;
- }
-}
-
-/*/////////////////////////////////////////////////////////////////// */
-/* Parsing resolv.conf files */
-
-static void
-evdns_resolv_set_defaults(int flags) {
- /* if the file isn't found then we assume a local resolver */
- if (flags & DNS_OPTION_SEARCH) search_set_from_hostname();
- if (flags & DNS_OPTION_NAMESERVERS) evdns_nameserver_ip_add("127.0.0.1");
-}
-
-/* helper version of atoi which returns -1 on error */
-static int
-strtoint(const char *const str) {
- char *endptr;
- const long r = strtol(str, &endptr, 10);
- if (*endptr || r > INT_MAX) return -1;
- return (int)r;
-}
-
-/* helper version of atoi that returns -1 on error and clips to bounds. */
-static int
-strtoint_clipped(const char *const str, int min, int max)
-{
- int r = strtoint(str);
- if (r == -1)
- return r;
- else if (r<min)
- return min;
- else if (r>max)
- return max;
- else
- return r;
-}
-
-/* exported function */
-int
-evdns_set_option(const char *option, const char *val, int flags)
-{
- if (!strncmp(option, "ndots:", 6)) {
- const int ndots = strtoint(val);
- if (ndots == -1) return -1;
- if (!(flags & DNS_OPTION_SEARCH)) return 0;
- evdns_log(EVDNS_LOG_DEBUG, "Setting ndots to %d", ndots);
- if (!global_search_state) global_search_state = search_state_new();
- if (!global_search_state) return -1;
- global_search_state->ndots = ndots;
- } else if (!strncmp(option, "timeout:", 8)) {
- const int timeout = strtoint(val);
- if (timeout == -1) return -1;
- if (!(flags & DNS_OPTION_MISC)) return 0;
- evdns_log(EVDNS_LOG_DEBUG, "Setting timeout to %d", timeout);
- global_timeout.tv_sec = timeout;
- } else if (!strncmp(option, "max-timeouts:", 12)) {
- const int maxtimeout = strtoint_clipped(val, 1, 255);
- if (maxtimeout == -1) return -1;
- if (!(flags & DNS_OPTION_MISC)) return 0;
- evdns_log(EVDNS_LOG_DEBUG, "Setting maximum allowed timeouts to %d",
- maxtimeout);
- global_max_nameserver_timeout = maxtimeout;
- } else if (!strncmp(option, "max-inflight:", 13)) {
- const int maxinflight = strtoint_clipped(val, 1, 65000);
- if (maxinflight == -1) return -1;
- if (!(flags & DNS_OPTION_MISC)) return 0;
- evdns_log(EVDNS_LOG_DEBUG, "Setting maximum inflight requests to %d",
- maxinflight);
- global_max_requests_inflight = maxinflight;
- } else if (!strncmp(option, "attempts:", 9)) {
- int retries = strtoint(val);
- if (retries == -1) return -1;
- if (retries > 255) retries = 255;
- if (!(flags & DNS_OPTION_MISC)) return 0;
- evdns_log(EVDNS_LOG_DEBUG, "Setting retries to %d", retries);
- global_max_retransmits = retries;
- } else if (!strncmp(option, "randomize-case:", 15)) {
- int randcase = strtoint(val);
- if (!(flags & DNS_OPTION_MISC)) return 0;
- evdns_log(EVDNS_LOG_DEBUG, "Setting randomize_case to %d", randcase);
- global_randomize_case = randcase;
- }
- return 0;
-}
-
-static void
-resolv_conf_parse_line(char *const start, int flags) {
- char *strtok_state;
- static const char *const delims = " \t";
-#define NEXT_TOKEN tor_strtok_r(NULL, delims, &strtok_state)
-
- char *const first_token = tor_strtok_r(start, delims, &strtok_state);
- if (!first_token) return;
-
- if (!strcmp(first_token, "nameserver") && (flags & DNS_OPTION_NAMESERVERS)) {
- const char *const nameserver = NEXT_TOKEN;
- if (nameserver)
- evdns_nameserver_ip_add(nameserver);
- } else if (!strcmp(first_token, "domain") && (flags & DNS_OPTION_SEARCH)) {
- const char *const domain = NEXT_TOKEN;
- if (domain) {
- search_postfix_clear();
- search_postfix_add(domain);
- }
- } else if (!strcmp(first_token, "search") && (flags & DNS_OPTION_SEARCH)) {
- const char *domain;
- search_postfix_clear();
-
- while ((domain = NEXT_TOKEN)) {
- search_postfix_add(domain);
- }
- search_reverse();
- } else if (!strcmp(first_token, "options")) {
- const char *option;
- while ((option = NEXT_TOKEN)) {
- const char *val = strchr(option, ':');
- evdns_set_option(option, val ? val+1 : "", flags);
- }
- }
-#undef NEXT_TOKEN
-}
-
-/* exported function */
-/* returns: */
-/* 0 no errors */
-/* 1 failed to open file */
-/* 2 failed to stat file */
-/* 3 file too large */
-/* 4 out of memory */
-/* 5 short read from file */
-int
-evdns_resolv_conf_parse(int flags, const char *const filename) {
- struct stat st;
- int fd, n, r;
- u8 *resolv;
- char *start;
- int err = 0;
-
- evdns_log(EVDNS_LOG_DEBUG, "Parsing resolv.conf file %s", filename);
-
- fd = tor_open_cloexec(filename, O_RDONLY, 0);
- if (fd < 0) {
- evdns_resolv_set_defaults(flags);
- return 1;
- }
-
- if (fstat(fd, &st)) { err = 2; goto out1; }
- if (!st.st_size) {
- evdns_resolv_set_defaults(flags);
- err = (flags & DNS_OPTION_NAMESERVERS) ? 6 : 0;
- goto out1;
- }
- if (st.st_size > 65535) { err = 3; goto out1; } /* no resolv.conf should be any bigger */
-
- resolv = (u8 *) mm_malloc((size_t)st.st_size + 1);
- if (!resolv) { err = 4; goto out1; }
-
- n = 0;
- while ((r = (int)read(fd, resolv+n, (size_t)st.st_size-n)) > 0) {
- n += r;
- if (n == st.st_size)
- break;
- assert(n < st.st_size);
- }
- if (r < 0) { err = 5; goto out2; }
- resolv[n] = 0; /* we malloced an extra byte; this should be fine. */
-
- start = (char *) resolv;
- for (;;) {
- char *const newline = strchr(start, '\n');
- if (!newline) {
- resolv_conf_parse_line(start, flags);
- break;
- } else {
- *newline = 0;
- resolv_conf_parse_line(start, flags);
- start = newline + 1;
- }
- }
-
- if (!server_head && (flags & DNS_OPTION_NAMESERVERS)) {
- /* no nameservers were configured. */
- evdns_nameserver_ip_add("127.0.0.1");
- err = 6;
- }
- if (flags & DNS_OPTION_SEARCH && (!global_search_state || global_search_state->num_domains == 0)) {
- search_set_from_hostname();
- }
-
-out2:
- mm_free(resolv);
-out1:
- close(fd);
- return err;
-}
-
-#ifdef _WIN32
-/* Add multiple nameservers from a space-or-comma-separated list. */
-static int
-evdns_nameserver_ip_add_line(const char *ips) {
- const char *addr;
- char *buf;
- int r;
- while (*ips) {
- while (ISSPACE(*ips) || *ips == ',' || *ips == '\t')
- ++ips;
- addr = ips;
- while (ISDIGIT(*ips) || *ips == '.' || *ips == ':' || *ips == '[' || *ips == ']')
- ++ips;
- buf = mm_malloc(ips-addr+1);
- if (!buf) return 4;
- memcpy(buf, addr, ips-addr);
- buf[ips-addr] = '\0';
- r = evdns_nameserver_ip_add(buf);
- mm_free(buf);
- if (r) return r;
- }
- return 0;
-}
-
-typedef DWORD(WINAPI *GetNetworkParams_fn_t)(FIXED_INFO *, DWORD*);
-
-/* Use the windows GetNetworkParams interface in iphlpapi.dll to */
-/* figure out what our nameservers are. */
-static int
-load_nameservers_with_getnetworkparams(void)
-{
- /* Based on MSDN examples and inspection of c-ares code. */
- FIXED_INFO *fixed;
- HMODULE handle = 0;
- ULONG size = sizeof(FIXED_INFO);
- void *buf = NULL;
- int status = 0, r, added_any;
- IP_ADDR_STRING *ns;
- GetNetworkParams_fn_t fn;
-
- if (!(handle = load_windows_system_library(TEXT("iphlpapi.dll")))) {
- evdns_log(EVDNS_LOG_WARN, "Could not open iphlpapi.dll");
- /* right now status = 0, doesn't that mean "good" - mikec */
- status = -1;
- goto done;
- }
- if (!(fn = (GetNetworkParams_fn_t) GetProcAddress(handle, TEXT("GetNetworkParams")))) {
- evdns_log(EVDNS_LOG_WARN, "Could not get address of function.");
- /* same as above */
- status = -1;
- goto done;
- }
-
- buf = mm_malloc(size);
- if (!buf) { status = 4; goto done; }
- fixed = buf;
- r = fn(fixed, &size);
- if (r != ERROR_SUCCESS && r != ERROR_BUFFER_OVERFLOW) {
- status = -1;
- goto done;
- }
- if (r != ERROR_SUCCESS) {
- mm_free(buf);
- buf = mm_malloc(size);
- if (!buf) { status = 4; goto done; }
- fixed = buf;
- r = fn(fixed, &size);
- if (r != ERROR_SUCCESS) {
- evdns_log(EVDNS_LOG_DEBUG, "fn() failed.");
- status = -1;
- goto done;
- }
- }
-
- assert(fixed);
- added_any = 0;
- ns = &(fixed->DnsServerList);
- while (ns) {
- r = evdns_nameserver_ip_add_line(ns->IpAddress.String);
- if (r) {
- evdns_log(EVDNS_LOG_DEBUG,"Could not add nameserver %s to list, "
- "error: %d; status: %d",
- (ns->IpAddress.String),(int)GetLastError(), r);
- status = r;
- } else {
- evdns_log(EVDNS_LOG_DEBUG,"Successfully added %s as nameserver",ns->IpAddress.String);
- added_any++;
- }
-
- ns = ns->Next;
- }
-
- if (!added_any) {
- evdns_log(EVDNS_LOG_DEBUG, "No nameservers added.");
- if (status == 0)
- status = -1;
- } else {
- status = 0;
- }
-
- done:
- if (buf)
- mm_free(buf);
- if (handle)
- FreeLibrary(handle);
- return status;
-}
-
-static int
-config_nameserver_from_reg_key(HKEY key, const TCHAR *subkey)
-{
- char *buf;
- char ansibuf[MAX_PATH] = {0};
- DWORD bufsz = 0, type = 0;
- int status = 0;
-
- if (RegQueryValueEx(key, subkey, 0, &type, NULL, &bufsz)
- != ERROR_MORE_DATA)
- return -1;
- if (!(buf = mm_malloc(bufsz)))
- return -1;
-
- if (RegQueryValueEx(key, subkey, 0, &type, (LPBYTE)buf, &bufsz)
- == ERROR_SUCCESS && bufsz > 1) {
- wcstombs(ansibuf,(wchar_t*)buf,MAX_PATH);/*XXXX UNICODE */
- abuf[MAX_PATH-1] = '\0';
- status = evdns_nameserver_ip_add_line(ansibuf);
- }
-
- mm_free(buf);
- return status;
-}
-
-#define SERVICES_KEY TEXT("System\\CurrentControlSet\\Services\\")
-#define WIN_NS_9X_KEY SERVICES_KEY TEXT("VxD\\MSTCP")
-#define WIN_NS_NT_KEY SERVICES_KEY TEXT("Tcpip\\Parameters")
-
-static int
-load_nameservers_from_registry(void)
-{
- int found = 0;
- int r;
- OSVERSIONINFO info;
- memset(&info, 0, sizeof(info));
- info.dwOSVersionInfoSize = sizeof (info);
- GetVersionEx(&info);
-
-#define TRY(k, name) \
- if (!found && config_nameserver_from_reg_key(k,TEXT(name)) == 0) { \
- evdns_log(EVDNS_LOG_DEBUG,"Found nameservers in %s/%s",#k,name); \
- found = 1; \
- } else if (!found) { \
- evdns_log(EVDNS_LOG_DEBUG,"Didn't find nameservers in %s/%s", \
- #k,#name); \
- }
-
- if (info.dwMajorVersion >= 5) { /* NT */
- HKEY nt_key = 0, interfaces_key = 0;
-
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0,
- KEY_READ, &nt_key) != ERROR_SUCCESS) {
- evdns_log(EVDNS_LOG_DEBUG,"Couldn't open nt key, %d",(int)GetLastError());
- return -1;
- }
- r = RegOpenKeyEx(nt_key, TEXT("Interfaces"), 0,
- KEY_QUERY_VALUE|KEY_ENUMERATE_SUB_KEYS,
- &interfaces_key);
- if (r != ERROR_SUCCESS) {
- evdns_log(EVDNS_LOG_DEBUG,"Couldn't open interfaces key, %d",(int)GetLastError());
- return -1;
- }
- TRY(nt_key, "NameServer");
- TRY(nt_key, "DhcpNameServer");
- TRY(interfaces_key, "NameServer");
- TRY(interfaces_key, "DhcpNameServer");
- RegCloseKey(interfaces_key);
- RegCloseKey(nt_key);
- } else {
- HKEY win_key = 0;
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_NS_9X_KEY, 0,
- KEY_READ, &win_key) != ERROR_SUCCESS) {
- evdns_log(EVDNS_LOG_DEBUG, "Couldn't open registry key, %d", (int)GetLastError());
- return -1;
- }
- TRY(win_key, "NameServer");
- RegCloseKey(win_key);
- }
-
- if (found == 0) {
- evdns_log(EVDNS_LOG_WARN,"Didn't find any nameservers.");
- }
-
- return found ? 0 : -1;
-#undef TRY
-}
-
-int
-evdns_config_windows_nameservers(void)
-{
- if (load_nameservers_with_getnetworkparams() == 0)
- return 0;
- return load_nameservers_from_registry();
-}
-#endif
-
-int
-evdns_init(void)
-{
- int res = 0;
-#ifdef _WIN32
- evdns_config_windows_nameservers();
-#else
- res = evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
-#endif
-
- return (res);
-}
-
-const char *
-evdns_err_to_string(int err)
-{
- switch (err) {
- case DNS_ERR_NONE: return "no error";
- case DNS_ERR_FORMAT: return "misformatted query";
- case DNS_ERR_SERVERFAILED: return "server failed";
- case DNS_ERR_NOTEXIST: return "name does not exist";
- case DNS_ERR_NOTIMPL: return "query not implemented";
- case DNS_ERR_REFUSED: return "refused";
-
- case DNS_ERR_TRUNCATED: return "reply truncated or ill-formed";
- case DNS_ERR_UNKNOWN: return "unknown";
- case DNS_ERR_TIMEOUT: return "request timed out";
- case DNS_ERR_SHUTDOWN: return "dns subsystem shut down";
- default: return "[Unknown error code]";
- }
-}
-
-void
-evdns_shutdown(int fail_requests)
-{
- struct nameserver *server, *server_next;
- struct search_domain *dom, *dom_next;
-
- while (req_head) {
- if (fail_requests)
- reply_callback(req_head, 0, DNS_ERR_SHUTDOWN, NULL);
- request_finished(req_head, &req_head);
- }
- while (req_waiting_head) {
- if (fail_requests)
- reply_callback(req_waiting_head, 0, DNS_ERR_SHUTDOWN, NULL);
- request_finished(req_waiting_head, &req_waiting_head);
- }
- global_requests_inflight = global_requests_waiting = 0;
-
- for (server = server_head; server; server = server_next) {
- server_next = server->next;
- if (server->socket >= 0)
- CLOSE_SOCKET(server->socket);
- (void) event_del(&server->event);
- del_timeout_event(server);
- CLEAR(server);
- mm_free(server);
- if (server_next == server_head)
- break;
- }
- server_head = NULL;
- global_good_nameservers = 0;
-
- if (global_search_state) {
- for (dom = global_search_state->head; dom; dom = dom_next) {
- dom_next = dom->next;
- CLEAR(dom);
- mm_free(dom);
- }
- CLEAR(global_search_state);
- mm_free(global_search_state);
- global_search_state = NULL;
- }
- evdns_log_fn = NULL;
-}
-
-#ifdef EVDNS_MAIN
-void
-main_callback(int result, char type, int count, int ttl,
- void *addrs, void *orig) {
- char *n = (char*)orig;
- int i;
- for (i = 0; i < count; ++i) {
- if (type == DNS_IPv4_A) {
- printf("%s: %s\n", n, debug_ntoa(((u32*)addrs)[i]));
- } else if (type == DNS_PTR) {
- printf("%s: %s\n", n, ((char**)addrs)[i]);
- }
- }
- if (!count) {
- printf("%s: No answer (%d)\n", n, result);
- }
- fflush(stdout);
-}
-void
-evdns_server_callback(struct evdns_server_request *req, void *data)
-{
- int i, r;
- (void)data;
- /* dummy; give 192.168.11.11 as an answer for all A questions,
- * give foo.bar.example.com as an answer for all PTR questions. */
- for (i = 0; i < req->nquestions; ++i) {
- u32 ans = htonl(0xc0a80b0bUL);
- if (req->questions[i]->type == EVDNS_TYPE_A &&
- req->questions[i]->dns_question_class == EVDNS_CLASS_INET) {
- printf(" -- replying for %s (A)\n", req->questions[i]->name);
- r = evdns_server_request_add_a_reply(req, req->questions[i]->name,
- 1, &ans, 10);
- if (r<0)
- printf("eeep, didn't work.\n");
- } else if (req->questions[i]->type == EVDNS_TYPE_PTR &&
- req->questions[i]->dns_question_class == EVDNS_CLASS_INET) {
- printf(" -- replying for %s (PTR)\n", req->questions[i]->name);
- r = evdns_server_request_add_ptr_reply(req, NULL, req->questions[i]->name,
- "foo.bar.example.com", 10);
- } else {
- printf(" -- skipping %s [%d %d]\n", req->questions[i]->name,
- req->questions[i]->type, req->questions[i]->dns_question_class);
- }
- }
-
- r = evdns_server_request_respond(req, 0);
- if (r<0)
- printf("eeek, couldn't send reply.\n");
-}
-
-void
-logfn(int is_warn, const char *msg) {
- (void) is_warn;
- fprintf(stderr, "%s\n", msg);
-}
-int
-main(int c, char **v) {
- int idx;
- int reverse = 0, verbose = 1, servertest = 0;
- if (c<2) {
- fprintf(stderr, "syntax: %s [-x] [-v] hostname\n", v[0]);
- fprintf(stderr, "syntax: %s [-servertest]\n", v[0]);
- return 1;
- }
- idx = 1;
- while (idx < c && v[idx][0] == '-') {
- if (!strcmp(v[idx], "-x"))
- reverse = 1;
- else if (!strcmp(v[idx], "-v"))
- verbose = 1;
- else if (!strcmp(v[idx], "-servertest"))
- servertest = 1;
- else
- fprintf(stderr, "Unknown option %s\n", v[idx]);
- ++idx;
- }
- event_init();
- if (verbose)
- evdns_set_log_fn(logfn);
- evdns_resolv_conf_parse(DNS_OPTION_NAMESERVERS, "/etc/resolv.conf");
- if (servertest) {
- int sock;
- struct sockaddr_in my_addr;
-#if 1
- sock = tor_open_socket_nonblocking(PF_INET, SOCK_DGRAM, 0)
-#else
- sock = tor_open_socket(PF_INET, SOCK_DGRAM, 0);
- fcntl(sock, F_SETFL, O_NONBLOCK);
-#endif
- my_addr.sin_family = AF_INET;
- my_addr.sin_port = htons(10053);
- my_addr.sin_addr.s_addr = INADDR_ANY;
- if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr))<0) {
- perror("bind");
- exit(1);
- }
- evdns_add_server_port(sock, 0, evdns_server_callback, NULL);
- }
- for (; idx < c; ++idx) {
- if (reverse) {
- struct in_addr addr;
- if (!inet_aton(v[idx], &addr)) {
- fprintf(stderr, "Skipping non-IP %s\n", v[idx]);
- continue;
- }
- fprintf(stderr, "resolving %s...\n",v[idx]);
- evdns_resolve_reverse(&addr, 0, main_callback, v[idx]);
- } else {
- fprintf(stderr, "resolving (fwd) %s...\n",v[idx]);
- evdns_resolve_ipv4(v[idx], 0, main_callback, v[idx]);
- }
- }
- fflush(stdout);
- event_dispatch();
- return 0;
-}
-#endif
-
-/* Local Variables: */
-/* tab-width: 4 */
-/* c-basic-offset: 4 */
-/* indent-tabs-mode: t */
-/* End: */
-
diff --git a/src/ext/eventdns.h b/src/ext/eventdns.h
deleted file mode 100644
index ad8c100dd6..0000000000
--- a/src/ext/eventdns.h
+++ /dev/null
@@ -1,337 +0,0 @@
-
-/*
- * The original DNS code is due to Adam Langley with heavy
- * modifications by Nick Mathewson. Adam put his DNS software in the
- * public domain. You can find his original copyright below. Please,
- * aware that the code as part of libevent is governed by the 3-clause
- * BSD license above.
- *
- * This software is Public Domain. To view a copy of the public domain dedication,
- * visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
- * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
- *
- * I ask and expect, but do not require, that all derivative works contain an
- * attribution similar to:
- * Parts developed by Adam Langley <agl@imperialviolet.org>
- *
- * You may wish to replace the word "Parts" with something else depending on
- * the amount of original code.
- *
- * (Derivative works does not include programs which link against, run or include
- * the source verbatim in their source distributions)
- */
-
-/*
- * Welcome, gentle reader
- *
- * Async DNS lookups are really a whole lot harder than they should be,
- * mostly stemming from the fact that the libc resolver has never been
- * very good at them. Before you use this library you should see if libc
- * can do the job for you with the modern async call getaddrinfo_a
- * (see http://www.imperialviolet.org/page25.html#e498). Otherwise,
- * please continue.
- *
- * This code is based on libevent and you must call event_init before
- * any of the APIs in this file. You must also seed the OpenSSL random
- * source if you are using OpenSSL for ids (see below).
- *
- * This library is designed to be included and shipped with your source
- * code. You statically link with it. You should also test for the
- * existence of strtok_r and define HAVE_STRTOK_R if you have it.
- *
- * The DNS protocol requires a good source of id numbers and these
- * numbers should be unpredictable for spoofing reasons. There are
- * three methods for generating them here and you must define exactly
- * one of them. In increasing order of preference:
- *
- * DNS_USE_GETTIMEOFDAY_FOR_ID:
- * Using the bottom 16 bits of the usec result from gettimeofday. This
- * is a pretty poor solution but should work anywhere.
- * DNS_USE_CPU_CLOCK_FOR_ID:
- * Using the bottom 16 bits of the nsec result from the CPU's time
- * counter. This is better, but may not work everywhere. Requires
- * POSIX realtime support and you'll need to link against -lrt on
- * glibc systems at least.
- * DNS_USE_OPENSSL_FOR_ID:
- * Uses the OpenSSL RAND_bytes call to generate the data. You must
- * have seeded the pool before making any calls to this library.
- *
- * The library keeps track of the state of nameservers and will avoid
- * them when they go down. Otherwise it will round robin between them.
- *
- * Quick start guide:
- * #include "evdns.h"
- * void callback(int result, char type, int count, int ttl,
- * void *addresses, void *arg);
- * evdns_resolv_conf_parse(DNS_OPTIONS_ALL, "/etc/resolv.conf");
- * evdns_resolve("www.hostname.com", 0, callback, NULL);
- *
- * When the lookup is complete the callback function is called. The
- * first argument will be one of the DNS_ERR_* defines in evdns.h.
- * Hopefully it will be DNS_ERR_NONE, in which case type will be
- * DNS_IPv4_A, count will be the number of IP addresses, ttl is the time
- * which the data can be cached for (in seconds), addresses will point
- * to an array of uint32_t's and arg will be whatever you passed to
- * evdns_resolve.
- *
- * Searching:
- *
- * In order for this library to be a good replacement for glibc's resolver it
- * supports searching. This involves setting a list of default domains, in
- * which names will be queried for. The number of dots in the query name
- * determines the order in which this list is used.
- *
- * Searching appears to be a single lookup from the point of view of the API,
- * although many DNS queries may be generated from a single call to
- * evdns_resolve. Searching can also drastically slow down the resolution
- * of names.
- *
- * To disable searching:
- * 1. Never set it up. If you never call evdns_resolv_conf_parse or
- * evdns_search_add then no searching will occur.
- *
- * 2. If you do call evdns_resolv_conf_parse then don't pass
- * DNS_OPTION_SEARCH (or DNS_OPTIONS_ALL, which implies it).
- *
- * 3. When calling evdns_resolve, pass the DNS_QUERY_NO_SEARCH flag.
- *
- * The order of searches depends on the number of dots in the name. If the
- * number is greater than the ndots setting then the names is first tried
- * globally. Otherwise each search domain is appended in turn.
- *
- * The ndots setting can either be set from a resolv.conf, or by calling
- * evdns_search_ndots_set.
- *
- * For example, with ndots set to 1 (the default) and a search domain list of
- * ["myhome.net"]:
- * Query: www
- * Order: www.myhome.net, www.
- *
- * Query: www.abc
- * Order: www.abc., www.abc.myhome.net
- *
- * API reference:
- *
- * int evdns_nameserver_add(uint32_t address)
- * Add a nameserver. The address should be an IP address in
- * network byte order. The type of address is chosen so that
- * it matches in_addr.s_addr.
- * Returns non-zero on error.
- *
- * int evdns_nameserver_ip_add(const char *ip_as_string)
- * This wraps the above function by parsing a string as an IP
- * address and adds it as a nameserver.
- * Returns non-zero on error
- *
- * int evdns_resolve(const char *name, int flags,
- * evdns_callback_type callback,
- * void *ptr)
- * Resolve a name. The name parameter should be a DNS name.
- * The flags parameter should be 0, or DNS_QUERY_NO_SEARCH
- * which disables searching for this query. (see defn of
- * searching above).
- *
- * The callback argument is a function which is called when
- * this query completes and ptr is an argument which is passed
- * to that callback function.
- *
- * Returns non-zero on error
- *
- * void evdns_search_clear()
- * Clears the list of search domains
- *
- * void evdns_search_add(const char *domain)
- * Add a domain to the list of search domains
- *
- * void evdns_search_ndots_set(int ndots)
- * Set the number of dots which, when found in a name, causes
- * the first query to be without any search domain.
- *
- * int evdns_count_nameservers(void)
- * Return the number of configured nameservers (not necessarily the
- * number of running nameservers). This is useful for double-checking
- * whether our calls to the various nameserver configuration functions
- * have been successful.
- *
- * int evdns_clear_nameservers_and_suspend(void)
- * Remove all currently configured nameservers, and suspend all pending
- * resolves. Resolves will not necessarily be re-attempted until
- * evdns_resume() is called.
- *
- * int evdns_resume(void)
- * Re-attempt resolves left in limbo after an earlier call to
- * evdns_clear_nameservers_and_suspend().
- *
- * int evdns_config_windows_nameservers(void)
- * Attempt to configure a set of nameservers based on platform settings on
- * a win32 host. Preferentially tries to use GetNetworkParams; if that fails,
- * looks in the registry. Returns 0 on success, nonzero on failure.
- *
- * int evdns_resolv_conf_parse(int flags, const char *filename)
- * Parse a resolv.conf like file from the given filename.
- *
- * See the man page for resolv.conf for the format of this file.
- * The flags argument determines what information is parsed from
- * this file:
- * DNS_OPTION_SEARCH - domain, search and ndots options
- * DNS_OPTION_NAMESERVERS - nameserver lines
- * DNS_OPTION_MISC - timeout and attempts options
- * DNS_OPTIONS_ALL - all of the above
- * The following directives are not parsed from the file:
- * sortlist, rotate, no-check-names, inet6, debug
- *
- * Returns non-zero on error:
- * 0 no errors
- * 1 failed to open file
- * 2 failed to stat file
- * 3 file too large
- * 4 out of memory
- * 5 short read from file
- * 6 no nameservers in file
- *
- * Internals:
- *
- * Requests are kept in two queues. The first is the inflight queue. In
- * this queue requests have an allocated transaction id and nameserver.
- * They will soon be transmitted if they haven't already been.
- *
- * The second is the waiting queue. The size of the inflight ring is
- * limited and all other requests wait in waiting queue for space. This
- * bounds the number of concurrent requests so that we don't flood the
- * nameserver. Several algorithms require a full walk of the inflight
- * queue and so bounding its size keeps thing going nicely under huge
- * (many thousands of requests) loads.
- *
- * If a nameserver loses too many requests it is considered down and we
- * try not to use it. After a while we send a probe to that nameserver
- * (a lookup for google.com) and, if it replies, we consider it working
- * again. If the nameserver fails a probe we wait longer to try again
- * with the next probe.
- */
-
-#ifndef TOR_EVENTDNS_H
-#define TOR_EVENTDNS_H
-
-/* Error codes 0-5 are as described in RFC 1035. */
-#define DNS_ERR_NONE 0
-/* The name server was unable to interpret the query */
-#define DNS_ERR_FORMAT 1
-/* The name server was unable to process this query due to a problem with the
- * name server */
-#define DNS_ERR_SERVERFAILED 2
-/* The domain name does not exist */
-#define DNS_ERR_NOTEXIST 3
-/* The name server does not support the requested kind of query */
-#define DNS_ERR_NOTIMPL 4
-/* The name server refuses to reform the specified operation for policy
- * reasons */
-#define DNS_ERR_REFUSED 5
-/* The reply was truncated or ill-formated */
-#define DNS_ERR_TRUNCATED 65
-/* An unknown error occurred */
-#define DNS_ERR_UNKNOWN 66
-/* Communication with the server timed out */
-#define DNS_ERR_TIMEOUT 67
-/* The request was canceled because the DNS subsystem was shut down. */
-#define DNS_ERR_SHUTDOWN 68
-
-#define DNS_IPv4_A 1
-#define DNS_PTR 2
-#define DNS_IPv6_AAAA 3
-
-#define DNS_QUERY_NO_SEARCH 1
-
-#define DNS_OPTION_SEARCH 1
-#define DNS_OPTION_NAMESERVERS 2
-#define DNS_OPTION_MISC 4
-#define DNS_OPTIONS_ALL 7
-
-/*
- * The callback that contains the results from a lookup.
- * - type is either DNS_IPv4_A or DNS_IPv6_AAAA or DNS_PTR
- * - count contains the number of addresses of form type
- * - ttl is the number of seconds the resolution may be cached for.
- * - addresses needs to be cast according to type
- */
-typedef void (*evdns_callback_type) (int result, char type, int count, int ttl, void *addresses, void *arg);
-
-int evdns_init(void);
-void evdns_shutdown(int fail_requests);
-const char *evdns_err_to_string(int err);
-int evdns_nameserver_add(uint32_t address);
-int evdns_count_nameservers(void);
-int evdns_clear_nameservers_and_suspend(void);
-int evdns_resume(void);
-int evdns_nameserver_ip_add(const char *ip_as_string);
-int evdns_nameserver_sockaddr_add(const struct sockaddr *sa, socklen_t len);
-void evdns_set_default_outgoing_bind_address(const struct sockaddr *addr, socklen_t addrlen);
-int evdns_resolve_ipv4(const char *name, int flags, evdns_callback_type callback, void *ptr);
-int evdns_resolve_ipv6(const char *name, int flags, evdns_callback_type callback, void *ptr);
-struct in_addr;
-struct in6_addr;
-int evdns_resolve_reverse(const struct in_addr *in, int flags, evdns_callback_type callback, void *ptr);
-int evdns_resolve_reverse_ipv6(const struct in6_addr *in, int flags, evdns_callback_type callback, void *ptr);
-int evdns_set_option(const char *option, const char *val, int flags);
-int evdns_resolv_conf_parse(int flags, const char *);
-#ifdef _WIN32
-int evdns_config_windows_nameservers(void);
-#endif
-void evdns_search_clear(void);
-void evdns_search_add(const char *domain);
-void evdns_search_ndots_set(const int ndots);
-
-typedef void (*evdns_debug_log_fn_type)(int is_warning, const char *msg);
-void evdns_set_log_fn(evdns_debug_log_fn_type fn);
-
-void evdns_set_transaction_id_fn(uint16_t (*fn)(void));
-void evdns_set_random_bytes_fn(void (*fn)(char *, size_t));
-
-#define DNS_NO_SEARCH 1
-
-/* Structures and functions used to implement a DNS server. */
-
-struct evdns_server_request {
- int flags;
- int nquestions;
- struct evdns_server_question **questions;
-};
-struct evdns_server_question {
- int type;
- int dns_question_class;
- char name[1];
-};
-typedef void (*evdns_request_callback_fn_type)(struct evdns_server_request *, void *);
-#define EVDNS_ANSWER_SECTION 0
-#define EVDNS_AUTHORITY_SECTION 1
-#define EVDNS_ADDITIONAL_SECTION 2
-
-#define EVDNS_TYPE_A 1
-#define EVDNS_TYPE_NS 2
-#define EVDNS_TYPE_CNAME 5
-#define EVDNS_TYPE_SOA 6
-#define EVDNS_TYPE_PTR 12
-#define EVDNS_TYPE_MX 15
-#define EVDNS_TYPE_TXT 16
-#define EVDNS_TYPE_AAAA 28
-
-#define EVDNS_QTYPE_AXFR 252
-#define EVDNS_QTYPE_ALL 255
-
-#define EVDNS_CLASS_INET 1
-
-struct evdns_server_port *evdns_add_server_port(tor_socket_t socket, int is_tcp, evdns_request_callback_fn_type callback, void *user_data);
-void evdns_close_server_port(struct evdns_server_port *port);
-
-int evdns_server_request_add_reply(struct evdns_server_request *req, int section, const char *name, int type, int class, int ttl, int datalen, int is_name, const char *data);
-int evdns_server_request_add_a_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl);
-int evdns_server_request_add_aaaa_reply(struct evdns_server_request *req, const char *name, int n, const void *addrs, int ttl);
-int evdns_server_request_add_ptr_reply(struct evdns_server_request *req, struct in_addr *in, const char *inaddr_name, const char *hostname, int ttl);
-int evdns_server_request_add_cname_reply(struct evdns_server_request *req, const char *name, const char *cname, int ttl);
-
-struct sockaddr;
-int evdns_server_request_get_requesting_addr(struct evdns_server_request *req, struct sockaddr *sa, int addr_len);
-
-int evdns_server_request_respond(struct evdns_server_request *req, int err);
-int evdns_server_request_drop(struct evdns_server_request *req);
-
-#endif // !EVENTDNS_H
diff --git a/src/ext/ht.h b/src/ext/ht.h
index 28d1fe49d5..a441d0b685 100644
--- a/src/ext/ht.h
+++ b/src/ext/ht.h
@@ -5,6 +5,96 @@
/* Based on ideas by Christopher Clark and interfaces from Niels Provos. */
+/*
+ These macros provide an intrustive implementation for a typesafe chaining
+ hash table, loosely based on the BSD tree.h and queue.h macros. Here's
+ how to use them.
+
+ First, pick a the structure that you'll be storing in the hashtable. Let's
+ say that's "struct dinosaur". To this structure, you add an HT_ENTRY()
+ member, as such:
+
+ struct dinosaur {
+ HT_ENTRY(dinosaur) node; // The name inside the () must match the
+ // struct.
+
+ // These are just fields from the dinosaur structure...
+ long dinosaur_id;
+ char *name;
+ long age;
+ int is_ornithischian;
+ int is_herbivorous;
+ };
+
+ You can declare the hashtable itself as:
+
+ HT_HEAD(dinosaur_ht, dinosaur);
+
+ This declares a new 'struct dinosaur_ht' type.
+
+ Now you need to declare two functions to help implement the hashtable: one
+ compares two dinosaurs for equality, and one computes the hash of a
+ dinosaur. Let's say that two dinosaurs are equal if they have the same ID
+ and name.
+
+ int
+ dinosaurs_equal(const struct dinosaur *d1, const struct dinosaur *d2)
+ {
+ return d1->dinosaur_id == d2->dinosaur_id &&
+ 0 == strcmp(d1->name, d2->name);
+ }
+
+ unsigned
+ dinosaur_hash(const struct dinosaur *d)
+ {
+ // This is a very bad hash function. Use siphash24g instead.
+ return (d->dinosaur_id + d->name[0] ) * 1337 + d->name[1] * 1337;
+ }
+
+ Now you'll need to declare the functions that manipulate the hash table.
+ To do this, you put this declaration either in a header file, or inside
+ a regular module, depending on what visibility you want.
+
+ HT_PROTOTYPE(dinosaur_ht, // The name of the hashtable struct
+ dinosaur, // The name of the element struct,
+ node, // The name of HT_ENTRY member
+ dinosaur_hash, dinosaurs_equal);
+
+ Later, inside a C function, you use this macro to declare the hashtable
+ functions.
+
+ HT_GENERATE2(dinosaur_ht, dinosaur, node, dinosaur_hash, dinosaurs_equal,
+ 0.6, tor_reallocarray, tor_free_);
+
+ Note the use of tor_free_, not tor_free. The 0.6 is magic.
+
+ Now you can use the hashtable! You can initialize one with
+
+ struct dinosaur_ht my_dinos = HT_INITIALIZER();
+
+ Or create one in core with
+
+ struct dinosaur_ht *dinos = tor_malloc(sizeof(dinosaur_ht));
+ HT_INIT(dinosaur_ht, dinos);
+
+ To the hashtable, you use the HT_FOO(dinosaur_ht, ...) macros. For
+ example, to put new_dino into dinos, you say:
+
+ HT_REPLACE(dinosaur_ht, dinos, new_dino);
+
+ If you're searching for an element, you need to use a dummy 'key' element in
+ the search. For example.
+
+ struct dinosaur dino_key;
+ dino_key.dinosaur_id = 12345;
+ dino_key.name = tor_strdup("Atrociraptor");
+
+ struct dinosaur *found = HT_FIND(dinosaurs_ht, dinos, &dino_key);
+
+ Have fun with your hash table!
+
+ */
+
#ifndef HT_H_INCLUDED_
#define HT_H_INCLUDED_
@@ -203,6 +293,7 @@ ht_string_hash(const char *s)
name##_HT_GROW(head, head->hth_n_entries+1); \
HT_SET_HASH_(elm, field, hashfn); \
p = name##_HT_FIND_P_(head, elm); \
+ HT_ASSERT_(p != NULL); /* this holds because we called HT_GROW */ \
r = *p; \
*p = elm; \
if (r && (r!=elm)) { \
@@ -470,6 +561,7 @@ ht_string_hash(const char *s)
name##_HT_GROW(var##_head_, var##_head_->hth_n_entries+1); \
HT_SET_HASH_((elm), field, hashfn); \
var = name##_HT_FIND_P_(var##_head_, (elm)); \
+ HT_ASSERT_(var); /* Holds because we called HT_GROW */ \
if (*var) { \
y; \
} else { \
diff --git a/src/ext/include.am b/src/ext/include.am
index bf678f2c9d..f00f3e031e 100644
--- a/src/ext/include.am
+++ b/src/ext/include.am
@@ -5,18 +5,22 @@ EXTRA_DIST += src/ext/README
EXTHEADERS = \
src/ext/ht.h \
- src/ext/eventdns.h \
src/ext/tinytest.h \
src/ext/tor_readpassphrase.h \
src/ext/strlcat.c \
src/ext/strlcpy.c \
src/ext/tinytest_macros.h \
src/ext/tor_queue.h \
- src/ext/siphash.h
+ src/ext/siphash.h \
+ src/ext/timeouts/timeout.h \
+ src/ext/timeouts/timeout-debug.h \
+ src/ext/timeouts/timeout-bitops.c \
+ src/ext/timeouts/timeout.c
noinst_HEADERS+= $(EXTHEADERS)
-src_ext_ed25519_ref10_libed25519_ref10_a_CFLAGS=
+src_ext_ed25519_ref10_libed25519_ref10_a_CFLAGS=\
+ @CFLAGS_CONSTTIME@
src_ext_ed25519_ref10_libed25519_ref10_a_SOURCES= \
src/ext/ed25519/ref10/fe_0.c \
@@ -93,7 +97,8 @@ noinst_HEADERS += $(ED25519_REF10_HDRS)
LIBED25519_REF10=src/ext/ed25519/ref10/libed25519_ref10.a
noinst_LIBRARIES += $(LIBED25519_REF10)
-src_ext_ed25519_donna_libed25519_donna_a_CFLAGS= \
+src_ext_ed25519_donna_libed25519_donna_a_CFLAGS=\
+ @CFLAGS_CONSTTIME@ \
-DED25519_CUSTOMRANDOM \
-DED25519_SUFFIX=_donna
@@ -135,7 +140,8 @@ noinst_HEADERS += $(ED25519_DONNA_HDRS)
LIBED25519_DONNA=src/ext/ed25519/donna/libed25519_donna.a
noinst_LIBRARIES += $(LIBED25519_DONNA)
-src_ext_keccak_tiny_libkeccak_tiny_a_CFLAGS=
+src_ext_keccak_tiny_libkeccak_tiny_a_CFLAGS=\
+ @CFLAGS_CONSTTIME@
src_ext_keccak_tiny_libkeccak_tiny_a_SOURCES= \
src/ext/keccak-tiny/keccak-tiny-unrolled.c
@@ -148,3 +154,21 @@ noinst_HEADERS += $(LIBKECCAK_TINY_HDRS)
LIBKECCAK_TINY=src/ext/keccak-tiny/libkeccak-tiny.a
noinst_LIBRARIES += $(LIBKECCAK_TINY)
+EXTRA_DIST += \
+ src/ext/timeouts/bench/bench-add.lua \
+ src/ext/timeouts/bench/bench-aux.lua \
+ src/ext/timeouts/bench/bench.c \
+ src/ext/timeouts/bench/bench-del.lua \
+ src/ext/timeouts/bench/bench-expire.lua \
+ src/ext/timeouts/bench/bench.h \
+ src/ext/timeouts/bench/bench-heap.c \
+ src/ext/timeouts/bench/bench-llrb.c \
+ src/ext/timeouts/bench/bench.plt \
+ src/ext/timeouts/bench/bench-wheel.c \
+ src/ext/timeouts/bench/Rules.mk \
+ src/ext/timeouts/lua/Rules.mk \
+ src/ext/timeouts/lua/timeout-lua.c \
+ src/ext/timeouts/Makefile \
+ src/ext/timeouts/Rules.shrc \
+ src/ext/timeouts/test-timeout.c
+
diff --git a/src/ext/mulodi/LICENSE.TXT b/src/ext/mulodi/LICENSE.TXT
new file mode 100644
index 0000000000..a17dc12b27
--- /dev/null
+++ b/src/ext/mulodi/LICENSE.TXT
@@ -0,0 +1,91 @@
+==============================================================================
+compiler_rt License
+==============================================================================
+
+The compiler_rt library is dual licensed under both the University of Illinois
+"BSD-Like" license and the MIT license. As a user of this code you may choose
+to use it under either license. As a contributor, you agree to allow your code
+to be used under both.
+
+Full text of the relevant licenses is included below.
+
+==============================================================================
+
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2009-2016 by the contributors listed in CREDITS.TXT
+
+All rights reserved.
+
+Developed by:
+
+ LLVM Team
+
+ University of Illinois at Urbana-Champaign
+
+ http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimers.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimers in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the names of the LLVM Team, University of Illinois at
+ Urbana-Champaign, nor the names of its contributors may be used to
+ endorse or promote products derived from this Software without specific
+ prior written permission.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.
+
+==============================================================================
+
+Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+==============================================================================
+Copyrights and Licenses for Third Party Software Distributed with LLVM:
+==============================================================================
+The LLVM software contains code written by third parties. Such software will
+have its own individual LICENSE.TXT file in the directory in which it appears.
+This file will describe the copyrights, license, and restrictions which apply
+to that code.
+
+The disclaimer of warranty in the University of Illinois Open Source License
+applies to all code in the LLVM Distribution, and nothing in any of the
+other licenses gives permission to use the names of the LLVM Team or the
+University of Illinois to endorse or promote products derived from this
+Software.
+
diff --git a/src/ext/mulodi/mulodi4.c b/src/ext/mulodi/mulodi4.c
new file mode 100644
index 0000000000..9891bbf1af
--- /dev/null
+++ b/src/ext/mulodi/mulodi4.c
@@ -0,0 +1,67 @@
+/*===-- mulodi4.c - Implement __mulodi4 -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ *
+ * This file implements __mulodi4 for the compiler_rt library.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#if 0
+#include "int_lib.h"
+#else
+#define COMPILER_RT_ABI
+#define di_int int64_t
+#define di_uint uint64_t
+#include "torint.h"
+
+di_int __mulodi4(di_int a, di_int b, int* overflow);
+#endif
+
+/* Returns: a * b */
+
+/* Effects: sets *overflow to 1 if a * b overflows */
+
+COMPILER_RT_ABI di_int
+__mulodi4(di_int a, di_int b, int* overflow)
+{
+ const int N = (int)(sizeof(di_int) * CHAR_BIT);
+ const di_int MIN = (di_int) ((di_uint)1 << (N-1));
+ const di_int MAX = ~MIN;
+ *overflow = 0;
+ di_int result = a * b;
+ if (a == MIN)
+ {
+ if (b != 0 && b != 1)
+ *overflow = 1;
+ return result;
+ }
+ if (b == MIN)
+ {
+ if (a != 0 && a != 1)
+ *overflow = 1;
+ return result;
+ }
+ di_int sa = a >> (N - 1);
+ di_int abs_a = (a ^ sa) - sa;
+ di_int sb = b >> (N - 1);
+ di_int abs_b = (b ^ sb) - sb;
+ if (abs_a < 2 || abs_b < 2)
+ return result;
+ if (sa == sb)
+ {
+ if (abs_a > MAX / abs_b)
+ *overflow = 1;
+ }
+ else
+ {
+ if (abs_a > MIN / -abs_b)
+ *overflow = 1;
+ }
+ return result;
+}
diff --git a/src/ext/timeouts/Makefile b/src/ext/timeouts/Makefile
new file mode 100644
index 0000000000..554ebb9ddd
--- /dev/null
+++ b/src/ext/timeouts/Makefile
@@ -0,0 +1,68 @@
+# NOTE: GNU Make 3.81 won't export MAKEFLAGS if .POSIX is specified, but
+# Solaris make won't export MAKEFLAGS unless .POSIX is specified.
+$(firstword ignore).POSIX:
+
+.DEFAULT_GOAL = all
+
+.SUFFIXES:
+
+all:
+
+#
+# USER-MODIFIABLE MACROS
+#
+top_srcdir = .
+top_builddir = .
+
+CFLAGS = -O2 -march=native -g -Wall -Wextra -Wno-unused-parameter -Wno-unused-function
+SOFLAGS = $$(auto_soflags)
+LIBS = $$(auto_libs)
+
+ALL_CPPFLAGS = -I$(top_srcdir) -DWHEEL_BIT=$(WHEEL_BIT) -DWHEEL_NUM=$(WHEEL_NUM) $(CPPFLAGS)
+ALL_CFLAGS = $(CFLAGS)
+ALL_SOFLAGS = $(SOFLAGS)
+ALL_LDFLAGS = $(LDFLAGS)
+ALL_LIBS = $(LIBS)
+
+LUA_API = 5.3
+LUA = lua
+LUA51_CPPFLAGS = $(LUA_CPPFLAGS)
+LUA52_CPPFLAGS = $(LUA_CPPFLAGS)
+LUA53_CPPFLAGS = $(LUA_CPPFLAGS)
+
+WHEEL_BIT = 6
+WHEEL_NUM = 4
+
+RM = rm -f
+
+# END MACROS
+
+SHRC = \
+ top_srcdir="$(top_srcdir)"; \
+ top_builddir="$(top_builddir)"; \
+ . "$${top_srcdir}/Rules.shrc"
+
+LUA_APIS = 5.1 5.2 5.3
+
+include $(top_srcdir)/lua/Rules.mk
+include $(top_srcdir)/bench/Rules.mk
+
+all: test-timeout
+
+timeout.o: $(top_srcdir)/timeout.c
+test-timeout.o: $(top_srcdir)/test-timeout.c
+
+timeout.o test-timeout.o:
+ @$(SHRC); echo_cmd $(CC) $(ALL_CFLAGS) -c -o $@ $${top_srcdir}/$(@F:%.o=%.c) $(ALL_CPPFLAGS)
+
+test-timeout: timeout.o test-timeout.o
+ @$(SHRC); echo_cmd $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -o $@ timeout.o test-timeout.o
+
+.PHONY: clean clean~
+
+clean:
+ $(RM) $(top_builddir)/test-timeout $(top_builddir)/*.o
+ $(RM) -r $(top_builddir)/*.dSYM
+
+clean~:
+ find $(top_builddir) $(top_srcdir) -name "*~" -exec $(RM) -- {} "+"
diff --git a/src/ext/timeouts/Rules.shrc b/src/ext/timeouts/Rules.shrc
new file mode 100644
index 0000000000..ece75d42d4
--- /dev/null
+++ b/src/ext/timeouts/Rules.shrc
@@ -0,0 +1,40 @@
+# convert to absolute paths
+top_srcdir="$(cd "${top_srcdir}" && pwd -L)"
+top_builddir="$(cd "${top_builddir}" && pwd -L)"
+
+# Paths for Lua modules (benchmarks and installed modules)
+export LUA_CPATH="${top_builddir}/lua/5.1/?.so;${top_builddir}/bench/?.so;;"
+export LUA_PATH="${top_srcdir}/lua/?.lua;${top_srcdir}/bench/?.lua;;"
+export LUA_CPATH_5_2="${top_builddir}/lua/5.2/?.so;${top_builddir}/bench/?.so;;"
+export LUA_PATH_5_2="${top_srcdir}/lua/?.lua;${top_srcdir}/bench/?.lua;;"
+export LUA_CPATH_5_3="${top_builddir}/lua/5.3/?.so;${top_builddir}/bench/?.so;;"
+export LUA_PATH_5_3="${top_srcdir}/lua/?.lua;${top_srcdir}/bench/?.lua;;"
+
+# preserve stdout so we can print commands to terminal
+exec 9>&1;
+echo_cmd() {
+ printf "%s\n" "$*" >&9;
+ "$@";
+}
+
+auto_soflags() {
+ case "$(uname -s)" in
+ Darwin)
+ printf -- "-bundle -undefined dynamic_lookup"
+ ;;
+ *)
+ printf -- "-fPIC -shared"
+ ;;
+ esac
+}
+
+auto_libs() {
+ case "$(uname -s)" in
+ Linux)
+ printf -- "-lrt"
+ ;;
+ *)
+ ;;
+ esac
+}
+
diff --git a/src/ext/timeouts/bench/Rules.mk b/src/ext/timeouts/bench/Rules.mk
new file mode 100644
index 0000000000..3ee72f3eff
--- /dev/null
+++ b/src/ext/timeouts/bench/Rules.mk
@@ -0,0 +1,49 @@
+BENCH_MODS = bench.so $(BENCH_ALGOS:%=bench-%.so)
+BENCH_ALGOS = wheel heap llrb
+BENCH_OPS = add del expire
+
+$(top_builddir)/bench/bench.so: $(top_srcdir)/bench/bench.c
+$(top_builddir)/bench/bench-wheel.so: $(top_srcdir)/bench/bench-wheel.c
+$(top_builddir)/bench/bench-heap.so: $(top_srcdir)/bench/bench-heap.c
+$(top_builddir)/bench/bench-llrb.so: $(top_srcdir)/bench/bench-llrb.c
+
+$(BENCH_MODS:%=$(top_builddir)/bench/%): $(top_srcdir)/timeout.h $(top_srcdir)/timeout.c $(top_srcdir)/bench/bench.h
+ mkdir -p $(@D)
+ @$(SHRC); echo_cmd $(CC) -o $@ $(top_srcdir)/bench/$(@F:%.so=%.c) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(ALL_SOFLAGS) $(ALL_LDFLAGS) $(ALL_LIBS)
+
+$(BENCH_OPS:%=$(top_builddir)/bench/wheel-%.dat): $(top_builddir)/bench/bench-wheel.so $(top_builddir)/bench/bench.so $(top_srcdir)/bench/bench-aux.lua
+$(BENCH_OPS:%=$(top_builddir)/bench/heap-%.dat): $(top_builddir)/bench/bench-heap.so $(top_builddir)/bench/bench.so $(top_srcdir)/bench/bench-aux.lua
+$(BENCH_OPS:%=$(top_builddir)/bench/llrb-%.dat): $(top_builddir)/bench/bench-llrb.so $(top_builddir)/bench/bench.so $(top_srcdir)/bench/bench-aux.lua
+
+$(BENCH_ALGOS:%=$(top_builddir)/bench/%-add.dat): $(top_srcdir)/bench/bench-add.lua
+ @$(SHRC); echo_cmd cd $(@D) && echo_cmd $(LUA) $${top_srcdir}/bench/bench-add.lua $${top_builddir}/bench/bench-$(@F:%-add.dat=%).so > $(@F).tmp
+ mv $@.tmp $@
+
+$(BENCH_ALGOS:%=$(top_builddir)/bench/%-del.dat): $(top_srcdir)/bench/bench-del.lua
+ @$(SHRC); echo_cmd cd $(@D) && echo_cmd $(LUA) $${top_srcdir}/bench/bench-del.lua $${top_builddir}/bench/bench-$(@F:%-del.dat=%).so > $(@F).tmp
+ mv $@.tmp $@
+
+$(BENCH_ALGOS:%=$(top_builddir)/bench/%-expire.dat): $(top_srcdir)/bench/bench-expire.lua
+ @$(SHRC); echo_cmd cd $(@D) && echo_cmd $(LUA) $${top_srcdir}/bench/bench-expire.lua $${top_builddir}/bench/bench-$(@F:%-expire.dat=%).so > $(@F).tmp
+ mv $@.tmp $@
+
+$(top_builddir)/bench/bench.eps: \
+ $(BENCH_OPS:%=$(top_builddir)/bench/wheel-%.dat) \
+ $(BENCH_OPS:%=$(top_builddir)/bench/heap-%.dat)
+# $(BENCH_OPS:%=$(top_builddir)/bench/llrb-%.dat)
+
+$(top_builddir)/bench/bench.eps: $(top_srcdir)/bench/bench.plt
+ @$(SHRC); echo_cmd cd $(@D) && echo_cmd gnuplot $${top_srcdir}/bench/bench.plt > $(@F).tmp
+ mv $@.tmp $@
+
+$(top_builddir)/bench/bench.pdf: $(top_builddir)/bench/bench.eps
+ @$(SHRC); echo_cmd ps2pdf $${top_builddir}/bench/bench.eps $@
+
+bench-mods: $(BENCH_MODS:%=$(top_builddir)/bench/%)
+
+bench-all: $(top_builddir)/bench/bench.pdf
+
+bench-clean:
+ $(RM) -r $(top_builddir)/bench/*.so $(top_builddir)/bench/*.dSYM
+ $(RM) $(top_builddir)/bench/*.dat $(top_builddir)/bench/*.tmp
+ $(RM) $(top_builddir)/bench/bench.{eps,pdf}
diff --git a/src/ext/timeouts/bench/bench-add.lua b/src/ext/timeouts/bench/bench-add.lua
new file mode 100755
index 0000000000..64a921d3de
--- /dev/null
+++ b/src/ext/timeouts/bench/bench-add.lua
@@ -0,0 +1,30 @@
+#!/usr/bin/env lua
+
+local bench = require"bench"
+local aux = require"bench-aux"
+
+local lib = ... or aux.optenv("BENCH_L", "bench-wheel.so")
+local limit = tonumber(aux.optenv("BENCH_N", 1000000))
+local step = tonumber(aux.optenv("BENCH_S", limit / 100))
+local exp_step = tonumber(aux.optenv("BENCH_E", 1.0))
+local verbose = aux.toboolean(os.getenv("BENCH_V", false))
+
+local B = bench.new(lib, count, nil, verbose)
+local fill_count, fill_last = B:fill(limit)
+
+for i=0,limit,step do
+ local exp_elapsed, fill_elapsed, fill_rate
+
+ -- expire all timeouts
+ --exp_elapsed = aux.time(B.expire, B, fill_count, fill_last * exp_step)
+ exp_elapsed = aux.time(B.del, B, 0, fill_count)
+ assert(B:empty())
+
+ -- add i timeouts
+ fill_elapsed, fill_count, fill_last = aux.time(B.fill, B, i)
+ assert(fill_count == i)
+ fill_rate = fill_elapsed > 0 and (fill_count / fill_elapsed) or 0
+
+ local fmt = verbose and "%d\t%f\t(%d/s)\t(exp:%f)" or "%d\t%f"
+ aux.say(fmt, i, fill_elapsed, fill_rate, exp_elapsed)
+end
diff --git a/src/ext/timeouts/bench/bench-aux.lua b/src/ext/timeouts/bench/bench-aux.lua
new file mode 100644
index 0000000000..6321247421
--- /dev/null
+++ b/src/ext/timeouts/bench/bench-aux.lua
@@ -0,0 +1,30 @@
+local bench = require"bench"
+local clock = bench.clock
+
+local aux = {}
+
+local function time_return(begun, ...)
+ local duration = clock() - begun
+ return duration, ...
+end
+
+function aux.time(f, ...)
+ local begun = clock()
+ return time_return(begun, f(...))
+end
+
+function aux.say(...)
+ print(string.format(...))
+end
+
+function aux.toboolean(s)
+ return tostring(s):match("^[1TtYy]") and true or false
+end
+
+function aux.optenv(k, def)
+ local s = os.getenv(k)
+
+ return (s and #s > 0 and s) or def
+end
+
+return aux
diff --git a/src/ext/timeouts/bench/bench-del.lua b/src/ext/timeouts/bench/bench-del.lua
new file mode 100755
index 0000000000..4306745f21
--- /dev/null
+++ b/src/ext/timeouts/bench/bench-del.lua
@@ -0,0 +1,25 @@
+#!/usr/bin/env lua
+
+local bench = require"bench"
+local aux = require"bench-aux"
+
+local lib = ... or aux.optenv("BENCH_L", "bench-wheel.so")
+local limit = tonumber(aux.optenv("BENCH_N", 1000000))
+local step = tonumber(aux.optenv("BENCH_S", limit / 100))
+local verbose = aux.toboolean(os.getenv("BENCH_V", false))
+
+local B = bench.new(lib, count)
+
+for i=0,limit,step do
+ -- add i timeouts
+ local fill_elapsed, fill_count = aux.time(B.fill, B, i, 60 * 1000000)
+ assert(i == fill_count)
+
+ --- delete i timeouts
+ local del_elapsed = aux.time(B.del, B, 0, fill_count)
+ assert(B:empty())
+ local del_rate = i > 0 and i / del_elapsed or 0
+
+ local fmt = verbose and "%d\t%f\t(%d/s)\t(fill:%f)" or "%d\t%f"
+ aux.say(fmt, i, del_elapsed, del_rate, fill_elapsed)
+end
diff --git a/src/ext/timeouts/bench/bench-expire.lua b/src/ext/timeouts/bench/bench-expire.lua
new file mode 100755
index 0000000000..3e6374ed52
--- /dev/null
+++ b/src/ext/timeouts/bench/bench-expire.lua
@@ -0,0 +1,29 @@
+#!/usr/bin/env lua
+
+local bench = require"bench"
+local aux = require"bench-aux"
+
+local lib = ... or aux.optenv("BENCH_L", "bench-wheel.so")
+local limit = tonumber(aux.optenv("BENCH_N", 1000000))
+local step = tonumber(aux.optenv("BENCH_S", limit / 100))
+-- expire 1/1000 * #timeouts per clock update
+local exp_step = tonumber(aux.optenv("BENCH_E", 0.0001))
+local verbose = aux.toboolean(os.getenv("BENCH_V", false))
+
+local B = require"bench".new(lib, count)
+
+for i=0,limit,step do
+ -- add i timeouts
+ local fill_elapsed, fill_count, fill_last = aux.time(B.fill, B, i)
+
+ -- expire timeouts by iteratively updating clock. exp_step is the
+ -- approximate number of timeouts (as a fraction of the total number
+ -- of timeouts) that will expire per update.
+ local exp_elapsed, exp_count = aux.time(B.expire, B, fill_count, math.floor(fill_last * exp_step))
+ assert(exp_count == i)
+ assert(B:empty())
+ local exp_rate = i > 0 and i / exp_elapsed or 0
+
+ local fmt = verbose and "%d\t%f\t(%d/s)\t(fill:%f)" or "%d\t%f"
+ aux.say(fmt, i, exp_elapsed, exp_rate, fill_elapsed)
+end
diff --git a/src/ext/timeouts/bench/bench-heap.c b/src/ext/timeouts/bench/bench-heap.c
new file mode 100644
index 0000000000..f1166a4d7e
--- /dev/null
+++ b/src/ext/timeouts/bench/bench-heap.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2006 Maxim Yegorushkin <maxim.yegorushkin@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _MIN_HEAP_H_
+#define _MIN_HEAP_H_
+
+#include <stdlib.h>
+#include <err.h>
+#include "timeout.h"
+#include "bench.h"
+
+#define min_heap_idx interval
+
+typedef timeout_t min_heap_idx_t;
+
+typedef struct min_heap
+{
+ struct timeout** p;
+ unsigned n, a;
+ timeout_t curtime;
+} min_heap_t;
+
+static inline void min_heap_ctor(min_heap_t* s);
+static inline void min_heap_dtor(min_heap_t* s);
+static inline void min_heap_elem_init(struct timeout* e);
+static inline int min_heap_elem_greater(struct timeout *a, struct timeout *b);
+static inline int min_heap_empty(min_heap_t* s);
+static inline unsigned min_heap_size(min_heap_t* s);
+static inline struct timeout* min_heap_top(min_heap_t* s);
+static inline int min_heap_reserve(min_heap_t* s, unsigned n);
+static inline int min_heap_push(min_heap_t* s, struct timeout* e);
+static inline struct timeout* min_heap_pop(min_heap_t* s);
+static inline int min_heap_erase(min_heap_t* s, struct timeout* e);
+static inline void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct timeout* e);
+static inline void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct timeout* e);
+
+int min_heap_elem_greater(struct timeout *a, struct timeout *b)
+{
+ return a->expires > b->expires;
+}
+
+void min_heap_ctor(min_heap_t* s) { s->p = 0; s->n = 0; s->a = 0; }
+void min_heap_dtor(min_heap_t* s) { if(s->p) free(s->p); }
+void min_heap_elem_init(struct timeout* e) { e->min_heap_idx = -1; }
+int min_heap_empty(min_heap_t* s) { return 0u == s->n; }
+unsigned min_heap_size(min_heap_t* s) { return s->n; }
+struct timeout* min_heap_top(min_heap_t* s) { return s->n ? *s->p : 0; }
+
+int min_heap_push(min_heap_t* s, struct timeout* e)
+{
+ if(min_heap_reserve(s, s->n + 1))
+ return -1;
+ min_heap_shift_up_(s, s->n++, e);
+ return 0;
+}
+
+struct timeout* min_heap_pop(min_heap_t* s)
+{
+ if(s->n)
+ {
+ struct timeout* e = *s->p;
+ min_heap_shift_down_(s, 0u, s->p[--s->n]);
+ e->min_heap_idx = -1;
+ return e;
+ }
+ return 0;
+}
+
+int min_heap_erase(min_heap_t* s, struct timeout* e)
+{
+ if(((min_heap_idx_t)-1) != e->min_heap_idx)
+ {
+ struct timeout *last = s->p[--s->n];
+ unsigned parent = (e->min_heap_idx - 1) / 2;
+ /* we replace e with the last element in the heap. We might need to
+ shift it upward if it is less than its parent, or downward if it is
+ greater than one or both its children. Since the children are known
+ to be less than the parent, it can't need to shift both up and
+ down. */
+ if (e->min_heap_idx > 0 && min_heap_elem_greater(s->p[parent], last))
+ min_heap_shift_up_(s, e->min_heap_idx, last);
+ else
+ min_heap_shift_down_(s, e->min_heap_idx, last);
+ e->min_heap_idx = -1;
+ return 0;
+ }
+ return -1;
+}
+
+int min_heap_reserve(min_heap_t* s, unsigned n)
+{
+ if(s->a < n)
+ {
+ struct timeout** p;
+ unsigned a = s->a ? s->a * 2 : 8;
+ if(a < n)
+ a = n;
+ if(!(p = (struct timeout**)realloc(s->p, a * sizeof *p)))
+ return -1;
+ s->p = p;
+ s->a = a;
+ }
+ return 0;
+}
+
+void min_heap_shift_up_(min_heap_t* s, unsigned hole_index, struct timeout* e)
+{
+ unsigned parent = (hole_index - 1) / 2;
+ while(hole_index && min_heap_elem_greater(s->p[parent], e))
+ {
+ (s->p[hole_index] = s->p[parent])->min_heap_idx = hole_index;
+ hole_index = parent;
+ parent = (hole_index - 1) / 2;
+ }
+ (s->p[hole_index] = e)->min_heap_idx = hole_index;
+}
+
+void min_heap_shift_down_(min_heap_t* s, unsigned hole_index, struct timeout* e)
+{
+ unsigned min_child = 2 * (hole_index + 1);
+ while(min_child <= s->n)
+ {
+ min_child -= min_child == s->n || min_heap_elem_greater(s->p[min_child], s->p[min_child - 1]);
+ if(!(min_heap_elem_greater(e, s->p[min_child])))
+ break;
+ (s->p[hole_index] = s->p[min_child])->min_heap_idx = hole_index;
+ hole_index = min_child;
+ min_child = 2 * (hole_index + 1);
+ }
+ min_heap_shift_up_(s, hole_index, e);
+}
+
+#endif /* _MIN_HEAP_H_ */
+
+
+static void *init(struct timeout *timeout, size_t count, int verbose) {
+ min_heap_t *H;
+ size_t i;
+
+ H = calloc(1, sizeof *H);
+
+ min_heap_ctor(H);
+ if (0 != min_heap_reserve(H, count))
+ err(1, "realloc");
+
+ for (i = 0; i < count; i++) {
+ min_heap_elem_init(&timeout[i]);
+ }
+
+ return H;
+} /* init() */
+
+
+static void add(void *ctx, struct timeout *to, timeout_t expires) {
+ min_heap_t *H = ctx;
+ min_heap_erase(H, to);
+ to->expires = H->curtime + expires;
+ if (0 != min_heap_push(H, to))
+ err(1, "realloc");
+} /* add() */
+
+
+static void del(void *ctx, struct timeout *to) {
+ min_heap_erase(ctx, to);
+} /* del() */
+
+
+static struct timeout *get(void *ctx) {
+ min_heap_t *H = ctx;
+ struct timeout *to;
+
+ if ((to = min_heap_top(H)) && to->expires <= H->curtime)
+ return min_heap_pop(H);
+
+ return NULL;
+} /* get() */
+
+
+static void update(void *ctx, timeout_t ts) {
+ min_heap_t *H = ctx;
+ H->curtime = ts;
+} /* update() */
+
+
+static void check(void *ctx) {
+ return;
+} /* check() */
+
+
+static int empty(void *ctx) {
+ min_heap_t *H = ctx;
+
+ return (NULL == min_heap_top(H));
+} /* empty() */
+
+
+static void destroy(void *H) {
+ free(H);
+ return;
+} /* destroy() */
+
+
+const struct benchops benchops = {
+ .init = &init,
+ .add = &add,
+ .del = &del,
+ .get = &get,
+ .update = &update,
+ .check = &check,
+ .empty = &empty,
+ .destroy = &destroy,
+};
+
diff --git a/src/ext/timeouts/bench/bench-llrb.c b/src/ext/timeouts/bench/bench-llrb.c
new file mode 100644
index 0000000000..bdb02f0704
--- /dev/null
+++ b/src/ext/timeouts/bench/bench-llrb.c
@@ -0,0 +1,425 @@
+/* ==========================================================================
+ * llrb.h - Iterative Left-leaning Red-Black Tree.
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2011, 2013 William Ahern <william@25thandClement.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * --------------------------------------------------------------------------
+ * CREDITS:
+ * o Algorithm courtesy of Robert Sedgewick, "Left-leaning Red-Black
+ * Trees" (September 2008); and Robert Sedgewick and Kevin Wayne,
+ * Algorithms (4th ed. 2011).
+ *
+ * Sedgewick touts the simplicity of the recursive implementation,
+ * but at least for the 2-3 tree variant the iterative approach is
+ * almost line-for-line identical. The magic of C pointers helps;
+ * it'd be uglier with Java.
+ *
+ * A couple of missing NULL checks were added to Sedgewick's deletion
+ * example, and insert was optimized to short-circuit rotations when
+ * walking up the tree.
+ *
+ * o Code implemented in the fashion of Niels Provos' excellent *BSD
+ * sys/tree.h pre-processor library.
+ *
+ * Regarding relative performance, I've refrained from sharing my own
+ * benchmarks. Differences in run-time speed were too correlated to
+ * compiler options and other external factors.
+ *
+ * Provos' delete implementation doesn't need to start at the root of
+ * the tree. However, RB_REMOVE must be passed the actual node to be
+ * removed. LLRB_REMOVE merely requires a key, much like
+ * RB_FIND/LLRB_FIND.
+ * ==========================================================================
+ */
+#ifndef LLRB_H
+#define LLRB_H
+
+#define LLRB_VENDOR "william@25thandClement.com"
+#define LLRB_VERSION 0x20130925
+
+#ifndef LLRB_STATIC
+#ifdef __GNUC__
+#define LLRB_STATIC __attribute__((__unused__)) static
+#else
+#define LLRB_STATIC static
+#endif
+#endif
+
+#define LLRB_HEAD(name, type) \
+struct name { struct type *rbh_root; }
+
+#define LLRB_INITIALIZER(root) { 0 }
+
+#define LLRB_INIT(root) do { (root)->rbh_root = 0; } while (0)
+
+#define LLRB_BLACK 0
+#define LLRB_RED 1
+
+#define LLRB_ENTRY(type) \
+struct { struct type *rbe_left, *rbe_right, *rbe_parent; _Bool rbe_color; }
+
+#define LLRB_LEFT(elm, field) (elm)->field.rbe_left
+#define LLRB_RIGHT(elm, field) (elm)->field.rbe_right
+#define LLRB_PARENT(elm, field) (elm)->field.rbe_parent
+#define LLRB_EDGE(head, elm, field) (((elm) == LLRB_ROOT(head))? &LLRB_ROOT(head) : ((elm) == LLRB_LEFT(LLRB_PARENT((elm), field), field))? &LLRB_LEFT(LLRB_PARENT((elm), field), field) : &LLRB_RIGHT(LLRB_PARENT((elm), field), field))
+#define LLRB_COLOR(elm, field) (elm)->field.rbe_color
+#define LLRB_ROOT(head) (head)->rbh_root
+#define LLRB_EMPTY(head) ((head)->rbh_root == 0)
+#define LLRB_ISRED(elm, field) ((elm) && LLRB_COLOR((elm), field) == LLRB_RED)
+
+#define LLRB_PROTOTYPE(name, type, field, cmp) \
+ LLRB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define LLRB_PROTOTYPE_STATIC(name, type, field, cmp) \
+ LLRB_PROTOTYPE_INTERNAL(name, type, field, cmp, LLRB_STATIC)
+#define LLRB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
+attr struct type *name##_LLRB_INSERT(struct name *, struct type *); \
+attr struct type *name##_LLRB_DELETE(struct name *, struct type *); \
+attr struct type *name##_LLRB_FIND(struct name *, struct type *); \
+attr struct type *name##_LLRB_MIN(struct type *); \
+attr struct type *name##_LLRB_MAX(struct type *); \
+attr struct type *name##_LLRB_NEXT(struct type *);
+
+#define LLRB_GENERATE(name, type, field, cmp) \
+ LLRB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define LLRB_GENERATE_STATIC(name, type, field, cmp) \
+ LLRB_GENERATE_INTERNAL(name, type, field, cmp, LLRB_STATIC)
+#define LLRB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
+static inline void name##_LLRB_ROTL(struct type **pivot) { \
+ struct type *a = *pivot; \
+ struct type *b = LLRB_RIGHT(a, field); \
+ if ((LLRB_RIGHT(a, field) = LLRB_LEFT(b, field))) \
+ LLRB_PARENT(LLRB_RIGHT(a, field), field) = a; \
+ LLRB_LEFT(b, field) = a; \
+ LLRB_COLOR(b, field) = LLRB_COLOR(a, field); \
+ LLRB_COLOR(a, field) = LLRB_RED; \
+ LLRB_PARENT(b, field) = LLRB_PARENT(a, field); \
+ LLRB_PARENT(a, field) = b; \
+ *pivot = b; \
+} \
+static inline void name##_LLRB_ROTR(struct type **pivot) { \
+ struct type *b = *pivot; \
+ struct type *a = LLRB_LEFT(b, field); \
+ if ((LLRB_LEFT(b, field) = LLRB_RIGHT(a, field))) \
+ LLRB_PARENT(LLRB_LEFT(b, field), field) = b; \
+ LLRB_RIGHT(a, field) = b; \
+ LLRB_COLOR(a, field) = LLRB_COLOR(b, field); \
+ LLRB_COLOR(b, field) = LLRB_RED; \
+ LLRB_PARENT(a, field) = LLRB_PARENT(b, field); \
+ LLRB_PARENT(b, field) = a; \
+ *pivot = a; \
+} \
+static inline void name##_LLRB_FLIP(struct type *root) { \
+ LLRB_COLOR(root, field) = !LLRB_COLOR(root, field); \
+ LLRB_COLOR(LLRB_LEFT(root, field), field) = !LLRB_COLOR(LLRB_LEFT(root, field), field); \
+ LLRB_COLOR(LLRB_RIGHT(root, field), field) = !LLRB_COLOR(LLRB_RIGHT(root, field), field); \
+} \
+static inline void name##_LLRB_FIXUP(struct type **root) { \
+ if (LLRB_ISRED(LLRB_RIGHT(*root, field), field) && !LLRB_ISRED(LLRB_LEFT(*root, field), field)) \
+ name##_LLRB_ROTL(root); \
+ if (LLRB_ISRED(LLRB_LEFT(*root, field), field) && LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*root, field), field), field)) \
+ name##_LLRB_ROTR(root); \
+ if (LLRB_ISRED(LLRB_LEFT(*root, field), field) && LLRB_ISRED(LLRB_RIGHT(*root, field), field)) \
+ name##_LLRB_FLIP(*root); \
+} \
+attr struct type *name##_LLRB_INSERT(struct name *head, struct type *elm) { \
+ struct type **root = &LLRB_ROOT(head); \
+ struct type *parent = 0; \
+ while (*root) { \
+ int comp = (cmp)((elm), (*root)); \
+ parent = *root; \
+ if (comp < 0) \
+ root = &LLRB_LEFT(*root, field); \
+ else if (comp > 0) \
+ root = &LLRB_RIGHT(*root, field); \
+ else \
+ return *root; \
+ } \
+ LLRB_LEFT((elm), field) = 0; \
+ LLRB_RIGHT((elm), field) = 0; \
+ LLRB_COLOR((elm), field) = LLRB_RED; \
+ LLRB_PARENT((elm), field) = parent; \
+ *root = (elm); \
+ while (parent && (LLRB_ISRED(LLRB_LEFT(parent, field), field) || LLRB_ISRED(LLRB_RIGHT(parent, field), field))) { \
+ root = LLRB_EDGE(head, parent, field); \
+ parent = LLRB_PARENT(parent, field); \
+ name##_LLRB_FIXUP(root); \
+ } \
+ LLRB_COLOR(LLRB_ROOT(head), field) = LLRB_BLACK; \
+ return 0; \
+} \
+static inline void name##_LLRB_MOVL(struct type **pivot) { \
+ name##_LLRB_FLIP(*pivot); \
+ if (LLRB_ISRED(LLRB_LEFT(LLRB_RIGHT(*pivot, field), field), field)) { \
+ name##_LLRB_ROTR(&LLRB_RIGHT(*pivot, field)); \
+ name##_LLRB_ROTL(pivot); \
+ name##_LLRB_FLIP(*pivot); \
+ } \
+} \
+static inline void name##_LLRB_MOVR(struct type **pivot) { \
+ name##_LLRB_FLIP(*pivot); \
+ if (LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*pivot, field), field), field)) { \
+ name##_LLRB_ROTR(pivot); \
+ name##_LLRB_FLIP(*pivot); \
+ } \
+} \
+static inline struct type *name##_DELETEMIN(struct name *head, struct type **root) { \
+ struct type **pivot = root, *deleted, *parent; \
+ while (LLRB_LEFT(*pivot, field)) { \
+ if (!LLRB_ISRED(LLRB_LEFT(*pivot, field), field) && !LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*pivot, field), field), field)) \
+ name##_LLRB_MOVL(pivot); \
+ pivot = &LLRB_LEFT(*pivot, field); \
+ } \
+ deleted = *pivot; \
+ parent = LLRB_PARENT(*pivot, field); \
+ *pivot = 0; \
+ while (root != pivot) { \
+ pivot = LLRB_EDGE(head, parent, field); \
+ parent = LLRB_PARENT(parent, field); \
+ name##_LLRB_FIXUP(pivot); \
+ } \
+ return deleted; \
+} \
+attr struct type *name##_LLRB_DELETE(struct name *head, struct type *elm) { \
+ struct type **root = &LLRB_ROOT(head), *parent = 0, *deleted = 0; \
+ int comp; \
+ while (*root) { \
+ parent = LLRB_PARENT(*root, field); \
+ comp = (cmp)(elm, *root); \
+ if (comp < 0) { \
+ if (LLRB_LEFT(*root, field) && !LLRB_ISRED(LLRB_LEFT(*root, field), field) && !LLRB_ISRED(LLRB_LEFT(LLRB_LEFT(*root, field), field), field)) \
+ name##_LLRB_MOVL(root); \
+ root = &LLRB_LEFT(*root, field); \
+ } else { \
+ if (LLRB_ISRED(LLRB_LEFT(*root, field), field)) { \
+ name##_LLRB_ROTR(root); \
+ comp = (cmp)(elm, *root); \
+ } \
+ if (!comp && !LLRB_RIGHT(*root, field)) { \
+ deleted = *root; \
+ *root = 0; \
+ break; \
+ } \
+ if (LLRB_RIGHT(*root, field) && !LLRB_ISRED(LLRB_RIGHT(*root, field), field) && !LLRB_ISRED(LLRB_LEFT(LLRB_RIGHT(*root, field), field), field)) { \
+ name##_LLRB_MOVR(root); \
+ comp = (cmp)(elm, *root); \
+ } \
+ if (!comp) { \
+ struct type *orphan = name##_DELETEMIN(head, &LLRB_RIGHT(*root, field)); \
+ LLRB_COLOR(orphan, field) = LLRB_COLOR(*root, field); \
+ LLRB_PARENT(orphan, field) = LLRB_PARENT(*root, field); \
+ if ((LLRB_RIGHT(orphan, field) = LLRB_RIGHT(*root, field))) \
+ LLRB_PARENT(LLRB_RIGHT(orphan, field), field) = orphan; \
+ if ((LLRB_LEFT(orphan, field) = LLRB_LEFT(*root, field))) \
+ LLRB_PARENT(LLRB_LEFT(orphan, field), field) = orphan; \
+ deleted = *root; \
+ *root = orphan; \
+ parent = *root; \
+ break; \
+ } else \
+ root = &LLRB_RIGHT(*root, field); \
+ } \
+ } \
+ while (parent) { \
+ root = LLRB_EDGE(head, parent, field); \
+ parent = LLRB_PARENT(parent, field); \
+ name##_LLRB_FIXUP(root); \
+ } \
+ if (LLRB_ROOT(head)) \
+ LLRB_COLOR(LLRB_ROOT(head), field) = LLRB_BLACK; \
+ return deleted; \
+} \
+attr struct type *name##_LLRB_FIND(struct name *head, struct type *key) { \
+ struct type *elm = LLRB_ROOT(head); \
+ while (elm) { \
+ int comp = (cmp)(key, elm); \
+ if (comp < 0) \
+ elm = LLRB_LEFT(elm, field); \
+ else if (comp > 0) \
+ elm = LLRB_RIGHT(elm, field); \
+ else \
+ return elm; \
+ } \
+ return 0; \
+} \
+attr struct type *name##_LLRB_MIN(struct type *elm) { \
+ while (elm && LLRB_LEFT(elm, field)) \
+ elm = LLRB_LEFT(elm, field); \
+ return elm; \
+} \
+attr struct type *name##_LLRB_MAX(struct type *elm) { \
+ while (elm && LLRB_RIGHT(elm, field)) \
+ elm = LLRB_RIGHT(elm, field); \
+ return elm; \
+} \
+attr struct type *name##_LLRB_NEXT(struct type *elm) { \
+ if (LLRB_RIGHT(elm, field)) { \
+ return name##_LLRB_MIN(LLRB_RIGHT(elm, field)); \
+ } else if (LLRB_PARENT(elm, field)) { \
+ if (elm == LLRB_LEFT(LLRB_PARENT(elm, field), field)) \
+ return LLRB_PARENT(elm, field); \
+ while (LLRB_PARENT(elm, field) && elm == LLRB_RIGHT(LLRB_PARENT(elm, field), field)) \
+ elm = LLRB_PARENT(elm, field); \
+ return LLRB_PARENT(elm, field); \
+ } else return 0; \
+}
+
+#define LLRB_INSERT(name, head, elm) name##_LLRB_INSERT((head), (elm))
+#define LLRB_DELETE(name, head, elm) name##_LLRB_DELETE((head), (elm))
+#define LLRB_REMOVE(name, head, elm) name##_LLRB_DELETE((head), (elm))
+#define LLRB_FIND(name, head, elm) name##_LLRB_FIND((head), (elm))
+#define LLRB_MIN(name, head) name##_LLRB_MIN(LLRB_ROOT((head)))
+#define LLRB_MAX(name, head) name##_LLRB_MAX(LLRB_ROOT((head)))
+#define LLRB_NEXT(name, head, elm) name##_LLRB_NEXT((elm))
+
+#define LLRB_FOREACH(elm, name, head) \
+for ((elm) = LLRB_MIN(name, head); (elm); (elm) = name##_LLRB_NEXT((elm)))
+
+#endif /* LLRB_H */
+
+
+#include <stdlib.h>
+
+#include "timeout.h"
+#include "bench.h"
+
+
+struct rbtimeout {
+ timeout_t expires;
+
+ int pending;
+
+ LLRB_ENTRY(rbtimeout) rbe;
+};
+
+struct rbtimeouts {
+ timeout_t curtime;
+ LLRB_HEAD(tree, rbtimeout) tree;
+};
+
+
+static int timeoutcmp(struct rbtimeout *a, struct rbtimeout *b) {
+ if (a->expires < b->expires) {
+ return -1;
+ } else if (a->expires > b->expires) {
+ return 1;
+ } else if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+} /* timeoutcmp() */
+
+LLRB_GENERATE_STATIC(tree, rbtimeout, rbe, timeoutcmp)
+
+static void *init(struct timeout *timeout, size_t count, int verbose) {
+ struct rbtimeouts *T;
+ size_t i;
+
+ T = malloc(sizeof *T);
+ T->curtime = 0;
+ LLRB_INIT(&T->tree);
+
+ for (i = 0; i < count; i++) {
+ struct rbtimeout *to = (void *)&timeout[i];
+ to->expires = 0;
+ to->pending = 0;
+ }
+
+ return T;
+} /* init() */
+
+
+static void add(void *ctx, struct timeout *_to, timeout_t expires) {
+ struct rbtimeouts *T = ctx;
+ struct rbtimeout *to = (void *)_to;
+
+ if (to->pending)
+ LLRB_REMOVE(tree, &T->tree, to);
+
+ to->expires = T->curtime + expires;
+ LLRB_INSERT(tree, &T->tree, to);
+ to->pending = 1;
+} /* add() */
+
+
+static void del(void *ctx, struct timeout *_to) {
+ struct rbtimeouts *T = ctx;
+ struct rbtimeout *to = (void *)_to;
+
+ LLRB_REMOVE(tree, &T->tree, to);
+ to->pending = 0;
+ to->expires = 0;
+} /* del() */
+
+
+static struct timeout *get(void *ctx) {
+ struct rbtimeouts *T = ctx;
+ struct rbtimeout *to;
+
+ if ((to = LLRB_MIN(tree, &T->tree)) && to->expires <= T->curtime) {
+ LLRB_REMOVE(tree, &T->tree, to);
+ to->pending = 0;
+ to->expires = 0;
+
+ return (void *)to;
+ }
+
+ return NULL;
+} /* get() */
+
+
+static void update(void *ctx, timeout_t ts) {
+ struct rbtimeouts *T = ctx;
+ T->curtime = ts;
+} /* update() */
+
+
+static void check(void *ctx) {
+ return;
+} /* check() */
+
+
+static int empty(void *ctx) {
+ struct rbtimeouts *T = ctx;
+
+ return LLRB_EMPTY(&T->tree);
+} /* empty() */
+
+
+static void destroy(void *ctx) {
+ free(ctx);
+ return;
+} /* destroy() */
+
+
+const struct benchops benchops = {
+ .init = &init,
+ .add = &add,
+ .del = &del,
+ .get = &get,
+ .update = &update,
+ .check = &check,
+ .empty = &empty,
+ .destroy = &destroy,
+};
+
diff --git a/src/ext/timeouts/bench/bench-wheel.c b/src/ext/timeouts/bench/bench-wheel.c
new file mode 100644
index 0000000000..0cba1af83e
--- /dev/null
+++ b/src/ext/timeouts/bench/bench-wheel.c
@@ -0,0 +1,81 @@
+#include <stdlib.h>
+
+#define TIMEOUT_PUBLIC static
+
+#include "timeout.h"
+#include "timeout.c"
+#include "bench.h"
+
+
+static void *init(struct timeout *timeout, size_t count, int verbose) {
+ struct timeouts *T;
+ size_t i;
+ int error;
+
+ T = timeouts_open(TIMEOUT_mHZ, &error);
+
+ for (i = 0; i < count; i++) {
+ timeout_init(&timeout[i], 0);
+ }
+
+#if TIMEOUT_DEBUG - 0
+ timeout_debug = verbose;
+#endif
+
+ return T;
+} /* init() */
+
+
+static void add(void *T, struct timeout *to, timeout_t expires) {
+ timeouts_add(T, to, expires);
+} /* add() */
+
+
+static void del(void *T, struct timeout *to) {
+ timeouts_del(T, to);
+} /* del() */
+
+
+static struct timeout *get(void *T) {
+ return timeouts_get(T);
+} /* get() */
+
+
+static void update(void *T, timeout_t ts) {
+ timeouts_update(T, ts);
+} /* update() */
+
+
+static void (check)(void *T) {
+ if (!timeouts_check(T, stderr))
+ _Exit(1);
+} /* check() */
+
+
+static int empty(void *T) {
+ return !(timeouts_pending(T) || timeouts_expired(T));
+} /* empty() */
+
+
+static struct timeout *next(void *T, struct timeouts_it *it) {
+ return timeouts_next(T, it);
+} /* next() */
+
+
+static void destroy(void *T) {
+ timeouts_close(T);
+} /* destroy() */
+
+
+const struct benchops benchops = {
+ .init = &init,
+ .add = &add,
+ .del = &del,
+ .get = &get,
+ .update = &update,
+ .check = &check,
+ .empty = &empty,
+ .next = &next,
+ .destroy = &destroy
+};
+
diff --git a/src/ext/timeouts/bench/bench.c b/src/ext/timeouts/bench/bench.c
new file mode 100644
index 0000000000..0d4cee44a0
--- /dev/null
+++ b/src/ext/timeouts/bench/bench.c
@@ -0,0 +1,293 @@
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#if __APPLE__
+#include <mach/mach_time.h>
+#endif
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "timeout.h"
+#include "bench.h"
+
+#if LUA_VERSION_NUM < 502
+static int lua_absindex(lua_State *L, int idx) {
+ return (idx > 0 || idx <= LUA_REGISTRYINDEX)? idx : lua_gettop(L) + idx + 1;
+} /* lua_absindex() */
+
+static void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) {
+ int i, t = lua_absindex(L, -1 - nup);
+
+ for (; l->name; l++) {
+ for (i = 0; i < nup; i++)
+ lua_pushvalue(L, -nup);
+ lua_pushcclosure(L, l->func, nup);
+ lua_setfield(L, t, l->name);
+ }
+
+ lua_pop(L, nup);
+} /* luaL_setfuncs() */
+
+#define luaL_newlibtable(L, l) \
+ lua_createtable(L, 0, (sizeof (l) / sizeof *(l)) - 1)
+
+#define luaL_newlib(L, l) \
+ (luaL_newlibtable((L), (l)), luaL_setfuncs((L), (l), 0))
+#endif
+
+#ifndef MAX
+#define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+
+struct bench {
+ const char *path;
+ void *solib;
+ size_t count;
+ timeout_t timeout_max;
+ int verbose;
+
+ void *state;
+ struct timeout *timeout;
+ struct benchops ops;
+ timeout_t curtime;
+}; /* struct bench */
+
+
+#if __APPLE__
+static mach_timebase_info_data_t timebase;
+#endif
+
+
+static int long long monotime(void) {
+#if __APPLE__
+ unsigned long long abt;
+
+ abt = mach_absolute_time();
+ abt = abt * timebase.numer / timebase.denom;
+
+ return abt / 1000LL;
+#else
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ return (ts.tv_sec * 1000000L) + (ts.tv_nsec / 1000L);
+#endif
+} /* monotime() */
+
+
+static int bench_clock(lua_State *L) {
+ lua_pushnumber(L, (double)monotime() / 1000000L);
+
+ return 1;
+} /* bench_clock() */
+
+
+static int bench_new(lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+ size_t count = luaL_optinteger(L, 2, 1000000);
+ timeout_t timeout_max = luaL_optinteger(L, 3, 300 * 1000000L);
+ int verbose = (lua_isnone(L, 4))? 0 : lua_toboolean(L, 4);
+ struct bench *B;
+ struct benchops *ops;
+
+ B = lua_newuserdata(L, sizeof *B);
+ memset(B, 0, sizeof *B);
+
+ luaL_getmetatable(L, "BENCH*");
+ lua_setmetatable(L, -2);
+
+ B->count = count;
+ B->timeout_max = timeout_max;
+ B->verbose = verbose;
+
+ if (!(B->timeout = calloc(count, sizeof *B->timeout)))
+ return luaL_error(L, "%s", strerror(errno));
+
+ if (!(B->solib = dlopen(path, RTLD_NOW|RTLD_LOCAL)))
+ return luaL_error(L, "%s: %s", path, dlerror());
+
+ if (!(ops = dlsym(B->solib, "benchops")))
+ return luaL_error(L, "%s: %s", path, dlerror());
+
+ B->ops = *ops;
+ B->state = B->ops.init(B->timeout, B->count, B->verbose);
+
+ return 1;
+} /* bench_new() */
+
+
+static int bench_add(lua_State *L) {
+ struct bench *B = lua_touserdata(L, 1);
+ unsigned i;
+ timeout_t t;
+
+ i = (lua_isnoneornil(L, 2))? random() % B->count : (unsigned)luaL_checkinteger(L, 2);
+ t = (lua_isnoneornil(L, 3))? random() % B->timeout_max : (unsigned)luaL_checkinteger(L, 3);
+
+ B->ops.add(B->state, &B->timeout[i], t);
+
+ return 0;
+} /* bench_add() */
+
+
+static int bench_del(lua_State *L) {
+ struct bench *B = lua_touserdata(L, 1);
+ size_t i = luaL_optinteger(L, 2, random() % B->count);
+ size_t j = luaL_optinteger(L, 3, i);
+
+ while (i <= j && i < B->count) {
+ B->ops.del(B->state, &B->timeout[i]);
+ ++i;
+ }
+
+ return 0;
+} /* bench_del() */
+
+
+static int bench_fill(lua_State *L) {
+ struct bench *B = lua_touserdata(L, 1);
+ size_t count = luaL_optinteger(L, 2, B->count);
+ long timeout_inc = luaL_optinteger(L, 3, -1), timeout_max = 0, timeout;
+ size_t i;
+
+ if (timeout_inc < 0) {
+ for (i = 0; i < count; i++) {
+ timeout = random() % B->timeout_max;
+ B->ops.add(B->state, &B->timeout[i], timeout);
+ timeout_max = MAX(timeout, timeout_max);
+ }
+ } else {
+ for (i = 0; i < count; i++) {
+ timeout = timeout_inc + i;
+ B->ops.add(B->state, &B->timeout[i], timeout_inc + i);
+ timeout_max = MAX(timeout, timeout_max);
+ }
+ }
+
+ lua_pushinteger(L, (lua_Integer)count);
+ lua_pushinteger(L, (lua_Integer)timeout_max);
+
+ return 2;
+} /* bench_fill() */
+
+
+static int bench_expire(lua_State *L) {
+ struct bench *B = lua_touserdata(L, 1);
+ unsigned count = luaL_optinteger(L, 2, B->count);
+ unsigned step = luaL_optinteger(L, 3, 300000);
+ size_t i = 0;
+
+ while (i < count && !B->ops.empty(B->state)) {
+ B->curtime += step;
+ B->ops.update(B->state, B->curtime);
+
+ while (B->ops.get(B->state))
+ i++;
+ }
+
+ lua_pushinteger(L, (lua_Integer)i);
+
+ return 1;
+} /* bench_expire() */
+
+
+static int bench_empty(lua_State *L) {
+ struct bench *B = lua_touserdata(L, 1);
+
+ lua_pushboolean(L, B->ops.empty(B->state));
+
+ return 1;
+} /* bench_empty() */
+
+
+static int bench__next(lua_State *L) {
+ struct bench *B = lua_touserdata(L, lua_upvalueindex(1));
+ struct timeouts_it *it = lua_touserdata(L, lua_upvalueindex(2));
+ struct timeout *to;
+
+ if (!B->ops.next || !(to = B->ops.next(B->state, it)))
+ return 0;
+
+ lua_pushinteger(L, luaL_optinteger(L, 2, 0) + 1);
+
+ lua_newtable(L);
+ lua_pushinteger(L, to->expires);
+ lua_setfield(L, -2, "expires");
+
+ return 2;
+} /* bench__next() */
+
+static int bench__pairs(lua_State *L) {
+ struct timeouts_it *it;
+
+ lua_settop(L, 1);
+
+ it = lua_newuserdata(L, sizeof *it);
+ TIMEOUTS_IT_INIT(it, TIMEOUTS_ALL);
+
+ lua_pushcclosure(L, &bench__next, 2);
+ lua_pushvalue(L, 1);
+ lua_pushinteger(L, 0);
+
+ return 3;
+} /* bench__pairs() */
+
+
+static int bench__gc(lua_State *L) {
+ struct bench *B = lua_touserdata(L, 1);
+
+ if (B->state) {
+ B->ops.destroy(B->state);
+ B->state = NULL;
+ }
+
+ return 0;
+} /* bench__gc() */
+
+
+static const luaL_Reg bench_methods[] = {
+ { "add", &bench_add },
+ { "del", &bench_del },
+ { "fill", &bench_fill },
+ { "expire", &bench_expire },
+ { "empty", &bench_empty },
+ { "close", &bench__gc },
+ { NULL, NULL }
+};
+
+static const luaL_Reg bench_metatable[] = {
+ { "__pairs", &bench__pairs },
+ { "__gc", &bench__gc },
+ { NULL, NULL }
+};
+
+static const luaL_Reg bench_globals[] = {
+ { "new", &bench_new },
+ { "clock", &bench_clock },
+ { NULL, NULL }
+};
+
+int luaopen_bench(lua_State *L) {
+#if __APPLE__
+ mach_timebase_info(&timebase);
+#endif
+
+ if (luaL_newmetatable(L, "BENCH*")) {
+ luaL_setfuncs(L, bench_metatable, 0);
+ luaL_newlib(L, bench_methods);
+ lua_setfield(L, -2, "__index");
+ }
+
+ luaL_newlib(L, bench_globals);
+
+ return 1;
+} /* luaopen_bench() */
+
diff --git a/src/ext/timeouts/bench/bench.h b/src/ext/timeouts/bench/bench.h
new file mode 100644
index 0000000000..bc1f7cf177
--- /dev/null
+++ b/src/ext/timeouts/bench/bench.h
@@ -0,0 +1,11 @@
+struct benchops {
+ void *(*init)(struct timeout *, size_t, int);
+ void (*add)(void *, struct timeout *, timeout_t);
+ void (*del)(void *, struct timeout *);
+ struct timeout *(*get)(void *);
+ void (*update)(void *, timeout_t);
+ void (*check)(void *);
+ int (*empty)(void *);
+ struct timeout *(*next)(void *, struct timeouts_it *);
+ void (*destroy)(void *);
+}; /* struct benchops() */
diff --git a/src/ext/timeouts/bench/bench.plt b/src/ext/timeouts/bench/bench.plt
new file mode 100644
index 0000000000..6e143c65e1
--- /dev/null
+++ b/src/ext/timeouts/bench/bench.plt
@@ -0,0 +1,19 @@
+set terminal postscript color
+
+set key top left
+set xlabel "Number of timeouts"
+set ylabel "Time\n(microseconds)"
+#set logscale x
+
+set title "Time spent installing timeouts" font ",20"
+plot 'heap-add.dat' using 1:($2*1000000) title "min-heap" with lines ls 1 lw 3 lc "red", \
+ 'wheel-add.dat' using 1:($2*1000000) title "hierarchical wheel" with lines ls 1 lw 3 lc "forest-green"
+
+set title "Time spent deleting timeouts" font ",20"
+plot 'heap-del.dat' using 1:($2*1000000) title "min-heap" with lines ls 1 lw 3 lc "red", \
+ 'wheel-del.dat' using 1:($2*1000000) title "hierarchical wheel" with lines ls 1 lw 3 lc "forest-green"
+
+set title "Time spent expiring timeouts\n(by iteratively updating clock ~1000 times)" font ",20"
+plot 'heap-expire.dat' using 1:($2*1000000) title "min-heap" with lines ls 1 lw 3 lc "red", \
+ 'wheel-expire.dat' using 1:($2*1000000) title "hierarchical wheel" with lines ls 1 lw 3 lc "forest-green"
+
diff --git a/src/ext/timeouts/lua/Rules.mk b/src/ext/timeouts/lua/Rules.mk
new file mode 100644
index 0000000000..0f06fce30b
--- /dev/null
+++ b/src/ext/timeouts/lua/Rules.mk
@@ -0,0 +1,20 @@
+$(LUA_APIS:%=$(top_builddir)/lua/%/timeout.so): $(top_srcdir)/lua/timeout-lua.c $(top_srcdir)/timeout.h $(top_srcdir)/timeout.c
+ mkdir -p $(@D)
+ @$(SHRC); echo_cmd $(CC) -o $@ $(top_srcdir)/lua/timeout-lua.c -I$(top_srcdir) -DWHEEL_BIT=$(WHEEL_BIT) -DWHEEL_NUM=$(WHEEL_NUM) $(LUA53_CPPFLAGS) $(ALL_CPPFLAGS) $(ALL_CFLAGS) $(ALL_SOFLAGS) $(ALL_LDFLAGS) $(ALL_LIBS)
+
+$(top_builddir)/lua/5.1/timeouts.so: $(top_builddir)/lua/5.1/timeout.so
+$(top_builddir)/lua/5.2/timeouts.so: $(top_builddir)/lua/5.2/timeout.so
+$(top_builddir)/lua/5.3/timeouts.so: $(top_builddir)/lua/5.3/timeout.so
+
+$(LUA_APIS:%=$(top_builddir)/lua/%/timeouts.so):
+ cd $(@D) && ln -fs timeout.so timeouts.so
+
+lua-5.1: $(top_builddir)/lua/5.1/timeout.so $(top_builddir)/lua/5.1/timeouts.so
+lua-5.2: $(top_builddir)/lua/5.2/timeout.so $(top_builddir)/lua/5.2/timeouts.so
+lua-5.3: $(top_builddir)/lua/5.3/timeout.so $(top_builddir)/lua/5.3/timeouts.so
+
+lua-clean:
+ $(RM) -r $(top_builddir)/lua/5.?
+
+clean: lua-clean
+
diff --git a/src/ext/timeouts/lua/timeout-lua.c b/src/ext/timeouts/lua/timeout-lua.c
new file mode 100644
index 0000000000..4d4e54cba6
--- /dev/null
+++ b/src/ext/timeouts/lua/timeout-lua.c
@@ -0,0 +1,396 @@
+#include <assert.h>
+#include <string.h>
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+#if LUA_VERSION_NUM != 503
+#error only Lua 5.3 supported
+#endif
+
+#define TIMEOUT_PUBLIC static
+#include "timeout.h"
+#include "timeout.c"
+
+#define TIMEOUT_METANAME "struct timeout"
+#define TIMEOUTS_METANAME "struct timeouts*"
+
+static struct timeout *
+to_checkudata(lua_State *L, int index)
+{
+ return luaL_checkudata(L, index, TIMEOUT_METANAME);
+}
+
+static struct timeouts *
+tos_checkudata(lua_State *L, int index)
+{
+ return *(struct timeouts **)luaL_checkudata(L, index, TIMEOUTS_METANAME);
+}
+
+static void
+tos_bind(lua_State *L, int tos_index, int to_index)
+{
+ lua_getuservalue(L, tos_index);
+ lua_pushlightuserdata(L, to_checkudata(L, to_index));
+ lua_pushvalue(L, to_index);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+}
+
+static void
+tos_unbind(lua_State *L, int tos_index, int to_index)
+{
+ lua_getuservalue(L, tos_index);
+ lua_pushlightuserdata(L, to_checkudata(L, to_index));
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+}
+
+static int
+to__index(lua_State *L)
+{
+ struct timeout *to = to_checkudata(L, 1);
+
+ if (lua_type(L, 2 == LUA_TSTRING)) {
+ const char *key = lua_tostring(L, 2);
+
+ if (!strcmp(key, "flags")) {
+ lua_pushinteger(L, to->flags);
+
+ return 1;
+ } else if (!strcmp(key, "expires")) {
+ lua_pushinteger(L, to->expires);
+
+ return 1;
+ }
+ }
+
+ if (LUA_TNIL != lua_getuservalue(L, 1)) {
+ lua_pushvalue(L, 2);
+ if (LUA_TNIL != lua_rawget(L, -2))
+ return 1;
+ }
+
+ lua_pushvalue(L, 2);
+ if (LUA_TNIL != lua_rawget(L, lua_upvalueindex(1)))
+ return 1;
+
+ return 0;
+}
+
+static int
+to__newindex(lua_State *L)
+{
+ if (LUA_TNIL == lua_getuservalue(L, 1)) {
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+ lua_setuservalue(L, 1);
+ }
+
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, 3);
+ lua_rawset(L, -3);
+
+ return 0;
+}
+
+static int
+to__gc(lua_State *L)
+{
+ struct timeout *to = to_checkudata(L, 1);
+
+ /*
+ * NB: On script exit it's possible for a timeout to still be
+ * associated with a timeouts object, particularly when the timeouts
+ * object was created first.
+ */
+ timeout_del(to);
+
+ return 0;
+}
+
+static int
+to_new(lua_State *L)
+{
+ int flags = luaL_optinteger(L, 1, 0);
+ struct timeout *to;
+
+ to = lua_newuserdata(L, sizeof *to);
+ timeout_init(to, flags);
+ luaL_setmetatable(L, TIMEOUT_METANAME);
+
+ return 1;
+}
+
+static const luaL_Reg to_methods[] = {
+ { NULL, NULL },
+};
+
+static const luaL_Reg to_metatable[] = {
+ { "__index", &to__index },
+ { "__newindex", &to__newindex },
+ { "__gc", &to__gc },
+ { NULL, NULL },
+};
+
+static const luaL_Reg to_globals[] = {
+ { "new", &to_new },
+ { NULL, NULL },
+};
+
+static void
+to_newmetatable(lua_State *L)
+{
+ if (luaL_newmetatable(L, TIMEOUT_METANAME)) {
+ /*
+ * fill metamethod table, capturing the methods table as an
+ * upvalue for use by __index metamethod
+ */
+ luaL_newlib(L, to_methods);
+ luaL_setfuncs(L, to_metatable, 1);
+ }
+}
+
+int
+luaopen_timeout(lua_State *L)
+{
+ to_newmetatable(L);
+
+ luaL_newlib(L, to_globals);
+ lua_pushinteger(L, TIMEOUT_INT);
+ lua_setfield(L, -2, "INT");
+ lua_pushinteger(L, TIMEOUT_ABS);
+ lua_setfield(L, -2, "ABS");
+
+ return 1;
+}
+
+static int
+tos_update(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+ lua_Number n = luaL_checknumber(L, 2);
+
+ timeouts_update(T, timeouts_f2i(T, n));
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+}
+
+static int
+tos_step(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+ lua_Number n = luaL_checknumber(L, 2);
+
+ timeouts_step(T, timeouts_f2i(T, n));
+
+ lua_pushvalue(L, 1);
+
+ return 1;
+}
+
+static int
+tos_timeout(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+
+ lua_pushnumber(L, timeouts_i2f(T, timeouts_timeout(T)));
+
+ return 1;
+}
+
+static int
+tos_add(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+ struct timeout *to = to_checkudata(L, 2);
+ lua_Number timeout = luaL_checknumber(L, 3);
+
+ tos_bind(L, 1, 2);
+ timeouts_addf(T, to, timeout);
+
+ return lua_pushvalue(L, 1), 1;
+}
+
+static int
+tos_del(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+ struct timeout *to = to_checkudata(L, 2);
+
+ timeouts_del(T, to);
+ tos_unbind(L, 1, 2);
+
+ return lua_pushvalue(L, 1), 1;
+}
+
+static int
+tos_get(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+ struct timeout *to;
+
+ if (!(to = timeouts_get(T)))
+ return 0;
+
+ lua_getuservalue(L, 1);
+ lua_rawgetp(L, -1, to);
+
+ if (!timeout_pending(to))
+ tos_unbind(L, 1, lua_absindex(L, -1));
+
+ return 1;
+}
+
+static int
+tos_pending(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+
+ lua_pushboolean(L, timeouts_pending(T));
+
+ return 1;
+}
+
+static int
+tos_expired(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+
+ lua_pushboolean(L, timeouts_expired(T));
+
+ return 1;
+}
+
+static int
+tos_check(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, 1);
+
+ lua_pushboolean(L, timeouts_check(T, NULL));
+
+ return 1;
+}
+
+static int
+tos__next(lua_State *L)
+{
+ struct timeouts *T = tos_checkudata(L, lua_upvalueindex(1));
+ struct timeouts_it *it = lua_touserdata(L, lua_upvalueindex(2));
+ struct timeout *to;
+
+ if (!(to = timeouts_next(T, it)))
+ return 0;
+
+ lua_getuservalue(L, lua_upvalueindex(1));
+ lua_rawgetp(L, -1, to);
+
+ return 1;
+}
+
+static int
+tos_timeouts(lua_State *L)
+{
+ int flags = luaL_checkinteger(L, 2);
+ struct timeouts_it *it;
+
+ tos_checkudata(L, 1);
+ lua_pushvalue(L, 1);
+ it = lua_newuserdata(L, sizeof *it);
+ TIMEOUTS_IT_INIT(it, flags);
+ lua_pushcclosure(L, &tos__next, 2);
+
+ return 1;
+}
+
+static int
+tos__gc(lua_State *L)
+{
+ struct timeouts **tos = luaL_checkudata(L, 1, TIMEOUTS_METANAME);
+ struct timeout *to;
+
+ TIMEOUTS_FOREACH(to, *tos, TIMEOUTS_ALL) {
+ timeouts_del(*tos, to);
+ }
+
+ timeouts_close(*tos);
+ *tos = NULL;
+
+ return 0;
+}
+
+static int
+tos_new(lua_State *L)
+{
+ timeout_t hz = luaL_optinteger(L, 1, 0);
+ struct timeouts **T;
+ int error;
+
+ T = lua_newuserdata(L, sizeof *T);
+ luaL_setmetatable(L, TIMEOUTS_METANAME);
+
+ lua_newtable(L);
+ lua_setuservalue(L, -2);
+
+ if (!(*T = timeouts_open(hz, &error)))
+ return luaL_error(L, "%s", strerror(error));
+
+ return 1;
+}
+
+static const luaL_Reg tos_methods[] = {
+ { "update", &tos_update },
+ { "step", &tos_step },
+ { "timeout", &tos_timeout },
+ { "add", &tos_add },
+ { "del", &tos_del },
+ { "get", &tos_get },
+ { "pending", &tos_pending },
+ { "expired", &tos_expired },
+ { "check", &tos_check },
+ { "timeouts", &tos_timeouts },
+ { NULL, NULL },
+};
+
+static const luaL_Reg tos_metatable[] = {
+ { "__gc", &tos__gc },
+ { NULL, NULL },
+};
+
+static const luaL_Reg tos_globals[] = {
+ { "new", &tos_new },
+ { NULL, NULL },
+};
+
+static void
+tos_newmetatable(lua_State *L)
+{
+ if (luaL_newmetatable(L, TIMEOUTS_METANAME)) {
+ luaL_setfuncs(L, tos_metatable, 0);
+ luaL_newlib(L, tos_methods);
+ lua_setfield(L, -2, "__index");
+ }
+}
+
+int
+luaopen_timeouts(lua_State *L)
+{
+ to_newmetatable(L);
+ tos_newmetatable(L);
+
+ luaL_newlib(L, tos_globals);
+ lua_pushinteger(L, TIMEOUTS_PENDING);
+ lua_setfield(L, -2, "PENDING");
+ lua_pushinteger(L, TIMEOUTS_EXPIRED);
+ lua_setfield(L, -2, "EXPIRED");
+ lua_pushinteger(L, TIMEOUTS_ALL);
+ lua_setfield(L, -2, "ALL");
+ lua_pushinteger(L, TIMEOUTS_CLEAR);
+ lua_setfield(L, -2, "CLEAR");
+
+ return 1;
+}
diff --git a/src/ext/timeouts/test-timeout.c b/src/ext/timeouts/test-timeout.c
new file mode 100644
index 0000000000..8077129376
--- /dev/null
+++ b/src/ext/timeouts/test-timeout.c
@@ -0,0 +1,530 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "timeout.h"
+
+#define THE_END_OF_TIME ((timeout_t)-1)
+
+static int check_misc(void) {
+ if (TIMEOUT_VERSION != timeout_version())
+ return 1;
+ if (TIMEOUT_V_REL != timeout_v_rel())
+ return 1;
+ if (TIMEOUT_V_API != timeout_v_api())
+ return 1;
+ if (TIMEOUT_V_ABI != timeout_v_abi())
+ return 1;
+ if (strcmp(timeout_vendor(), TIMEOUT_VENDOR))
+ return 1;
+ return 0;
+}
+
+static int check_open_close(timeout_t hz_set, timeout_t hz_expect) {
+ int err=0;
+ struct timeouts *tos = timeouts_open(hz_set, &err);
+ if (!tos)
+ return 1;
+ if (err)
+ return 1;
+ if (hz_expect != timeouts_hz(tos))
+ return 1;
+ timeouts_close(tos);
+ return 0;
+}
+
+/* Not very random */
+static timeout_t random_to(timeout_t min, timeout_t max)
+{
+ if (max <= min)
+ return min;
+ /* Not actually all that random, but should exercise the code. */
+ timeout_t rand64 = random() * (timeout_t)INT_MAX + random();
+ return min + (rand64 % (max-min));
+}
+
+/* configuration for check_randomized */
+struct rand_cfg {
+ /* When creating timeouts, smallest possible delay */
+ timeout_t min_timeout;
+ /* When creating timeouts, largest possible delay */
+ timeout_t max_timeout;
+ /* First time to start the clock at. */
+ timeout_t start_at;
+ /* Do not advance the clock past this time. */
+ timeout_t end_at;
+ /* Number of timeouts to create and monitor. */
+ int n_timeouts;
+ /* Advance the clock by no more than this each step. */
+ timeout_t max_step;
+ /* Use relative timers and stepping */
+ int relative;
+ /* Every time the clock ticks, try removing this many timeouts at
+ * random. */
+ int try_removing;
+ /* When we're done, advance the clock to the end of time. */
+ int finalize;
+};
+
+static int check_randomized(const struct rand_cfg *cfg)
+{
+#define FAIL() do { \
+ printf("Failure on line %d\n", __LINE__); \
+ goto done; \
+ } while (0)
+
+ int i, err;
+ int rv = 1;
+ struct timeout *t = calloc(cfg->n_timeouts, sizeof(struct timeout));
+ timeout_t *timeouts = calloc(cfg->n_timeouts, sizeof(timeout_t));
+ uint8_t *fired = calloc(cfg->n_timeouts, sizeof(uint8_t));
+ uint8_t *found = calloc(cfg->n_timeouts, sizeof(uint8_t));
+ uint8_t *deleted = calloc(cfg->n_timeouts, sizeof(uint8_t));
+ struct timeouts *tos = timeouts_open(0, &err);
+ timeout_t now = cfg->start_at;
+ int n_added_pending = 0, cnt_added_pending = 0;
+ int n_added_expired = 0, cnt_added_expired = 0;
+ struct timeouts_it it_p, it_e, it_all;
+ int p_done = 0, e_done = 0, all_done = 0;
+ struct timeout *to = NULL;
+ const int rel = cfg->relative;
+
+ if (!t || !timeouts || !tos || !fired || !found || !deleted)
+ FAIL();
+ timeouts_update(tos, cfg->start_at);
+
+ for (i = 0; i < cfg->n_timeouts; ++i) {
+ if (&t[i] != timeout_init(&t[i], rel ? 0 : TIMEOUT_ABS))
+ FAIL();
+ if (timeout_pending(&t[i]))
+ FAIL();
+ if (timeout_expired(&t[i]))
+ FAIL();
+
+ timeouts[i] = random_to(cfg->min_timeout, cfg->max_timeout);
+
+ timeouts_add(tos, &t[i], timeouts[i] - (rel ? now : 0));
+ if (timeouts[i] <= cfg->start_at) {
+ if (timeout_pending(&t[i]))
+ FAIL();
+ if (! timeout_expired(&t[i]))
+ FAIL();
+ ++n_added_expired;
+ } else {
+ if (! timeout_pending(&t[i]))
+ FAIL();
+ if (timeout_expired(&t[i]))
+ FAIL();
+ ++n_added_pending;
+ }
+ }
+
+ if (!!n_added_pending != timeouts_pending(tos))
+ FAIL();
+ if (!!n_added_expired != timeouts_expired(tos))
+ FAIL();
+
+ /* Test foreach, interleaving a few iterators. */
+ TIMEOUTS_IT_INIT(&it_p, TIMEOUTS_PENDING);
+ TIMEOUTS_IT_INIT(&it_e, TIMEOUTS_EXPIRED);
+ TIMEOUTS_IT_INIT(&it_all, TIMEOUTS_ALL);
+ while (! (p_done && e_done && all_done)) {
+ if (!p_done) {
+ to = timeouts_next(tos, &it_p);
+ if (to) {
+ i = to - &t[0];
+ ++found[i];
+ ++cnt_added_pending;
+ } else {
+ p_done = 1;
+ }
+ }
+ if (!e_done) {
+ to = timeouts_next(tos, &it_e);
+ if (to) {
+ i = to - &t[0];
+ ++found[i];
+ ++cnt_added_expired;
+ } else {
+ e_done = 1;
+ }
+ }
+ if (!all_done) {
+ to = timeouts_next(tos, &it_all);
+ if (to) {
+ i = to - &t[0];
+ ++found[i];
+ } else {
+ all_done = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < cfg->n_timeouts; ++i) {
+ if (found[i] != 2)
+ FAIL();
+ }
+ if (cnt_added_expired != n_added_expired)
+ FAIL();
+ if (cnt_added_pending != n_added_pending)
+ FAIL();
+
+ while (NULL != (to = timeouts_get(tos))) {
+ i = to - &t[0];
+ assert(&t[i] == to);
+ if (timeouts[i] > cfg->start_at)
+ FAIL(); /* shouldn't have happened yet */
+
+ --n_added_expired; /* drop expired timeouts. */
+ ++fired[i];
+ }
+
+ if (n_added_expired != 0)
+ FAIL();
+
+ while (now < cfg->end_at) {
+ int n_fired_this_time = 0;
+ timeout_t first_at = timeouts_timeout(tos) + now;
+
+ timeout_t oldtime = now;
+ timeout_t step = random_to(1, cfg->max_step);
+ int another;
+ now += step;
+ if (rel)
+ timeouts_step(tos, step);
+ else
+ timeouts_update(tos, now);
+
+ for (i = 0; i < cfg->try_removing; ++i) {
+ int idx = random() % cfg->n_timeouts;
+ if (! fired[idx]) {
+ timeout_del(&t[idx]);
+ ++deleted[idx];
+ }
+ }
+
+ another = (timeouts_timeout(tos) == 0);
+
+ while (NULL != (to = timeouts_get(tos))) {
+ if (! another)
+ FAIL(); /* Thought we saw the last one! */
+ i = to - &t[0];
+ assert(&t[i] == to);
+ if (timeouts[i] > now)
+ FAIL(); /* shouldn't have happened yet */
+ if (timeouts[i] <= oldtime)
+ FAIL(); /* should have happened already */
+ if (timeouts[i] < first_at)
+ FAIL(); /* first_at should've been earlier */
+ fired[i]++;
+ n_fired_this_time++;
+ another = (timeouts_timeout(tos) == 0);
+ }
+ if (n_fired_this_time && first_at > now)
+ FAIL(); /* first_at should've been earlier */
+ if (another)
+ FAIL(); /* Huh? We think there are more? */
+ if (!timeouts_check(tos, stderr))
+ FAIL();
+ }
+
+ for (i = 0; i < cfg->n_timeouts; ++i) {
+ if (fired[i] > 1)
+ FAIL(); /* Nothing fired twice. */
+ if (timeouts[i] <= now) {
+ if (!(fired[i] || deleted[i]))
+ FAIL();
+ } else {
+ if (fired[i])
+ FAIL();
+ }
+ if (fired[i] && deleted[i])
+ FAIL();
+ if (cfg->finalize > 1) {
+ if (!fired[i])
+ timeout_del(&t[i]);
+ }
+ }
+
+ /* Now nothing more should fire between now and the end of time. */
+ if (cfg->finalize) {
+ timeouts_update(tos, THE_END_OF_TIME);
+ if (cfg->finalize > 1) {
+ if (timeouts_get(tos))
+ FAIL();
+ TIMEOUTS_FOREACH(to, tos, TIMEOUTS_ALL)
+ FAIL();
+ }
+ }
+ rv = 0;
+
+ done:
+ if (tos) timeouts_close(tos);
+ if (t) free(t);
+ if (timeouts) free(timeouts);
+ if (fired) free(fired);
+ if (found) free(found);
+ if (deleted) free(deleted);
+ return rv;
+}
+
+struct intervals_cfg {
+ const timeout_t *timeouts;
+ int n_timeouts;
+ timeout_t start_at;
+ timeout_t end_at;
+ timeout_t skip;
+};
+
+int
+check_intervals(struct intervals_cfg *cfg)
+{
+ int i, err;
+ int rv = 1;
+ struct timeout *to;
+ struct timeout *t = calloc(cfg->n_timeouts, sizeof(struct timeout));
+ unsigned *fired = calloc(cfg->n_timeouts, sizeof(unsigned));
+ struct timeouts *tos = timeouts_open(0, &err);
+
+ timeout_t now = cfg->start_at;
+ if (!t || !tos || !fired)
+ FAIL();
+
+ timeouts_update(tos, now);
+
+ for (i = 0; i < cfg->n_timeouts; ++i) {
+ if (&t[i] != timeout_init(&t[i], TIMEOUT_INT))
+ FAIL();
+ if (timeout_pending(&t[i]))
+ FAIL();
+ if (timeout_expired(&t[i]))
+ FAIL();
+
+ timeouts_add(tos, &t[i], cfg->timeouts[i]);
+ if (! timeout_pending(&t[i]))
+ FAIL();
+ if (timeout_expired(&t[i]))
+ FAIL();
+ }
+
+ while (now < cfg->end_at) {
+ timeout_t delay = timeouts_timeout(tos);
+ if (cfg->skip && delay < cfg->skip)
+ delay = cfg->skip;
+ timeouts_step(tos, delay);
+ now += delay;
+
+ while (NULL != (to = timeouts_get(tos))) {
+ i = to - &t[0];
+ assert(&t[i] == to);
+ fired[i]++;
+ if (0 != (to->expires - cfg->start_at) % cfg->timeouts[i])
+ FAIL();
+ if (to->expires <= now)
+ FAIL();
+ if (to->expires > now + cfg->timeouts[i])
+ FAIL();
+ }
+ if (!timeouts_check(tos, stderr))
+ FAIL();
+ }
+
+ timeout_t duration = now - cfg->start_at;
+ for (i = 0; i < cfg->n_timeouts; ++i) {
+ if (cfg->skip) {
+ if (fired[i] > duration / cfg->timeouts[i])
+ FAIL();
+ } else {
+ if (fired[i] != duration / cfg->timeouts[i])
+ FAIL();
+ }
+ if (!timeout_pending(&t[i]))
+ FAIL();
+ }
+
+ rv = 0;
+ done:
+ if (t) free(t);
+ if (fired) free(fired);
+ if (tos) free(tos);
+ return rv;
+}
+
+int
+main(int argc, char **argv)
+{
+ int j;
+ int n_failed = 0;
+#define DO(fn) do { \
+ printf("."); fflush(stdout); \
+ if (fn) { \
+ ++n_failed; \
+ printf("%s failed\n", #fn); \
+ } \
+ } while (0)
+
+#define DO_N(n, fn) do { \
+ for (j = 0; j < (n); ++j) { \
+ DO(fn); \
+ } \
+ } while (0)
+
+ DO(check_misc());
+ DO(check_open_close(1000, 1000));
+ DO(check_open_close(0, TIMEOUT_mHZ));
+
+ struct rand_cfg cfg1 = {
+ .min_timeout = 1,
+ .max_timeout = 100,
+ .start_at = 5,
+ .end_at = 1000,
+ .n_timeouts = 1000,
+ .max_step = 10,
+ .relative = 0,
+ .try_removing = 0,
+ .finalize = 2,
+ };
+ DO_N(300,check_randomized(&cfg1));
+
+ struct rand_cfg cfg2 = {
+ .min_timeout = 20,
+ .max_timeout = 1000,
+ .start_at = 10,
+ .end_at = 100,
+ .n_timeouts = 1000,
+ .max_step = 5,
+ .relative = 1,
+ .try_removing = 0,
+ .finalize = 2,
+ };
+ DO_N(300,check_randomized(&cfg2));
+
+ struct rand_cfg cfg2b = {
+ .min_timeout = 20,
+ .max_timeout = 1000,
+ .start_at = 10,
+ .end_at = 100,
+ .n_timeouts = 1000,
+ .max_step = 5,
+ .relative = 1,
+ .try_removing = 0,
+ .finalize = 1,
+ };
+ DO_N(300,check_randomized(&cfg2b));
+
+ struct rand_cfg cfg2c = {
+ .min_timeout = 20,
+ .max_timeout = 1000,
+ .start_at = 10,
+ .end_at = 100,
+ .n_timeouts = 1000,
+ .max_step = 5,
+ .relative = 1,
+ .try_removing = 0,
+ .finalize = 0,
+ };
+ DO_N(300,check_randomized(&cfg2c));
+
+ struct rand_cfg cfg3 = {
+ .min_timeout = 2000,
+ .max_timeout = ((uint64_t)1) << 50,
+ .start_at = 100,
+ .end_at = ((uint64_t)1) << 49,
+ .n_timeouts = 1000,
+ .max_step = 1<<31,
+ .relative = 0,
+ .try_removing = 0,
+ .finalize = 2,
+ };
+ DO_N(10,check_randomized(&cfg3));
+
+ struct rand_cfg cfg3b = {
+ .min_timeout = ((uint64_t)1) << 50,
+ .max_timeout = ((uint64_t)1) << 52,
+ .start_at = 100,
+ .end_at = ((uint64_t)1) << 53,
+ .n_timeouts = 1000,
+ .max_step = ((uint64_t)1)<<48,
+ .relative = 0,
+ .try_removing = 0,
+ .finalize = 2,
+ };
+ DO_N(10,check_randomized(&cfg3b));
+
+ struct rand_cfg cfg4 = {
+ .min_timeout = 2000,
+ .max_timeout = ((uint64_t)1) << 30,
+ .start_at = 100,
+ .end_at = ((uint64_t)1) << 26,
+ .n_timeouts = 10000,
+ .max_step = 1<<16,
+ .relative = 0,
+ .try_removing = 3,
+ .finalize = 2,
+ };
+ DO_N(10,check_randomized(&cfg4));
+
+ const timeout_t primes[] = {
+ 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
+ 59,61,67,71,73,79,83,89,97
+ };
+ const timeout_t factors_of_1337[] = {
+ 1, 7, 191, 1337
+ };
+ const timeout_t multiples_of_five[] = {
+ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50
+ };
+
+ struct intervals_cfg icfg1 = {
+ .timeouts = primes,
+ .n_timeouts = sizeof(primes)/sizeof(timeout_t),
+ .start_at = 50,
+ .end_at = 5322,
+ .skip = 0,
+ };
+ DO(check_intervals(&icfg1));
+
+ struct intervals_cfg icfg2 = {
+ .timeouts = factors_of_1337,
+ .n_timeouts = sizeof(factors_of_1337)/sizeof(timeout_t),
+ .start_at = 50,
+ .end_at = 50000,
+ .skip = 0,
+ };
+ DO(check_intervals(&icfg2));
+
+ struct intervals_cfg icfg3 = {
+ .timeouts = multiples_of_five,
+ .n_timeouts = sizeof(multiples_of_five)/sizeof(timeout_t),
+ .start_at = 49,
+ .end_at = 5333,
+ .skip = 0,
+ };
+ DO(check_intervals(&icfg3));
+
+ struct intervals_cfg icfg4 = {
+ .timeouts = primes,
+ .n_timeouts = sizeof(primes)/sizeof(timeout_t),
+ .start_at = 50,
+ .end_at = 5322,
+ .skip = 16,
+ };
+ DO(check_intervals(&icfg4));
+
+ if (n_failed) {
+ puts("\nFAIL");
+ } else {
+ puts("\nOK");
+ }
+ return !!n_failed;
+}
+
+/* TODO:
+
+ * Solve PR#3.
+
+ * Investigate whether any untaken branches are possible.
+
+ */
diff --git a/src/ext/timeouts/timeout-bitops.c b/src/ext/timeouts/timeout-bitops.c
new file mode 100644
index 0000000000..a018f33b95
--- /dev/null
+++ b/src/ext/timeouts/timeout-bitops.c
@@ -0,0 +1,254 @@
+#include <stdint.h>
+#include <limits.h>
+#ifdef _MSC_VER
+#include <intrin.h> /* _BitScanForward, _BitScanReverse */
+#endif
+
+/* First define ctz and clz functions; these are compiler-dependent if
+ * you want them to be fast. */
+#if defined(__GNUC__) && !defined(TIMEOUT_DISABLE_GNUC_BITOPS)
+
+#ifndef LONG_BIT
+#define LONG_BIT (SIZEOF_LONG*CHAR_BIT)
+#endif
+
+/* On GCC and clang and some others, we can use __builtin functions. They
+ * are not defined for n==0, but timeout.s never calls them with n==0. */
+
+#define ctz64(n) __builtin_ctzll(n)
+#define clz64(n) __builtin_clzll(n)
+#if LONG_BIT == 32
+#define ctz32(n) __builtin_ctzl(n)
+#define clz32(n) __builtin_clzl(n)
+#else
+#define ctz32(n) __builtin_ctz(n)
+#define clz32(n) __builtin_clz(n)
+#endif
+
+#elif defined(_MSC_VER) && !defined(TIMEOUT_DISABLE_MSVC_BITOPS)
+
+/* On MSVC, we have these handy functions. We can ignore their return
+ * values, since we will never supply val == 0. */
+
+static __inline int ctz32(unsigned long val)
+{
+ DWORD zeros = 0;
+ _BitScanForward(&zeros, val);
+ return zeros;
+}
+static __inline int clz32(unsigned long val)
+{
+ DWORD zeros = 0;
+ _BitScanReverse(&zeros, val);
+ return zeros;
+}
+#ifdef _WIN64
+/* According to the documentation, these only exist on Win64. */
+static __inline int ctz64(uint64_t val)
+{
+ DWORD zeros = 0;
+ _BitScanForward64(&zeros, val);
+ return zeros;
+}
+static __inline int clz64(uint64_t val)
+{
+ DWORD zeros = 0;
+ _BitScanReverse64(&zeros, val);
+ return zeros;
+}
+#else
+static __inline int ctz64(uint64_t val)
+{
+ uint32_t lo = (uint32_t) val;
+ uint32_t hi = (uint32_t) (val >> 32);
+ return lo ? ctz32(lo) : 32 + ctz32(hi);
+}
+static __inline int clz64(uint64_t val)
+{
+ uint32_t lo = (uint32_t) val;
+ uint32_t hi = (uint32_t) (val >> 32);
+ return hi ? clz32(hi) : 32 + clz32(lo);
+}
+#endif
+
+/* End of MSVC case. */
+
+#else
+
+/* TODO: There are more clever ways to do this in the generic case. */
+
+
+#define process_(one, cz_bits, bits) \
+ if (x < ( one << (cz_bits - bits))) { rv += bits; x <<= bits; }
+
+#define process64(bits) process_((UINT64_C(1)), 64, (bits))
+static inline int clz64(uint64_t x)
+{
+ int rv = 0;
+
+ process64(32);
+ process64(16);
+ process64(8);
+ process64(4);
+ process64(2);
+ process64(1);
+ return rv;
+}
+#define process32(bits) process_((UINT32_C(1)), 32, (bits))
+static inline int clz32(uint32_t x)
+{
+ int rv = 0;
+
+ process32(16);
+ process32(8);
+ process32(4);
+ process32(2);
+ process32(1);
+ return rv;
+}
+
+#undef process_
+#undef process32
+#undef process64
+#define process_(one, bits) \
+ if ((x & ((one << (bits))-1)) == 0) { rv += bits; x >>= bits; }
+
+#define process64(bits) process_((UINT64_C(1)), bits)
+static inline int ctz64(uint64_t x)
+{
+ int rv = 0;
+
+ process64(32);
+ process64(16);
+ process64(8);
+ process64(4);
+ process64(2);
+ process64(1);
+ return rv;
+}
+
+#define process32(bits) process_((UINT32_C(1)), bits)
+static inline int ctz32(uint32_t x)
+{
+ int rv = 0;
+
+ process32(16);
+ process32(8);
+ process32(4);
+ process32(2);
+ process32(1);
+ return rv;
+}
+
+#undef process32
+#undef process64
+#undef process_
+
+/* End of generic case */
+
+#endif /* End of defining ctz */
+
+#ifdef TEST_BITOPS
+#include <stdio.h>
+#include <stdlib.h>
+
+static uint64_t testcases[] = {
+ 13371337 * 10,
+ 100,
+ 385789752,
+ 82574,
+ (((uint64_t)1)<<63) + (((uint64_t)1)<<31) + 10101
+};
+
+static int
+naive_clz(int bits, uint64_t v)
+{
+ int r = 0;
+ uint64_t bit = ((uint64_t)1) << (bits-1);
+ while (bit && 0 == (v & bit)) {
+ r++;
+ bit >>= 1;
+ }
+ /* printf("clz(%d,%lx) -> %d\n", bits, v, r); */
+ return r;
+}
+
+static int
+naive_ctz(int bits, uint64_t v)
+{
+ int r = 0;
+ uint64_t bit = 1;
+ while (bit && 0 == (v & bit)) {
+ r++;
+ bit <<= 1;
+ if (r == bits)
+ break;
+ }
+ /* printf("ctz(%d,%lx) -> %d\n", bits, v, r); */
+ return r;
+}
+
+static int
+check(uint64_t vv)
+{
+ uint32_t v32 = (uint32_t) vv;
+
+ if (vv == 0)
+ return 1; /* c[tl]z64(0) is undefined. */
+
+ if (ctz64(vv) != naive_ctz(64, vv)) {
+ printf("mismatch with ctz64: %d\n", ctz64(vv));
+ exit(1);
+ return 0;
+ }
+ if (clz64(vv) != naive_clz(64, vv)) {
+ printf("mismatch with clz64: %d\n", clz64(vv));
+ exit(1);
+ return 0;
+ }
+
+ if (v32 == 0)
+ return 1; /* c[lt]z(0) is undefined. */
+
+ if (ctz32(v32) != naive_ctz(32, v32)) {
+ printf("mismatch with ctz32: %d\n", ctz32(v32));
+ exit(1);
+ return 0;
+ }
+ if (clz32(v32) != naive_clz(32, v32)) {
+ printf("mismatch with clz32: %d\n", clz32(v32));
+ exit(1);
+ return 0;
+ }
+ return 1;
+}
+
+int
+main(int c, char **v)
+{
+ unsigned int i;
+ const unsigned int n = sizeof(testcases)/sizeof(testcases[0]);
+ int result = 0;
+
+ for (i = 0; i <= 63; ++i) {
+ uint64_t x = 1 << i;
+ if (!check(x))
+ result = 1;
+ --x;
+ if (!check(x))
+ result = 1;
+ }
+
+ for (i = 0; i < n; ++i) {
+ if (! check(testcases[i]))
+ result = 1;
+ }
+ if (result) {
+ puts("FAIL");
+ } else {
+ puts("OK");
+ }
+ return result;
+}
+#endif
+
diff --git a/src/ext/timeouts/timeout-debug.h b/src/ext/timeouts/timeout-debug.h
new file mode 100644
index 0000000000..fc727a6b42
--- /dev/null
+++ b/src/ext/timeouts/timeout-debug.h
@@ -0,0 +1,77 @@
+/*
+ * D E B U G R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if TIMEOUT_DEBUG - 0
+#include <stdlib.h>
+#include <stdio.h>
+
+#undef TIMEOUT_DEBUG
+#define TIMEOUT_DEBUG 1
+#define DEBUG_LEVEL timeout_debug
+
+static int timeout_debug;
+
+#define SAYit_(lvl, fmt, ...) do { \
+ if (DEBUG_LEVEL >= (lvl)) \
+ fprintf(stderr, fmt "%s", __FILE__, __LINE__, __func__, __VA_ARGS__); \
+} while (0)
+
+#define SAYit(lvl, ...) SAYit_((lvl), "%s:%d:%s: " __VA_ARGS__, "\n")
+
+#define PANIC(...) do { \
+ SAYit(0, __VA_ARGS__); \
+ _Exit(EXIT_FAILURE); \
+} while (0)
+#else
+#undef TIMEOUT_DEBUG
+#define TIMEOUT_DEBUG 0
+#define DEBUG_LEVEL 0
+
+#define SAYit(...) (void)0
+#endif
+
+#define SAY(...) SAYit(1, __VA_ARGS__)
+#define HAI SAY("HAI")
+
+
+static inline char *fmt_(char *buf, uint64_t ts, int wheel_bit, int wheel_num) {
+ char *p = buf;
+ int wheel, n, i;
+
+ for (wheel = wheel_num - 2; wheel >= 0; wheel--) {
+ n = ((1 << wheel_bit) - 1) & (ts >> (wheel * WHEEL_BIT));
+
+ for (i = wheel_bit - 1; i >= 0; i--) {
+ *p++ = '0' + !!(n & (1 << i));
+ }
+
+ if (wheel != 0)
+ *p++ = ':';
+ }
+
+ *p = 0;
+
+ return buf;
+} /* fmt_() */
+
+#define fmt(ts) fmt_(((char[((1 << WHEEL_BIT) * WHEEL_NUM) + WHEEL_NUM + 1]){ 0 }), (ts), WHEEL_BIT, WHEEL_NUM)
+
+
+static inline char *bin64_(char *buf, uint64_t n, int wheel_bit) {
+ char *p = buf;
+ int i;
+
+ for (i = 0; i < (1 << wheel_bit); i++) {
+ *p++ = "01"[0x1 & (n >> (((1 << wheel_bit) - 1) - i))];
+ }
+
+ *p = 0;
+
+ return buf;
+} /* bin64_() */
+
+#define bin64(ts) bin64_(((char[((1 << WHEEL_BIT) * WHEEL_NUM) + 1]){ 0 }), (ts), WHEEL_BIT)
+
+
diff --git a/src/ext/timeouts/timeout.c b/src/ext/timeouts/timeout.c
new file mode 100644
index 0000000000..713ec219ce
--- /dev/null
+++ b/src/ext/timeouts/timeout.c
@@ -0,0 +1,754 @@
+/* ==========================================================================
+ * timeout.c - Tickless hierarchical timing wheel.
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2013, 2014 William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#ifdef HAVE_CONFIG_H
+#include "orconfig.h"
+#endif
+#include <limits.h> /* CHAR_BIT */
+
+#include <stddef.h> /* NULL */
+#include <stdlib.h> /* malloc(3) free(3) */
+#include <stdio.h> /* FILE fprintf(3) */
+
+#include <inttypes.h> /* UINT64_C uint64_t */
+
+#include <string.h> /* memset(3) */
+
+#include <errno.h> /* errno */
+
+#include "tor_queue.h" /* TAILQ(3) */
+
+#include "timeout.h"
+
+#ifndef TIMEOUT_DEBUG
+#define TIMEOUT_DEBUG 0
+#endif
+
+#if TIMEOUT_DEBUG - 0
+#include "timeout-debug.h"
+#endif
+
+#ifdef TIMEOUT_DISABLE_RELATIVE_ACCESS
+#define TO_SET_TIMEOUTS(to, T) ((void)0)
+#else
+#define TO_SET_TIMEOUTS(to, T) ((to)->timeouts = (T))
+#endif
+
+/*
+ * A N C I L L A R Y R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define abstime_t timeout_t /* for documentation purposes */
+#define reltime_t timeout_t /* "" */
+
+#if !defined countof
+#define countof(a) (sizeof (a) / sizeof *(a))
+#endif
+
+#if !defined endof
+#define endof(a) (&(a)[countof(a)])
+#endif
+
+#if !defined MIN
+#define MIN(a, b) (((a) < (b))? (a) : (b))
+#endif
+
+#if !defined MAX
+#define MAX(a, b) (((a) > (b))? (a) : (b))
+#endif
+
+#if !defined TOR_TAILQ_CONCAT
+#define TOR_TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TOR_TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TOR_TAILQ_INIT((head2)); \
+ } \
+} while (0)
+#endif
+
+#if !defined TOR_TAILQ_FOREACH_SAFE
+#define TOR_TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TOR_TAILQ_FIRST(head); \
+ (var) && ((tvar) = TOR_TAILQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+#endif
+
+
+/*
+ * B I T M A N I P U L A T I O N R O U T I N E S
+ *
+ * The macros and routines below implement wheel parameterization. The
+ * inputs are:
+ *
+ * WHEEL_BIT - The number of value bits mapped in each wheel. The
+ * lowest-order WHEEL_BIT bits index the lowest-order (highest
+ * resolution) wheel, the next group of WHEEL_BIT bits the
+ * higher wheel, etc.
+ *
+ * WHEEL_NUM - The number of wheels. WHEEL_BIT * WHEEL_NUM = the number of
+ * value bits used by all the wheels. For the default of 6 and
+ * 4, only the low 24 bits are processed. Any timeout value
+ * larger than this will cycle through again.
+ *
+ * The implementation uses bit fields to remember which slot in each wheel
+ * is populated, and to generate masks of expiring slots according to the
+ * current update interval (i.e. the "tickless" aspect). The slots to
+ * process in a wheel are (populated-set & interval-mask).
+ *
+ * WHEEL_BIT cannot be larger than 6 bits because 2^6 -> 64 is the largest
+ * number of slots which can be tracked in a uint64_t integer bit field.
+ * WHEEL_BIT cannot be smaller than 3 bits because of our rotr and rotl
+ * routines, which only operate on all the value bits in an integer, and
+ * there's no integer smaller than uint8_t.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if !defined WHEEL_BIT
+#define WHEEL_BIT 6
+#endif
+
+#if !defined WHEEL_NUM
+#define WHEEL_NUM 4
+#endif
+
+#define WHEEL_LEN (1U << WHEEL_BIT)
+#define WHEEL_MAX (WHEEL_LEN - 1)
+#define WHEEL_MASK (WHEEL_LEN - 1)
+#define TIMEOUT_MAX ((TIMEOUT_C(1) << (WHEEL_BIT * WHEEL_NUM)) - 1)
+
+#include "timeout-bitops.c"
+
+#if WHEEL_BIT == 6
+#define ctz(n) ctz64(n)
+#define clz(n) clz64(n)
+#define fls(n) ((int)(64 - clz64(n)))
+#else
+#define ctz(n) ctz32(n)
+#define clz(n) clz32(n)
+#define fls(n) ((int)(32 - clz32(n)))
+#endif
+
+#if WHEEL_BIT == 6
+#define WHEEL_C(n) UINT64_C(n)
+#define WHEEL_PRIu PRIu64
+#define WHEEL_PRIx PRIx64
+
+typedef uint64_t wheel_t;
+
+#elif WHEEL_BIT == 5
+
+#define WHEEL_C(n) UINT32_C(n)
+#define WHEEL_PRIu PRIu32
+#define WHEEL_PRIx PRIx32
+
+typedef uint32_t wheel_t;
+
+#elif WHEEL_BIT == 4
+
+#define WHEEL_C(n) UINT16_C(n)
+#define WHEEL_PRIu PRIu16
+#define WHEEL_PRIx PRIx16
+
+typedef uint16_t wheel_t;
+
+#elif WHEEL_BIT == 3
+
+#define WHEEL_C(n) UINT8_C(n)
+#define WHEEL_PRIu PRIu8
+#define WHEEL_PRIx PRIx8
+
+typedef uint8_t wheel_t;
+
+#else
+#error invalid WHEEL_BIT value
+#endif
+
+
+static inline wheel_t rotl(const wheel_t v, int c) {
+ if (!(c &= (sizeof v * CHAR_BIT - 1)))
+ return v;
+
+ return (v << c) | (v >> (sizeof v * CHAR_BIT - c));
+} /* rotl() */
+
+
+static inline wheel_t rotr(const wheel_t v, int c) {
+ if (!(c &= (sizeof v * CHAR_BIT - 1)))
+ return v;
+
+ return (v >> c) | (v << (sizeof v * CHAR_BIT - c));
+} /* rotr() */
+
+
+/*
+ * T I M E R R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+TOR_TAILQ_HEAD(timeout_list, timeout);
+
+struct timeouts {
+ struct timeout_list wheel[WHEEL_NUM][WHEEL_LEN], expired;
+
+ wheel_t pending[WHEEL_NUM];
+
+ timeout_t curtime;
+ timeout_t hertz;
+}; /* struct timeouts */
+
+
+static struct timeouts *timeouts_init(struct timeouts *T, timeout_t hz) {
+ unsigned i, j;
+
+ for (i = 0; i < countof(T->wheel); i++) {
+ for (j = 0; j < countof(T->wheel[i]); j++) {
+ TOR_TAILQ_INIT(&T->wheel[i][j]);
+ }
+ }
+
+ TOR_TAILQ_INIT(&T->expired);
+
+ for (i = 0; i < countof(T->pending); i++) {
+ T->pending[i] = 0;
+ }
+
+ T->curtime = 0;
+ T->hertz = (hz)? hz : TIMEOUT_mHZ;
+
+ return T;
+} /* timeouts_init() */
+
+
+TIMEOUT_PUBLIC struct timeouts *timeouts_open(timeout_t hz, int *error) {
+ struct timeouts *T;
+
+ if ((T = malloc(sizeof *T)))
+ return timeouts_init(T, hz);
+
+ *error = errno;
+
+ return NULL;
+} /* timeouts_open() */
+
+
+static void timeouts_reset(struct timeouts *T) {
+ struct timeout_list reset;
+ struct timeout *to;
+ unsigned i, j;
+
+ TOR_TAILQ_INIT(&reset);
+
+ for (i = 0; i < countof(T->wheel); i++) {
+ for (j = 0; j < countof(T->wheel[i]); j++) {
+ TOR_TAILQ_CONCAT(&reset, &T->wheel[i][j], tqe);
+ }
+ }
+
+ TOR_TAILQ_CONCAT(&reset, &T->expired, tqe);
+
+ TOR_TAILQ_FOREACH(to, &reset, tqe) {
+ to->pending = NULL;
+ TO_SET_TIMEOUTS(to, NULL);
+ }
+} /* timeouts_reset() */
+
+
+TIMEOUT_PUBLIC void timeouts_close(struct timeouts *T) {
+ /*
+ * NOTE: Delete installed timeouts so timeout_pending() and
+ * timeout_expired() worked as expected.
+ */
+ timeouts_reset(T);
+
+ free(T);
+} /* timeouts_close() */
+
+
+TIMEOUT_PUBLIC timeout_t timeouts_hz(struct timeouts *T) {
+ return T->hertz;
+} /* timeouts_hz() */
+
+
+TIMEOUT_PUBLIC void timeouts_del(struct timeouts *T, struct timeout *to) {
+ if (to->pending) {
+ TOR_TAILQ_REMOVE(to->pending, to, tqe);
+
+ if (to->pending != &T->expired && TOR_TAILQ_EMPTY(to->pending)) {
+ ptrdiff_t index_ = to->pending - &T->wheel[0][0];
+ int wheel = (int) (index_ / WHEEL_LEN);
+ int slot = index_ % WHEEL_LEN;
+
+ T->pending[wheel] &= ~(WHEEL_C(1) << slot);
+ }
+
+ to->pending = NULL;
+ TO_SET_TIMEOUTS(to, NULL);
+ }
+} /* timeouts_del() */
+
+
+static inline reltime_t timeout_rem(struct timeouts *T, struct timeout *to) {
+ return to->expires - T->curtime;
+} /* timeout_rem() */
+
+
+static inline int timeout_wheel(timeout_t timeout) {
+ /* must be called with timeout != 0, so fls input is nonzero */
+ return (fls(MIN(timeout, TIMEOUT_MAX)) - 1) / WHEEL_BIT;
+} /* timeout_wheel() */
+
+
+static inline int timeout_slot(int wheel, timeout_t expires) {
+ return WHEEL_MASK & ((expires >> (wheel * WHEEL_BIT)) - !!wheel);
+} /* timeout_slot() */
+
+
+static void timeouts_sched(struct timeouts *T, struct timeout *to, timeout_t expires) {
+ timeout_t rem;
+ int wheel, slot;
+
+ timeouts_del(T, to);
+
+ to->expires = expires;
+
+ TO_SET_TIMEOUTS(to, T);
+
+ if (expires > T->curtime) {
+ rem = timeout_rem(T, to);
+
+ /* rem is nonzero since:
+ * rem == timeout_rem(T,to),
+ * == to->expires - T->curtime
+ * and above we have expires > T->curtime.
+ */
+ wheel = timeout_wheel(rem);
+ slot = timeout_slot(wheel, to->expires);
+
+ to->pending = &T->wheel[wheel][slot];
+ TOR_TAILQ_INSERT_TAIL(to->pending, to, tqe);
+
+ T->pending[wheel] |= WHEEL_C(1) << slot;
+ } else {
+ to->pending = &T->expired;
+ TOR_TAILQ_INSERT_TAIL(to->pending, to, tqe);
+ }
+} /* timeouts_sched() */
+
+
+#ifndef TIMEOUT_DISABLE_INTERVALS
+static void timeouts_readd(struct timeouts *T, struct timeout *to) {
+ to->expires += to->interval;
+
+ if (to->expires <= T->curtime) {
+ /* If we've missed the next firing of this timeout, reschedule
+ * it to occur at the next multiple of its interval after
+ * the last time that it fired.
+ */
+ timeout_t n = T->curtime - to->expires;
+ timeout_t r = n % to->interval;
+ to->expires = T->curtime + (to->interval - r);
+ }
+
+ timeouts_sched(T, to, to->expires);
+} /* timeouts_readd() */
+#endif
+
+
+TIMEOUT_PUBLIC void timeouts_add(struct timeouts *T, struct timeout *to, timeout_t timeout) {
+#ifndef TIMEOUT_DISABLE_INTERVALS
+ if (to->flags & TIMEOUT_INT)
+ to->interval = MAX(1, timeout);
+#endif
+
+ if (to->flags & TIMEOUT_ABS)
+ timeouts_sched(T, to, timeout);
+ else
+ timeouts_sched(T, to, T->curtime + timeout);
+} /* timeouts_add() */
+
+
+TIMEOUT_PUBLIC void timeouts_update(struct timeouts *T, abstime_t curtime) {
+ timeout_t elapsed = curtime - T->curtime;
+ struct timeout_list todo;
+ int wheel;
+
+ TOR_TAILQ_INIT(&todo);
+
+ /*
+ * There's no avoiding looping over every wheel. It's best to keep
+ * WHEEL_NUM smallish.
+ */
+ for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
+ wheel_t pending;
+
+ /*
+ * Calculate the slots expiring in this wheel
+ *
+ * If the elapsed time is greater than the maximum period of
+ * the wheel, mark every position as expiring.
+ *
+ * Otherwise, to determine the expired slots fill in all the
+ * bits between the last slot processed and the current
+ * slot, inclusive of the last slot. We'll bitwise-AND this
+ * with our pending set below.
+ *
+ * If a wheel rolls over, force a tick of the next higher
+ * wheel.
+ */
+ if ((elapsed >> (wheel * WHEEL_BIT)) > WHEEL_MAX) {
+ pending = (wheel_t)~WHEEL_C(0);
+ } else {
+ wheel_t _elapsed = WHEEL_MASK & (elapsed >> (wheel * WHEEL_BIT));
+ int oslot, nslot;
+
+ /*
+ * TODO: It's likely that at least one of the
+ * following three bit fill operations is redundant
+ * or can be replaced with a simpler operation.
+ */
+ oslot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
+ pending = rotl(((UINT64_C(1) << _elapsed) - 1), oslot);
+
+ nslot = WHEEL_MASK & (curtime >> (wheel * WHEEL_BIT));
+ pending |= rotr(rotl(((WHEEL_C(1) << _elapsed) - 1), nslot), (int)_elapsed);
+ pending |= WHEEL_C(1) << nslot;
+ }
+
+ while (pending & T->pending[wheel]) {
+ /* ctz input cannot be zero: loop condition. */
+ int slot = ctz(pending & T->pending[wheel]);
+ TOR_TAILQ_CONCAT(&todo, &T->wheel[wheel][slot], tqe);
+ T->pending[wheel] &= ~(UINT64_C(1) << slot);
+ }
+
+ if (!(0x1 & pending))
+ break; /* break if we didn't wrap around end of wheel */
+
+ /* if we're continuing, the next wheel must tick at least once */
+ elapsed = MAX(elapsed, (WHEEL_LEN << (wheel * WHEEL_BIT)));
+ }
+
+ T->curtime = curtime;
+
+ while (!TOR_TAILQ_EMPTY(&todo)) {
+ struct timeout *to = TOR_TAILQ_FIRST(&todo);
+
+ TOR_TAILQ_REMOVE(&todo, to, tqe);
+ to->pending = NULL;
+
+ timeouts_sched(T, to, to->expires);
+ }
+
+ return;
+} /* timeouts_update() */
+
+TIMEOUT_PUBLIC timeout_t timeouts_get_curtime(struct timeouts *T) {
+ return T->curtime;
+} /* timeouts_get_curtime() */
+
+TIMEOUT_PUBLIC void timeouts_step(struct timeouts *T, reltime_t elapsed) {
+ timeouts_update(T, T->curtime + elapsed);
+} /* timeouts_step() */
+
+
+TIMEOUT_PUBLIC bool timeouts_pending(struct timeouts *T) {
+ wheel_t pending = 0;
+ int wheel;
+
+ for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
+ pending |= T->pending[wheel];
+ }
+
+ return !!pending;
+} /* timeouts_pending() */
+
+
+TIMEOUT_PUBLIC bool timeouts_expired(struct timeouts *T) {
+ return !TOR_TAILQ_EMPTY(&T->expired);
+} /* timeouts_expired() */
+
+
+/*
+ * Calculate the interval before needing to process any timeouts pending on
+ * any wheel.
+ *
+ * (This is separated from the public API routine so we can evaluate our
+ * wheel invariant assertions irrespective of the expired queue.)
+ *
+ * This might return a timeout value sooner than any installed timeout if
+ * only higher-order wheels have timeouts pending. We can only know when to
+ * process a wheel, not precisely when a timeout is scheduled. Our timeout
+ * accuracy could be off by 2^(N*M)-1 units where N is the wheel number and
+ * M is WHEEL_BIT. Only timeouts which have fallen through to wheel 0 can be
+ * known exactly.
+ *
+ * We should never return a timeout larger than the lowest actual timeout.
+ */
+static timeout_t timeouts_int(struct timeouts *T) {
+ timeout_t timeout = ~TIMEOUT_C(0), _timeout;
+ timeout_t relmask;
+ int wheel, slot;
+
+ relmask = 0;
+
+ for (wheel = 0; wheel < WHEEL_NUM; wheel++) {
+ if (T->pending[wheel]) {
+ slot = WHEEL_MASK & (T->curtime >> (wheel * WHEEL_BIT));
+
+ /* ctz input cannot be zero: T->pending[wheel] is
+ * nonzero, so rotr() is nonzero. */
+ _timeout = (ctz(rotr(T->pending[wheel], slot)) + !!wheel) << (wheel * WHEEL_BIT);
+ /* +1 to higher order wheels as those timeouts are one rotation in the future (otherwise they'd be on a lower wheel or expired) */
+
+ _timeout -= relmask & T->curtime;
+ /* reduce by how much lower wheels have progressed */
+
+ timeout = MIN(_timeout, timeout);
+ }
+
+ relmask <<= WHEEL_BIT;
+ relmask |= WHEEL_MASK;
+ }
+
+ return timeout;
+} /* timeouts_int() */
+
+
+/*
+ * Calculate the interval our caller can wait before needing to process
+ * events.
+ */
+TIMEOUT_PUBLIC timeout_t timeouts_timeout(struct timeouts *T) {
+ if (!TOR_TAILQ_EMPTY(&T->expired))
+ return 0;
+
+ return timeouts_int(T);
+} /* timeouts_timeout() */
+
+
+TIMEOUT_PUBLIC struct timeout *timeouts_get(struct timeouts *T) {
+ if (!TOR_TAILQ_EMPTY(&T->expired)) {
+ struct timeout *to = TOR_TAILQ_FIRST(&T->expired);
+
+ TOR_TAILQ_REMOVE(&T->expired, to, tqe);
+ to->pending = NULL;
+ TO_SET_TIMEOUTS(to, NULL);
+
+#ifndef TIMEOUT_DISABLE_INTERVALS
+ if ((to->flags & TIMEOUT_INT) && to->interval > 0)
+ timeouts_readd(T, to);
+#endif
+
+ return to;
+ } else {
+ return 0;
+ }
+} /* timeouts_get() */
+
+
+/*
+ * Use dumb looping to locate the earliest timeout pending on the wheel so
+ * our invariant assertions can check the result of our optimized code.
+ */
+static struct timeout *timeouts_min(struct timeouts *T) {
+ struct timeout *to, *min = NULL;
+ unsigned i, j;
+
+ for (i = 0; i < countof(T->wheel); i++) {
+ for (j = 0; j < countof(T->wheel[i]); j++) {
+ TOR_TAILQ_FOREACH(to, &T->wheel[i][j], tqe) {
+ if (!min || to->expires < min->expires)
+ min = to;
+ }
+ }
+ }
+
+ return min;
+} /* timeouts_min() */
+
+
+/*
+ * Check some basic algorithm invariants. If these invariants fail then
+ * something is definitely broken.
+ */
+#define report(...) do { \
+ if ((fp)) \
+ fprintf(fp, __VA_ARGS__); \
+} while (0)
+
+#define check(expr, ...) do { \
+ if (!(expr)) { \
+ report(__VA_ARGS__); \
+ return 0; \
+ } \
+} while (0)
+
+TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *T, FILE *fp) {
+ timeout_t timeout;
+ struct timeout *to;
+
+ if ((to = timeouts_min(T))) {
+ check(to->expires > T->curtime, "missed timeout (expires:%" TIMEOUT_PRIu " <= curtime:%" TIMEOUT_PRIu ")\n", to->expires, T->curtime);
+
+ timeout = timeouts_int(T);
+ check(timeout <= to->expires - T->curtime, "wrong soft timeout (soft:%" TIMEOUT_PRIu " > hard:%" TIMEOUT_PRIu ") (expires:%" TIMEOUT_PRIu "; curtime:%" TIMEOUT_PRIu ")\n", timeout, (to->expires - T->curtime), to->expires, T->curtime);
+
+ timeout = timeouts_timeout(T);
+ check(timeout <= to->expires - T->curtime, "wrong soft timeout (soft:%" TIMEOUT_PRIu " > hard:%" TIMEOUT_PRIu ") (expires:%" TIMEOUT_PRIu "; curtime:%" TIMEOUT_PRIu ")\n", timeout, (to->expires - T->curtime), to->expires, T->curtime);
+ } else {
+ timeout = timeouts_timeout(T);
+
+ if (!TOR_TAILQ_EMPTY(&T->expired))
+ check(timeout == 0, "wrong soft timeout (soft:%" TIMEOUT_PRIu " != hard:%" TIMEOUT_PRIu ")\n", timeout, TIMEOUT_C(0));
+ else
+ check(timeout == ~TIMEOUT_C(0), "wrong soft timeout (soft:%" TIMEOUT_PRIu " != hard:%" TIMEOUT_PRIu ")\n", timeout, ~TIMEOUT_C(0));
+ }
+
+ return 1;
+} /* timeouts_check() */
+
+
+#define ENTER \
+ do { \
+ static const int pc0 = __LINE__; \
+ switch (pc0 + it->pc) { \
+ case __LINE__: (void)0
+
+#define SAVE_AND_DO(do_statement) \
+ do { \
+ it->pc = __LINE__ - pc0; \
+ do_statement; \
+ case __LINE__: (void)0; \
+ } while (0)
+
+#define YIELD(rv) \
+ SAVE_AND_DO(return (rv))
+
+#define LEAVE \
+ SAVE_AND_DO(break); \
+ } \
+ } while (0)
+
+TIMEOUT_PUBLIC struct timeout *timeouts_next(struct timeouts *T, struct timeouts_it *it) {
+ struct timeout *to;
+
+ ENTER;
+
+ if (it->flags & TIMEOUTS_EXPIRED) {
+ if (it->flags & TIMEOUTS_CLEAR) {
+ while ((to = timeouts_get(T))) {
+ YIELD(to);
+ }
+ } else {
+ TOR_TAILQ_FOREACH_SAFE(to, &T->expired, tqe, it->to) {
+ YIELD(to);
+ }
+ }
+ }
+
+ if (it->flags & TIMEOUTS_PENDING) {
+ for (it->i = 0; it->i < countof(T->wheel); it->i++) {
+ for (it->j = 0; it->j < countof(T->wheel[it->i]); it->j++) {
+ TOR_TAILQ_FOREACH_SAFE(to, &T->wheel[it->i][it->j], tqe, it->to) {
+ YIELD(to);
+ }
+ }
+ }
+ }
+
+ LEAVE;
+
+ return NULL;
+} /* timeouts_next */
+
+#undef LEAVE
+#undef YIELD
+#undef SAVE_AND_DO
+#undef ENTER
+
+
+/*
+ * T I M E O U T R O U T I N E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *to, int flags) {
+ memset(to, 0, sizeof *to);
+
+ to->flags = flags;
+
+ return to;
+} /* timeout_init() */
+
+
+#ifndef TIMEOUT_DISABLE_RELATIVE_ACCESS
+TIMEOUT_PUBLIC bool timeout_pending(struct timeout *to) {
+ return to->pending && to->pending != &to->timeouts->expired;
+} /* timeout_pending() */
+
+
+TIMEOUT_PUBLIC bool timeout_expired(struct timeout *to) {
+ return to->pending && to->pending == &to->timeouts->expired;
+} /* timeout_expired() */
+
+
+TIMEOUT_PUBLIC void timeout_del(struct timeout *to) {
+ timeouts_del(to->timeouts, to);
+} /* timeout_del() */
+#endif
+
+
+/*
+ * V E R S I O N I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+TIMEOUT_PUBLIC int timeout_version(void) {
+ return TIMEOUT_VERSION;
+} /* timeout_version() */
+
+
+TIMEOUT_PUBLIC const char *timeout_vendor(void) {
+ return TIMEOUT_VENDOR;
+} /* timeout_version() */
+
+
+TIMEOUT_PUBLIC int timeout_v_rel(void) {
+ return TIMEOUT_V_REL;
+} /* timeout_version() */
+
+
+TIMEOUT_PUBLIC int timeout_v_abi(void) {
+ return TIMEOUT_V_ABI;
+} /* timeout_version() */
+
+
+TIMEOUT_PUBLIC int timeout_v_api(void) {
+ return TIMEOUT_V_API;
+} /* timeout_version() */
+
diff --git a/src/ext/timeouts/timeout.h b/src/ext/timeouts/timeout.h
new file mode 100644
index 0000000000..b35874e153
--- /dev/null
+++ b/src/ext/timeouts/timeout.h
@@ -0,0 +1,256 @@
+/* ==========================================================================
+ * timeout.h - Tickless hierarchical timing wheel.
+ * --------------------------------------------------------------------------
+ * Copyright (c) 2013, 2014 William Ahern
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit
+ * persons to whom the Software is furnished to do so, subject to the
+ * following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+ * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * ==========================================================================
+ */
+#ifndef TIMEOUT_H
+#define TIMEOUT_H
+
+#include <stdbool.h> /* bool */
+#include <stdio.h> /* FILE */
+
+#include <inttypes.h> /* PRIu64 PRIx64 PRIX64 uint64_t */
+
+#include "tor_queue.h" /* TAILQ(3) */
+
+
+/*
+ * V E R S I O N I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#if !defined TIMEOUT_PUBLIC
+#define TIMEOUT_PUBLIC
+#endif
+
+#define TIMEOUT_VERSION TIMEOUT_V_REL
+#define TIMEOUT_VENDOR "william@25thandClement.com"
+
+#define TIMEOUT_V_REL 0x20160226
+#define TIMEOUT_V_ABI 0x20160224
+#define TIMEOUT_V_API 0x20160226
+
+TIMEOUT_PUBLIC int timeout_version(void);
+
+TIMEOUT_PUBLIC const char *timeout_vendor(void);
+
+TIMEOUT_PUBLIC int timeout_v_rel(void);
+
+TIMEOUT_PUBLIC int timeout_v_abi(void);
+
+TIMEOUT_PUBLIC int timeout_v_api(void);
+
+
+/*
+ * I N T E G E R T Y P E I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#define TIMEOUT_C(n) UINT64_C(n)
+#define TIMEOUT_PRIu PRIu64
+#define TIMEOUT_PRIx PRIx64
+#define TIMEOUT_PRIX PRIX64
+
+#define TIMEOUT_mHZ TIMEOUT_C(1000)
+#define TIMEOUT_uHZ TIMEOUT_C(1000000)
+#define TIMEOUT_nHZ TIMEOUT_C(1000000000)
+
+typedef uint64_t timeout_t;
+
+#define timeout_error_t int /* for documentation purposes */
+
+
+/*
+ * C A L L B A C K I N T E R F A C E
+ *
+ * Callback function parameters unspecified to make embedding into existing
+ * applications easier.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef TIMEOUT_CB_OVERRIDE
+struct timeout_cb {
+ void (*fn)(void);
+ void *arg;
+}; /* struct timeout_cb */
+#endif
+
+/*
+ * T I M E O U T I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef TIMEOUT_DISABLE_INTERVALS
+#define TIMEOUT_INT 0x01 /* interval (repeating) timeout */
+#endif
+#define TIMEOUT_ABS 0x02 /* treat timeout values as absolute */
+
+#define TIMEOUT_INITIALIZER(flags) { (flags) }
+
+#define timeout_setcb(to, fn, arg) do { \
+ (to)->callback.fn = (fn); \
+ (to)->callback.arg = (arg); \
+} while (0)
+
+struct timeout {
+ int flags;
+
+ timeout_t expires;
+ /* absolute expiration time */
+
+ struct timeout_list *pending;
+ /* timeout list if pending on wheel or expiry queue */
+
+ TOR_TAILQ_ENTRY(timeout) tqe;
+ /* entry member for struct timeout_list lists */
+
+#ifndef TIMEOUT_DISABLE_CALLBACKS
+ struct timeout_cb callback;
+ /* optional callback information */
+#endif
+
+#ifndef TIMEOUT_DISABLE_INTERVALS
+ timeout_t interval;
+ /* timeout interval if periodic */
+#endif
+
+#ifndef TIMEOUT_DISABLE_RELATIVE_ACCESS
+ struct timeouts *timeouts;
+ /* timeouts collection if member of */
+#endif
+}; /* struct timeout */
+
+
+TIMEOUT_PUBLIC struct timeout *timeout_init(struct timeout *, int);
+/* initialize timeout structure (same as TIMEOUT_INITIALIZER) */
+
+#ifndef TIMEOUT_DISABLE_RELATIVE_ACCESS
+TIMEOUT_PUBLIC bool timeout_pending(struct timeout *);
+/* true if on timing wheel, false otherwise */
+
+TIMEOUT_PUBLIC bool timeout_expired(struct timeout *);
+/* true if on expired queue, false otherwise */
+
+TIMEOUT_PUBLIC void timeout_del(struct timeout *);
+/* remove timeout from any timing wheel (okay if not member of any) */
+#endif
+
+/*
+ * T I M I N G W H E E L I N T E R F A C E S
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+struct timeouts;
+
+TIMEOUT_PUBLIC struct timeouts *timeouts_open(timeout_t, timeout_error_t *);
+/* open a new timing wheel, setting optional HZ (for float conversions) */
+
+TIMEOUT_PUBLIC void timeouts_close(struct timeouts *);
+/* destroy timing wheel */
+
+TIMEOUT_PUBLIC timeout_t timeouts_hz(struct timeouts *);
+/* return HZ setting (for float conversions) */
+
+TIMEOUT_PUBLIC void timeouts_update(struct timeouts *, timeout_t);
+/* update timing wheel with current absolute time */
+
+TIMEOUT_PUBLIC void timeouts_step(struct timeouts *, timeout_t);
+/* step timing wheel by relative time */
+
+TIMEOUT_PUBLIC timeout_t timeouts_get_curtime(struct timeouts *);
+/* Return the current tick. */
+
+TIMEOUT_PUBLIC timeout_t timeouts_timeout(struct timeouts *);
+/* return interval to next required update */
+
+TIMEOUT_PUBLIC void timeouts_add(struct timeouts *, struct timeout *, timeout_t);
+/* add timeout to timing wheel */
+
+TIMEOUT_PUBLIC void timeouts_del(struct timeouts *, struct timeout *);
+/* remove timeout from any timing wheel or expired queue (okay if on neither) */
+
+TIMEOUT_PUBLIC struct timeout *timeouts_get(struct timeouts *);
+/* return any expired timeout (caller should loop until NULL-return) */
+
+TIMEOUT_PUBLIC bool timeouts_pending(struct timeouts *);
+/* return true if any timeouts pending on timing wheel */
+
+TIMEOUT_PUBLIC bool timeouts_expired(struct timeouts *);
+/* return true if any timeouts on expired queue */
+
+TIMEOUT_PUBLIC bool timeouts_check(struct timeouts *, FILE *);
+/* return true if invariants hold. describes failures to optional file handle. */
+
+#define TIMEOUTS_PENDING 0x10
+#define TIMEOUTS_EXPIRED 0x20
+#define TIMEOUTS_ALL (TIMEOUTS_PENDING|TIMEOUTS_EXPIRED)
+#define TIMEOUTS_CLEAR 0x40
+
+#define TIMEOUTS_IT_INITIALIZER(flags) { (flags), 0, 0, 0, 0 }
+
+#define TIMEOUTS_IT_INIT(cur, _flags) do { \
+ (cur)->flags = (_flags); \
+ (cur)->pc = 0; \
+} while (0)
+
+struct timeouts_it {
+ int flags;
+ unsigned pc, i, j;
+ struct timeout *to;
+}; /* struct timeouts_it */
+
+TIMEOUT_PUBLIC struct timeout *timeouts_next(struct timeouts *, struct timeouts_it *);
+/* return next timeout in pending wheel or expired queue. caller can delete
+ * the returned timeout, but should not otherwise manipulate the timing
+ * wheel. in particular, caller SHOULD NOT delete any other timeout as that
+ * could invalidate cursor state and trigger a use-after-free.
+ */
+
+#define TIMEOUTS_FOREACH(var, T, flags) \
+ struct timeouts_it _it = TIMEOUTS_IT_INITIALIZER((flags)); \
+ while (((var) = timeouts_next((T), &_it)))
+
+
+/*
+ * B O N U S W H E E L I N T E R F A C E S
+ *
+ * I usually use floating point timeouts in all my code, but it's cleaner to
+ * separate it to keep the core algorithmic code simple.
+ *
+ * Using macros instead of static inline routines where <math.h> routines
+ * might be used to keep -lm linking optional.
+ *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include <math.h> /* ceil(3) */
+
+#define timeouts_f2i(T, f) \
+ ((timeout_t)ceil((f) * timeouts_hz((T)))) /* prefer late expiration over early */
+
+#define timeouts_i2f(T, i) \
+ ((double)(i) / timeouts_hz((T)))
+
+#define timeouts_addf(T, to, timeout) \
+ timeouts_add((T), (to), timeouts_f2i((T), (timeout)))
+
+#endif /* TIMEOUT_H */
diff --git a/src/ext/tinytest.c b/src/ext/tinytest.c
index f6baeeb9a5..3fb1b39c71 100644
--- a/src/ext/tinytest.c
+++ b/src/ext/tinytest.c
@@ -69,15 +69,16 @@ static int n_skipped = 0; /**< Number of tests that have been skipped. */
static int opt_forked = 0; /**< True iff we're called from inside a win32 fork*/
static int opt_nofork = 0; /**< Suppress calls to fork() for debugging. */
static int opt_verbosity = 1; /**< -==quiet,0==terse,1==normal,2==verbose */
-const char *verbosity_flag = "";
+static const char *verbosity_flag = "";
-const struct testlist_alias_t *cfg_aliases=NULL;
+static const struct testlist_alias_t *cfg_aliases=NULL;
enum outcome { SKIP=2, OK=1, FAIL=0 };
static enum outcome cur_test_outcome = 0;
-const char *cur_test_prefix = NULL; /**< prefix of the current test group */
+/** prefix of the current test group */
+static const char *cur_test_prefix = NULL;
/** Name of the current test, if we haven't logged is yet. Used for --quiet */
-const char *cur_test_name = NULL;
+static const char *cur_test_name = NULL;
#ifdef _WIN32
/* Copy of argv[0] for win32. */
diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h
index dfe5f89e1a..3ffde6e09b 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.4.4
+/* trunnel-impl.h -- copied from Trunnel v1.4.6
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -11,12 +11,12 @@
#ifndef TRUNNEL_IMPL_H_INCLUDED_
#define TRUNNEL_IMPL_H_INCLUDED_
-#include "trunnel.h"
-#include <assert.h>
-#include <string.h>
#ifdef TRUNNEL_LOCAL_H
#include "trunnel-local.h"
#endif
+#include "trunnel.h"
+#include <assert.h>
+#include <string.h>
#if defined(_MSC_VER) && (_MSC_VER < 1600)
#define uint8_t unsigned char
diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c
index 0ed75aa9a4..3994422643 100644
--- a/src/ext/trunnel/trunnel.c
+++ b/src/ext/trunnel/trunnel.c
@@ -1,4 +1,4 @@
-/* trunnel.c -- copied from Trunnel v1.4.4
+/* trunnel.c -- copied from Trunnel v1.4.6
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -10,9 +10,9 @@
* See trunnel-impl.h for documentation of these functions.
*/
+#include "trunnel-impl.h"
#include <stdlib.h>
#include <string.h>
-#include "trunnel-impl.h"
#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h
index 62e87ee50c..41068b8fb3 100644
--- a/src/ext/trunnel/trunnel.h
+++ b/src/ext/trunnel/trunnel.h
@@ -1,4 +1,4 @@
-/* trunnel.h -- copied from Trunnel v1.4.4
+/* trunnel.h -- copied from Trunnel v1.4.6
* 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 047a863ef5..33fd7e0f4a 100644
--- a/src/or/addressmap.c
+++ b/src/or/addressmap.c
@@ -264,18 +264,18 @@ addressmap_clear_invalid_automaps(const or_options_t *options)
clear_all = 1; /* This should be impossible, but let's be sure. */
STRMAP_FOREACH_MODIFY(addressmap, src_address, addressmap_entry_t *, ent) {
- int remove = clear_all;
+ int remove_this = clear_all;
if (ent->source != ADDRMAPSRC_AUTOMAP)
continue; /* not an automap mapping. */
- if (!remove) {
- remove = ! addressmap_address_should_automap(src_address, options);
+ if (!remove_this) {
+ remove_this = ! addressmap_address_should_automap(src_address, options);
}
- if (!remove && ! address_is_in_virtual_range(ent->new_address))
- remove = 1;
+ if (!remove_this && ! address_is_in_virtual_range(ent->new_address))
+ remove_this = 1;
- if (remove) {
+ if (remove_this) {
addressmap_ent_remove(src_address, ent);
MAP_DEL_CURRENT(src_address);
}
@@ -774,7 +774,7 @@ parse_virtual_addr_network(const char *val, sa_family_t family,
const int ipv6 = (family == AF_INET6);
tor_addr_t addr;
maskbits_t bits;
- const int max_bits = ipv6 ? 40 : 16;
+ const int max_prefix_bits = ipv6 ? 104 : 16;
virtual_addr_conf_t *conf = ipv6 ? &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4;
if (!val || val[0] == '\0') {
@@ -804,10 +804,10 @@ parse_virtual_addr_network(const char *val, sa_family_t family,
}
#endif
- if (bits > max_bits) {
+ if (bits > max_prefix_bits) {
if (msg)
tor_asprintf(msg, "VirtualAddressNetwork%s expects a /%d "
- "network or larger",ipv6?"IPv6":"", max_bits);
+ "network or larger",ipv6?"IPv6":"", max_prefix_bits);
return -1;
}
@@ -896,10 +896,10 @@ addressmap_get_virtual_address(int type)
tor_assert(addressmap);
if (type == RESOLVED_TYPE_HOSTNAME) {
- char rand[10];
+ char rand_bytes[10];
do {
- crypto_rand(rand, sizeof(rand));
- base32_encode(buf,sizeof(buf),rand,sizeof(rand));
+ crypto_rand(rand_bytes, sizeof(rand_bytes));
+ base32_encode(buf,sizeof(buf),rand_bytes,sizeof(rand_bytes));
strlcat(buf, ".virtual", sizeof(buf));
} while (strmap_get(addressmap, buf));
return tor_strdup(buf);
@@ -1107,11 +1107,11 @@ addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
smartlist_add_asprintf(sl, "%s%s %s%s NEVER",
src_wc, key, dst_wc, val->new_address);
else {
- char time[ISO_TIME_LEN+1];
- format_iso_time(time, val->expires);
+ char isotime[ISO_TIME_LEN+1];
+ format_iso_time(isotime, val->expires);
smartlist_add_asprintf(sl, "%s%s %s%s \"%s\"",
src_wc, key, dst_wc, val->new_address,
- time);
+ isotime);
}
} else {
smartlist_add_asprintf(sl, "%s%s %s%s",
diff --git a/src/or/buffers.c b/src/or/buffers.c
index a41af5f429..89382d1d8e 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -6,10 +6,22 @@
/**
* \file buffers.c
- * \brief Implements a generic interface buffer. Buffers are
- * fairly opaque string holders that can read to or flush from:
- * memory, file descriptors, or TLS connections. Buffers are implemented
- * as linked lists of memory chunks.
+ * \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"
@@ -128,7 +140,7 @@ chunk_repack(chunk_t *chunk)
/** Keep track of total size of allocated chunks for consistency asserts */
static size_t total_bytes_allocated_in_chunks = 0;
static void
-chunk_free_unchecked(chunk_t *chunk)
+buf_chunk_free_unchecked(chunk_t *chunk)
{
if (!chunk)
return;
@@ -190,9 +202,12 @@ chunk_grow(chunk_t *chunk, size_t sz)
/** Return the allocation size we'd like to use to hold <b>target</b>
* bytes. */
-static inline size_t
+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;
@@ -252,7 +267,7 @@ buf_pullup(buf_t *buf, size_t bytes)
dest->next = src->next;
if (buf->tail == src)
buf->tail = dest;
- chunk_free_unchecked(src);
+ buf_chunk_free_unchecked(src);
} else {
memcpy(CHUNK_WRITE_PTR(dest), src->data, n);
dest->datalen += n;
@@ -298,7 +313,7 @@ buf_remove_from_front(buf_t *buf, size_t n)
buf->head = victim->next;
if (buf->tail == victim)
buf->tail = NULL;
- chunk_free_unchecked(victim);
+ buf_chunk_free_unchecked(victim);
}
}
check();
@@ -338,7 +353,7 @@ buf_clear(buf_t *buf)
buf->datalen = 0;
for (chunk = buf->head; chunk; chunk = next) {
next = chunk->next;
- chunk_free_unchecked(chunk);
+ buf_chunk_free_unchecked(chunk);
}
buf->head = buf->tail = NULL;
}
@@ -429,7 +444,7 @@ static chunk_t *
buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
{
chunk_t *chunk;
- struct timeval now;
+
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) {
@@ -438,8 +453,7 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
chunk = chunk_new_with_alloc_size(preferred_chunk_size(capacity));
}
- tor_gettimeofday_cached_monotonic(&now);
- chunk->inserted_time = (uint32_t)tv_to_msec(&now);
+ chunk->inserted_time = (uint32_t)monotime_coarse_absolute_msec();
if (buf->tail) {
tor_assert(buf->head);
@@ -454,8 +468,8 @@ buf_add_chunk_with_capacity(buf_t *buf, size_t capacity, int capped)
}
/** Return the age of the oldest chunk in the buffer <b>buf</b>, in
- * milliseconds. Requires the current time, in truncated milliseconds since
- * the epoch, as its input <b>now</b>.
+ * 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)
@@ -533,12 +547,12 @@ read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls,
* (because of EOF), set *<b>reached_eof</b> to 1 and return 0. Return -1 on
* error; else return the number of bytes read.
*/
-/* XXXX024 indicate "read blocked" somehow? */
+/* 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)
{
- /* XXXX024 It's stupid to overload the return values for these functions:
+ /* 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;
@@ -711,7 +725,7 @@ flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk,
int
flush_buf(tor_socket_t s, buf_t *buf, size_t sz, size_t *buf_flushlen)
{
- /* XXXX024 It's stupid to overload the return values for these functions:
+ /* 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;
@@ -938,97 +952,6 @@ fetch_var_cell_from_buf(buf_t *buf, var_cell_t **out, int linkproto)
return 1;
}
-#ifdef USE_BUFFEREVENTS
-/** Try to read <b>n</b> bytes from <b>buf</b> at <b>pos</b> (which may be
- * NULL for the start of the buffer), copying the data only if necessary. Set
- * *<b>data_out</b> to a pointer to the desired bytes. Set <b>free_out</b>
- * to 1 if we needed to malloc *<b>data</b> because the original bytes were
- * noncontiguous; 0 otherwise. Return the number of bytes actually available
- * at *<b>data_out</b>.
- */
-static ssize_t
-inspect_evbuffer(struct evbuffer *buf, char **data_out, size_t n,
- int *free_out, struct evbuffer_ptr *pos)
-{
- int n_vecs, i;
-
- if (evbuffer_get_length(buf) < n)
- n = evbuffer_get_length(buf);
- if (n == 0)
- return 0;
- n_vecs = evbuffer_peek(buf, n, pos, NULL, 0);
- tor_assert(n_vecs > 0);
- if (n_vecs == 1) {
- struct evbuffer_iovec v;
- i = evbuffer_peek(buf, n, pos, &v, 1);
- tor_assert(i == 1);
- *data_out = v.iov_base;
- *free_out = 0;
- return v.iov_len;
- } else {
- ev_ssize_t copied;
- *data_out = tor_malloc(n);
- *free_out = 1;
- copied = evbuffer_copyout(buf, *data_out, n);
- tor_assert(copied >= 0 && (size_t)copied == n);
- return copied;
- }
-}
-
-/** As fetch_var_cell_from_buf, buf works on an evbuffer. */
-int
-fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
- int linkproto)
-{
- char *hdr = NULL;
- int free_hdr = 0;
- size_t n;
- size_t buf_len;
- uint8_t command;
- uint16_t cell_length;
- var_cell_t *cell;
- int result = 0;
- 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;
- buf_len = evbuffer_get_length(buf);
- if (buf_len < header_len)
- return 0;
-
- n = inspect_evbuffer(buf, &hdr, header_len, &free_hdr, NULL);
- tor_assert(n >= header_len);
-
- command = get_uint8(hdr + circ_id_len);
- if (!(cell_command_is_var_length(command, linkproto))) {
- goto done;
- }
-
- cell_length = ntohs(get_uint16(hdr + circ_id_len + 1));
- if (buf_len < (size_t)(header_len+cell_length)) {
- result = 1; /* Not all here yet. */
- goto done;
- }
-
- cell = var_cell_new(cell_length);
- cell->command = command;
- if (wide_circ_ids)
- cell->circ_id = ntohl(get_uint32(hdr));
- else
- cell->circ_id = ntohs(get_uint16(hdr));
- evbuffer_drain(buf, header_len);
- evbuffer_remove(buf, cell->payload, cell_length);
- *out = cell;
- result = 1;
-
- done:
- if (free_hdr && hdr)
- tor_free(hdr);
- return result;
-}
-#endif
-
/** 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.
@@ -1271,94 +1194,6 @@ fetch_from_buf_http(buf_t *buf,
return 1;
}
-#ifdef USE_BUFFEREVENTS
-/** As fetch_from_buf_http, buf works on an evbuffer. */
-int
-fetch_from_evbuffer_http(struct evbuffer *buf,
- char **headers_out, size_t max_headerlen,
- char **body_out, size_t *body_used, size_t max_bodylen,
- int force_complete)
-{
- struct evbuffer_ptr crlf, content_length;
- size_t headerlen, bodylen, contentlen;
-
- /* Find the first \r\n\r\n in the buffer */
- crlf = evbuffer_search(buf, "\r\n\r\n", 4, NULL);
- if (crlf.pos < 0) {
- /* We didn't find one. */
- if (evbuffer_get_length(buf) > max_headerlen)
- return -1; /* Headers too long. */
- return 0; /* Headers not here yet. */
- } else if (crlf.pos > (int)max_headerlen) {
- return -1; /* Headers too long. */
- }
-
- headerlen = crlf.pos + 4; /* Skip over the \r\n\r\n */
- bodylen = evbuffer_get_length(buf) - headerlen;
- if (bodylen > max_bodylen)
- return -1; /* body too long */
-
- /* Look for the first occurrence of CONTENT_LENGTH insize buf before the
- * crlfcrlf */
- content_length = evbuffer_search_range(buf, CONTENT_LENGTH,
- strlen(CONTENT_LENGTH), NULL, &crlf);
-
- if (content_length.pos >= 0) {
- /* We found a content_length: parse it and figure out if the body is here
- * yet. */
- struct evbuffer_ptr eol;
- char *data = NULL;
- int free_data = 0;
- int n, i;
- n = evbuffer_ptr_set(buf, &content_length, strlen(CONTENT_LENGTH),
- EVBUFFER_PTR_ADD);
- tor_assert(n == 0);
- eol = evbuffer_search_eol(buf, &content_length, NULL, EVBUFFER_EOL_CRLF);
- tor_assert(eol.pos > content_length.pos);
- tor_assert(eol.pos <= crlf.pos);
- inspect_evbuffer(buf, &data, eol.pos - content_length.pos, &free_data,
- &content_length);
-
- i = atoi(data);
- if (free_data)
- tor_free(data);
- if (i < 0) {
- log_warn(LD_PROTOCOL, "Content-Length is less than zero; it looks like "
- "someone is trying to crash us.");
- return -1;
- }
- contentlen = i;
- /* 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);
- }
- }
-
- if (headers_out) {
- *headers_out = tor_malloc(headerlen+1);
- evbuffer_remove(buf, *headers_out, headerlen);
- (*headers_out)[headerlen] = '\0';
- }
- if (body_out) {
- tor_assert(headers_out);
- tor_assert(body_used);
- *body_used = bodylen;
- *body_out = tor_malloc(bodylen+1);
- evbuffer_remove(buf, *body_out, bodylen);
- (*body_out)[bodylen] = '\0';
- }
- return 1;
-}
-#endif
-
/**
* Wait this many seconds before warning the user about using SOCKS unsafely
* again (requires that WarnUnsafeSocks is turned on). */
@@ -1478,86 +1313,6 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req,
return res;
}
-#ifdef USE_BUFFEREVENTS
-/* As fetch_from_buf_socks(), but targets an evbuffer instead. */
-int
-fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
- int log_sockstype, int safe_socks)
-{
- char *data;
- ssize_t n_drain;
- size_t datalen, buflen, want_length;
- int res;
-
- buflen = evbuffer_get_length(buf);
- if (buflen < 2)
- return 0;
-
- {
- /* See if we can find the socks request in the first chunk of the buffer.
- */
- struct evbuffer_iovec v;
- int i;
- n_drain = 0;
- i = evbuffer_peek(buf, -1, NULL, &v, 1);
- tor_assert(i == 1);
- data = v.iov_base;
- datalen = v.iov_len;
- want_length = 0;
-
- res = parse_socks(data, datalen, req, log_sockstype,
- safe_socks, &n_drain, &want_length);
-
- if (n_drain < 0)
- evbuffer_drain(buf, evbuffer_get_length(buf));
- else if (n_drain > 0)
- evbuffer_drain(buf, n_drain);
-
- if (res)
- return res;
- }
-
- /* Okay, the first chunk of the buffer didn't have a complete socks request.
- * That means that either we don't have a whole socks request at all, or
- * it's gotten split up. We're going to try passing parse_socks() bigger
- * and bigger chunks until either it says "Okay, I got it", or it says it
- * will need more data than we currently have. */
-
- /* Loop while we have more data that we haven't given parse_socks() yet. */
- do {
- int free_data = 0;
- const size_t last_wanted = want_length;
- n_drain = 0;
- data = NULL;
- datalen = inspect_evbuffer(buf, &data, want_length, &free_data, NULL);
-
- want_length = 0;
- res = parse_socks(data, datalen, req, log_sockstype,
- safe_socks, &n_drain, &want_length);
-
- if (free_data)
- tor_free(data);
-
- if (n_drain < 0)
- evbuffer_drain(buf, evbuffer_get_length(buf));
- else if (n_drain > 0)
- evbuffer_drain(buf, n_drain);
-
- if (res == 0 && n_drain == 0 && want_length <= last_wanted) {
- /* If we drained nothing, and we didn't ask for more than last time,
- * then we probably wanted more data than the buffer actually had,
- * and we're finding out that we're not satisified with it. It's
- * time to break until we have more data. */
- break;
- }
-
- buflen = evbuffer_get_length(buf);
- } while (res == 0 && want_length <= buflen && buflen >= 2);
-
- return res;
-}
-#endif
-
/** 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
@@ -1588,34 +1343,6 @@ fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out)
return 1;
}
-#ifdef USE_BUFFEREVENTS
-/** 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_evbuffer(struct evbuffer *buf, ext_or_cmd_t **out)
-{
- char hdr[EXT_OR_CMD_HEADER_SIZE];
- uint16_t len;
- size_t buf_len = evbuffer_get_length(buf);
-
- if (buf_len < EXT_OR_CMD_HEADER_SIZE)
- return 0;
- evbuffer_copyout(buf, hdr, EXT_OR_CMD_HEADER_SIZE);
- len = ntohs(get_uint16(hdr+2));
- if (buf_len < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
- return 0;
- *out = ext_or_cmd_new(len);
- (*out)->cmd = ntohs(get_uint16(hdr));
- (*out)->len = len;
- evbuffer_drain(buf, EXT_OR_CMD_HEADER_SIZE);
- evbuffer_remove(buf, (*out)->body, len);
- return 1;
-}
-#endif
-
/** 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>.
*/
@@ -1868,6 +1595,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req,
return -1;
}
tor_assert(0);
+ break;
case 4: { /* socks4 */
enum {socks4, socks4a} socks4_prot = socks4a;
const char *authstart, *authend;
@@ -2060,34 +1788,6 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason)
return r;
}
-#ifdef USE_BUFFEREVENTS
-/** As fetch_from_buf_socks_client, buf works on an evbuffer */
-int
-fetch_from_evbuffer_socks_client(struct evbuffer *buf, int state,
- char **reason)
-{
- ssize_t drain = 0;
- uint8_t *data;
- size_t datalen;
- int r;
-
- /* Linearize the SOCKS response in the buffer, up to 128 bytes.
- * (parse_socks_client shouldn't need to see anything beyond that.) */
- datalen = evbuffer_get_length(buf);
- if (datalen > MAX_SOCKS_MESSAGE_LEN)
- datalen = MAX_SOCKS_MESSAGE_LEN;
- data = evbuffer_pullup(buf, datalen);
-
- r = parse_socks_client(data, datalen, state, reason, &drain);
- if (drain > 0)
- evbuffer_drain(buf, drain);
- else if (drain < 0)
- evbuffer_drain(buf, evbuffer_get_length(buf));
-
- return r;
-}
-#endif
-
/** Implementation logic for fetch_from_*_socks_client. */
static int
parse_socks_client(const uint8_t *data, size_t datalen,
@@ -2218,27 +1918,6 @@ peek_buf_has_control0_command(buf_t *buf)
return 0;
}
-#ifdef USE_BUFFEREVENTS
-int
-peek_evbuffer_has_control0_command(struct evbuffer *buf)
-{
- int result = 0;
- if (evbuffer_get_length(buf) >= 4) {
- int free_out = 0;
- char *data = NULL;
- size_t n = inspect_evbuffer(buf, &data, 4, &free_out, NULL);
- uint16_t cmd;
- tor_assert(n >= 4);
- cmd = ntohs(get_uint16(data+2));
- if (cmd <= 0x14)
- result = 1;
- if (free_out)
- tor_free(data);
- }
- return result;
-}
-#endif
-
/** 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
@@ -2336,93 +2015,14 @@ write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state,
return 0;
}
-#ifdef USE_BUFFEREVENTS
-int
-write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
- const char *data, size_t data_len,
- int done)
-{
- char *next;
- size_t old_avail, avail;
- int over = 0, n;
- struct evbuffer_iovec vec[1];
- do {
- {
- size_t cap = data_len / 4;
- if (cap < 128)
- cap = 128;
- /* XXXX NM this strategy is fragmentation-prone. We should really have
- * two iovecs, and write first into the one, and then into the
- * second if the first gets full. */
- n = evbuffer_reserve_space(buf, cap, vec, 1);
- tor_assert(n == 1);
- }
-
- next = vec[0].iov_base;
- avail = old_avail = vec[0].iov_len;
-
- switch (tor_zlib_process(state, &next, &avail, &data, &data_len, done)) {
- case TOR_ZLIB_DONE:
- over = 1;
- break;
- case TOR_ZLIB_ERR:
- return -1;
- case TOR_ZLIB_OK:
- if (data_len == 0)
- over = 1;
- break;
- case TOR_ZLIB_BUF_FULL:
- if (avail) {
- /* Zlib says we need more room (ZLIB_BUF_FULL). Start a new chunk
- * automatically, whether were going to or not. */
- }
- break;
- }
-
- /* XXXX possible infinite loop on BUF_FULL. */
- vec[0].iov_len = old_avail - avail;
- evbuffer_commit_space(buf, vec, 1);
-
- } while (!over);
- check();
- return 0;
-}
-#endif
-
/** Set *<b>output</b> to contain a copy of the data in *<b>input</b> */
int
-generic_buffer_set_to_copy(generic_buffer_t **output,
- const generic_buffer_t *input)
+buf_set_to_copy(buf_t **output,
+ const buf_t *input)
{
-#ifdef USE_BUFFEREVENTS
- struct evbuffer_ptr ptr;
- size_t remaining = evbuffer_get_length(input);
- if (*output) {
- evbuffer_drain(*output, evbuffer_get_length(*output));
- } else {
- if (!(*output = evbuffer_new()))
- return -1;
- }
- evbuffer_ptr_set((struct evbuffer*)input, &ptr, 0, EVBUFFER_PTR_SET);
- while (remaining) {
- struct evbuffer_iovec v[4];
- int n_used, i;
- n_used = evbuffer_peek((struct evbuffer*)input, -1, &ptr, v, 4);
- if (n_used < 0)
- return -1;
- for (i=0;i<n_used;++i) {
- evbuffer_add(*output, v[i].iov_base, v[i].iov_len);
- tor_assert(v[i].iov_len <= remaining);
- remaining -= v[i].iov_len;
- evbuffer_ptr_set((struct evbuffer*)input,
- &ptr, v[i].iov_len, EVBUFFER_PTR_ADD);
- }
- }
-#else
if (*output)
buf_free(*output);
*output = buf_copy(input);
-#endif
return 0;
}
diff --git a/src/or/buffers.h b/src/or/buffers.h
index 2b43ea14b1..52b21d5885 100644
--- a/src/or/buffers.h
+++ b/src/or/buffers.h
@@ -56,46 +56,8 @@ int peek_buf_has_control0_command(buf_t *buf);
int fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out);
-#ifdef USE_BUFFEREVENTS
-int fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
- int linkproto);
-int fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
- int log_sockstype, int safe_socks);
-int fetch_from_evbuffer_socks_client(struct evbuffer *buf, int state,
- char **reason);
-int fetch_from_evbuffer_http(struct evbuffer *buf,
- char **headers_out, size_t max_headerlen,
- char **body_out, size_t *body_used, size_t max_bodylen,
- int force_complete);
-int peek_evbuffer_has_control0_command(struct evbuffer *buf);
-int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
- const char *data, size_t data_len,
- int done);
-int fetch_ext_or_command_from_evbuffer(struct evbuffer *buf,
- ext_or_cmd_t **out);
-#endif
-
-#ifdef USE_BUFFEREVENTS
-#define generic_buffer_new() evbuffer_new()
-#define generic_buffer_len(b) evbuffer_get_length((b))
-#define generic_buffer_add(b,dat,len) evbuffer_add((b),(dat),(len))
-#define generic_buffer_get(b,buf,buflen) evbuffer_remove((b),(buf),(buflen))
-#define generic_buffer_clear(b) evbuffer_drain((b), evbuffer_get_length((b)))
-#define generic_buffer_free(b) evbuffer_free((b))
-#define generic_buffer_fetch_ext_or_cmd(b, out) \
- fetch_ext_or_command_from_evbuffer((b), (out))
-#else
-#define generic_buffer_new() buf_new()
-#define generic_buffer_len(b) buf_datalen((b))
-#define generic_buffer_add(b,dat,len) write_to_buf((dat),(len),(b))
-#define generic_buffer_get(b,buf,buflen) fetch_from_buf((buf),(buflen),(b))
-#define generic_buffer_clear(b) buf_clear((b))
-#define generic_buffer_free(b) buf_free((b))
-#define generic_buffer_fetch_ext_or_cmd(b, out) \
- fetch_ext_or_command_from_buf((b), (out))
-#endif
-int generic_buffer_set_to_copy(generic_buffer_t **output,
- const generic_buffer_t *input);
+int buf_set_to_copy(buf_t **output,
+ const buf_t *input);
void assert_buf_ok(buf_t *buf);
@@ -103,6 +65,7 @@ void assert_buf_ok(buf_t *buf);
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);
void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz);
+STATIC size_t preferred_chunk_size(size_t target);
#define DEBUG_CHUNK_ALLOC
/** A single chunk on a buffer. */
diff --git a/src/or/channel.c b/src/or/channel.c
index 5f69a0864b..f547aea1b3 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -8,6 +8,32 @@
* transfer cells from Tor instance to Tor instance.
* Currently, there is only one implementation of the channel abstraction: in
* channeltls.c.
+ *
+ * Channels are a higher-level abstraction than or_connection_t: In general,
+ * any means that two Tor relays use to exchange cells, or any means that a
+ * relay and a client use to exchange cells, is a channel.
+ *
+ * Channels differ from pluggable transports in that they do not wrap an
+ * underlying protocol over which cells are transmitted: they <em>are</em> the
+ * underlying protocol.
+ *
+ * This module defines the generic parts of the channel_t interface, and
+ * provides the machinery necessary for specialized implementations to be
+ * created. At present, there is one specialized implementation in
+ * channeltls.c, which uses connection_or.c to send cells over a TLS
+ * connection.
+ *
+ * Every channel implementation is responsible for being able to transmit
+ * cells that are added to it with channel_write_cell() and related functions,
+ * and to receive incoming cells with the channel_queue_cell() and related
+ * functions. See the channel_t documentation for more information.
+ *
+ * When new cells arrive on a channel, they are passed to cell handler
+ * functions, which can be set by channel_set_cell_handlers()
+ * functions. (Tor's cell handlers are in command.c.)
+ *
+ * Tor flushes cells to channels from relay.c in
+ * channel_flush_from_first_active_circuit().
**/
/*
@@ -122,7 +148,7 @@ STATIC uint64_t estimated_total_queue_size = 0;
* If more than one channel exists, follow the next_with_same_id pointer
* as a linked list.
*/
-HT_HEAD(channel_idmap, channel_idmap_entry_s) channel_identity_map =
+static HT_HEAD(channel_idmap, channel_idmap_entry_s) channel_identity_map =
HT_INITIALIZER();
typedef struct channel_idmap_entry_s {
@@ -145,9 +171,9 @@ channel_idmap_eq(const channel_idmap_entry_t *a,
}
HT_PROTOTYPE(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash,
- channel_idmap_eq);
+ channel_idmap_eq)
HT_GENERATE2(channel_idmap, channel_idmap_entry_s, node, channel_idmap_hash,
- channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_);
+ channel_idmap_eq, 0.5, tor_reallocarray_, tor_free_)
static cell_queue_entry_t * cell_queue_entry_dup(cell_queue_entry_t *q);
#if 0
@@ -838,7 +864,7 @@ channel_free(channel_t *chan)
}
/* Call a free method if there is one */
- if (chan->free) chan->free(chan);
+ if (chan->free_fn) chan->free_fn(chan);
channel_clear_remote_end(chan);
@@ -878,7 +904,7 @@ channel_listener_free(channel_listener_t *chan_l)
tor_assert(!(chan_l->registered));
/* Call a free method if there is one */
- if (chan_l->free) chan_l->free(chan_l);
+ if (chan_l->free_fn) chan_l->free_fn(chan_l);
/*
* We're in CLOSED or ERROR, so the incoming channel queue is already
@@ -916,7 +942,7 @@ channel_force_free(channel_t *chan)
}
/* Call a free method if there is one */
- if (chan->free) chan->free(chan);
+ if (chan->free_fn) chan->free_fn(chan);
channel_clear_remote_end(chan);
@@ -958,7 +984,7 @@ channel_listener_force_free(channel_listener_t *chan_l)
chan_l);
/* Call a free method if there is one */
- if (chan_l->free) chan_l->free(chan_l);
+ if (chan_l->free_fn) chan_l->free_fn(chan_l);
/*
* The incoming list just gets emptied and freed; we request close on
@@ -3510,7 +3536,7 @@ channel_dump_statistics, (channel_t *chan, int severity))
have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr);
if (have_remote_addr) {
char *actual = tor_strdup(channel_get_actual_remote_descr(chan));
- remote_addr_str = tor_dup_addr(&remote_addr);
+ remote_addr_str = tor_addr_to_str_dup(&remote_addr);
tor_log(severity, LD_GENERAL,
" * Channel " U64_FORMAT " says its remote address"
" is %s, and gives a canonical description of \"%s\" and an "
@@ -4524,8 +4550,8 @@ channel_update_xmit_queue_size(channel_t *chan)
/* Next, adjust by the overhead factor, if any is available */
if (chan->get_overhead_estimate) {
overhead = chan->get_overhead_estimate(chan);
- if (overhead >= 1.0f) {
- queued *= overhead;
+ if (overhead >= 1.0) {
+ queued = (uint64_t)(queued * overhead);
} else {
/* Ignore silly overhead factors */
log_notice(LD_CHANNEL, "Ignoring silly overhead factor %f", overhead);
diff --git a/src/or/channel.h b/src/or/channel.h
index 129c0c2013..a711b56d44 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -18,7 +18,7 @@ typedef void (*channel_cell_handler_fn_ptr)(channel_t *, cell_t *);
typedef void (*channel_var_cell_handler_fn_ptr)(channel_t *, var_cell_t *);
struct cell_queue_entry_s;
-TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s) incoming_queue;
+TOR_SIMPLEQ_HEAD(chan_cell_queue, cell_queue_entry_s);
typedef struct chan_cell_queue chan_cell_queue_t;
/**
@@ -90,7 +90,7 @@ struct channel_s {
/* Methods implemented by the lower layer */
/** Free a channel */
- void (*free)(channel_t *);
+ void (*free_fn)(channel_t *);
/** Close an open channel */
void (*close)(channel_t *);
/** Describe the transport subclass for this channel */
@@ -273,7 +273,7 @@ struct channel_listener_s {
/* Methods implemented by the lower layer */
/** Free a channel */
- void (*free)(channel_listener_t *);
+ void (*free_fn)(channel_listener_t *);
/** Close an open channel */
void (*close)(channel_listener_t *);
/** Describe the transport subclass for this channel */
@@ -469,6 +469,10 @@ void channel_notify_flushed(channel_t *chan);
/* Handle stuff we need to do on open like notifying circuits */
void channel_do_open_actions(channel_t *chan);
+#ifdef TOR_UNIT_TESTS
+extern uint64_t estimated_total_queue_size;
+#endif
+
#endif
/* Helper functions to perform operations on channels */
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index c65af5d040..09cca95b64 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -6,6 +6,28 @@
*
* \brief A concrete subclass of channel_t using or_connection_t to transfer
* cells between Tor instances.
+ *
+ * This module fills in the various function pointers in channel_t, to
+ * implement the channel_tls_t channels as used in Tor today. These channels
+ * are created from channel_tls_connect() and
+ * channel_tls_handle_incoming(). Each corresponds 1:1 to or_connection_t
+ * object, as implemented in connection_or.c. These channels transmit cells
+ * to the underlying or_connection_t by calling
+ * connection_or_write_*_cell_to_buf(), and receive cells from the underlying
+ * or_connection_t when connection_or_process_cells_from_inbuf() calls
+ * channel_tls_handle_*_cell().
+ *
+ * Here we also implement the server (responder) side of the v3+ Tor link
+ * handshake, which uses CERTS and AUTHENTICATE cell to negotiate versions,
+ * exchange expected and observed IP and time information, and bootstrap a
+ * level of authentication higher than we have gotten on the raw TLS
+ * handshake.
+ *
+ * NOTE: Since there is currently only one type of channel, there are probably
+ * more than a few cases where functionality that is currently in
+ * channeltls.c, connection_or.c, and channel.c ought to be divided up
+ * differently. The right time to do this is probably whenever we introduce
+ * our next channel type.
**/
/*
@@ -22,6 +44,7 @@
#include "channeltls.h"
#include "circuitmux.h"
#include "circuitmux_ewma.h"
+#include "command.h"
#include "config.h"
#include "connection.h"
#include "connection_or.h"
@@ -51,7 +74,7 @@ uint64_t stats_n_authenticate_cells_processed = 0;
uint64_t stats_n_authorize_cells_processed = 0;
/** Active listener, if any */
-channel_listener_t *channel_tls_listener = NULL;
+static channel_listener_t *channel_tls_listener = NULL;
/* channel_tls_t method declarations */
@@ -116,7 +139,7 @@ channel_tls_common_init(channel_tls_t *tlschan)
chan->state = CHANNEL_STATE_OPENING;
chan->close = channel_tls_close_method;
chan->describe_transport = channel_tls_describe_transport_method;
- chan->free = channel_tls_free_method;
+ chan->free_fn = channel_tls_free_method;
chan->get_overhead_estimate = channel_tls_get_overhead_estimate_method;
chan->get_remote_addr = channel_tls_get_remote_addr_method;
chan->get_remote_descr = channel_tls_get_remote_descr_method;
@@ -445,7 +468,7 @@ channel_tls_free_method(channel_t *chan)
static double
channel_tls_get_overhead_estimate_method(channel_t *chan)
{
- double overhead = 1.0f;
+ double overhead = 1.0;
channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
tor_assert(tlschan);
@@ -462,7 +485,8 @@ channel_tls_get_overhead_estimate_method(channel_t *chan)
* Never estimate more than 2.0; otherwise we get silly large estimates
* at the very start of a new TLS connection.
*/
- if (overhead > 2.0f) overhead = 2.0f;
+ if (overhead > 2.0)
+ overhead = 2.0;
}
log_debug(LD_CHANNEL,
@@ -554,7 +578,7 @@ channel_tls_get_remote_descr_method(channel_t *chan, int flags)
break;
case GRD_FLAG_ORIGINAL:
/* Actual address with port */
- addr_str = tor_dup_addr(&(tlschan->conn->real_addr));
+ addr_str = tor_addr_to_str_dup(&(tlschan->conn->real_addr));
tor_snprintf(buf, MAX_DESCR_LEN + 1,
"%s:%u", addr_str, conn->port);
tor_free(addr_str);
@@ -567,7 +591,7 @@ channel_tls_get_remote_descr_method(channel_t *chan, int flags)
break;
case GRD_FLAG_ORIGINAL|GRD_FLAG_ADDR_ONLY:
/* Actual address, no port */
- addr_str = tor_dup_addr(&(tlschan->conn->real_addr));
+ addr_str = tor_addr_to_str_dup(&(tlschan->conn->real_addr));
strlcpy(buf, addr_str, sizeof(buf));
tor_free(addr_str);
answer = buf;
@@ -797,6 +821,7 @@ static int
channel_tls_write_packed_cell_method(channel_t *chan,
packed_cell_t *packed_cell)
{
+ tor_assert(chan);
channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
size_t cell_network_size = get_cell_network_size(chan->wide_circ_ids);
int written = 0;
@@ -1189,6 +1214,8 @@ channel_tls_handle_var_cell(var_cell_t *var_cell, or_connection_t *conn)
* notice "hey, data arrived!" before we notice "hey, the handshake
* finished!" And we need to be accepting both at once to handle both
* the v2 and v3 handshakes. */
+ /* But that should be happening any longer've disabled bufferevents. */
+ tor_assert_nonfatal_unreached_once();
/* fall through */
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
@@ -1898,8 +1925,8 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
}
err:
- for (unsigned i = 0; i < ARRAY_LENGTH(certs); ++i) {
- tor_x509_cert_free(certs[i]);
+ for (unsigned u = 0; u < ARRAY_LENGTH(certs); ++u) {
+ tor_x509_cert_free(certs[u]);
}
certs_cell_free(cc);
#undef ERR
diff --git a/src/or/channeltls.h b/src/or/channeltls.h
index a4d9c7a095..8b5863a461 100644
--- a/src/or/channeltls.h
+++ b/src/or/channeltls.h
@@ -52,6 +52,14 @@ void channel_tls_update_marks(or_connection_t *conn);
/* Cleanup at shutdown */
void channel_tls_free_all(void);
+extern uint64_t stats_n_authorize_cells_processed;
+extern uint64_t stats_n_authenticate_cells_processed;
+extern uint64_t stats_n_versions_cells_processed;
+extern uint64_t stats_n_netinfo_cells_processed;
+extern uint64_t stats_n_vpadding_cells_processed;
+extern uint64_t stats_n_certs_cells_processed;
+extern uint64_t stats_n_auth_challenge_cells_processed;
+
#ifdef CHANNELTLS_PRIVATE
STATIC void channel_tls_process_certs_cell(var_cell_t *cell,
channel_tls_t *tlschan);
diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c
index 552947eba2..9f93e737f7 100644
--- a/src/or/circpathbias.c
+++ b/src/or/circpathbias.c
@@ -85,7 +85,6 @@ pathbias_get_notice_rate(const or_options_t *options)
DFLT_PATH_BIAS_NOTICE_PCT, 0, 100)/100.0;
}
-/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
/** The circuit success rate below which we issue a warn */
static double
pathbias_get_warn_rate(const or_options_t *options)
@@ -98,7 +97,7 @@ pathbias_get_warn_rate(const or_options_t *options)
DFLT_PATH_BIAS_WARN_PCT, 0, 100)/100.0;
}
-/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/* XXXX I'd like to have this be static again, but entrynodes.c needs it. */
/**
* The extreme rate is the rate at which we would drop the guard,
* if pb_dropguard is also set. Otherwise we just warn.
@@ -114,7 +113,7 @@ pathbias_get_extreme_rate(const or_options_t *options)
DFLT_PATH_BIAS_EXTREME_PCT, 0, 100)/100.0;
}
-/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
+/* XXXX I'd like to have this be static again, but entrynodes.c needs it. */
/**
* If 1, we actually disable use of guards that fall below
* the extreme_pct.
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 28d286cd72..cb9c146fb7 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -28,6 +28,7 @@
#include "connection_edge.h"
#include "connection_or.h"
#include "control.h"
+#include "crypto.h"
#include "directory.h"
#include "entrynodes.h"
#include "main.h"
@@ -38,18 +39,14 @@
#include "onion_tap.h"
#include "onion_fast.h"
#include "policies.h"
-#include "transports.h"
#include "relay.h"
+#include "rendcommon.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
-#include "crypto.h"
-
-#ifndef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
-#endif
+#include "transports.h"
static channel_t * channel_connect_for_circuit(const tor_addr_t *addr,
uint16_t port,
@@ -62,7 +59,6 @@ static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
static int onion_extend_cpath(origin_circuit_t *circ);
static int count_acceptable_nodes(smartlist_t *routers);
static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
-static int circuits_can_use_ntor(void);
/** This function tries to get a channel to the specified endpoint,
* and then calls command_setup_channel() to give it the right
@@ -369,7 +365,7 @@ circuit_rep_hist_note_result(origin_circuit_t *circ)
} while (hop!=circ->cpath);
}
-/** Return 1 iff at least one node in circ's cpath supports ntor. */
+/** Return 1 iff every node in circ's cpath definitely supports ntor. */
static int
circuit_cpath_supports_ntor(const origin_circuit_t *circ)
{
@@ -377,16 +373,19 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ)
cpath = head = circ->cpath;
do {
- if (cpath->extend_info &&
- !tor_mem_is_zero(
- (const char*)cpath->extend_info->curve25519_onion_key.public_key,
- CURVE25519_PUBKEY_LEN))
- return 1;
+ /* if the extend_info is missing, we can't tell if it supports ntor */
+ if (!cpath->extend_info) {
+ return 0;
+ }
+ /* if the key is blank, it definitely doesn't support ntor */
+ if (!extend_info_supports_ntor(cpath->extend_info)) {
+ return 0;
+ }
cpath = cpath->next;
} while (cpath != head);
- return 0;
+ return 1;
}
/** Pick all the entries in our cpath. Stop and return 0 when we're
@@ -394,41 +393,61 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ)
static int
onion_populate_cpath(origin_circuit_t *circ)
{
- int n_tries = 0;
- const int using_ntor = circuits_can_use_ntor();
+ int r = 0;
-#define MAX_POPULATE_ATTEMPTS 32
+ /* onion_extend_cpath assumes these are non-NULL */
+ tor_assert(circ);
+ tor_assert(circ->build_state);
- while (1) {
- int r = onion_extend_cpath(circ);
+ while (r == 0) {
+ r = onion_extend_cpath(circ);
if (r < 0) {
log_info(LD_CIRC,"Generating cpath hop failed.");
return -1;
}
- if (r == 1) {
- /* This circuit doesn't need/shouldn't be forced to have an ntor hop */
- if (circ->build_state->desired_path_len <= 1 || ! using_ntor)
- return 0;
+ }
- /* This circuit has an ntor hop. great! */
- if (circuit_cpath_supports_ntor(circ))
- return 0;
+ /* The path is complete */
+ tor_assert(r == 1);
- /* No node in the circuit supports ntor. Have we already tried too many
- * times? */
- if (++n_tries >= MAX_POPULATE_ATTEMPTS)
- break;
+ /* Does every node in this path support ntor? */
+ int path_supports_ntor = circuit_cpath_supports_ntor(circ);
- /* Clear the path and retry */
- circuit_clear_cpath(circ);
+ /* We would like every path to support ntor, but we have to allow for some
+ * edge cases. */
+ tor_assert(circuit_get_cpath_len(circ));
+ if (circuit_can_use_tap(circ)) {
+ /* Circuits from clients to intro points, and hidden services to
+ * rend points do not support ntor, because the hidden service protocol
+ * does not include ntor onion keys. This is also true for Tor2web clients
+ * and Single Onion Services. */
+ return 0;
+ }
+
+ if (circuit_get_cpath_len(circ) == 1) {
+ /* Allow for bootstrapping: when we're fetching directly from a fallback,
+ * authority, or bridge, we have no way of knowing its ntor onion key
+ * before we connect to it. So instead, we try connecting, and end up using
+ * CREATE_FAST. */
+ tor_assert(circ->cpath);
+ tor_assert(circ->cpath->extend_info);
+ const node_t *node = node_get_by_id(
+ circ->cpath->extend_info->identity_digest);
+ /* If we don't know the node and its descriptor, we must be bootstrapping.
+ */
+ if (!node || !node_has_descriptor(node)) {
+ return 0;
}
}
- log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop "
- "circuit with at least one node that supports ntor.",
- MAX_POPULATE_ATTEMPTS,
- circ->build_state->desired_path_len);
- return -1;
+ if (BUG(!path_supports_ntor)) {
+ /* If we're building a multi-hop path, and it's not one of the HS or
+ * bootstrapping exceptions, and it doesn't support ntor, something has
+ * gone wrong. */
+ return -1;
+ }
+
+ return 0;
}
/** Create and return a new origin circuit. Initialize its purpose and
@@ -461,14 +480,14 @@ origin_circuit_init(uint8_t purpose, int flags)
* it's not open already.
*/
origin_circuit_t *
-circuit_establish_circuit(uint8_t purpose, extend_info_t *exit, int flags)
+circuit_establish_circuit(uint8_t purpose, extend_info_t *exit_ei, int flags)
{
origin_circuit_t *circ;
int err_reason = 0;
circ = origin_circuit_init(purpose, flags);
- if (onion_pick_cpath_exit(circ, exit) < 0 ||
+ if (onion_pick_cpath_exit(circ, exit_ei) < 0 ||
onion_populate_cpath(circ) < 0) {
circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOPATH);
return NULL;
@@ -761,10 +780,13 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ)
tor_assert(circ->cpath);
tor_assert(circ->cpath->extend_info);
- if (!circ->cpath->extend_info->onion_key)
- return 1; /* our hand is forced: only a create_fast will work. */
+ if (!circuit_has_usable_onion_key(circ)) {
+ /* We don't have ntor, and we don't have or can't use TAP,
+ * so our hand is forced: only a create_fast will work. */
+ return 1;
+ }
if (public_server_mode(options)) {
- /* We're a server, and we know an onion key. We can choose.
+ /* We're a server, and we have a usable onion key. We can choose.
* Prefer to blend our circuit into the other circuits we are
* creating on behalf of others. */
return 0;
@@ -789,62 +811,56 @@ circuit_timeout_want_to_count_circ(origin_circuit_t *circ)
&& circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN;
}
-/** Return true if the ntor handshake is enabled in the configuration, or if
- * it's been set to "auto" in the configuration and it's enabled in the
- * consensus. */
-static int
-circuits_can_use_ntor(void)
-{
- const or_options_t *options = get_options();
- if (options->UseNTorHandshake != -1)
- return options->UseNTorHandshake;
- return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1);
-}
-
/** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
* directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b>
- * accordingly. */
+ * accordingly.
+ * Note that TAP handshakes in CREATE cells are only used for direct
+ * connections:
+ * - from Tor2web to intro points not in the client's consensus, and
+ * - from Single Onions to rend points not in the service's consensus.
+ * This is checked in onion_populate_cpath. */
static void
circuit_pick_create_handshake(uint8_t *cell_type_out,
uint16_t *handshake_type_out,
const extend_info_t *ei)
{
- if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key,
- CURVE25519_PUBKEY_LEN) &&
- circuits_can_use_ntor()) {
+ /* torspec says: In general, clients SHOULD use CREATE whenever they are
+ * using the TAP handshake, and CREATE2 otherwise. */
+ if (extend_info_supports_ntor(ei)) {
*cell_type_out = CELL_CREATE2;
*handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR;
- return;
+ } else {
+ /* XXXX030 Remove support for deciding to use TAP and EXTEND. */
+ *cell_type_out = CELL_CREATE;
+ *handshake_type_out = ONION_HANDSHAKE_TYPE_TAP;
}
-
- *cell_type_out = CELL_CREATE;
- *handshake_type_out = ONION_HANDSHAKE_TYPE_TAP;
}
-/** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b>
- * directly, and set *<b>handshake_type_out</b> accordingly. Decide whether,
- * in extending through <b>node</b> to do so, we should use an EXTEND2 or an
- * EXTEND cell to do so, and set *<b>cell_type_out</b> and
- * *<b>create_cell_type_out</b> accordingly. */
+/** Decide whether to use a TAP or ntor handshake for extending to <b>ei</b>
+ * and set *<b>handshake_type_out</b> accordingly. Decide whether we should
+ * use an EXTEND2 or an EXTEND cell to do so, and set *<b>cell_type_out</b>
+ * and *<b>create_cell_type_out</b> accordingly.
+ * Note that TAP handshakes in EXTEND cells are only used:
+ * - from clients to intro points, and
+ * - from hidden services to rend points.
+ * This is checked in onion_populate_cpath.
+ */
static void
circuit_pick_extend_handshake(uint8_t *cell_type_out,
uint8_t *create_cell_type_out,
uint16_t *handshake_type_out,
- const node_t *node_prev,
const extend_info_t *ei)
{
uint8_t t;
circuit_pick_create_handshake(&t, handshake_type_out, ei);
- /* XXXX024 The check for whether the node has a curve25519 key is a bad
- * proxy for whether it can do extend2 cells; once a version that
- * handles extend2 cells is out, remove it. */
- if (node_prev &&
- *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP &&
- (node_has_curve25519_onion_key(node_prev) ||
- (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) {
+
+ /* torspec says: Clients SHOULD use the EXTEND format whenever sending a TAP
+ * handshake... In other cases, clients SHOULD use EXTEND2. */
+ if (*handshake_type_out != ONION_HANDSHAKE_TYPE_TAP) {
*cell_type_out = RELAY_COMMAND_EXTEND2;
*create_cell_type_out = CELL_CREATE2;
} else {
+ /* XXXX030 Remove support for deciding to use TAP and EXTEND. */
*cell_type_out = RELAY_COMMAND_EXTEND;
*create_cell_type_out = CELL_CREATE;
}
@@ -888,14 +904,12 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
*/
circuit_pick_create_handshake(&cc.cell_type, &cc.handshake_type,
circ->cpath->extend_info);
- note_request("cell: create", 1);
} 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;
- note_request("cell: create fast", 1);
}
len = onion_skin_create(cc.handshake_type,
@@ -1002,15 +1016,10 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
return - END_CIRC_REASON_INTERNAL;
}
- {
- const node_t *prev_node;
- prev_node = node_get_by_id(hop->prev->extend_info->identity_digest);
- circuit_pick_extend_handshake(&ec.cell_type,
- &ec.create_cell.cell_type,
- &ec.create_cell.handshake_type,
- prev_node,
- hop->extend_info);
- }
+ 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;
@@ -1028,7 +1037,6 @@ circuit_send_next_onion_skin(origin_circuit_t *circ)
ec.create_cell.handshake_len = len;
log_info(LD_CIRC,"Sending extend relay cell.");
- note_request("cell: extend", 1);
{
uint8_t command = 0;
uint16_t payload_len=0;
@@ -1440,7 +1448,7 @@ onionskin_answer(or_circuit_t *circ,
* to handle the desired path length, return -1.
*/
static int
-new_route_len(uint8_t purpose, extend_info_t *exit, smartlist_t *nodes)
+new_route_len(uint8_t purpose, extend_info_t *exit_ei, smartlist_t *nodes)
{
int num_acceptable_routers;
int routelen;
@@ -1448,7 +1456,7 @@ new_route_len(uint8_t purpose, extend_info_t *exit, smartlist_t *nodes)
tor_assert(nodes);
routelen = DEFAULT_ROUTE_LEN;
- if (exit &&
+ if (exit_ei &&
purpose != CIRCUIT_PURPOSE_TESTING &&
purpose != CIRCUIT_PURPOSE_S_ESTABLISH_INTRO)
routelen++;
@@ -1572,7 +1580,7 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
int n_best_support=0;
const or_options_t *options = get_options();
const smartlist_t *the_nodes;
- const node_t *node=NULL;
+ const node_t *selected_node=NULL;
connections = get_connection_array();
@@ -1699,7 +1707,7 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
smartlist_add(supporting, (void*)node);
});
- node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
+ selected_node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
smartlist_free(supporting);
} else {
/* Either there are no pending connections, or no routers even seem to
@@ -1737,8 +1745,8 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
}
} SMARTLIST_FOREACH_END(node);
- node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
- if (node)
+ selected_node = node_sl_choose_by_bandwidth(supporting, WEIGHT_FOR_EXIT);
+ if (selected_node)
break;
smartlist_clear(supporting);
/* If we reach this point, we can't actually support any unhandled
@@ -1752,9 +1760,9 @@ choose_good_exit_server_general(int need_uptime, int need_capacity)
}
tor_free(n_supported);
- if (node) {
- log_info(LD_CIRC, "Chose exit server '%s'", node_describe(node));
- return node;
+ if (selected_node) {
+ log_info(LD_CIRC, "Chose exit server '%s'", node_describe(selected_node));
+ return selected_node;
}
if (options->ExitNodes) {
log_warn(LD_CIRC,
@@ -1833,13 +1841,32 @@ pick_rendezvous_node(router_crn_flags_t flags)
flags |= CRN_ALLOW_INVALID;
#ifdef ENABLE_TOR2WEB_MODE
+ /* We want to connect directly to the node if we can */
+ router_crn_flags_t direct_flags = flags;
+ direct_flags |= CRN_PREF_ADDR;
+ direct_flags |= CRN_DIRECT_CONN;
+
/* The user wants us to pick specific RPs. */
if (options->Tor2webRendezvousPoints) {
- const node_t *tor2web_rp = pick_tor2web_rendezvous_node(flags, options);
+ const node_t *tor2web_rp = pick_tor2web_rendezvous_node(direct_flags,
+ options);
if (tor2web_rp) {
return tor2web_rp;
}
- /* Else, if no tor2web RP was found, fall back to choosing a random node */
+ }
+
+ /* Else, if no direct, preferred tor2web RP was found, fall back to choosing
+ * a random direct node */
+ const node_t *node = router_choose_random_node(NULL, options->ExcludeNodes,
+ direct_flags);
+ /* Return the direct node (if found), or log a message and fall back to an
+ * indirect connection. */
+ if (node) {
+ return node;
+ } else {
+ log_info(LD_REND,
+ "Unable to find a random rendezvous point that is reachable via "
+ "a direct connection, falling back to a 3-hop path.");
}
#endif
@@ -1892,7 +1919,8 @@ choose_good_exit_server(uint8_t purpose,
/** Log a warning if the user specified an exit for the circuit that
* has been excluded from use by ExcludeNodes or ExcludeExitNodes. */
static void
-warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
+warn_if_last_router_excluded(origin_circuit_t *circ,
+ const extend_info_t *exit_ei)
{
const or_options_t *options = get_options();
routerset_t *rs = options->ExcludeNodes;
@@ -1939,13 +1967,13 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
break;
}
- if (routerset_contains_extendinfo(rs, exit)) {
+ if (routerset_contains_extendinfo(rs, exit_ei)) {
/* We should never get here if StrictNodes is set to 1. */
if (options->StrictNodes) {
log_warn(LD_BUG, "Using %s '%s' which is listed in ExcludeNodes%s, "
"even though StrictNodes is set. Please report. "
"(Circuit purpose: %s)",
- description, extend_info_describe(exit),
+ description, extend_info_describe(exit_ei),
rs==options->ExcludeNodes?"":" or ExcludeExitNodes",
circuit_purpose_to_string(purpose));
} else {
@@ -1954,7 +1982,7 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
"prevent this (and possibly break your Tor functionality), "
"set the StrictNodes configuration option. "
"(Circuit purpose: %s)",
- description, extend_info_describe(exit),
+ description, extend_info_describe(exit_ei),
rs==options->ExcludeNodes?"":" or ExcludeExitNodes",
circuit_purpose_to_string(purpose));
}
@@ -1968,25 +1996,27 @@ warn_if_last_router_excluded(origin_circuit_t *circ, const extend_info_t *exit)
* router (or use <b>exit</b> if provided). Store these in the
* cpath. Return 0 if ok, -1 if circuit should be closed. */
static int
-onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
+onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
{
cpath_build_state_t *state = circ->build_state;
if (state->onehop_tunnel) {
- log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel.");
+ log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel%s.",
+ (rend_allow_non_anonymous_connection(get_options()) ?
+ ", or intro or rendezvous connection" : ""));
state->desired_path_len = 1;
} else {
- int r = new_route_len(circ->base_.purpose, exit, nodelist_get_list());
+ int r = new_route_len(circ->base_.purpose, exit_ei, nodelist_get_list());
if (r < 1) /* must be at least 1 */
return -1;
state->desired_path_len = r;
}
- if (exit) { /* the circuit-builder pre-requested one */
- warn_if_last_router_excluded(circ, exit);
+ if (exit_ei) { /* the circuit-builder pre-requested one */
+ warn_if_last_router_excluded(circ, exit_ei);
log_info(LD_CIRC,"Using requested exit node '%s'",
- extend_info_describe(exit));
- exit = extend_info_dup(exit);
+ extend_info_describe(exit_ei));
+ exit_ei = extend_info_dup(exit_ei);
} else { /* we have to decide one */
const node_t *node =
choose_good_exit_server(circ->base_.purpose, state->need_uptime,
@@ -1995,10 +2025,10 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
log_warn(LD_CIRC,"Failed to choose an exit server");
return -1;
}
- exit = extend_info_from_node(node, 0);
- tor_assert(exit);
+ exit_ei = extend_info_from_node(node, 0);
+ tor_assert(exit_ei);
}
- state->chosen_exit = exit;
+ state->chosen_exit = exit_ei;
return 0;
}
@@ -2007,19 +2037,19 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit)
* the caller will do this if it wants to.
*/
int
-circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *exit)
+circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
{
cpath_build_state_t *state;
- tor_assert(exit);
+ tor_assert(exit_ei);
tor_assert(circ);
state = circ->build_state;
tor_assert(state);
extend_info_free(state->chosen_exit);
- state->chosen_exit = extend_info_dup(exit);
+ state->chosen_exit = extend_info_dup(exit_ei);
++circ->build_state->desired_path_len;
- onion_append_hop(&circ->cpath, exit);
+ onion_append_hop(&circ->cpath, exit_ei);
return 0;
}
@@ -2028,18 +2058,18 @@ circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *exit)
* send the next extend cell to begin connecting to that hop.
*/
int
-circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit)
+circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei)
{
int err_reason = 0;
- warn_if_last_router_excluded(circ, exit);
+ warn_if_last_router_excluded(circ, exit_ei);
tor_gettimeofday(&circ->base_.timestamp_began);
- circuit_append_new_exit(circ, exit);
+ circuit_append_new_exit(circ, exit_ei);
circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING);
if ((err_reason = circuit_send_next_onion_skin(circ))<0) {
log_warn(LD_CIRC, "Couldn't extend circuit to new point %s.",
- extend_info_describe(exit));
+ extend_info_describe(exit_ei));
circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason);
return -1;
}
@@ -2064,15 +2094,18 @@ count_acceptable_nodes(smartlist_t *nodes)
if (! node->is_running)
// log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i);
continue;
+ /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
+ * allows this node in some places, then we're getting an inaccurate
+ * count. For now, be conservative and don't count it. But later we
+ * should try to be smarter. */
if (! node->is_valid)
// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i);
continue;
if (! node_has_descriptor(node))
continue;
- /* XXX This clause makes us count incorrectly: if AllowInvalidRouters
- * allows this node in some places, then we're getting an inaccurate
- * count. For now, be conservative and don't count it. But later we
- * should try to be smarter. */
+ /* The node has a descriptor, so we can just check the ntor key directly */
+ if (!node_has_curve25519_onion_key(node))
+ continue;
++num;
} SMARTLIST_FOREACH_END(node);
@@ -2150,7 +2183,6 @@ choose_good_middle_server(uint8_t purpose,
* If <b>state</b> is NULL, we're choosing a router to serve as an entry
* guard, not for any particular circuit.
*/
-/* XXXX024 I'd like to have this be static again, but entrynodes.c needs it. */
const node_t *
choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
{
@@ -2184,7 +2216,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state)
* This is an incomplete fix, but is no worse than the previous behaviour,
* and only applies to minimal, testing tor networks
* (so it's no less secure) */
- /*XXXX025 use the using_as_guard flag to accomplish this.*/
+ /*XXXX++ use the using_as_guard flag to accomplish this.*/
if (options->UseEntryGuards
&& (!options->TestingTorNetwork ||
smartlist_len(nodelist_get_list()) > smartlist_len(get_entry_guards())
@@ -2363,6 +2395,14 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
log_warn(LD_CIRC, "Could not choose valid address for %s",
node->ri ? node->ri->nickname : node->rs->nickname);
+ /* Every node we connect or extend to must support ntor */
+ if (!node_has_curve25519_onion_key(node)) {
+ log_fn(LOG_PROTOCOL_WARN, LD_CIRC,
+ "Attempted to create extend_info for a node that does not support "
+ "ntor: %s", node_describe(node));
+ return NULL;
+ }
+
if (valid_addr && node->ri)
return extend_info_new(node->ri->nickname,
node->identity,
@@ -2448,3 +2488,66 @@ extend_info_addr_is_allowed(const tor_addr_t *addr)
return 0;
}
+/* Does ei have a valid TAP key? */
+int
+extend_info_supports_tap(const extend_info_t* ei)
+{
+ tor_assert(ei);
+ /* Valid TAP keys are not NULL */
+ return ei->onion_key != NULL;
+}
+
+/* Does ei have a valid ntor key? */
+int
+extend_info_supports_ntor(const extend_info_t* ei)
+{
+ tor_assert(ei);
+ /* Valid ntor keys have at least one non-zero byte */
+ return !tor_mem_is_zero(
+ (const char*)ei->curve25519_onion_key.public_key,
+ CURVE25519_PUBKEY_LEN);
+}
+
+/* Is circuit purpose allowed to use the deprecated TAP encryption protocol?
+ * The hidden service protocol still uses TAP for some connections, because
+ * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */
+static int
+circuit_purpose_can_use_tap_impl(uint8_t purpose)
+{
+ return (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND ||
+ purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+}
+
+/* Is circ allowed to use the deprecated TAP encryption protocol?
+ * The hidden service protocol still uses TAP for some connections, because
+ * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */
+int
+circuit_can_use_tap(const origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(circ->cpath);
+ tor_assert(circ->cpath->extend_info);
+ return (circuit_purpose_can_use_tap_impl(circ->base_.purpose) &&
+ extend_info_supports_tap(circ->cpath->extend_info));
+}
+
+/* Does circ have an onion key which it's allowed to use? */
+int
+circuit_has_usable_onion_key(const origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(circ->cpath);
+ tor_assert(circ->cpath->extend_info);
+ return (extend_info_supports_ntor(circ->cpath->extend_info) ||
+ circuit_can_use_tap(circ));
+}
+
+/* Does ei have an onion key which it would prefer to use?
+ * Currently, we prefer ntor keys*/
+int
+extend_info_has_preferred_onion_key(const extend_info_t* ei)
+{
+ tor_assert(ei);
+ return extend_info_supports_ntor(ei);
+}
+
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 7f5fd511a9..1244601f71 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -54,6 +54,11 @@ extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect);
extend_info_t *extend_info_dup(extend_info_t *info);
void extend_info_free(extend_info_t *info);
int extend_info_addr_is_allowed(const tor_addr_t *addr);
+int extend_info_supports_tap(const extend_info_t* ei);
+int extend_info_supports_ntor(const extend_info_t* ei);
+int circuit_can_use_tap(const origin_circuit_t *circ);
+int circuit_has_usable_onion_key(const origin_circuit_t *circ);
+int extend_info_has_preferred_onion_key(const extend_info_t* ei);
const node_t *build_state_get_exit_node(cpath_build_state_t *state);
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index b710485908..977afca18d 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -109,7 +109,7 @@ HT_GENERATE2(chan_circid_map, chan_circid_circuit_map_t, node,
* used to improve performance when many cells arrive in a row from the
* same circuit.
*/
-chan_circid_circuit_map_t *_last_circid_chan_ent = NULL;
+static chan_circid_circuit_map_t *_last_circid_chan_ent = NULL;
/** Implementation helper for circuit_set_{p,n}_circid_channel: A circuit ID
* and/or channel for circ has just changed from <b>old_chan, old_id</b>
@@ -1613,7 +1613,8 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info,
return best;
}
-/** Return the number of hops in circuit's path. */
+/** Return the number of hops in circuit's path. If circ has no entries,
+ * or is NULL, returns 0. */
int
circuit_get_cpath_len(origin_circuit_t *circ)
{
@@ -1629,7 +1630,8 @@ circuit_get_cpath_len(origin_circuit_t *circ)
}
/** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there
- * aren't that many hops in the list. */
+ * aren't that many hops in the list. <b>hopnum</b> starts at 1.
+ * Returns NULL if <b>hopnum</b> is 0 or negative. */
crypt_path_t *
circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum)
{
@@ -2022,7 +2024,7 @@ circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
/** Return the age in milliseconds of the oldest buffer chunk on <b>conn</b>,
* where age is taken in milliseconds before the time <b>now</b> (in truncated
- * milliseconds since the epoch). If the connection has no data, treat
+ * absolute monotonic msec). If the connection has no data, treat
* it as having age zero.
**/
static uint32_t
@@ -2145,7 +2147,6 @@ circuits_handle_oom(size_t current_allocation)
size_t mem_recovered=0;
int n_circuits_killed=0;
int n_dirconns_killed=0;
- struct timeval now;
uint32_t now_ms;
log_notice(LD_GENERAL, "We're low on memory. Killing circuits with "
"over-long queues. (This behavior is controlled by "
@@ -2159,8 +2160,7 @@ circuits_handle_oom(size_t current_allocation)
mem_to_recover = current_allocation - mem_target;
}
- tor_gettimeofday_cached_monotonic(&now);
- now_ms = (uint32_t)tv_to_msec(&now);
+ now_ms = (uint32_t)monotime_coarse_absolute_msec();
circlist = circuit_get_global_list();
SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) {
diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c
index cc1c4cd401..96a3647aab 100644
--- a/src/or/circuitmux.c
+++ b/src/or/circuitmux.c
@@ -4,49 +4,20 @@
/**
* \file circuitmux.c
* \brief Circuit mux/cell selection abstraction
- **/
-
-#include "or.h"
-#include "channel.h"
-#include "circuitlist.h"
-#include "circuitmux.h"
-#include "relay.h"
-
-/*
- * Private typedefs for circuitmux.c
- */
-
-/*
- * Map of muxinfos for circuitmux_t to use; struct is defined below (name
- * of struct must match HT_HEAD line).
- */
-typedef struct chanid_circid_muxinfo_map chanid_circid_muxinfo_map_t;
-
-/*
- * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to
- * break the hash table code).
- */
-typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t;
-
-/*
- * Anything the mux wants to store per-circuit in the map; right now just
- * a count of queued cells.
- */
-
-typedef struct circuit_muxinfo_s circuit_muxinfo_t;
-
-/*
- * Structures for circuitmux.c
- */
-
-/*
- * A circuitmux is a collection of circuits; it tracks which subset
- * of the attached circuits are 'active' (i.e., have cells available
- * to transmit) and how many cells on each. It expoes three distinct
+ *
+ * A circuitmux is responsible for <b>MU</b>ltiple<b>X</b>ing all of the
+ * circuits that are writing on a single channel. It keeps track of which of
+ * these circuits has something to write (aka, "active" circuits), and which
+ * one should write next. A circuitmux corresponds 1:1 with a channel.
+ *
+ * There can be different implementations of the circuitmux's rules (which
+ * decide which circuit is next to write).
+ *
+ * A circuitmux exposes three distinct
* interfaces to other components:
*
* To channels, which each have a circuitmux_t, the supported operations
- * are:
+ * (invoked from relay.c) are:
*
* circuitmux_get_first_active_circuit():
*
@@ -74,7 +45,9 @@ typedef struct circuit_muxinfo_s circuit_muxinfo_t;
*
* circuitmux_set_num_cells():
*
- * Set the circuitmux's cell counter for this circuit.
+ * Set the circuitmux's cell counter for this circuit. One of
+ * circuitmuc_clear_num_cells() or circuitmux_set_num_cells() MUST be
+ * called when the number of cells queued on a circuit changes.
*
* See circuitmux.h for the circuitmux_policy_t data structure, which contains
* a table of function pointers implementing a circuit selection policy, and
@@ -94,7 +67,39 @@ typedef struct circuit_muxinfo_s circuit_muxinfo_t;
*
* Install a policy on a circuitmux_t; the appropriate callbacks will be
* made to attach all existing circuits to the new policy.
- *
+ **/
+
+#include "or.h"
+#include "channel.h"
+#include "circuitlist.h"
+#include "circuitmux.h"
+#include "relay.h"
+
+/*
+ * Private typedefs for circuitmux.c
+ */
+
+/*
+ * Map of muxinfos for circuitmux_t to use; struct is defined below (name
+ * of struct must match HT_HEAD line).
+ */
+typedef struct chanid_circid_muxinfo_map chanid_circid_muxinfo_map_t;
+
+/*
+ * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to
+ * break the hash table code).
+ */
+typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t;
+
+/*
+ * Anything the mux wants to store per-circuit in the map; right now just
+ * a count of queued cells.
+ */
+
+typedef struct circuit_muxinfo_s circuit_muxinfo_t;
+
+/*
+ * Structures for circuitmux.c
*/
struct circuitmux_s {
@@ -362,7 +367,7 @@ HT_HEAD(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t);
/* Emit a bunch of hash table stuff */
HT_PROTOTYPE(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node,
- chanid_circid_entry_hash, chanid_circid_entries_eq);
+ chanid_circid_entry_hash, chanid_circid_entries_eq)
HT_GENERATE2(chanid_circid_muxinfo_map, chanid_circid_muxinfo_t, node,
chanid_circid_entry_hash, chanid_circid_entries_eq, 0.6,
tor_reallocarray_, tor_free_)
diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c
index b784a140ac..5c2ebde73b 100644
--- a/src/or/circuitmux_ewma.c
+++ b/src/or/circuitmux_ewma.c
@@ -4,10 +4,34 @@
/**
* \file circuitmux_ewma.c
* \brief EWMA circuit selection as a circuitmux_t policy
+ *
+ * The "EWMA" in this module stands for the "exponentially weighted moving
+ * average" of the number of cells sent on each circuit. The goal is to
+ * prioritize cells on circuits that have been quiet recently, by looking at
+ * those that have sent few cells over time, prioritizing recent times
+ * more than older ones.
+ *
+ * Specifically, a cell sent at time "now" has weight 1, but a time X ticks
+ * before now has weight ewma_scale_factor ^ X , where ewma_scale_factor is
+ * between 0.0 and 1.0.
+ *
+ * For efficiency, we do not re-scale these averages every time we send a
+ * cell: that would be horribly inefficient. Instead, we we keep the cell
+ * count on all circuits on the same circuitmux scaled relative to a single
+ * tick. When we add a new cell, we scale its weight depending on the time
+ * that has elapsed since the tick. We do re-scale the circuits on the
+ * circuitmux periodically, so that we don't overflow double.
+ *
+ *
+ * This module should be used through the interfaces in circuitmux.c, which it
+ * implements.
+ *
**/
#define TOR_CIRCUITMUX_EWMA_C_
+#include "orconfig.h"
+
#include <math.h>
#include "or.h"
@@ -26,9 +50,10 @@
/*** Some useful constant #defines ***/
-/*DOCDOC*/
+/** Any halflife smaller than this number of seconds is considered to be
+ * "disabled". */
#define EPSILON 0.00001
-/*DOCDOC*/
+/** The natural logarithm of 0.5. */
#define LOG_ONEHALF -0.69314718055994529
/*** EWMA structures ***/
diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h
index 58aac1e196..a7b8961ac6 100644
--- a/src/or/circuitmux_ewma.h
+++ b/src/or/circuitmux_ewma.h
@@ -12,13 +12,8 @@
#include "or.h"
#include "circuitmux.h"
-/* Everything but circuitmux_ewma.c should see this extern */
-#ifndef TOR_CIRCUITMUX_EWMA_C_
-
extern circuitmux_policy_t ewma_policy;
-#endif /* !(TOR_CIRCUITMUX_EWMA_C_) */
-
/* Externally visible EWMA functions */
int cell_ewma_enabled(void);
unsigned int cell_ewma_get_tick(void);
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 9ac2d565b5..418acc0024 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -9,6 +9,18 @@
*
* \brief Maintains and analyzes statistics about circuit built times, so we
* can tell how long we may need to wait for a fast circuit to be constructed.
+ *
+ * By keeping these statistics, a client learns when it should time out a slow
+ * circuit for being too slow, and when it should keep a circuit open in order
+ * to wait for it to complete.
+ *
+ * The information here is kept in a circuit_built_times_t structure, which is
+ * currently a singleton, but doesn't need to be. It's updated by calls to
+ * circuit_build_times_count_timeout() from circuituse.c,
+ * circuit_build_times_count_close() from circuituse.c, and
+ * circuit_build_times_add_time() from circuitbuild.c, and inspected by other
+ * calls into this module, mostly from circuitlist.c. Observations are
+ * persisted to disk via the or_state_t-related calls.
*/
#define CIRCUITSTATS_PRIVATE
@@ -21,6 +33,8 @@
#include "control.h"
#include "main.h"
#include "networkstatus.h"
+#include "rendclient.h"
+#include "rendservice.h"
#include "statefile.h"
#undef log
@@ -81,12 +95,14 @@ get_circuit_build_timeout_ms(void)
/**
* This function decides if CBT learning should be disabled. It returns
- * true if one or more of the following four conditions are met:
+ * true if one or more of the following conditions are met:
*
* 1. If the cbtdisabled consensus parameter is set.
* 2. If the torrc option LearnCircuitBuildTimeout is false.
* 3. If we are a directory authority
* 4. If we fail to write circuit build time history to our state file.
+ * 5. If we are compiled or configured in Tor2web mode
+ * 6. If we are configured in Single Onion mode
*/
int
circuit_build_times_disabled(void)
@@ -94,14 +110,30 @@ circuit_build_times_disabled(void)
if (unit_tests) {
return 0;
} else {
+ const or_options_t *options = get_options();
int consensus_disabled = networkstatus_get_param(NULL, "cbtdisabled",
0, 0, 1);
- int config_disabled = !get_options()->LearnCircuitBuildTimeout;
- int dirauth_disabled = get_options()->AuthoritativeDir;
+ int config_disabled = !options->LearnCircuitBuildTimeout;
+ int dirauth_disabled = options->AuthoritativeDir;
int state_disabled = did_last_state_file_write_fail() ? 1 : 0;
+ /* LearnCircuitBuildTimeout and Tor2web/Single Onion Services are
+ * incompatible in two ways:
+ *
+ * - LearnCircuitBuildTimeout results in a low CBT, which
+ * Single Onion use of one-hop intro and rendezvous circuits lowers
+ * much further, producing *far* too many timeouts.
+ *
+ * - The adaptive CBT code does not update its timeout estimate
+ * using build times for single-hop circuits.
+ *
+ * If we fix both of these issues someday, we should test
+ * these modes with LearnCircuitBuildTimeout on again. */
+ int tor2web_disabled = rend_client_allow_non_anonymous_connection(options);
+ int single_onion_disabled = rend_service_allow_non_anonymous_connection(
+ options);
if (consensus_disabled || config_disabled || dirauth_disabled ||
- state_disabled) {
+ state_disabled || tor2web_disabled || single_onion_disabled) {
#if 0
log_debug(LD_CIRC,
"CircuitBuildTime learning is disabled. "
@@ -309,7 +341,6 @@ circuit_build_times_min_timeout(void)
"circuit_build_times_min_timeout() called, cbtmintimeout is %d",
num);
}
-
return num;
}
@@ -469,7 +500,7 @@ circuit_build_times_get_initial_timeout(void)
*/
if (!unit_tests && get_options()->CircuitBuildTimeout) {
timeout = get_options()->CircuitBuildTimeout*1000;
- if (get_options()->LearnCircuitBuildTimeout &&
+ if (!circuit_build_times_disabled() &&
timeout < circuit_build_times_min_timeout()) {
log_warn(LD_CIRC, "Config CircuitBuildTimeout too low. Setting to %ds",
circuit_build_times_min_timeout()/1000);
@@ -578,18 +609,18 @@ circuit_build_times_rewind_history(circuit_build_times_t *cbt, int n)
* array is full.
*/
int
-circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t time)
+circuit_build_times_add_time(circuit_build_times_t *cbt, build_time_t btime)
{
- if (time <= 0 || time > CBT_BUILD_TIME_MAX) {
+ if (btime <= 0 || btime > CBT_BUILD_TIME_MAX) {
log_warn(LD_BUG, "Circuit build time is too large (%u)."
- "This is probably a bug.", time);
+ "This is probably a bug.", btime);
tor_fragile_assert();
return -1;
}
- log_debug(LD_CIRC, "Adding circuit build time %u", time);
+ log_debug(LD_CIRC, "Adding circuit build time %u", btime);
- cbt->circuit_build_times[cbt->build_times_idx] = time;
+ cbt->circuit_build_times[cbt->build_times_idx] = btime;
cbt->build_times_idx = (cbt->build_times_idx + 1) % CBT_NCIRCUITS_TO_OBSERVE;
if (cbt->total_build_times < CBT_NCIRCUITS_TO_OBSERVE)
cbt->total_build_times++;
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 2c724dee05..84574cd5b9 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -203,7 +203,7 @@ circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
timercmp(&a->timestamp_began, &b->timestamp_began, OP_GT))
return 1;
if (ob->build_state->is_internal)
- /* XXX023 what the heck is this internal thing doing here. I
+ /* XXXX++ what the heck is this internal thing doing here. I
* think we can get rid of it. circuit_is_acceptable() already
* makes sure that is_internal is exactly what we need it to
* be. -RD */
@@ -222,7 +222,7 @@ circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
break;
}
- /* XXXX023 Maybe this check should get a higher priority to avoid
+ /* XXXX Maybe this check should get a higher priority to avoid
* using up circuits too rapidly. */
a_bits = connection_edge_update_circuit_isolation(conn,
@@ -788,6 +788,8 @@ static time_t last_expired_clientside_circuits = 0;
* As a diagnostic for bug 8387, log information about how many one-hop
* circuits we have around that have been there for at least <b>age</b>
* seconds. Log a few of them.
+ * Ignores Single Onion Service intro and Tor2web redezvous circuits, they are
+ * expected to be long-term one-hop circuits.
*/
void
circuit_log_ancient_one_hop_circuits(int age)
@@ -797,6 +799,7 @@ circuit_log_ancient_one_hop_circuits(int age)
time_t cutoff = now - age;
int n_found = 0;
smartlist_t *log_these = smartlist_new();
+ const or_options_t *options = get_options();
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
const origin_circuit_t *ocirc;
@@ -804,6 +807,19 @@ circuit_log_ancient_one_hop_circuits(int age)
continue;
if (circ->timestamp_created.tv_sec >= cutoff)
continue;
+ /* Single Onion Services deliberately make long term one-hop intro
+ * connections. We only ignore active intro point connections, if we take
+ * a long time establishing, that's worth logging. */
+ if (rend_service_allow_non_anonymous_connection(options) &&
+ circ->purpose == CIRCUIT_PURPOSE_S_INTRO)
+ continue;
+ /* Tor2web deliberately makes long term one-hop rend connections,
+ * particularly when Tor2webRendezvousPoints is used. We only ignore
+ * active rend point connections, if we take a long time to rendezvous,
+ * that's worth logging. */
+ if (rend_client_allow_non_anonymous_connection(options) &&
+ circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED)
+ continue;
ocirc = CONST_TO_ORIGIN_CIRCUIT(circ);
if (ocirc->build_state && ocirc->build_state->onehop_tunnel) {
@@ -839,7 +855,7 @@ circuit_log_ancient_one_hop_circuits(int age)
tor_asprintf(&dirty, "Dirty since %s (%ld seconds vs %ld-second cutoff)",
dirty_since, (long)(now - circ->timestamp_dirty),
- (long) get_options()->MaxCircuitDirtiness);
+ (long) options->MaxCircuitDirtiness);
} else {
dirty = tor_strdup("Not marked dirty");
}
@@ -1067,7 +1083,7 @@ circuit_predict_and_launch_new(void)
if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime,
&hidserv_needs_capacity) &&
((num_uptime_internal<2 && hidserv_needs_uptime) ||
- num_internal<2)
+ num_internal<3)
&& router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) {
if (hidserv_needs_uptime)
flags |= CIRCLAUNCH_NEED_UPTIME;
@@ -1936,8 +1952,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
return -1;
}
} else {
- /* XXXX024 Duplicates checks in connection_ap_handshake_attach_circuit:
- * refactor into a single function? */
+ /* 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);
int opt = conn->chosen_exit_optional;
if (node && !connection_ap_can_use_exit(conn, node)) {
@@ -2028,7 +2044,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
char *hexdigest = conn->chosen_exit_name+1;
tor_addr_t addr;
if (strlen(hexdigest) < HEX_DIGEST_LEN ||
- base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN)<0) {
+ base16_decode(digest,DIGEST_LEN,
+ hexdigest,HEX_DIGEST_LEN) != DIGEST_LEN) {
log_info(LD_DIR, "Broken exit digest on tunnel conn. Closing.");
return -1;
}
@@ -2146,10 +2163,11 @@ optimistic_data_enabled(void)
{
const or_options_t *options = get_options();
if (options->OptimisticData < 0) {
- /* XXX023 consider having auto default to 1 rather than 0 before
- * the 0.2.3 branch goes stable. See bug 3617. -RD */
+ /* Note: this default was 0 before #18815 was merged. We can't take the
+ * parameter out of the consensus until versions before that are all
+ * obsolete. */
const int32_t enabled =
- networkstatus_get_param(NULL, "UseOptimisticData", 0, 0, 1);
+ networkstatus_get_param(NULL, "UseOptimisticData", /*default*/ 1, 0, 1);
return (int)enabled;
}
return options->OptimisticData;
@@ -2415,7 +2433,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
/* find the circuit that we should use, if there is one. */
retval = circuit_get_open_circ_or_launch(
conn, CIRCUIT_PURPOSE_C_GENERAL, &circ);
- if (retval < 1) // XXX023 if we totally fail, this still returns 0 -RD
+ if (retval < 1) // XXXX++ if we totally fail, this still returns 0 -RD
return retval;
log_debug(LD_APP|LD_CIRC,
@@ -2590,7 +2608,7 @@ mark_circuit_unusable_for_new_conns(origin_circuit_t *circ)
const or_options_t *options = get_options();
tor_assert(circ);
- /* XXXX025 This is a kludge; we're only keeping it around in case there's
+ /* XXXX This is a kludge; we're only keeping it around in case there's
* something that doesn't check unusable_for_new_conns, and to avoid
* deeper refactoring of our expiration logic. */
if (! circ->base_.timestamp_dirty)
diff --git a/src/or/command.c b/src/or/command.c
index 5ad92bed1e..5866c386e4 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -7,6 +7,26 @@
/**
* \file command.c
* \brief Functions for processing incoming cells.
+ *
+ * When we receive a cell from a client or a relay, it arrives on some
+ * channel, and tells us what to do with it. In this module, we dispatch based
+ * on the cell type using the functions command_process_cell() and
+ * command_process_var_cell(), and deal with the cell accordingly. (These
+ * handlers are installed on a channel with the command_setup_channel()
+ * function.)
+ *
+ * Channels have a chance to handle some cell types on their own before they
+ * are ever passed here --- typically, they do this for cells that are
+ * specific to a given channel type. For example, in channeltls.c, the cells
+ * for the initial connection handshake are handled before we get here. (Of
+ * course, the fact that there _is_ only one channel type for now means that
+ * we may have gotten the factoring wrong here.)
+ *
+ * Handling other cell types is mainly farmed off to other modules, after
+ * initial sanity-checking. CREATE* cells are handled ultimately in onion.c,
+ * CREATED* cells trigger circuit creation in circuitbuild.c, DESTROY cells
+ * are handled here (since they're simple), and RELAY cells, in all their
+ * complexity, are passed off to relay.c.
**/
/* In-points to command.c:
diff --git a/src/or/config.c b/src/or/config.c
index 557790a81e..ddf49b037e 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -18,6 +18,7 @@
#include "circuitlist.h"
#include "circuitmux.h"
#include "circuitmux_ewma.h"
+#include "circuitstats.h"
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
@@ -65,11 +66,11 @@
#include <systemd/sd-daemon.h>
#endif
-/* From main.c */
-extern int quiet_level;
-
/* Prefix used to indicate a Unix socket in a FooPort configuration. */
static const char unix_socket_prefix[] = "unix:";
+/* Prefix used to indicate a Unix socket with spaces in it, in a FooPort
+ * configuration. */
+static const char unix_q_socket_prefix[] = "unix:\"";
/** A list of abbreviations and aliases to map command-line options, obsolete
* option names, or alternative option names, to their current values. */
@@ -99,7 +100,7 @@ static config_abbrev_t option_abbrevs_[] = {
{ "BandwidthRateBytes", "BandwidthRate", 0, 0},
{ "BandwidthBurstBytes", "BandwidthBurst", 0, 0},
{ "DirFetchPostPeriod", "StatusFetchPeriod", 0, 0},
- { "DirServer", "DirAuthority", 0, 0}, /* XXXX024 later, make this warn? */
+ { "DirServer", "DirAuthority", 0, 0}, /* XXXX later, make this warn? */
{ "MaxConn", "ConnLimit", 0, 1},
{ "MaxMemInCellQueues", "MaxMemInQueues", 0, 0},
{ "ORBindAddress", "ORListenAddress", 0, 0},
@@ -116,7 +117,6 @@ static config_abbrev_t option_abbrevs_[] = {
{ "BridgeAuthoritativeDirectory", "BridgeAuthoritativeDir", 0, 0},
{ "HashedControlPassword", "__HashedControlSessionPassword", 1, 0},
{ "VirtualAddrNetwork", "VirtualAddrNetworkIPv4", 0, 0},
- { "_UseFilteringSSLBufferevents", "UseFilteringSSLBufferevents", 0, 1},
{ NULL, NULL, 0, 0},
};
@@ -215,6 +215,7 @@ static config_var_t option_vars_[] = {
V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL),
V(DataDirectoryGroupReadable, BOOL, "0"),
+ V(DisableOOSCheck, BOOL, "1"),
V(DisableNetwork, BOOL, "0"),
V(DirAllowPrivateAddresses, BOOL, "0"),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"),
@@ -228,7 +229,7 @@ static config_var_t option_vars_[] = {
V(DirAuthorityFallbackRate, DOUBLE, "1.0"),
V(DisableAllSwap, BOOL, "0"),
V(DisableDebuggerAttachment, BOOL, "1"),
- V(DisableIOCP, BOOL, "1"),
+ OBSOLETE("DisableIOCP"),
OBSOLETE("DisableV2DirectoryInfo_"),
OBSOLETE("DynamicDHGroups"),
VPORT(DNSPort, LINELIST, NULL),
@@ -247,6 +248,7 @@ static config_var_t option_vars_[] = {
V(ExitNodes, ROUTERSET, NULL),
V(ExitPolicy, LINELIST, NULL),
V(ExitPolicyRejectPrivate, BOOL, "1"),
+ V(ExitPolicyRejectLocalInterfaces, BOOL, "0"),
V(ExitPortStatistics, BOOL, "0"),
V(ExtendAllowPrivateAddresses, BOOL, "0"),
V(ExitRelay, AUTOBOOL, "auto"),
@@ -299,6 +301,8 @@ static config_var_t option_vars_[] = {
V(HidServAuth, LINELIST, NULL),
V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"),
+ V(HiddenServiceSingleHopMode, BOOL, "0"),
+ V(HiddenServiceNonAnonymousMode,BOOL, "0"),
V(HTTPProxy, STRING, NULL),
V(HTTPProxyAuthenticator, STRING, NULL),
V(HTTPSProxy, STRING, NULL),
@@ -328,6 +332,7 @@ static config_var_t option_vars_[] = {
VAR("MaxMemInQueues", MEMUNIT, MaxMemInQueues_raw, "0"),
OBSOLETE("MaxOnionsPending"),
V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"),
+ V(MaxUnparseableDescSizeToLog, MEMUNIT, "10 MB"),
V(MinMeasuredBWsForAuthToIgnoreAdvertised, INT, "500"),
V(MyFamily, STRING, NULL),
V(NewCircuitPeriod, INTERVAL, "30 seconds"),
@@ -435,13 +440,14 @@ static config_var_t option_vars_[] = {
OBSOLETE("TunnelDirConns"),
V(UpdateBridgesFromAuthority, BOOL, "0"),
V(UseBridges, BOOL, "0"),
- V(UseEntryGuards, BOOL, "1"),
+ VAR("UseEntryGuards", BOOL, UseEntryGuards_option, "1"),
V(UseEntryGuardsAsDirGuards, BOOL, "1"),
V(UseGuardFraction, AUTOBOOL, "auto"),
V(UseMicrodescriptors, AUTOBOOL, "auto"),
- V(UseNTorHandshake, AUTOBOOL, "1"),
+ OBSOLETE("UseNTorHandshake"),
V(User, STRING, NULL),
- V(UserspaceIOCPBuffers, BOOL, "0"),
+ OBSOLETE("UserspaceIOCPBuffers"),
+ V(AuthDirSharedRandomness, BOOL, "1"),
OBSOLETE("V1AuthoritativeDirectory"),
OBSOLETE("V2AuthoritativeDirectory"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
@@ -461,7 +467,8 @@ static config_var_t option_vars_[] = {
V(VirtualAddrNetworkIPv4, STRING, "127.192.0.0/10"),
V(VirtualAddrNetworkIPv6, STRING, "[FE80::]/10"),
V(WarnPlaintextPorts, CSV, "23,109,110,143"),
- V(UseFilteringSSLBufferevents, BOOL, "0"),
+ OBSOLETE("UseFilteringSSLBufferevents"),
+ OBSOLETE("__UseFilteringSSLBufferevents"),
VAR("__ReloadTorrcOnSIGHUP", BOOL, ReloadTorrcOnSIGHUP, "1"),
VAR("__AllDirActionsPrivate", BOOL, AllDirActionsPrivate, "0"),
VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
@@ -494,7 +501,7 @@ static config_var_t option_vars_[] = {
* When clients have authorities and fallbacks available, they use these
* schedules: (we stagger the times to avoid thundering herds) */
V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
- "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */),
+ "6, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */),
V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
"0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"),
/* When clients only have authorities available, they use this schedule: */
@@ -505,7 +512,7 @@ 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, "3600, 900, 900, 3600"),
+ V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "1200, 900, 900, 3600"),
V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
V(TestingConsensusMaxDownloadTries, UINT, "8"),
@@ -545,7 +552,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"),
+ V(ClientDNSRejectInternalAddresses, BOOL,"0"), // deprecated in 0.2.9.2-alpha
V(ClientRejectInternalAddresses, BOOL, "0"),
V(CountPrivateBandwidth, BOOL, "1"),
V(ExitPolicyRejectPrivate, BOOL, "0"),
@@ -588,6 +595,44 @@ static const config_var_t testing_tor_network_defaults[] = {
#undef V
#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." },
+ { "AllowInvalidNodes", "There is no reason to enable this option; at best "
+ "it will make you easier to track." },
+ { "AllowSingleHopCircuits", "Almost no relays actually allow single-hop "
+ "exits, making this option pointless." },
+ { "AllowSingleHopExits", "Turning this on will make your relay easier "
+ "to abuse." },
+ { "ClientDNSRejectInternalAddresses", "Turning this on makes your client "
+ "easier to fingerprint, and may open you to esoteric attacks." },
+ { "ExcludeSingleHopRelays", "Turning it on makes your client easier to "
+ "fingerprint." },
+ { "FastFirstHopPK", "Changing this option does not make your client more "
+ "secure, but does make it easier to fingerprint." },
+ { "CloseHSClientCircuitsImmediatelyOnTimeout", "This option makes your "
+ "client easier to fingerprint." },
+ { "CloseHSServiceRendCircuitsImmediatelyOnTimeout", "This option makes "
+ "your hidden services easier to fingerprint." },
+ { "WarnUnsafeSocks", "Changing this option makes it easier for you "
+ "to accidentally lose your anonymity by leaking DNS information" },
+ { "TLSECGroup", "The default is a nice secure choice; the other option "
+ "is less secure." },
+ { "ControlListenAddress", "Use ControlPort instead." },
+ { "DirListenAddress", "Use DirPort instead, possibly with the "
+ "NoAdvertise sub-option" },
+ { "DNSListenAddress", "Use DNSPort instead." },
+ { "SocksListenAddress", "Use SocksPort instead." },
+ { "TransListenAddress", "Use TransPort instead." },
+ { "NATDListenAddress", "Use NATDPort instead." },
+ { "ORListenAddress", "Use ORPort instead, possibly with the "
+ "NoAdvertise sub-option" },
+ /* End of options deprecated since 0.2.9.2-alpha. */
+
+ { NULL, NULL }
+};
+
#ifdef _WIN32
static char *get_windows_conf_root(void);
#endif
@@ -636,6 +681,7 @@ STATIC config_format_t options_format = {
OR_OPTIONS_MAGIC,
STRUCT_OFFSET(or_options_t, magic_),
option_abbrevs_,
+ option_deprecation_notes_,
option_vars_,
options_validate_cb,
NULL
@@ -746,7 +792,7 @@ set_options(or_options_t *new_val, char **msg)
}
if (old_options != global_options)
- config_free(&options_format, old_options);
+ or_options_free(old_options);
return 0;
}
@@ -1337,6 +1383,35 @@ options_act_reversible(const or_options_t *old_options, char **msg)
connection_mark_for_close(conn);
}
});
+
+ if (set_conn_limit) {
+ /*
+ * If we adjusted the conn limit, recompute the OOS threshold too
+ *
+ * How many possible sockets to keep in reserve? If we have lots of
+ * possible sockets, keep this below a limit and set ConnLimit_high_thresh
+ * very close to ConnLimit_, but if ConnLimit_ is low, shrink it in
+ * proportion.
+ *
+ * Somewhat arbitrarily, set socks_in_reserve to 5% of ConnLimit_, but
+ * cap it at 64.
+ */
+ int socks_in_reserve = options->ConnLimit_ / 20;
+ if (socks_in_reserve > 64) socks_in_reserve = 64;
+
+ options->ConnLimit_high_thresh = options->ConnLimit_ - socks_in_reserve;
+ options->ConnLimit_low_thresh = (options->ConnLimit_ / 4) * 3;
+ log_info(LD_GENERAL,
+ "Recomputed OOS thresholds: ConnLimit %d, ConnLimit_ %d, "
+ "ConnLimit_high_thresh %d, ConnLimit_low_thresh %d",
+ options->ConnLimit, options->ConnLimit_,
+ options->ConnLimit_high_thresh,
+ options->ConnLimit_low_thresh);
+
+ /* Give the OOS handler a chance with the new thresholds */
+ connection_check_oos(get_n_open_sockets(), 0);
+ }
+
goto done;
rollback:
@@ -1492,10 +1567,10 @@ options_act(const or_options_t *old_options)
if (consider_adding_dir_servers(options, old_options) < 0)
return -1;
-#ifdef NON_ANONYMOUS_MODE_ENABLED
- log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a "
- "non-anonymous mode. It will provide NO ANONYMITY.");
-#endif
+ if (rend_non_anonymous_mode_enabled(options)) {
+ log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run "
+ "in a non-anonymous mode. It will provide NO ANONYMITY.");
+ }
#ifdef ENABLE_TOR2WEB_MODE
/* LCOV_EXCL_START */
@@ -1658,7 +1733,7 @@ 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()<0) {
+ if (rend_service_load_all_keys(NULL)<0) {
log_warn(LD_GENERAL,"Error loading rendezvous service keys");
return -1;
}
@@ -1677,17 +1752,6 @@ options_act(const or_options_t *old_options)
if (accounting_is_enabled(options))
configure_accounting(time(NULL));
-#ifdef USE_BUFFEREVENTS
- /* If we're using the bufferevents implementation and our rate limits
- * changed, we need to tell the rate-limiting system about it. */
- if (!old_options ||
- old_options->BandwidthRate != options->BandwidthRate ||
- old_options->BandwidthBurst != options->BandwidthBurst ||
- old_options->RelayBandwidthRate != options->RelayBandwidthRate ||
- old_options->RelayBandwidthBurst != options->RelayBandwidthBurst)
- connection_bucket_init();
-#endif
-
old_ewma_enabled = cell_ewma_enabled();
/* Change the cell EWMA settings */
cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus());
@@ -2002,11 +2066,6 @@ static const struct {
{ "--list-fingerprint", TAKES_NO_ARGUMENT },
{ "--keygen", TAKES_NO_ARGUMENT },
{ "--newpass", TAKES_NO_ARGUMENT },
-#if 0
-/* XXXX028: This is not working yet in 0.2.7, so disabling with the
- * minimal code modification. */
- { "--master-key", ARGUMENT_NECESSARY },
-#endif
{ "--no-passphrase", TAKES_NO_ARGUMENT },
{ "--passphrase-fd", ARGUMENT_NECESSARY },
{ "--verify-config", TAKES_NO_ARGUMENT },
@@ -2018,6 +2077,7 @@ static const struct {
{ "-h", TAKES_NO_ARGUMENT },
{ "--help", TAKES_NO_ARGUMENT },
{ "--list-torrc-options", TAKES_NO_ARGUMENT },
+ { "--list-deprecated-options",TAKES_NO_ARGUMENT },
{ "--nt-service", TAKES_NO_ARGUMENT },
{ "-nt-service", TAKES_NO_ARGUMENT },
{ NULL, 0 },
@@ -2080,7 +2140,7 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
if (want_arg == ARGUMENT_NECESSARY && is_last) {
if (ignore_errors) {
- arg = strdup("");
+ arg = tor_strdup("");
} else {
log_warn(LD_CONFIG,"Command-line option '%s' with no value. Failing.",
argv[i]);
@@ -2154,31 +2214,30 @@ option_get_assignment(const or_options_t *options, const char *key)
* what went wrong.
*/
setopt_err_t
-options_trial_assign(config_line_t *list, int use_defaults,
- int clear_first, char **msg)
+options_trial_assign(config_line_t *list, unsigned flags, char **msg)
{
int r;
or_options_t *trial_options = config_dup(&options_format, get_options());
if ((r=config_assign(&options_format, trial_options,
- list, use_defaults, clear_first, msg)) < 0) {
- config_free(&options_format, trial_options);
+ list, flags, msg)) < 0) {
+ or_options_free(trial_options);
return r;
}
if (options_validate(get_options_mutable(), trial_options,
global_default_options, 1, msg) < 0) {
- config_free(&options_format, trial_options);
+ or_options_free(trial_options);
return SETOPT_ERR_PARSE; /*XXX make this a separate return value. */
}
if (options_transition_allowed(get_options(), trial_options, msg) < 0) {
- config_free(&options_format, trial_options);
+ or_options_free(trial_options);
return SETOPT_ERR_TRANSITION;
}
if (set_options(trial_options, msg)<0) {
- config_free(&options_format, trial_options);
+ or_options_free(trial_options);
return SETOPT_ERR_SETTING;
}
@@ -2204,7 +2263,6 @@ static void
list_torrc_options(void)
{
int i;
- smartlist_t *lines = smartlist_new();
for (i = 0; option_vars_[i].name; ++i) {
const config_var_t *var = &option_vars_[i];
if (var->type == CONFIG_TYPE_OBSOLETE ||
@@ -2212,7 +2270,16 @@ list_torrc_options(void)
continue;
printf("%s\n", var->name);
}
- smartlist_free(lines);
+}
+
+/** Print all deprecated but non-obsolete torrc options. */
+static void
+list_deprecated_options(void)
+{
+ const config_deprecation_t *d;
+ for (d = option_deprecation_notes_; d->name; ++d) {
+ printf("%s\n", d->name);
+ }
}
/** Last value actually set by resolve_my_address. */
@@ -2232,6 +2299,14 @@ reset_last_resolved_addr(void)
last_resolved_addr = 0;
}
+/* Return true if <b>options</b> is using the default authorities, and false
+ * if any authority-related option has been overridden. */
+int
+using_default_dir_authorities(const or_options_t *options)
+{
+ return (!options->DirAuthorities && !options->AlternateDirAuthority);
+}
+
/**
* Attempt getting our non-local (as judged by tor_addr_is_internal()
* function) IP address using following techniques, listed in
@@ -2391,7 +2466,7 @@ resolve_my_address(int warn_severity, const or_options_t *options,
addr_string = tor_dup_ip(addr);
if (tor_addr_is_internal(&myaddr, 0)) {
/* make sure we're ok with publishing an internal IP */
- if (!options->DirAuthorities && !options->AlternateDirAuthority) {
+ if (using_default_dir_authorities(options)) {
/* if they are using the default authorities, disallow internal IPs
* always. */
log_fn(warn_severity, LD_CONFIG,
@@ -2487,7 +2562,6 @@ is_local_addr, (const tor_addr_t *addr))
if (get_options()->EnforceDistinctSubnets == 0)
return 0;
if (tor_addr_family(addr) == AF_INET) {
- /*XXXX023 IP6 what corresponds to an /24? */
uint32_t ip = tor_addr_to_ipv4h(addr);
/* It's possible that this next check will hit before the first time
@@ -2681,7 +2755,7 @@ options_validate_cb(void *old_options, void *options, void *default_options,
#define REJECT(arg) \
STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
-#ifdef __GNUC__
+#if defined(__GNUC__) && __GNUC__ <= 3
#define COMPLAIN(args...) \
STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END
#else
@@ -2739,6 +2813,71 @@ warn_about_relative_paths(or_options_t *options)
}
}
+/* Validate options related to single onion services.
+ * Modifies some options that are incompatible with single onion services.
+ * On failure returns -1, and sets *msg to an error string.
+ * Returns 0 on success. */
+STATIC int
+options_validate_single_onion(or_options_t *options, char **msg)
+{
+ /* The two single onion service options must have matching values. */
+ if (options->HiddenServiceSingleHopMode &&
+ !options->HiddenServiceNonAnonymousMode) {
+ REJECT("HiddenServiceSingleHopMode does not provide any server anonymity. "
+ "It must be used with HiddenServiceNonAnonymousMode set to 1.");
+ }
+ if (options->HiddenServiceNonAnonymousMode &&
+ !options->HiddenServiceSingleHopMode) {
+ REJECT("HiddenServiceNonAnonymousMode does not provide any server "
+ "anonymity. It must be used with HiddenServiceSingleHopMode set to "
+ "1.");
+ }
+
+ /* Now that we've checked that the two options are consistent, we can safely
+ * call the rend_service_* functions that abstract these options. */
+
+ /* If you run an anonymous client with an active Single Onion service, the
+ * client loses anonymity. */
+ const int client_port_set = (options->SocksPort_set ||
+ options->TransPort_set ||
+ options->NATDPort_set ||
+ options->DNSPort_set);
+ if (rend_service_non_anonymous_mode_enabled(options) && client_port_set &&
+ !options->Tor2webMode) {
+ REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as "
+ "an anonymous client. Please set Socks/Trans/NATD/DNSPort to 0, or "
+ "HiddenServiceNonAnonymousMode to 0, or use the non-anonymous "
+ "Tor2webMode.");
+ }
+
+ /* If you run a hidden service in non-anonymous mode, the hidden service
+ * loses anonymity, even if SOCKSPort / Tor2web mode isn't used. */
+ if (!rend_service_non_anonymous_mode_enabled(options) &&
+ options->RendConfigLines && options->Tor2webMode) {
+ REJECT("Non-anonymous (Tor2web) mode is incompatible with using Tor as a "
+ "hidden service. Please remove all HiddenServiceDir lines, or use "
+ "a version of tor compiled without --enable-tor2web-mode, or use "
+ " HiddenServiceNonAnonymousMode.");
+ }
+
+ if (rend_service_allow_non_anonymous_connection(options)
+ && options->UseEntryGuards) {
+ /* Single Onion services only use entry guards when uploading descriptors,
+ * all other connections are one-hop. Further, Single Onions causes the
+ * hidden service code to do things which break the path bias
+ * detector, and it's far easier to turn off entry guards (and
+ * thus the path bias detector with it) than to figure out how to
+ * make path bias compatible with single onions.
+ */
+ log_notice(LD_CONFIG,
+ "HiddenServiceSingleHopMode is enabled; disabling "
+ "UseEntryGuards.");
+ options->UseEntryGuards = 0;
+ }
+
+ return 0;
+}
+
/** Return 0 if every setting in <b>options</b> is reasonable, is a
* permissible transition from <b>old_options</b>, and none of the
* testing-only settings differ from <b>default_options</b> unless in
@@ -2765,6 +2904,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
tor_assert(msg);
*msg = NULL;
+ /* Set UseEntryGuards from the configured value, before we check it below.
+ * We change UseEntryGuards whenn it's incompatible with other options,
+ * but leave UseEntryGuards_option with the original value.
+ * Always use the value of UseEntryGuards, not UseEntryGuards_option. */
+ options->UseEntryGuards = options->UseEntryGuards_option;
+
warn_about_relative_paths(options);
if (server_mode(options) &&
@@ -2794,7 +2939,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
} else {
if (!is_legal_nickname(options->Nickname)) {
tor_asprintf(msg,
- "Nickname '%s' is wrong length or contains illegal characters.",
+ "Nickname '%s', nicknames must be between 1 and 19 characters "
+ "inclusive, and must contain only the characters [a-zA-Z0-9].",
options->Nickname);
return -1;
}
@@ -2861,7 +3007,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
} else if (!strcasecmp(options->TransProxyType, "ipfw")) {
#ifndef KERNEL_MAY_SUPPORT_IPFW
/* Earlier versions of OS X have ipfw */
- REJECT("ipfw is a FreeBSD-specific"
+ REJECT("ipfw is a FreeBSD-specific "
"and OS X/Darwin-specific feature.");
#else
options->TransProxyType_parsed = TPT_IPFW;
@@ -3139,10 +3285,6 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->UseBridges && options->EntryNodes)
REJECT("You cannot set both UseBridges and EntryNodes.");
- if (options->EntryNodes && !options->UseEntryGuards) {
- REJECT("If EntryNodes is set, UseEntryGuards must be enabled.");
- }
-
options->MaxMemInQueues =
compute_real_max_mem_in_queues(options->MaxMemInQueues_raw,
server_mode(options));
@@ -3233,25 +3375,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
options->PredictedPortsRelevanceTime = MAX_PREDICTED_CIRCS_RELEVANCE;
}
-#ifdef ENABLE_TOR2WEB_MODE
- if (options->Tor2webMode && options->LearnCircuitBuildTimeout) {
- /* LearnCircuitBuildTimeout and Tor2webMode are incompatible in
- * two ways:
- *
- * - LearnCircuitBuildTimeout results in a low CBT, which
- * Tor2webMode's use of one-hop rendezvous circuits lowers
- * much further, producing *far* too many timeouts.
- *
- * - The adaptive CBT code does not update its timeout estimate
- * using build times for single-hop circuits.
- *
- * If we fix both of these issues someday, we should test
- * Tor2webMode with LearnCircuitBuildTimeout on again. */
- log_notice(LD_CONFIG,"Tor2webMode is enabled; turning "
- "LearnCircuitBuildTimeout off.");
- options->LearnCircuitBuildTimeout = 0;
- }
+ /* Check the Single Onion Service options */
+ if (options_validate_single_onion(options, msg) < 0)
+ return -1;
+#ifdef ENABLE_TOR2WEB_MODE
if (options->Tor2webMode && options->UseEntryGuards) {
/* tor2web mode clients do not (and should not) use entry guards
* in any meaningful way. Further, tor2web mode causes the hidden
@@ -3271,8 +3399,13 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("Tor2webRendezvousPoints cannot be set without Tor2webMode.");
}
+ if (options->EntryNodes && !options->UseEntryGuards) {
+ REJECT("If EntryNodes is set, UseEntryGuards must be enabled.");
+ }
+
if (!(options->UseEntryGuards) &&
- (options->RendConfigLines != NULL)) {
+ (options->RendConfigLines != NULL) &&
+ !rend_service_allow_non_anonymous_connection(options)) {
log_warn(LD_CONFIG,
"UseEntryGuards is disabled, but you have configured one or more "
"hidden services on this Tor instance. Your hidden services "
@@ -3295,6 +3428,17 @@ options_validate(or_options_t *old_options, or_options_t *options,
return -1;
}
+ /* Single Onion Services: non-anonymous hidden services */
+ if (rend_service_non_anonymous_mode_enabled(options)) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceNonAnonymousMode is set. Every hidden service on "
+ "this tor instance is NON-ANONYMOUS. If "
+ "the HiddenServiceNonAnonymousMode option is changed, Tor will "
+ "refuse to launch hidden services from the same directories, to "
+ "protect your anonymity against config errors. This setting is "
+ "for experimental use only.");
+ }
+
if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout &&
options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) {
log_warn(LD_CONFIG,
@@ -3306,8 +3450,15 @@ options_validate(or_options_t *old_options, or_options_t *options,
RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT );
} else if (!options->LearnCircuitBuildTimeout &&
!options->CircuitBuildTimeout) {
- log_notice(LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but didn't "
- "a CircuitBuildTimeout. I'll pick a plausible default.");
+ int severity = LOG_NOTICE;
+ /* Be a little quieter if we've deliberately disabled
+ * LearnCircuitBuildTimeout. */
+ if (circuit_build_times_disabled()) {
+ severity = LOG_INFO;
+ }
+ log_fn(severity, LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but "
+ "didn't specify a CircuitBuildTimeout. I'll pick a plausible "
+ "default.");
}
if (options->PathBiasNoticeRate > 1.0) {
@@ -3497,10 +3648,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
}
if (server_mode(options)) {
- char *msg = NULL;
- if (have_enough_mem_for_dircache(options, 0, &msg)) {
- log_warn(LD_CONFIG, "%s", msg);
- tor_free(msg);
+ char *dircache_msg = NULL;
+ if (have_enough_mem_for_dircache(options, 0, &dircache_msg)) {
+ log_warn(LD_CONFIG, "%s", dircache_msg);
+ tor_free(dircache_msg);
}
}
@@ -4130,11 +4281,11 @@ have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem,
if (options->DirCache) {
if (total_mem < DIRCACHE_MIN_BANDWIDTH) {
if (options->BridgeRelay) {
- *msg = strdup("Running a Bridge with less than "
+ *msg = tor_strdup("Running a Bridge with less than "
STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is "
"not recommended.");
} else {
- *msg = strdup("Being a directory cache (default) with less than "
+ *msg = tor_strdup("Being a directory cache (default) with less than "
STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is "
"not recommended and may consume most of the available "
"resources, consider disabling this functionality by "
@@ -4143,7 +4294,7 @@ have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem,
}
} else {
if (total_mem >= DIRCACHE_MIN_BANDWIDTH) {
- *msg = strdup("DirCache is disabled and we are configured as a "
+ *msg = tor_strdup("DirCache is disabled and we are configured as a "
"relay. This may disqualify us from becoming a guard in the "
"future.");
}
@@ -4237,9 +4388,16 @@ options_transition_allowed(const or_options_t *old,
return -1;
}
- if (old->DisableIOCP != new_val->DisableIOCP) {
- *msg = tor_strdup("While Tor is running, changing DisableIOCP "
- "is not allowed.");
+ if (old->HiddenServiceSingleHopMode != new_val->HiddenServiceSingleHopMode) {
+ *msg = tor_strdup("While Tor is running, changing "
+ "HiddenServiceSingleHopMode is not allowed.");
+ return -1;
+ }
+
+ if (old->HiddenServiceNonAnonymousMode !=
+ new_val->HiddenServiceNonAnonymousMode) {
+ *msg = tor_strdup("While Tor is running, changing "
+ "HiddenServiceNonAnonymousMode is not allowed.");
return -1;
}
@@ -4299,6 +4457,7 @@ options_transition_affects_workers(const or_options_t *old_options,
new_options->ServerDNSSearchDomains ||
old_options->SafeLogging_ != new_options->SafeLogging_ ||
old_options->ClientOnly != new_options->ClientOnly ||
+ server_mode(old_options) != server_mode(new_options) ||
public_server_mode(old_options) != public_server_mode(new_options) ||
!config_lines_eq(old_options->Logs, new_options->Logs) ||
old_options->LogMessageDomains != new_options->LogMessageDomains)
@@ -4325,6 +4484,8 @@ options_transition_affects_descriptor(const or_options_t *old_options,
old_options->ExitRelay != new_options->ExitRelay ||
old_options->ExitPolicyRejectPrivate !=
new_options->ExitPolicyRejectPrivate ||
+ old_options->ExitPolicyRejectLocalInterfaces !=
+ new_options->ExitPolicyRejectLocalInterfaces ||
old_options->IPv6Exit != new_options->IPv6Exit ||
!config_lines_eq(old_options->ORPort_lines,
new_options->ORPort_lines) ||
@@ -4670,10 +4831,15 @@ options_init_from_torrc(int argc, char **argv)
exit(0);
}
if (config_line_find(cmdline_only_options, "--list-torrc-options")) {
- /* For documenting validating whether we've documented everything. */
+ /* For validating whether we've documented everything. */
list_torrc_options();
exit(0);
}
+ if (config_line_find(cmdline_only_options, "--list-deprecated-options")) {
+ /* For validating whether what we have deprecated really exists. */
+ list_deprecated_options();
+ exit(0);
+ }
if (config_line_find(cmdline_only_options, "--version")) {
printf("Tor version %s.\n",get_version());
@@ -4829,7 +4995,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
{
or_options_t *oldoptions, *newoptions, *newdefaultoptions=NULL;
config_line_t *cl;
- int retval, i;
+ int retval;
setopt_err_t err = SETOPT_ERR_MISC;
tor_assert(msg);
@@ -4842,7 +5008,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->command = command;
newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
- for (i = 0; i < 2; ++i) {
+ for (int i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
@@ -4852,7 +5018,8 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err = SETOPT_ERR_PARSE;
goto err;
}
- retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+ retval = config_assign(&options_format, newoptions, cl,
+ CAL_WARN_DEPRECATIONS, msg);
config_free_lines(cl);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
@@ -4868,7 +5035,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
/* Go through command-line variables too */
retval = config_assign(&options_format, newoptions,
- global_cmdline_options, 0, 0, msg);
+ global_cmdline_options, CAL_WARN_DEPRECATIONS, msg);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
@@ -4886,19 +5053,24 @@ options_init_from_string(const char *cf_defaults, const char *cf,
* let's clean it up. -NM */
/* Change defaults. */
- int i;
- for (i = 0; testing_tor_network_defaults[i].name; ++i) {
+ for (int i = 0; testing_tor_network_defaults[i].name; ++i) {
const config_var_t *new_var = &testing_tor_network_defaults[i];
config_var_t *old_var =
config_find_option_mutable(&options_format, new_var->name);
tor_assert(new_var);
tor_assert(old_var);
old_var->initvalue = new_var->initvalue;
+
+ if ((config_find_deprecation(&options_format, new_var->name))) {
+ log_warn(LD_GENERAL, "Testing options override the deprecated "
+ "option %s. Is that intentional?",
+ new_var->name);
+ }
}
/* Clear newoptions and re-initialize them with new defaults. */
- config_free(&options_format, newoptions);
- config_free(&options_format, newdefaultoptions);
+ or_options_free(newoptions);
+ or_options_free(newdefaultoptions);
newdefaultoptions = NULL;
newoptions = tor_malloc_zero(sizeof(or_options_t));
newoptions->magic_ = OR_OPTIONS_MAGIC;
@@ -4907,7 +5079,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
newoptions->command_arg = command_arg ? tor_strdup(command_arg) : NULL;
/* Assign all options a second time. */
- for (i = 0; i < 2; ++i) {
+ for (int i = 0; i < 2; ++i) {
const char *body = i==0 ? cf_defaults : cf;
if (!body)
continue;
@@ -4917,7 +5089,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err = SETOPT_ERR_PARSE;
goto err;
}
- retval = config_assign(&options_format, newoptions, cl, 0, 0, msg);
+ retval = config_assign(&options_format, newoptions, cl, 0, msg);
config_free_lines(cl);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
@@ -4928,7 +5100,7 @@ options_init_from_string(const char *cf_defaults, const char *cf,
}
/* Assign command-line variables a second time too */
retval = config_assign(&options_format, newoptions,
- global_cmdline_options, 0, 0, msg);
+ global_cmdline_options, 0, msg);
if (retval < 0) {
err = SETOPT_ERR_PARSE;
goto err;
@@ -4951,14 +5123,14 @@ options_init_from_string(const char *cf_defaults, const char *cf,
err = SETOPT_ERR_SETTING;
goto err; /* frees and replaces old options */
}
- config_free(&options_format, global_default_options);
+ or_options_free(global_default_options);
global_default_options = newdefaultoptions;
return SETOPT_OK;
err:
- config_free(&options_format, newoptions);
- config_free(&options_format, newdefaultoptions);
+ or_options_free(newoptions);
+ or_options_free(newdefaultoptions);
if (*msg) {
char *old_msg = *msg;
tor_asprintf(msg, "Failed to parse/validate config: %s", old_msg);
@@ -5028,7 +5200,7 @@ config_register_addressmaps(const or_options_t *options)
/** As addressmap_register(), but detect the wildcarded status of "from" and
* "to", and do not steal a reference to <b>to</b>. */
-/* XXXX024 move to connection_edge.c */
+/* XXXX move to connection_edge.c */
int
addressmap_register_auto(const char *from, const char *to,
time_t expires,
@@ -5080,7 +5252,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
config_line_t *opt;
int ok;
smartlist_t *elts;
- int daemon =
+ int run_as_daemon =
#ifdef _WIN32
0;
#else
@@ -5141,7 +5313,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
int err = smartlist_len(elts) &&
!strcasecmp(smartlist_get(elts,0), "stderr");
if (!validate_only) {
- if (daemon) {
+ if (run_as_daemon) {
log_warn(LD_CONFIG,
"Can't log to %s with RunAsDaemon set; skipping stdout",
err?"stderr":"stdout");
@@ -5170,19 +5342,19 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
char *fname = expand_filename(smartlist_get(elts, 1));
/* Truncate if TruncateLogFile is set and we haven't seen this option
line before. */
- int truncate = 0;
+ int truncate_log = 0;
if (options->TruncateLogFile) {
- truncate = 1;
+ truncate_log = 1;
if (old_options) {
config_line_t *opt2;
for (opt2 = old_options->Logs; opt2; opt2 = opt2->next)
if (!strcmp(opt->value, opt2->value)) {
- truncate = 0;
+ truncate_log = 0;
break;
}
}
}
- if (add_file_log(severity, fname, truncate) < 0) {
+ if (add_file_log(severity, fname, truncate_log) < 0) {
log_warn(LD_CONFIG, "Couldn't open file for 'Log %s': %s",
opt->value, strerror(errno));
ok = 0;
@@ -5260,10 +5432,14 @@ bridge_line_free(bridge_line_t *bridge_line)
tor_free(bridge_line);
}
-/** Read the contents of a Bridge line from <b>line</b>. Return 0
- * if the line is well-formed, and -1 if it isn't. If
- * <b>validate_only</b> is 0, and the line is well-formed, then add
- * the bridge described in the line to our internal bridge list.
+/** Parse the contents of a string, <b>line</b>, containing a Bridge line,
+ * into a bridge_line_t.
+ *
+ * Validates that the IP:PORT, fingerprint, and SOCKS arguments (given to the
+ * Pluggable Transport, if a one was specified) are well-formed.
+ *
+ * Returns NULL If the Bridge line could not be validated, and returns a
+ * bridge_line_t containing the parsed information otherwise.
*
* Bridge line format:
* Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ...
@@ -5336,7 +5512,7 @@ parse_bridge_line(const char *line)
goto err;
}
if (base16_decode(bridge_line->digest, DIGEST_LEN,
- fingerprint, HEX_DIGEST_LEN)<0) {
+ fingerprint, HEX_DIGEST_LEN) != DIGEST_LEN) {
log_warn(LD_CONFIG, "Unable to decode Bridge key digest.");
goto err;
}
@@ -5779,7 +5955,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
} else if (!strcmpstart(flag, "weight=")) {
int ok;
const char *wstring = flag + strlen("weight=");
- weight = tor_parse_double(wstring, 0, UINT64_MAX, &ok, NULL);
+ weight = tor_parse_double(wstring, 0, (double)UINT64_MAX, &ok, NULL);
if (!ok) {
log_warn(LD_CONFIG, "Invalid weight '%s' on DirAuthority line.",flag);
weight=1.0;
@@ -5787,7 +5963,8 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
} else if (!strcasecmpstart(flag, "v3ident=")) {
char *idstr = flag + strlen("v3ident=");
if (strlen(idstr) != HEX_DIGEST_LEN ||
- base16_decode(v3_digest, DIGEST_LEN, idstr, HEX_DIGEST_LEN)<0) {
+ base16_decode(v3_digest, DIGEST_LEN,
+ idstr, HEX_DIGEST_LEN) != DIGEST_LEN) {
log_warn(LD_CONFIG, "Bad v3 identity digest '%s' on DirAuthority line",
flag);
} else {
@@ -5836,7 +6013,8 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
fingerprint, (int)strlen(fingerprint));
goto err;
}
- if (base16_decode(digest, DIGEST_LEN, fingerprint, HEX_DIGEST_LEN)<0) {
+ if (base16_decode(digest, DIGEST_LEN,
+ fingerprint, HEX_DIGEST_LEN) != DIGEST_LEN) {
log_warn(LD_CONFIG, "Unable to decode DirAuthority key digest.");
goto err;
}
@@ -5904,8 +6082,8 @@ parse_dir_fallback_line(const char *line,
orport = (int)tor_parse_long(cp+strlen("orport="), 10,
1, 65535, &ok, NULL);
} else if (!strcmpstart(cp, "id=")) {
- ok = !base16_decode(id, DIGEST_LEN,
- cp+strlen("id="), strlen(cp)-strlen("id="));
+ ok = base16_decode(id, DIGEST_LEN, cp+strlen("id="),
+ strlen(cp)-strlen("id=")) == DIGEST_LEN;
} else if (!strcasecmpstart(cp, "ipv6=")) {
if (ipv6_addrport_ptr) {
log_warn(LD_CONFIG, "Redundant ipv6 addr/port on FallbackDir line");
@@ -5921,10 +6099,10 @@ parse_dir_fallback_line(const char *line,
ipv6_addrport_ptr = &ipv6_addrport;
}
} else if (!strcmpstart(cp, "weight=")) {
- int ok;
+ int num_ok;
const char *wstring = cp + strlen("weight=");
- weight = tor_parse_double(wstring, 0, UINT64_MAX, &ok, NULL);
- if (!ok) {
+ weight = tor_parse_double(wstring, 0, (double)UINT64_MAX, &num_ok, NULL);
+ if (!num_ok) {
log_warn(LD_CONFIG, "Invalid weight '%s' on FallbackDir line.", cp);
weight=1.0;
}
@@ -5987,6 +6165,8 @@ port_cfg_new(size_t namelen)
tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1);
port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t) + namelen + 1);
cfg->entry_cfg.ipv4_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;
@@ -6089,54 +6269,75 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
} SMARTLIST_FOREACH_END(port);
}
-#ifdef HAVE_SYS_UN_H
-
-/** Parse the given <b>addrport</b> and set <b>path_out</b> if a Unix socket
- * path is found. Return 0 on success. On error, a negative value is
- * returned, -ENOENT if no Unix statement found, -EINVAL if the socket path
- * is empty and -ENOSYS if AF_UNIX is not supported (see function in the
- * #else statement below). */
-
+/**
+ * Take a string (<b>line</b>) that begins with either an address:port, a
+ * port, or an AF_UNIX address, optionally quoted, prefixed with
+ * "unix:". Parse that line, and on success, set <b>addrport_out</b> to a new
+ * string containing the beginning portion (without prefix). Iff there was a
+ * unix: prefix, set <b>is_unix_out</b> to true. On success, also set
+ * <b>rest_out</b> to point to the part of the line after the address portion.
+ *
+ * Return 0 on success, -1 on failure.
+ */
int
-config_parse_unix_port(const char *addrport, char **path_out)
+port_cfg_line_extract_addrport(const char *line,
+ char **addrport_out,
+ int *is_unix_out,
+ const char **rest_out)
{
- tor_assert(path_out);
- tor_assert(addrport);
-
- if (strcmpstart(addrport, unix_socket_prefix)) {
- /* Not a Unix socket path. */
- return -ENOENT;
- }
+ tor_assert(line);
+ tor_assert(addrport_out);
+ tor_assert(is_unix_out);
+ tor_assert(rest_out);
+
+ line = eat_whitespace(line);
+
+ if (!strcmpstart(line, unix_q_socket_prefix)) {
+ // It starts with unix:"
+ size_t sz;
+ *is_unix_out = 1;
+ *addrport_out = NULL;
+ line += strlen(unix_socket_prefix); /*No q: Keep the quote */
+ *rest_out = unescape_string(line, addrport_out, &sz);
+ if (!*rest_out || (*addrport_out && sz != strlen(*addrport_out))) {
+ tor_free(*addrport_out);
+ return -1;
+ }
+ *rest_out = eat_whitespace(*rest_out);
+ return 0;
+ } else {
+ // Is there a unix: prefix?
+ if (!strcmpstart(line, unix_socket_prefix)) {
+ line += strlen(unix_socket_prefix);
+ *is_unix_out = 1;
+ } else {
+ *is_unix_out = 0;
+ }
- if (strlen(addrport + strlen(unix_socket_prefix)) == 0) {
- /* Empty socket path, not very usable. */
- return -EINVAL;
+ const char *end = find_whitespace(line);
+ if (BUG(!end)) {
+ end = strchr(line, '\0'); // LCOV_EXCL_LINE -- this can't be NULL
+ }
+ tor_assert(end && end >= line);
+ *addrport_out = tor_strndup(line, end - line);
+ *rest_out = eat_whitespace(end);
+ return 0;
}
-
- *path_out = tor_strdup(addrport + strlen(unix_socket_prefix));
- return 0;
}
-#else /* defined(HAVE_SYS_UN_H) */
-
-int
-config_parse_unix_port(const char *addrport, char **path_out)
+static void
+warn_client_dns_cache(const char *option, int disabling)
{
- tor_assert(path_out);
- tor_assert(addrport);
-
- if (strcmpstart(addrport, unix_socket_prefix)) {
- /* Not a Unix socket path. */
- return -ENOENT;
- }
+ if (disabling)
+ return;
- log_warn(LD_CONFIG,
- "Port configuration %s is for an AF_UNIX socket, but we have no"
- "support available on this platform",
- escaped(addrport));
- return -ENOSYS;
+ warn_deprecated_option(option,
+ "Client-side DNS cacheing enables a wide variety of route-"
+ "capture attacks. If a single bad exit node lies to you about "
+ "an IP address, cacheing that address would make you visit "
+ "an address of the attacker's choice every time you connected "
+ "to your destination.");
}
-#endif /* defined(HAVE_SYS_UN_H) */
/**
* Parse port configuration for a single port type.
@@ -6243,8 +6444,7 @@ parse_port_config(smartlist_t *out,
tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */
cfg->server_cfg.no_listen = 1;
cfg->server_cfg.bind_ipv4_only = 1;
- cfg->entry_cfg.ipv4_traffic = 1;
- cfg->entry_cfg.prefer_ipv6_virtaddr = 1;
+ /* cfg->entry_cfg defaults are already set by port_cfg_new */
smartlist_add(out, cfg);
}
@@ -6303,44 +6503,54 @@ parse_port_config(smartlist_t *out,
/* At last we can actually parse the FooPort lines. The syntax is:
* [Addr:](Port|auto) [Options].*/
elts = smartlist_new();
+ char *addrport = NULL;
for (; ports; ports = ports->next) {
tor_addr_t addr;
- int port, ret;
+ int port;
int sessiongroup = SESSION_GROUP_UNSET;
unsigned isolation = ISO_DEFAULT;
int prefer_no_auth = 0;
int socks_iso_keep_alive = 0;
- char *addrport;
uint16_t ptmp=0;
int ok;
+ /* This must be kept in sync with port_cfg_new's defaults */
int no_listen = 0, no_advertise = 0, all_addrs = 0,
bind_ipv4_only = 0, bind_ipv6_only = 0,
- ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0,
+ ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0, dns_request = 1,
+ onion_traffic = 1,
cache_ipv4 = 1, 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,
has_used_unix_socket_only_option = 0;
- smartlist_split_string(elts, ports->value, NULL,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(elts) == 0) {
- log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname);
+ int is_unix_tagged_addr = 0;
+ const char *rest_of_line = NULL;
+ if (port_cfg_line_extract_addrport(ports->value,
+ &addrport, &is_unix_tagged_addr, &rest_of_line)<0) {
+ log_warn(LD_CONFIG, "Invalid %sPort line with unparsable address",
+ portname);
+ goto err;
+ }
+ if (strlen(addrport) == 0) {
+ log_warn(LD_CONFIG, "Invalid %sPort line with no address", portname);
goto err;
}
- /* Now parse the addr/port value */
- addrport = smartlist_get(elts, 0);
+ /* Split the remainder... */
+ smartlist_split_string(elts, rest_of_line, NULL,
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
/* Let's start to check if it's a Unix socket path. */
- ret = config_parse_unix_port(addrport, &unix_socket_path);
- if (ret < 0 && ret != -ENOENT) {
- if (ret == -EINVAL) {
- log_warn(LD_CONFIG, "Empty Unix socket path.");
- }
+ if (is_unix_tagged_addr) {
+#ifndef HAVE_SYS_UN_H
+ log_warn(LD_CONFIG, "Unix sockets not supported on this system.");
goto err;
+#endif
+ unix_socket_path = addrport;
+ addrport = NULL;
}
if (unix_socket_path &&
@@ -6352,6 +6562,8 @@ parse_port_config(smartlist_t *out,
if (unix_socket_path) {
port = 1;
} else if (is_unix_socket) {
+ if (BUG(!addrport))
+ goto err; // LCOV_EXCL_LINE unreachable, but coverity can't tell that
unix_socket_path = tor_strdup(addrport);
if (!strcmp(addrport, "0"))
port = 0;
@@ -6398,9 +6610,6 @@ parse_port_config(smartlist_t *out,
if (use_server_options) {
/* This is a server port; parse advertising options */
SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
- if (elt_sl_idx == 0)
- continue; /* Skip addr:port */
-
if (!strcasecmp(elt, "NoAdvertise")) {
no_advertise = 1;
} else if (!strcasecmp(elt, "NoListen")) {
@@ -6448,8 +6657,6 @@ parse_port_config(smartlist_t *out,
SMARTLIST_FOREACH_BEGIN(elts, char *, elt) {
int no = 0, isoflag = 0;
const char *elt_orig = elt;
- if (elt_sl_idx == 0)
- continue; /* Skip addr:port */
if (!strcasecmpstart(elt, "SessionGroup=")) {
int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
@@ -6503,24 +6710,48 @@ parse_port_config(smartlist_t *out,
} else if (!strcasecmp(elt, "PreferIPv6")) {
prefer_ipv6 = ! no;
continue;
+ } else if (!strcasecmp(elt, "DNSRequest")) {
+ dns_request = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "OnionTraffic")) {
+ onion_traffic = ! no;
+ continue;
+ } else if (!strcasecmp(elt, "OnionTrafficOnly")) {
+ /* Only connect to .onion addresses. Equivalent to
+ * NoDNSRequest, NoIPv4Traffic, NoIPv6Traffic. The option
+ * NoOnionTrafficOnly is not supported, it's too confusing. */
+ if (no) {
+ log_warn(LD_CONFIG, "Unsupported %sPort option 'No%s'. Use "
+ "DNSRequest, IPv4Traffic, and/or IPv6Traffic instead.",
+ portname, escaped(elt));
+ } else {
+ ipv4_traffic = ipv6_traffic = dns_request = 0;
+ }
+ continue;
}
}
if (!strcasecmp(elt, "CacheIPv4DNS")) {
+ warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha
cache_ipv4 = ! no;
continue;
} else if (!strcasecmp(elt, "CacheIPv6DNS")) {
+ warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha
cache_ipv6 = ! no;
continue;
} else if (!strcasecmp(elt, "CacheDNS")) {
+ warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha
cache_ipv4 = cache_ipv6 = ! no;
continue;
} else if (!strcasecmp(elt, "UseIPv4Cache")) {
+ warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha
use_cached_ipv4 = ! no;
continue;
} else if (!strcasecmp(elt, "UseIPv6Cache")) {
+ warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha
use_cached_ipv6 = ! no;
continue;
} else if (!strcasecmp(elt, "UseDNSCache")) {
+ warn_client_dns_cache(elt, no); // since 0.2.9.2-alpha
use_cached_ipv4 = use_cached_ipv6 = ! no;
continue;
} else if (!strcasecmp(elt, "PreferIPv6Automap")) {
@@ -6565,9 +6796,24 @@ parse_port_config(smartlist_t *out,
else
got_zero_port = 1;
- if (ipv4_traffic == 0 && ipv6_traffic == 0) {
- log_warn(LD_CONFIG, "You have a %sPort entry with both IPv4 and "
- "IPv6 disabled; that won't work.", portname);
+ if (dns_request == 0 && listener_type == CONN_TYPE_AP_DNS_LISTENER) {
+ log_warn(LD_CONFIG, "You have a %sPort entry with DNS disabled; that "
+ "won't work.", portname);
+ goto err;
+ }
+
+ if (ipv4_traffic == 0 && ipv6_traffic == 0 && onion_traffic == 0
+ && listener_type != CONN_TYPE_AP_DNS_LISTENER) {
+ log_warn(LD_CONFIG, "You have a %sPort entry with all of IPv4 and "
+ "IPv6 and .onion disabled; that won't work.", portname);
+ goto err;
+ }
+
+ if (dns_request == 1 && ipv4_traffic == 0 && ipv6_traffic == 0
+ && listener_type != CONN_TYPE_AP_DNS_LISTENER) {
+ log_warn(LD_CONFIG, "You have a %sPort entry with DNSRequest enabled, "
+ "but IPv4 and IPv6 disabled; DNS-based sites won't work.",
+ portname);
goto err;
}
@@ -6585,6 +6831,13 @@ parse_port_config(smartlist_t *out,
goto err;
}
+ if (unix_socket_path && (isolation & ISO_CLIENTADDR)) {
+ /* `IsolateClientAddr` is nonsensical in the context of AF_LOCAL.
+ * just silently remove the isolation flag.
+ */
+ isolation &= ~ISO_CLIENTADDR;
+ }
+
if (out && port) {
size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0;
port_cfg_t *cfg = port_cfg_new(namelen);
@@ -6611,6 +6864,8 @@ parse_port_config(smartlist_t *out,
cfg->entry_cfg.ipv4_traffic = ipv4_traffic;
cfg->entry_cfg.ipv6_traffic = ipv6_traffic;
cfg->entry_cfg.prefer_ipv6 = prefer_ipv6;
+ cfg->entry_cfg.dns_request = dns_request;
+ cfg->entry_cfg.onion_traffic = onion_traffic;
cfg->entry_cfg.cache_ipv4_answers = cache_ipv4;
cfg->entry_cfg.cache_ipv6_answers = cache_ipv6;
cfg->entry_cfg.use_cached_ipv4_answers = use_cached_ipv4;
@@ -6625,6 +6880,7 @@ parse_port_config(smartlist_t *out,
}
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_clear(elts);
+ tor_free(addrport);
}
if (warn_nonlocal && out) {
@@ -6648,18 +6904,22 @@ parse_port_config(smartlist_t *out,
SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp));
smartlist_free(elts);
tor_free(unix_socket_path);
+ tor_free(addrport);
return retval;
}
/** Return the number of ports which are actually going to listen with type
- * <b>listenertype</b>. Do not count no_listen ports. Do not count unix
- * sockets. */
+ * <b>listenertype</b>. Do not count no_listen ports. Only count unix
+ * sockets if count_sockets is true. */
static int
-count_real_listeners(const smartlist_t *ports, int listenertype)
+count_real_listeners(const smartlist_t *ports, int listenertype,
+ int count_sockets)
{
int n = 0;
SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) {
- if (port->server_cfg.no_listen || port->is_unix_addr)
+ if (port->server_cfg.no_listen)
+ continue;
+ if (!count_sockets && port->is_unix_addr)
continue;
if (port->type != listenertype)
continue;
@@ -6668,9 +6928,8 @@ count_real_listeners(const smartlist_t *ports, int listenertype)
return n;
}
-/** Parse all client port types (Socks, DNS, Trans, NATD) from
- * <b>options</b>. On success, set *<b>n_ports_out</b> to the number
- * of ports that are listed, update the *Port_set values in
+/** Parse all ports from <b>options</b>. On success, set *<b>n_ports_out</b>
+ * to the number of ports that are listed, update the *Port_set values in
* <b>options</b>, and return 0. On failure, set *<b>msg</b> to a
* description of the problem and return -1.
*
@@ -6796,21 +7055,22 @@ parse_ports(or_options_t *options, int validate_only,
/* Update the *Port_set options. The !! here is to force a boolean out of
an integer. */
options->ORPort_set =
- !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER, 0);
options->SocksPort_set =
- !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER, 1);
options->TransPort_set =
- !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1);
options->NATDPort_set =
- !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1);
+ /* Use options->ControlSocket to test if a control socket is set */
options->ControlPort_set =
- !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0);
options->DirPort_set =
- !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER, 0);
options->DNSPort_set =
- !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER, 1);
options->ExtORPort_set =
- !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER);
+ !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER, 0);
if (world_writable_control_socket) {
SMARTLIST_FOREACH(ports, port_cfg_t *, p,
@@ -6840,6 +7100,24 @@ parse_ports(or_options_t *options, int validate_only,
return retval;
}
+/* Does port bind to IPv4? */
+static int
+port_binds_ipv4(const port_cfg_t *port)
+{
+ return tor_addr_family(&port->addr) == AF_INET ||
+ (tor_addr_family(&port->addr) == AF_UNSPEC
+ && !port->server_cfg.bind_ipv6_only);
+}
+
+/* Does port bind to IPv6? */
+static int
+port_binds_ipv6(const port_cfg_t *port)
+{
+ return tor_addr_family(&port->addr) == AF_INET6 ||
+ (tor_addr_family(&port->addr) == AF_UNSPEC
+ && !port->server_cfg.bind_ipv4_only);
+}
+
/** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal
* consistency and warn as appropriate. Set *<b>n_low_ports_out</b> to the
* number of sub-1024 ports we will be binding. */
@@ -6865,9 +7143,7 @@ check_server_ports(const smartlist_t *ports,
} else if (port->type == CONN_TYPE_OR_LISTENER) {
if (! port->server_cfg.no_advertise) {
++n_orport_advertised;
- if (tor_addr_family(&port->addr) == AF_INET ||
- (tor_addr_family(&port->addr) == AF_UNSPEC &&
- !port->server_cfg.bind_ipv6_only))
+ if (port_binds_ipv4(port))
++n_orport_advertised_ipv4;
}
if (! port->server_cfg.no_listen)
@@ -7001,19 +7277,20 @@ get_first_listener_addrport_string(int listener_type)
}
/** Return the first advertised port of type <b>listener_type</b> in
- <b>address_family</b>. */
+ * <b>address_family</b>. Returns 0 when no port is found, and when passed
+ * AF_UNSPEC. */
int
get_first_advertised_port_by_type_af(int listener_type, int address_family)
{
+ if (address_family == AF_UNSPEC)
+ return 0;
+
const smartlist_t *conf_ports = get_configured_ports();
SMARTLIST_FOREACH_BEGIN(conf_ports, const port_cfg_t *, cfg) {
if (cfg->type == listener_type &&
- !cfg->server_cfg.no_advertise &&
- (tor_addr_family(&cfg->addr) == address_family ||
- tor_addr_family(&cfg->addr) == AF_UNSPEC)) {
- if (tor_addr_family(&cfg->addr) != AF_UNSPEC ||
- (address_family == AF_INET && !cfg->server_cfg.bind_ipv6_only) ||
- (address_family == AF_INET6 && !cfg->server_cfg.bind_ipv4_only)) {
+ !cfg->server_cfg.no_advertise) {
+ if ((address_family == AF_INET && port_binds_ipv4(cfg)) ||
+ (address_family == AF_INET6 && port_binds_ipv6(cfg))) {
return cfg->port;
}
}
@@ -7021,6 +7298,87 @@ get_first_advertised_port_by_type_af(int listener_type, int address_family)
return 0;
}
+/** Return the first advertised address of type <b>listener_type</b> in
+ * <b>address_family</b>. Returns NULL if there is no advertised address,
+ * and when passed AF_UNSPEC. */
+const tor_addr_t *
+get_first_advertised_addr_by_type_af(int listener_type, int address_family)
+{
+ if (address_family == AF_UNSPEC)
+ return NULL;
+ if (!configured_ports)
+ return NULL;
+ SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) {
+ if (cfg->type == listener_type &&
+ !cfg->server_cfg.no_advertise) {
+ if ((address_family == AF_INET && port_binds_ipv4(cfg)) ||
+ (address_family == AF_INET6 && port_binds_ipv6(cfg))) {
+ return &cfg->addr;
+ }
+ }
+ } SMARTLIST_FOREACH_END(cfg);
+ return NULL;
+}
+
+/** Return 1 if a port exists of type <b>listener_type</b> on <b>addr</b> and
+ * <b>port</b>. If <b>check_wildcard</b> is true, INADDR[6]_ANY and AF_UNSPEC
+ * addresses match any address of the appropriate family; and port -1 matches
+ * any port.
+ * To match auto ports, pass CFG_PORT_AUTO. (Does not match on the actual
+ * automatically chosen listener ports.) */
+int
+port_exists_by_type_addr_port(int listener_type, const tor_addr_t *addr,
+ int port, int check_wildcard)
+{
+ if (!configured_ports || !addr)
+ return 0;
+ SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) {
+ if (cfg->type == listener_type) {
+ if (cfg->port == port || (check_wildcard && port == -1)) {
+ /* Exact match */
+ if (tor_addr_eq(&cfg->addr, addr)) {
+ return 1;
+ }
+ /* Skip wildcard matches if we're not doing them */
+ if (!check_wildcard) {
+ continue;
+ }
+ /* Wildcard matches IPv4 */
+ const int cfg_v4 = port_binds_ipv4(cfg);
+ const int cfg_any_v4 = tor_addr_is_null(&cfg->addr) && cfg_v4;
+ const int addr_v4 = tor_addr_family(addr) == AF_INET ||
+ tor_addr_family(addr) == AF_UNSPEC;
+ const int addr_any_v4 = tor_addr_is_null(&cfg->addr) && addr_v4;
+ if ((cfg_any_v4 && addr_v4) || (cfg_v4 && addr_any_v4)) {
+ return 1;
+ }
+ /* Wildcard matches IPv6 */
+ const int cfg_v6 = port_binds_ipv6(cfg);
+ const int cfg_any_v6 = tor_addr_is_null(&cfg->addr) && cfg_v6;
+ const int addr_v6 = tor_addr_family(addr) == AF_INET6 ||
+ tor_addr_family(addr) == AF_UNSPEC;
+ const int addr_any_v6 = tor_addr_is_null(&cfg->addr) && addr_v6;
+ if ((cfg_any_v6 && addr_v6) || (cfg_v6 && addr_any_v6)) {
+ return 1;
+ }
+ }
+ }
+ } SMARTLIST_FOREACH_END(cfg);
+ return 0;
+}
+
+/* Like port_exists_by_type_addr_port, but accepts a host-order IPv4 address
+ * instead. */
+int
+port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h,
+ int port, int check_wildcard)
+{
+ tor_addr_t ipv4;
+ tor_addr_from_ipv4h(&ipv4, addr_ipv4h);
+ return port_exists_by_type_addr_port(listener_type, &ipv4, port,
+ check_wildcard);
+}
+
/** Adjust the value of options->DataDirectory, or fill it in if it's
* absent. Return 0 on success, -1 on failure. */
static int
@@ -7206,10 +7564,7 @@ init_libevent(const or_options_t *options)
*/
suppress_libevent_log_msg("Function not implemented");
- tor_check_libevent_header_compatibility();
-
memset(&cfg, 0, sizeof(cfg));
- cfg.disable_iocp = options->DisableIOCP;
cfg.num_cpus = get_num_cpus(options);
cfg.msec_per_tick = options->TokenBucketRefillInterval;
@@ -7232,10 +7587,10 @@ init_libevent(const or_options_t *options)
*
* Note: Consider using the get_datadir_fname* macros in or.h.
*/
-char *
-options_get_datadir_fname2_suffix(const or_options_t *options,
- const char *sub1, const char *sub2,
- const char *suffix)
+MOCK_IMPL(char *,
+options_get_datadir_fname2_suffix,(const or_options_t *options,
+ const char *sub1, const char *sub2,
+ const char *suffix))
{
char *fname = NULL;
size_t len;
@@ -7416,8 +7771,8 @@ getinfo_helper_config(control_connection_t *conn,
smartlist_free(sl);
} else if (!strcmp(question, "config/defaults")) {
smartlist_t *sl = smartlist_new();
- int i, dirauth_lines_seen = 0, fallback_lines_seen = 0;
- for (i = 0; option_vars_[i].name; ++i) {
+ int dirauth_lines_seen = 0, fallback_lines_seen = 0;
+ for (int i = 0; option_vars_[i].name; ++i) {
const config_var_t *var = &option_vars_[i];
if (var->initvalue != NULL) {
if (strcmp(option_vars_[i].name, "DirAuthority") == 0) {
@@ -7445,14 +7800,13 @@ getinfo_helper_config(control_connection_t *conn,
* We didn't see any directory authorities with default values,
* so add the list of default authorities manually.
*/
- const char **i;
/*
* default_authorities is defined earlier in this file and
* is a const char ** NULL-terminated array of dirauth config
* lines.
*/
- for (i = default_authorities; *i != NULL; ++i) {
+ for (const char **i = default_authorities; *i != NULL; ++i) {
char *val = esc_for_log(*i);
smartlist_add_asprintf(sl, "DirAuthority %s\n", val);
tor_free(val);
@@ -7569,7 +7923,7 @@ static void
config_maybe_load_geoip_files_(const or_options_t *options,
const or_options_t *old_options)
{
- /* XXXX024 Reload GeoIPFile on SIGHUP. -NM */
+ /* XXXX Reload GeoIPFile on SIGHUP. -NM */
if (options->GeoIPFile &&
((!old_options || !opt_streq(old_options->GeoIPFile,
diff --git a/src/or/config.h b/src/or/config.h
index 02121cf95c..6645532514 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -29,8 +29,8 @@ const char *escaped_safe_str_client(const char *address);
const char *escaped_safe_str(const char *address);
const char *get_version(void);
const char *get_short_version(void);
-setopt_err_t options_trial_assign(config_line_t *list, int use_defaults,
- int clear_first, char **msg);
+setopt_err_t options_trial_assign(config_line_t *list, unsigned flags,
+ char **msg);
uint32_t get_last_resolved_addr(void);
void reset_last_resolved_addr(void);
@@ -53,9 +53,11 @@ config_line_t *option_get_assignment(const or_options_t *options,
const char *key);
int options_save_current(void);
const char *get_torrc_fname(int defaults_fname);
-char *options_get_datadir_fname2_suffix(const or_options_t *options,
- const char *sub1, const char *sub2,
- const char *suffix);
+MOCK_DECL(char *,
+ options_get_datadir_fname2_suffix,
+ (const or_options_t *options,
+ const char *sub1, const char *sub2,
+ const char *suffix));
#define get_datadir_fname2_suffix(sub1, sub2, suffix) \
options_get_datadir_fname2_suffix(get_options(), (sub1), (sub2), (suffix))
/** Return a newly allocated string containing datadir/sub1. See
@@ -74,6 +76,8 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options,
#define get_datadir_fname_suffix(sub1, suffix) \
get_datadir_fname2_suffix((sub1), NULL, (suffix))
+int using_default_dir_authorities(const or_options_t *options);
+
int check_or_create_data_subdir(const char *subdir);
int write_to_data_subdir(const char* subdir, const char* fname,
const char* str, const char* descr);
@@ -87,6 +91,12 @@ int get_first_advertised_port_by_type_af(int listener_type,
(get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET))
#define get_primary_dir_port() \
(get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET))
+const tor_addr_t *get_first_advertised_addr_by_type_af(int listener_type,
+ int address_family);
+int port_exists_by_type_addr_port(int listener_type, const tor_addr_t *addr,
+ int port, int check_wildcard);
+int port_exists_by_type_addr32h_port(int listener_type, uint32_t addr_ipv4h,
+ int port, int check_wildcard);
char *get_first_listener_addrport_string(int listener_type);
@@ -115,12 +125,16 @@ int config_parse_commandline(int argc, char **argv, int ignore_errors,
config_line_t **cmdline_result);
void config_register_addressmaps(const or_options_t *options);
-/* XXXX024 move to connection_edge.h */
+/* XXXX move to connection_edge.h */
int addressmap_register_auto(const char *from, const char *to,
time_t expires,
addressmap_entry_source_t addrmap_source,
const char **msg);
-int config_parse_unix_port(const char *addrport, char **path_out);
+
+int port_cfg_line_extract_addrport(const char *line,
+ char **addrport_out,
+ int *is_unix_out,
+ const char **rest_out);
/** Represents the information stored in a torrc Bridge line. */
typedef struct bridge_line_t {
@@ -158,6 +172,8 @@ extern struct config_format_t options_format;
STATIC port_cfg_t *port_cfg_new(size_t namelen);
STATIC void port_cfg_free(port_cfg_t *port);
STATIC void or_options_free(or_options_t *options);
+STATIC int options_validate_single_onion(or_options_t *options,
+ char **msg);
STATIC int options_validate(or_options_t *old_options,
or_options_t *options,
or_options_t *default_options,
diff --git a/src/or/confparse.c b/src/or/confparse.c
index 4f446d07c3..efcf4f981e 100644
--- a/src/or/confparse.c
+++ b/src/or/confparse.c
@@ -181,6 +181,26 @@ config_free_lines(config_line_t *front)
}
}
+/** If <b>key</b> is a deprecated configuration option, return the message
+ * explaining why it is deprecated (which may be an empty string). Return NULL
+ * if it is not deprecated. The <b>key</b> field must be fully expanded. */
+const char *
+config_find_deprecation(const config_format_t *fmt, const char *key)
+{
+ if (BUG(fmt == NULL) || BUG(key == NULL))
+ return NULL;
+ if (fmt->deprecations == NULL)
+ return NULL;
+
+ const config_deprecation_t *d;
+ for (d = fmt->deprecations; d->name; ++d) {
+ if (!strcasecmp(d->name, key)) {
+ return d->why_deprecated ? d->why_deprecated : "";
+ }
+ }
+ return NULL;
+}
+
/** As config_find_option, but return a non-const pointer. */
config_var_t *
config_find_option_mutable(config_format_t *fmt, const char *key)
@@ -463,6 +483,16 @@ config_mark_lists_fragile(const config_format_t *fmt, void *options)
}
}
+void
+warn_deprecated_option(const char *what, const char *why)
+{
+ const char *space = (why && strlen(why)) ? " " : "";
+ log_warn(LD_CONFIG, "The %s option is deprecated, and will most likely "
+ "be removed in a future version of Tor.%s%s (If you think this is "
+ "a mistake, please let us know!)",
+ what, space, why);
+}
+
/** If <b>c</b> is a syntactically valid configuration line, update
* <b>options</b> with its value and return 0. Otherwise return -1 for bad
* key, -2 for bad value.
@@ -474,9 +504,12 @@ config_mark_lists_fragile(const config_format_t *fmt, void *options)
*/
static int
config_assign_line(const config_format_t *fmt, void *options,
- config_line_t *c, int use_defaults,
- int clear_first, bitarray_t *options_seen, char **msg)
+ config_line_t *c, unsigned flags,
+ bitarray_t *options_seen, char **msg)
{
+ const unsigned use_defaults = flags & CAL_USE_DEFAULTS;
+ const unsigned clear_first = flags & CAL_CLEAR_FIRST;
+ const unsigned warn_deprecations = flags & CAL_WARN_DEPRECATIONS;
const config_var_t *var;
CONFIG_CHECK(fmt, options);
@@ -502,6 +535,12 @@ config_assign_line(const config_format_t *fmt, void *options,
c->key = tor_strdup(var->name);
}
+ const char *deprecation_msg;
+ if (warn_deprecations &&
+ (deprecation_msg = config_find_deprecation(fmt, var->name))) {
+ warn_deprecated_option(var->name, deprecation_msg);
+ }
+
if (!strlen(c->value)) {
/* reset or clear it, then return */
if (!clear_first) {
@@ -604,7 +643,7 @@ config_lines_dup(const config_line_t *inp)
* escape that value. Return NULL if no such key exists. */
config_line_t *
config_get_assigned_option(const config_format_t *fmt, const void *options,
- const char *key, int escape_val)
+ const char *key, int escape_val)
{
const config_var_t *var;
const void *value;
@@ -804,11 +843,13 @@ options_trial_assign() calls config_assign(1, 1)
*/
int
config_assign(const config_format_t *fmt, void *options, config_line_t *list,
- int use_defaults, int clear_first, char **msg)
+ unsigned config_assign_flags, char **msg)
{
config_line_t *p;
bitarray_t *options_seen;
const int n_options = config_count_options(fmt);
+ const unsigned clear_first = config_assign_flags & CAL_CLEAR_FIRST;
+ const unsigned use_defaults = config_assign_flags & CAL_USE_DEFAULTS;
CONFIG_CHECK(fmt, options);
@@ -832,8 +873,8 @@ config_assign(const config_format_t *fmt, void *options, config_line_t *list,
/* pass 3: assign. */
while (list) {
int r;
- if ((r=config_assign_line(fmt, options, list, use_defaults,
- clear_first, options_seen, msg))) {
+ if ((r=config_assign_line(fmt, options, list, config_assign_flags,
+ options_seen, msg))) {
bitarray_free(options_seen);
return r;
}
@@ -1029,7 +1070,7 @@ config_dup(const config_format_t *fmt, const void *old)
line = config_get_assigned_option(fmt, old, fmt->vars[i].name, 0);
if (line) {
char *msg = NULL;
- if (config_assign(fmt, newopts, line, 0, 0, &msg) < 0) {
+ if (config_assign(fmt, newopts, line, 0, &msg) < 0) {
log_err(LD_BUG, "config_get_assigned_option() generated "
"something we couldn't config_assign(): %s", msg);
tor_free(msg);
@@ -1238,7 +1279,7 @@ config_parse_units(const char *val, struct unit_table_t *u, int *ok)
v = tor_parse_uint64(val, 10, 0, UINT64_MAX, ok, &cp);
if (!*ok || (cp && *cp == '.')) {
- d = tor_parse_double(val, 0, UINT64_MAX, ok, &cp);
+ d = tor_parse_double(val, 0, (double)UINT64_MAX, ok, &cp);
if (!*ok)
goto done;
use_float = 1;
@@ -1255,7 +1296,7 @@ config_parse_units(const char *val, struct unit_table_t *u, int *ok)
for ( ;u->unit;++u) {
if (!strcasecmp(u->unit, cp)) {
if (use_float)
- v = u->multiplier * d;
+ v = (uint64_t)(u->multiplier * d);
else
v *= u->multiplier;
*ok = 1;
diff --git a/src/or/confparse.h b/src/or/confparse.h
index 885c615202..8d915d266b 100644
--- a/src/or/confparse.h
+++ b/src/or/confparse.h
@@ -48,6 +48,11 @@ typedef struct config_abbrev_t {
int warn;
} config_abbrev_t;
+typedef struct config_deprecation_t {
+ const char *name;
+ const char *why_deprecated;
+} config_deprecation_t;
+
/* Handy macro for declaring "In the config file or on the command line,
* you can abbreviate <b>tok</b>s as <b>tok</b>". */
#define PLURAL(tok) { #tok, #tok "s", 0, 0 }
@@ -61,13 +66,6 @@ typedef struct config_var_t {
const char *initvalue; /**< String (or null) describing initial value. */
} config_var_t;
-/** Represents an English description of a configuration variable; used when
- * generating configuration file comments. */
-typedef struct config_var_description_t {
- const char *name;
- const char *description;
-} config_var_description_t;
-
/** Type of a callback to validate whether a given configuration is
* well-formed and consistent. See options_trial_assign() for documentation
* of arguments. */
@@ -83,6 +81,7 @@ typedef struct config_format_t {
off_t magic_offset; /**< Offset of the magic value within the struct. */
config_abbrev_t *abbrevs; /**< List of abbreviations that we expand when
* parsing this format. */
+ const config_deprecation_t *deprecations; /** List of deprecated options */
config_var_t *vars; /**< List of variables we recognize, their default
* values, and where we stick them in the structure. */
validate_fn_t validate_fn; /**< Function to validate config. */
@@ -99,6 +98,10 @@ typedef struct config_format_t {
*(uint32_t*)STRUCT_VAR_P(cfg,fmt->magic_offset)); \
STMT_END
+#define CAL_USE_DEFAULTS (1u<<0)
+#define CAL_CLEAR_FIRST (1u<<1)
+#define CAL_WARN_DEPRECATIONS (1u<<2)
+
void *config_new(const config_format_t *fmt);
void config_line_append(config_line_t **lst,
const char *key, const char *val);
@@ -121,9 +124,11 @@ char *config_dump(const config_format_t *fmt, const void *default_options,
int comment_defaults);
int config_assign(const config_format_t *fmt, void *options,
config_line_t *list,
- int use_defaults, int clear_first, char **msg);
+ unsigned flags, char **msg);
config_var_t *config_find_option_mutable(config_format_t *fmt,
const char *key);
+const char *config_find_deprecation(const config_format_t *fmt,
+ const char *key);
const config_var_t *config_find_option(const config_format_t *fmt,
const char *key);
@@ -132,6 +137,7 @@ void config_free_lines(config_line_t *front);
const char *config_expand_abbrev(const config_format_t *fmt,
const char *option,
int command_line, int warn_obsolete);
+void warn_deprecated_option(const char *what, const char *why);
#endif
diff --git a/src/or/connection.c b/src/or/connection.c
index 05e56c1a52..8b00d637f6 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -8,6 +8,50 @@
* \file connection.c
* \brief General high-level functions to handle reading and writing
* on connections.
+ *
+ * Each connection (ideally) represents a TLS connection, a TCP socket, a unix
+ * socket, or a UDP socket on which reads and writes can occur. (But see
+ * connection_edge.c for cases where connections can also represent streams
+ * that do not have a corresponding socket.)
+ *
+ * The module implements the abstract type, connection_t. The subtypes are:
+ * <ul>
+ * <li>listener_connection_t, implemented here in connection.c
+ * <li>dir_connection_t, implemented in directory.c
+ * <li>or_connection_t, implemented in connection_or.c
+ * <li>edge_connection_t, implemented in connection_edge.c, along with
+ * its subtype(s):
+ * <ul><li>entry_connection_t, also implemented in connection_edge.c
+ * </ul>
+ * <li>control_connection_t, implemented in control.c
+ * </ul>
+ *
+ * The base type implemented in this module is responsible for basic
+ * rate limiting, flow control, and marshalling bytes onto and off of the
+ * network (either directly or via TLS).
+ *
+ * Connections are registered with the main loop with connection_add(). As
+ * they become able to read or write register the fact with the event main
+ * loop by calling connection_watch_events(), connection_start_reading(), or
+ * connection_start_writing(). When they no longer want to read or write,
+ * they call connection_stop_reading() or connection_start_writing().
+ *
+ * To queue data to be written on a connection, call
+ * connection_write_to_buf(). 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
+ * receive notification via connection_flushed_some() and
+ * connection_finished_flushing(). These functions all delegate to
+ * type-specific implementations.
+ *
+ * Additionally, beyond the core of connection_t, this module also implements:
+ * <ul>
+ * <li>Listeners, which wait for incoming sockets and launch connections
+ * <li>Outgoing SOCKS proxy support
+ * <li>Outgoing HTTP proxy support
+ * <li>An out-of-sockets handler for dealing with socket exhaustion
+ * </ul>
**/
#define CONNECTION_PRIVATE
@@ -52,10 +96,6 @@
#include "sandbox.h"
#include "transports.h"
-#ifdef USE_BUFFEREVENTS
-#include <event2/event.h>
-#endif
-
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
@@ -75,10 +115,8 @@ static void connection_init(time_t now, connection_t *conn, int type,
static int connection_init_accepted_conn(connection_t *conn,
const listener_connection_t *listener);
static int connection_handle_listener_read(connection_t *conn, int new_type);
-#ifndef USE_BUFFEREVENTS
static int connection_bucket_should_increase(int bucket,
or_connection_t *conn);
-#endif
static int connection_finished_flushing(connection_t *conn);
static int connection_flushed_some(connection_t *conn);
static int connection_finished_connecting(connection_t *conn);
@@ -98,7 +136,7 @@ static int get_proxy_type(void);
/** The last addresses that our network interface seemed to have been
* binding to. We use this as one way to detect when our IP changes.
*
- * XXX024 We should really use the entire list of interfaces here.
+ * XXXX+ We should really use the entire list of interfaces here.
**/
static tor_addr_t *last_interface_ipv4 = NULL;
/* DOCDOC last_interface_ipv6 */
@@ -236,26 +274,6 @@ conn_state_to_string(int type, int state)
return buf;
}
-#ifdef USE_BUFFEREVENTS
-/** Return true iff the connection's type is one that can use a
- bufferevent-based implementation. */
-int
-connection_type_uses_bufferevent(connection_t *conn)
-{
- switch (conn->type) {
- case CONN_TYPE_AP:
- case CONN_TYPE_EXIT:
- case CONN_TYPE_DIR:
- case CONN_TYPE_CONTROL:
- case CONN_TYPE_OR:
- case CONN_TYPE_EXT_OR:
- return 1;
- default:
- return 0;
- }
-}
-#endif
-
/** Allocate and return a new dir_connection_t, initialized as by
* connection_init(). */
dir_connection_t *
@@ -427,13 +445,11 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
conn->type = type;
conn->socket_family = socket_family;
-#ifndef USE_BUFFEREVENTS
if (!connection_is_listener(conn)) {
/* listeners never use their buf */
conn->inbuf = buf_new();
conn->outbuf = buf_new();
}
-#endif
conn->timestamp_created = now;
conn->timestamp_lastread = now;
@@ -577,10 +593,10 @@ connection_free_(connection_t *conn)
if (entry_conn->socks_request)
socks_request_free(entry_conn->socks_request);
if (entry_conn->pending_optimistic_data) {
- generic_buffer_free(entry_conn->pending_optimistic_data);
+ buf_free(entry_conn->pending_optimistic_data);
}
if (entry_conn->sending_optimistic_data) {
- generic_buffer_free(entry_conn->sending_optimistic_data);
+ buf_free(entry_conn->sending_optimistic_data);
}
}
if (CONN_IS_EDGE(conn)) {
@@ -603,15 +619,6 @@ connection_free_(connection_t *conn)
tor_event_free(conn->read_event);
tor_event_free(conn->write_event);
conn->read_event = conn->write_event = NULL;
- IF_HAS_BUFFEREVENT(conn, {
- /* This was a workaround to handle bugs in some old versions of libevent
- * where callbacks can occur after calling bufferevent_free(). Setting
- * the callbacks to NULL prevented this. It shouldn't be necessary any
- * more, but let's not tempt fate for now. */
- bufferevent_setcb(conn->bufev, NULL, NULL, NULL, NULL);
- bufferevent_free(conn->bufev);
- conn->bufev = NULL;
- });
if (conn->type == CONN_TYPE_DIR) {
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
@@ -645,13 +652,6 @@ connection_free_(connection_t *conn)
tor_free(TO_OR_CONN(conn)->ext_or_transport);
}
-#ifdef USE_BUFFEREVENTS
- if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) {
- ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg);
- TO_OR_CONN(conn)->bucket_cfg = NULL;
- }
-#endif
-
memwipe(mem, 0xCC, memlen); /* poison memory */
tor_free(mem);
}
@@ -665,9 +665,7 @@ connection_free,(connection_t *conn))
return;
tor_assert(!connection_is_on_closeable_list(conn));
tor_assert(!connection_in_array(conn));
- if (conn->linked_conn) {
- log_err(LD_BUG, "Called with conn->linked_conn still set.");
- tor_fragile_assert();
+ if (BUG(conn->linked_conn)) {
conn->linked_conn->linked_conn = NULL;
if (! conn->linked_conn->marked_for_close &&
conn->linked_conn->reading_from_linked_conn)
@@ -804,9 +802,9 @@ connection_mark_for_close_(connection_t *conn, int line, const char *file)
* For all other cases, use connection_mark_and_flush() instead, which
* checks for or_connection_t properly, instead. See below.
*/
-void
-connection_mark_for_close_internal_(connection_t *conn,
- int line, const char *file)
+MOCK_IMPL(void,
+connection_mark_for_close_internal_, (connection_t *conn,
+ int line, const char *file))
{
assert_connection_ok(conn,0);
tor_assert(line);
@@ -1140,6 +1138,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
int start_reading = 0;
static int global_next_session_group = SESSION_GROUP_FIRST_AUTO;
tor_addr_t addr;
+ int exhaustion = 0;
if (listensockaddr->sa_family == AF_INET ||
listensockaddr->sa_family == AF_INET6) {
@@ -1158,6 +1157,11 @@ connection_listener_new(const struct sockaddr *listensockaddr,
int e = tor_socket_errno(s);
if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
+ /*
+ * We'll call the OOS handler at the error exit, so set the
+ * exhaustion flag for it.
+ */
+ exhaustion = 1;
} else {
log_warn(LD_NET, "Socket creation failed: %s",
tor_socket_strerror(e));
@@ -1276,6 +1280,11 @@ connection_listener_new(const struct sockaddr *listensockaddr,
int e = tor_socket_errno(s);
if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
+ /*
+ * We'll call the OOS handler at the error exit, so set the
+ * exhaustion flag for it.
+ */
+ exhaustion = 1;
} else {
log_warn(LD_NET,"Socket creation failed: %s.", strerror(e));
}
@@ -1394,6 +1403,12 @@ connection_listener_new(const struct sockaddr *listensockaddr,
dnsserv_configure_listener(conn);
}
+ /*
+ * Normal exit; call the OOS handler since connection count just changed;
+ * the exhaustion flag will always be zero here though.
+ */
+ connection_check_oos(get_n_open_sockets(), 0);
+
return conn;
err:
@@ -1402,6 +1417,9 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (conn)
connection_free(conn);
+ /* Call the OOS handler, indicate if we saw an exhaustion-related error */
+ connection_check_oos(get_n_open_sockets(), exhaustion);
+
return NULL;
}
@@ -1492,21 +1510,34 @@ connection_handle_listener_read(connection_t *conn, int new_type)
if (!SOCKET_OK(news)) { /* accept() error */
int e = tor_socket_errno(conn->s);
if (ERRNO_IS_ACCEPT_EAGAIN(e)) {
- return 0; /* they hung up before we could accept(). that's fine. */
+ /*
+ * they hung up before we could accept(). that's fine.
+ *
+ * give the OOS handler a chance to run though
+ */
+ connection_check_oos(get_n_open_sockets(), 0);
+ return 0;
} else if (ERRNO_IS_RESOURCE_LIMIT(e)) {
warn_too_many_conns();
+ /* Exhaustion; tell the OOS handler */
+ connection_check_oos(get_n_open_sockets(), 1);
return 0;
}
/* else there was a real error. */
log_warn(LD_NET,"accept() failed: %s. Closing listener.",
tor_socket_strerror(e));
connection_mark_for_close(conn);
+ /* Tell the OOS handler about this too */
+ connection_check_oos(get_n_open_sockets(), 0);
return -1;
}
log_debug(LD_NET,
"Connection accepted on socket %d (child of fd %d).",
(int)news,(int)conn->s);
+ /* We accepted a new conn; run OOS handler */
+ connection_check_oos(get_n_open_sockets(), 0);
+
if (make_socket_reuseable(news) < 0) {
if (tor_socket_errno(news) == EINVAL) {
/* This can happen on OSX if we get a badly timed shutdown. */
@@ -1567,16 +1598,19 @@ connection_handle_listener_read(connection_t *conn, int new_type)
/* remember the remote address */
tor_addr_copy(&newconn->addr, &addr);
- newconn->port = port;
- newconn->address = tor_dup_addr(&addr);
+ if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) {
+ newconn->port = 0;
+ newconn->address = tor_strdup(conn->address);
+ } else {
+ newconn->port = port;
+ newconn->address = tor_addr_to_str_dup(&addr);
+ }
if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) {
log_info(LD_NET, "New SOCKS connection opened from %s.",
fmt_and_decorate_addr(&addr));
}
if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) {
- newconn->port = 0;
- newconn->address = tor_strdup(conn->address);
log_info(LD_NET, "New SOCKS AF_UNIX connection opened");
}
if (new_type == CONN_TYPE_CONTROL) {
@@ -1711,12 +1745,18 @@ connection_connect_sockaddr,(connection_t *conn,
s = tor_open_socket_nonblocking(protocol_family, SOCK_STREAM, proto);
if (! SOCKET_OK(s)) {
+ /*
+ * Early OOS handler calls; it matters if it's an exhaustion-related
+ * error or not.
+ */
*socket_error = tor_socket_errno(s);
if (ERRNO_IS_RESOURCE_LIMIT(*socket_error)) {
warn_too_many_conns();
+ connection_check_oos(get_n_open_sockets(), 1);
} else {
log_warn(LD_NET,"Error creating network socket: %s",
tor_socket_strerror(*socket_error));
+ connection_check_oos(get_n_open_sockets(), 0);
}
return -1;
}
@@ -1726,6 +1766,13 @@ connection_connect_sockaddr,(connection_t *conn,
tor_socket_strerror(errno));
}
+ /*
+ * We've got the socket open; give the OOS handler a chance to check
+ * against configuured maximum socket number, but tell it no exhaustion
+ * failure.
+ */
+ connection_check_oos(get_n_open_sockets(), 0);
+
if (bindaddr && bind(s, bindaddr, bindaddr_len) < 0) {
*socket_error = tor_socket_errno(s);
log_warn(LD_NET,"Error binding network socket: %s",
@@ -2246,7 +2293,7 @@ connection_send_socks5_connect(connection_t *conn)
} else { /* AF_INET6 */
buf[3] = 4;
reqsize += 16;
- memcpy(buf + 4, tor_addr_to_in6(&conn->addr), 16);
+ memcpy(buf + 4, tor_addr_to_in6_addr8(&conn->addr), 16);
memcpy(buf + 20, &port, 2);
}
@@ -2255,18 +2302,13 @@ connection_send_socks5_connect(connection_t *conn)
conn->proxy_state = PROXY_SOCKS5_WANT_CONNECT_OK;
}
-/** Wrapper around fetch_from_(buf/evbuffer)_socks_client: see those functions
+/** Wrapper around fetch_from_buf_socks_client: see that functions
* for documentation of its behavior. */
static int
connection_fetch_from_buf_socks_client(connection_t *conn,
int state, char **reason)
{
- IF_HAS_BUFFEREVENT(conn, {
- struct evbuffer *input = bufferevent_get_input(conn->bufev);
- return fetch_from_evbuffer_socks_client(input, state, reason);
- }) ELSE_IF_NO_BUFFEREVENT {
- return fetch_from_buf_socks_client(conn->inbuf, state, reason);
- }
+ return fetch_from_buf_socks_client(conn->inbuf, state, reason);
}
/** Call this from connection_*_process_inbuf() to advance the proxy
@@ -2542,7 +2584,7 @@ retry_listener_ports(smartlist_t *old_conns,
real_port,
listensockaddr,
sizeof(struct sockaddr_storage));
- address = tor_dup_addr(&port->addr);
+ address = tor_addr_to_str_dup(&port->addr);
}
if (listensockaddr) {
@@ -2700,23 +2742,15 @@ connection_is_rate_limited(connection_t *conn)
return 1;
}
-#ifdef USE_BUFFEREVENTS
-static struct bufferevent_rate_limit_group *global_rate_limit = NULL;
-#else
-extern int global_read_bucket, global_write_bucket;
-extern int global_relayed_read_bucket, global_relayed_write_bucket;
-
/** Did either global write bucket run dry last second? If so,
* we are likely to run dry again this second, so be stingy with the
* tokens we just put in. */
static int write_buckets_empty_last_second = 0;
-#endif
/** How many seconds of no active local circuits will make the
* connection revert to the "relayed" bandwidth class? */
#define CLIENT_IDLE_TIME_FOR_PRIORITY 30
-#ifndef USE_BUFFEREVENTS
/** Return 1 if <b>conn</b> should use tokens from the "relayed"
* bandwidth rates, else 0. Currently, only OR conns with bandwidth
* class 1, and directory conns that are serving data out, count.
@@ -2828,20 +2862,6 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
return connection_bucket_round_robin(base, priority,
global_bucket, conn_bucket);
}
-#else
-static ssize_t
-connection_bucket_read_limit(connection_t *conn, time_t now)
-{
- (void) now;
- return bufferevent_get_max_to_read(conn->bufev);
-}
-ssize_t
-connection_bucket_write_limit(connection_t *conn, time_t now)
-{
- (void) now;
- return bufferevent_get_max_to_write(conn->bufev);
-}
-#endif
/** Return 1 if the global write buckets are low enough that we
* shouldn't send <b>attempt</b> bytes of low-priority directory stuff
@@ -2865,12 +2885,8 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
int
global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
{
-#ifdef USE_BUFFEREVENTS
- ssize_t smaller_bucket = bufferevent_get_max_to_write(conn->bufev);
-#else
int smaller_bucket = global_write_bucket < global_relayed_write_bucket ?
global_write_bucket : global_relayed_write_bucket;
-#endif
if (authdir_mode(get_options()) && priority>1)
return 0; /* there's always room to answer v2 if we're an auth dir */
@@ -2880,10 +2896,8 @@ global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
if (smaller_bucket < (int)attempt)
return 1; /* not enough space no matter the priority */
-#ifndef USE_BUFFEREVENTS
if (write_buckets_empty_last_second)
return 1; /* we're already hitting our limits, no more please */
-#endif
if (priority == 1) { /* old-style v1 query */
/* Could we handle *two* of these requests within the next two seconds? */
@@ -2931,29 +2945,6 @@ record_num_bytes_transferred_impl(connection_t *conn,
rep_hist_note_exit_bytes(conn->port, num_written, num_read);
}
-#ifdef USE_BUFFEREVENTS
-/** Wrapper around fetch_from_(buf/evbuffer)_socks_client: see those functions
- * for documentation of its behavior. */
-static void
-record_num_bytes_transferred(connection_t *conn,
- time_t now, size_t num_read, size_t num_written)
-{
- /* XXX024 check if this is necessary */
- if (num_written >= INT_MAX || num_read >= INT_MAX) {
- log_err(LD_BUG, "Value out of range. num_read=%lu, num_written=%lu, "
- "connection type=%s, state=%s",
- (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();
- }
-
- record_num_bytes_transferred_impl(conn,now,num_read,num_written);
-}
-#endif
-
/** Helper: convert given <b>tvnow</b> time value to milliseconds since
* midnight. */
static uint32_t
@@ -2998,7 +2989,6 @@ connection_buckets_note_empty_ts(uint32_t *timestamp_var,
*timestamp_var = msec_since_midnight(tvnow);
}
-#ifndef USE_BUFFEREVENTS
/** Last time at which the global or relay buckets were emptied in msec
* since midnight. */
static uint32_t global_relayed_read_emptied = 0,
@@ -3329,92 +3319,6 @@ connection_bucket_should_increase(int bucket, or_connection_t *conn)
return 1;
}
-#else
-static void
-connection_buckets_decrement(connection_t *conn, time_t now,
- size_t num_read, size_t num_written)
-{
- (void) conn;
- (void) now;
- (void) num_read;
- (void) num_written;
- /* Libevent does this for us. */
-}
-
-void
-connection_bucket_refill(int seconds_elapsed, time_t now)
-{
- (void) seconds_elapsed;
- (void) now;
- /* Libevent does this for us. */
-}
-void
-connection_bucket_init(void)
-{
- const or_options_t *options = get_options();
- const struct timeval *tick = tor_libevent_get_one_tick_timeout();
- struct ev_token_bucket_cfg *bucket_cfg;
-
- uint64_t rate, burst;
- if (options->RelayBandwidthRate) {
- rate = options->RelayBandwidthRate;
- burst = options->RelayBandwidthBurst;
- } else {
- rate = options->BandwidthRate;
- burst = options->BandwidthBurst;
- }
-
- /* This can't overflow, since TokenBucketRefillInterval <= 1000,
- * and rate started out less than INT32_MAX. */
- rate = (rate * options->TokenBucketRefillInterval) / 1000;
-
- bucket_cfg = ev_token_bucket_cfg_new((uint32_t)rate, (uint32_t)burst,
- (uint32_t)rate, (uint32_t)burst,
- tick);
-
- if (!global_rate_limit) {
- global_rate_limit =
- bufferevent_rate_limit_group_new(tor_libevent_get_base(), bucket_cfg);
- } else {
- bufferevent_rate_limit_group_set_cfg(global_rate_limit, bucket_cfg);
- }
- ev_token_bucket_cfg_free(bucket_cfg);
-}
-
-void
-connection_get_rate_limit_totals(uint64_t *read_out, uint64_t *written_out)
-{
- if (global_rate_limit == NULL) {
- *read_out = *written_out = 0;
- } else {
- bufferevent_rate_limit_group_get_totals(
- global_rate_limit, read_out, written_out);
- }
-}
-
-/** Perform whatever operations are needed on <b>conn</b> to enable
- * rate-limiting. */
-void
-connection_enable_rate_limiting(connection_t *conn)
-{
- if (conn->bufev) {
- if (!global_rate_limit)
- connection_bucket_init();
- tor_add_bufferevent_to_rate_limit_group(conn->bufev, global_rate_limit);
- }
-}
-
-static void
-connection_consider_empty_write_buckets(connection_t *conn)
-{
- (void) conn;
-}
-static void
-connection_consider_empty_read_buckets(connection_t *conn)
-{
- (void) conn;
-}
-#endif
/** Read bytes from conn-\>s and process them.
*
@@ -3648,7 +3552,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
* 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);
- if (r2<0) {
+ if (BUG(r2<0)) {
log_warn(LD_BUG, "apparently, reading pending bytes can fail.");
return -1;
}
@@ -3745,171 +3649,11 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
return 0;
}
-#ifdef USE_BUFFEREVENTS
-/* XXXX These generic versions could be simplified by making them
- type-specific */
-
-/** Callback: Invoked whenever bytes are added to or drained from an input
- * evbuffer. Used to track the number of bytes read. */
-static void
-evbuffer_inbuf_callback(struct evbuffer *buf,
- const struct evbuffer_cb_info *info, void *arg)
-{
- connection_t *conn = arg;
- (void) buf;
- /* XXXX These need to get real counts on the non-nested TLS case. - NM */
- if (info->n_added) {
- time_t now = approx_time();
- conn->timestamp_lastread = now;
- record_num_bytes_transferred(conn, now, info->n_added, 0);
- connection_consider_empty_read_buckets(conn);
- if (conn->type == CONN_TYPE_AP) {
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- /*XXXX024 check for overflow*/
- edge_conn->n_read += (int)info->n_added;
- }
- }
-}
-
-/** Callback: Invoked whenever bytes are added to or drained from an output
- * evbuffer. Used to track the number of bytes written. */
-static void
-evbuffer_outbuf_callback(struct evbuffer *buf,
- const struct evbuffer_cb_info *info, void *arg)
-{
- connection_t *conn = arg;
- (void)buf;
- if (info->n_deleted) {
- time_t now = approx_time();
- conn->timestamp_lastwritten = now;
- record_num_bytes_transferred(conn, now, 0, info->n_deleted);
- connection_consider_empty_write_buckets(conn);
- if (conn->type == CONN_TYPE_AP) {
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- /*XXXX024 check for overflow*/
- edge_conn->n_written += (int)info->n_deleted;
- }
- }
-}
-
-/** Callback: invoked whenever a bufferevent has read data. */
-void
-connection_handle_read_cb(struct bufferevent *bufev, void *arg)
-{
- connection_t *conn = arg;
- (void) bufev;
- if (!conn->marked_for_close) {
- if (connection_process_inbuf(conn, 1)<0) /* XXXX Always 1? */
- if (!conn->marked_for_close)
- connection_mark_for_close(conn);
- }
-}
-
-/** Callback: invoked whenever a bufferevent has written data. */
-void
-connection_handle_write_cb(struct bufferevent *bufev, void *arg)
-{
- connection_t *conn = arg;
- struct evbuffer *output;
- if (connection_flushed_some(conn)<0) {
- if (!conn->marked_for_close)
- connection_mark_for_close(conn);
- return;
- }
-
- output = bufferevent_get_output(bufev);
- if (!evbuffer_get_length(output)) {
- connection_finished_flushing(conn);
- if (conn->marked_for_close && conn->hold_open_until_flushed) {
- conn->hold_open_until_flushed = 0;
- if (conn->linked) {
- /* send eof */
- bufferevent_flush(conn->bufev, EV_WRITE, BEV_FINISHED);
- }
- }
- }
-}
-
-/** Callback: invoked whenever a bufferevent has had an event (like a
- * connection, or an eof, or an error) occur. */
-void
-connection_handle_event_cb(struct bufferevent *bufev, short event, void *arg)
-{
- connection_t *conn = arg;
- (void) bufev;
- if (conn->marked_for_close)
- return;
-
- if (event & BEV_EVENT_CONNECTED) {
- tor_assert(connection_state_is_connecting(conn));
- if (connection_finished_connecting(conn)<0)
- return;
- }
- if (event & BEV_EVENT_EOF) {
- if (!conn->marked_for_close) {
- conn->inbuf_reached_eof = 1;
- if (connection_reached_eof(conn)<0)
- return;
- }
- }
- if (event & BEV_EVENT_ERROR) {
- int socket_error = evutil_socket_geterror(conn->s);
- if (conn->type == CONN_TYPE_OR &&
- conn->state == OR_CONN_STATE_CONNECTING) {
- connection_or_connect_failed(TO_OR_CONN(conn),
- errno_to_orconn_end_reason(socket_error),
- tor_socket_strerror(socket_error));
- } else if (CONN_IS_EDGE(conn)) {
- edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
- if (!edge_conn->edge_has_sent_end)
- connection_edge_end_errno(edge_conn);
- if (conn->type == CONN_TYPE_AP && TO_ENTRY_CONN(conn)->socks_request) {
- /* broken, don't send a socks reply back */
- TO_ENTRY_CONN(conn)->socks_request->has_finished = 1;
- }
- }
- connection_close_immediate(conn); /* Connection is dead. */
- if (!conn->marked_for_close)
- connection_mark_for_close(conn);
- }
-}
-
-/** Set up the generic callbacks for the bufferevent on <b>conn</b>. */
-void
-connection_configure_bufferevent_callbacks(connection_t *conn)
-{
- struct bufferevent *bufev;
- struct evbuffer *input, *output;
- tor_assert(conn->bufev);
- bufev = conn->bufev;
- bufferevent_setcb(bufev,
- connection_handle_read_cb,
- connection_handle_write_cb,
- connection_handle_event_cb,
- conn);
- /* Set a fairly high write low-watermark so that we get the write callback
- called whenever data is written to bring us under 128K. Leave the
- high-watermark at 0.
- */
- bufferevent_setwatermark(bufev, EV_WRITE, 128*1024, 0);
-
- input = bufferevent_get_input(bufev);
- output = bufferevent_get_output(bufev);
- evbuffer_add_cb(input, evbuffer_inbuf_callback, conn);
- evbuffer_add_cb(output, evbuffer_outbuf_callback, conn);
-}
-#endif
-
/** A pass-through to fetch_from_buf. */
int
connection_fetch_from_buf(char *string, size_t len, connection_t *conn)
{
- IF_HAS_BUFFEREVENT(conn, {
- /* XXX overflow -seb */
- return (int)bufferevent_read(conn->bufev, string, len);
- }) ELSE_IF_NO_BUFFEREVENT {
- return fetch_from_buf(string, len, conn->inbuf);
- }
+ return fetch_from_buf(string, len, conn->inbuf);
}
/** As fetch_from_buf_line(), but read from a connection's input buffer. */
@@ -3917,43 +3661,19 @@ int
connection_fetch_from_buf_line(connection_t *conn, char *data,
size_t *data_len)
{
- IF_HAS_BUFFEREVENT(conn, {
- int r;
- size_t eol_len=0;
- struct evbuffer *input = bufferevent_get_input(conn->bufev);
- struct evbuffer_ptr ptr =
- evbuffer_search_eol(input, NULL, &eol_len, EVBUFFER_EOL_LF);
- if (ptr.pos == -1)
- return 0; /* No EOL found. */
- if ((size_t)ptr.pos+eol_len >= *data_len) {
- return -1; /* Too long */
- }
- *data_len = ptr.pos+eol_len;
- r = evbuffer_remove(input, data, ptr.pos+eol_len);
- tor_assert(r >= 0);
- data[ptr.pos+eol_len] = '\0';
- return 1;
- }) ELSE_IF_NO_BUFFEREVENT {
- return fetch_from_buf_line(conn->inbuf, data, data_len);
- }
+ return fetch_from_buf_line(conn->inbuf, data, data_len);
}
-/** As fetch_from_buf_http, but fetches from a connection's input buffer_t or
- * its bufferevent as appropriate. */
+/** As fetch_from_buf_http, but fetches from a connection's input buffer_t as
+ * appropriate. */
int
connection_fetch_from_buf_http(connection_t *conn,
char **headers_out, size_t max_headerlen,
char **body_out, size_t *body_used,
size_t max_bodylen, int force_complete)
{
- IF_HAS_BUFFEREVENT(conn, {
- struct evbuffer *input = bufferevent_get_input(conn->bufev);
- return fetch_from_evbuffer_http(input, headers_out, max_headerlen,
- body_out, body_used, max_bodylen, force_complete);
- }) ELSE_IF_NO_BUFFEREVENT {
- return fetch_from_buf_http(conn->inbuf, headers_out, max_headerlen,
- body_out, body_used, max_bodylen, force_complete);
- }
+ return fetch_from_buf_http(conn->inbuf, headers_out, max_headerlen,
+ body_out, body_used, max_bodylen, force_complete);
}
/** Return conn-\>outbuf_flushlen: how many bytes conn wants to flush
@@ -4145,7 +3865,7 @@ connection_handle_write_impl(connection_t *conn, int force)
or_conn->bytes_xmitted += result;
or_conn->bytes_xmitted_by_tls += n_written;
/* So we notice bytes were written even on error */
- /* XXXX024 This cast is safe since we can never write INT_MAX bytes in a
+ /* XXXX This cast is safe since we can never write INT_MAX bytes in a
* single set of TLS operations. But it looks kinda ugly. If we refactor
* the *_buf_tls functions, we should make them return ssize_t or size_t
* or something. */
@@ -4257,7 +3977,7 @@ connection_handle_write(connection_t *conn, int force)
* Try to flush data that's waiting for a write on <b>conn</b>. Return
* -1 on failure, 0 on success.
*
- * Don't use this function for regular writing; the buffers/bufferevents
+ * Don't use this function for regular writing; the buffers
* system should be good enough at scheduling writes there. Instead, this
* function is for cases when we're about to exit or something and we want
* to report it right away.
@@ -4265,10 +3985,6 @@ connection_handle_write(connection_t *conn, int force)
int
connection_flush(connection_t *conn)
{
- IF_HAS_BUFFEREVENT(conn, {
- int r = bufferevent_flush(conn->bufev, EV_WRITE, BEV_FLUSH);
- return (r < 0) ? -1 : 0;
- });
return connection_handle_write(conn, 1);
}
@@ -4297,22 +4013,6 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
if (conn->marked_for_close && !conn->hold_open_until_flushed)
return;
- IF_HAS_BUFFEREVENT(conn, {
- if (zlib) {
- int done = zlib < 0;
- r = write_to_evbuffer_zlib(bufferevent_get_output(conn->bufev),
- TO_DIR_CONN(conn)->zlib_state,
- string, len, done);
- } else {
- r = bufferevent_write(conn->bufev, string, len);
- }
- if (r < 0) {
- /* XXXX mark for close? */
- log_warn(LD_NET, "bufferevent_write failed! That shouldn't happen.");
- }
- return;
- });
-
old_datalen = buf_datalen(conn->outbuf);
if (zlib) {
dir_connection_t *dir_conn = TO_DIR_CONN(conn);
@@ -4758,7 +4458,7 @@ connection_flushed_some(connection_t *conn)
}
/** We just finished flushing bytes to the appropriately low network layer,
- * and there are no more bytes remaining in conn-\>outbuf, conn-\>bev, or
+ * and there are no more bytes remaining in conn-\>outbuf or
* conn-\>tls to be flushed.
*
* This function just passes conn to the connection-specific
@@ -4775,8 +4475,7 @@ connection_finished_flushing(connection_t *conn)
// log_fn(LOG_DEBUG,"entered. Socket %u.", conn->s);
- IF_HAS_NO_BUFFEREVENT(conn)
- connection_stop_writing(conn);
+ connection_stop_writing(conn);
switch (conn->type) {
case CONN_TYPE_OR:
@@ -4852,6 +4551,256 @@ connection_reached_eof(connection_t *conn)
}
}
+/** Comparator for the two-orconn case in OOS victim sort */
+static int
+oos_victim_comparator_for_orconns(or_connection_t *a, or_connection_t *b)
+{
+ int a_circs, b_circs;
+ /* Fewer circuits == higher priority for OOS kill, sort earlier */
+
+ a_circs = connection_or_get_num_circuits(a);
+ b_circs = connection_or_get_num_circuits(b);
+
+ if (a_circs < b_circs) return 1;
+ else if (a_circs > b_circs) return -1;
+ else return 0;
+}
+
+/** Sort comparator for OOS victims; better targets sort before worse
+ * ones. */
+static int
+oos_victim_comparator(const void **a_v, const void **b_v)
+{
+ connection_t *a = NULL, *b = NULL;
+
+ /* Get connection pointers out */
+
+ a = (connection_t *)(*a_v);
+ b = (connection_t *)(*b_v);
+
+ tor_assert(a != NULL);
+ tor_assert(b != NULL);
+
+ /*
+ * We always prefer orconns as victims currently; we won't even see
+ * these non-orconn cases, but if we do, sort them after orconns.
+ */
+ if (a->type == CONN_TYPE_OR && b->type == CONN_TYPE_OR) {
+ return oos_victim_comparator_for_orconns(TO_OR_CONN(a), TO_OR_CONN(b));
+ } else {
+ /*
+ * One isn't an orconn; if one is, it goes first. We currently have no
+ * opinions about cases where neither is an orconn.
+ */
+ if (a->type == CONN_TYPE_OR) return -1;
+ else if (b->type == CONN_TYPE_OR) return 1;
+ else return 0;
+ }
+}
+
+/** Pick n victim connections for the OOS handler and return them in a
+ * smartlist.
+ */
+MOCK_IMPL(STATIC smartlist_t *,
+pick_oos_victims, (int n))
+{
+ smartlist_t *eligible = NULL, *victims = NULL;
+ smartlist_t *conns;
+ int conn_counts_by_type[CONN_TYPE_MAX_ + 1], i;
+
+ /*
+ * Big damn assumption (someone improve this someday!):
+ *
+ * Socket exhaustion normally happens on high-volume relays, and so
+ * most of the connections involved are orconns. We should pick victims
+ * by assembling a list of all orconns, and sorting them in order of
+ * how much 'damage' by some metric we'd be doing by dropping them.
+ *
+ * If we move on from orconns, we should probably think about incoming
+ * directory connections next, or exit connections. Things we should
+ * probably never kill are controller connections and listeners.
+ *
+ * This function will count how many connections of different types
+ * exist and log it for purposes of gathering data on typical OOS
+ * situations to guide future improvements.
+ */
+
+ /* First, get the connection array */
+ conns = get_connection_array();
+ /*
+ * Iterate it and pick out eligible connection types, and log some stats
+ * along the way.
+ */
+ eligible = smartlist_new();
+ memset(conn_counts_by_type, 0, sizeof(conn_counts_by_type));
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) {
+ /* Bump the counter */
+ tor_assert(c->type <= CONN_TYPE_MAX_);
+ ++(conn_counts_by_type[c->type]);
+
+ /* Skip anything without a socket we can free */
+ if (!(SOCKET_OK(c->s))) {
+ continue;
+ }
+
+ /* Skip anything we would count as moribund */
+ if (connection_is_moribund(c)) {
+ continue;
+ }
+
+ switch (c->type) {
+ case CONN_TYPE_OR:
+ /* We've got an orconn, it's eligible to be OOSed */
+ smartlist_add(eligible, c);
+ break;
+ default:
+ /* We don't know what to do with it, ignore it */
+ break;
+ }
+ } SMARTLIST_FOREACH_END(c);
+
+ /* Log some stats */
+ if (smartlist_len(conns) > 0) {
+ /* At least one counter must be non-zero */
+ log_info(LD_NET, "Some stats on conn types seen during OOS follow");
+ for (i = CONN_TYPE_MIN_; i <= CONN_TYPE_MAX_; ++i) {
+ /* Did we see any? */
+ if (conn_counts_by_type[i] > 0) {
+ log_info(LD_NET, "%s: %d conns",
+ conn_type_to_string(i),
+ conn_counts_by_type[i]);
+ }
+ }
+ log_info(LD_NET, "Done with OOS conn type stats");
+ }
+
+ /* Did we find more eligible targets than we want to kill? */
+ if (smartlist_len(eligible) > n) {
+ /* Sort the list in order of target preference */
+ smartlist_sort(eligible, oos_victim_comparator);
+ /* Pick first n as victims */
+ victims = smartlist_new();
+ for (i = 0; i < n; ++i) {
+ smartlist_add(victims, smartlist_get(eligible, i));
+ }
+ /* Free the original list */
+ smartlist_free(eligible);
+ } else {
+ /* No, we can just call them all victims */
+ victims = eligible;
+ }
+
+ return victims;
+}
+
+/** Kill a list of connections for the OOS handler. */
+MOCK_IMPL(STATIC void,
+kill_conn_list_for_oos, (smartlist_t *conns))
+{
+ if (!conns) return;
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, c) {
+ /* Make sure the channel layer gets told about orconns */
+ if (c->type == CONN_TYPE_OR) {
+ connection_or_close_for_error(TO_OR_CONN(c), 1);
+ } else {
+ connection_mark_for_close(c);
+ }
+ } SMARTLIST_FOREACH_END(c);
+
+ log_notice(LD_NET,
+ "OOS handler marked %d connections",
+ smartlist_len(conns));
+}
+
+/** Out-of-Sockets handler; n_socks is the current number of open
+ * sockets, and failed is non-zero if a socket exhaustion related
+ * error immediately preceded this call. This is where to do
+ * circuit-killing heuristics as needed.
+ */
+void
+connection_check_oos(int n_socks, int failed)
+{
+ int target_n_socks = 0, moribund_socks, socks_to_kill;
+ smartlist_t *conns;
+
+ /* Early exit: is OOS checking disabled? */
+ if (get_options()->DisableOOSCheck) {
+ return;
+ }
+
+ /* Sanity-check args */
+ tor_assert(n_socks >= 0);
+
+ /*
+ * Make some log noise; keep it at debug level since this gets a chance
+ * to run on every connection attempt.
+ */
+ log_debug(LD_NET,
+ "Running the OOS handler (%d open sockets, %s)",
+ n_socks, (failed != 0) ? "exhaustion seen" : "no exhaustion");
+
+ /*
+ * Check if we're really handling an OOS condition, and if so decide how
+ * many sockets we want to get down to. Be sure we check if the threshold
+ * is distinct from zero first; it's possible for this to be called a few
+ * times before we've finished reading the config.
+ */
+ if (n_socks >= get_options()->ConnLimit_high_thresh &&
+ get_options()->ConnLimit_high_thresh != 0 &&
+ get_options()->ConnLimit_ != 0) {
+ /* Try to get down to the low threshold */
+ target_n_socks = get_options()->ConnLimit_low_thresh;
+ log_notice(LD_NET,
+ "Current number of sockets %d is greater than configured "
+ "limit %d; OOS handler trying to get down to %d",
+ n_socks, get_options()->ConnLimit_high_thresh,
+ target_n_socks);
+ } else if (failed) {
+ /*
+ * If we're not at the limit but we hit a socket exhaustion error, try to
+ * drop some (but not as aggressively as ConnLimit_low_threshold, which is
+ * 3/4 of ConnLimit_)
+ */
+ target_n_socks = (n_socks * 9) / 10;
+ log_notice(LD_NET,
+ "We saw socket exhaustion at %d open sockets; OOS handler "
+ "trying to get down to %d",
+ n_socks, target_n_socks);
+ }
+
+ if (target_n_socks > 0) {
+ /*
+ * It's an OOS!
+ *
+ * Count moribund sockets; it's be important that anything we decide
+ * to get rid of here but don't immediately close get counted as moribund
+ * on subsequent invocations so we don't try to kill too many things if
+ * connection_check_oos() gets called multiple times.
+ */
+ moribund_socks = connection_count_moribund();
+
+ if (moribund_socks < n_socks - target_n_socks) {
+ socks_to_kill = n_socks - target_n_socks - moribund_socks;
+
+ conns = pick_oos_victims(socks_to_kill);
+ if (conns) {
+ kill_conn_list_for_oos(conns);
+ log_notice(LD_NET,
+ "OOS handler killed %d conns", smartlist_len(conns));
+ smartlist_free(conns);
+ } else {
+ log_notice(LD_NET, "OOS handler failed to pick any victim conns");
+ }
+ } else {
+ log_notice(LD_NET,
+ "Not killing any sockets for OOS because there are %d "
+ "already moribund, and we only want to eliminate %d",
+ moribund_socks, n_socks - target_n_socks);
+ }
+ }
+}
+
/** Log how many bytes are used by buffers of different kinds and sizes. */
void
connection_dump_buffer_mem_stats(int severity)
@@ -4910,15 +4859,6 @@ assert_connection_ok(connection_t *conn, time_t now)
tor_assert(conn->type >= CONN_TYPE_MIN_);
tor_assert(conn->type <= CONN_TYPE_MAX_);
-#ifdef USE_BUFFEREVENTS
- if (conn->bufev) {
- tor_assert(conn->read_event == NULL);
- tor_assert(conn->write_event == NULL);
- tor_assert(conn->inbuf == NULL);
- tor_assert(conn->outbuf == NULL);
- }
-#endif
-
switch (conn->type) {
case CONN_TYPE_OR:
case CONN_TYPE_EXT_OR:
@@ -5182,11 +5122,6 @@ connection_free_all(void)
tor_free(last_interface_ipv4);
tor_free(last_interface_ipv6);
-
-#ifdef USE_BUFFEREVENTS
- if (global_rate_limit)
- bufferevent_rate_limit_group_free(global_rate_limit);
-#endif
}
/** Log a warning, and possibly emit a control event, that <b>received</b> came
diff --git a/src/or/connection.h b/src/or/connection.h
index 4835235fba..d25e002fa4 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -34,8 +34,8 @@ void connection_about_to_close_connection(connection_t *conn);
void connection_close_immediate(connection_t *conn);
void connection_mark_for_close_(connection_t *conn,
int line, const char *file);
-void connection_mark_for_close_internal_(connection_t *conn,
- int line, const char *file);
+MOCK_DECL(void, connection_mark_for_close_internal_,
+ (connection_t *conn, int line, const char *file));
#define connection_mark_for_close(c) \
connection_mark_for_close_((c), __LINE__, SHORT_FILE__)
@@ -52,13 +52,11 @@ void connection_mark_for_close_internal_(connection_t *conn,
* For all other cases, use connection_mark_and_flush() instead, which
* checks for or_connection_t properly, instead. See below.
*/
-#define connection_mark_and_flush_internal_(c,line,file) \
- do { \
- connection_t *tmp_conn_ = (c); \
- connection_mark_for_close_internal_(tmp_conn_, (line), (file)); \
- tmp_conn_->hold_open_until_flushed = 1; \
- IF_HAS_BUFFEREVENT(tmp_conn_, \
- connection_start_writing(tmp_conn_)); \
+#define connection_mark_and_flush_internal_(c,line,file) \
+ do { \
+ connection_t *tmp_conn__ = (c); \
+ connection_mark_for_close_internal_(tmp_conn__, (line), (file)); \
+ tmp_conn__->hold_open_until_flushed = 1; \
} while (0)
#define connection_mark_and_flush_internal(c) \
@@ -166,21 +164,13 @@ static size_t connection_get_outbuf_len(connection_t *conn);
static inline size_t
connection_get_inbuf_len(connection_t *conn)
{
- IF_HAS_BUFFEREVENT(conn, {
- return evbuffer_get_length(bufferevent_get_input(conn->bufev));
- }) ELSE_IF_NO_BUFFEREVENT {
- return conn->inbuf ? buf_datalen(conn->inbuf) : 0;
- }
+ return conn->inbuf ? buf_datalen(conn->inbuf) : 0;
}
static inline size_t
connection_get_outbuf_len(connection_t *conn)
{
- IF_HAS_BUFFEREVENT(conn, {
- return evbuffer_get_length(bufferevent_get_output(conn->bufev));
- }) ELSE_IF_NO_BUFFEREVENT {
return conn->outbuf ? buf_datalen(conn->outbuf) : 0;
- }
}
connection_t *connection_get_by_global_id(uint64_t id);
@@ -257,19 +247,21 @@ void clock_skew_warning(const connection_t *conn, long apparent_skew,
int trusted, log_domain_mask_t domain,
const char *received, const char *source);
-#ifdef USE_BUFFEREVENTS
-int connection_type_uses_bufferevent(connection_t *conn);
-void connection_configure_bufferevent_callbacks(connection_t *conn);
-void connection_handle_read_cb(struct bufferevent *bufev, void *arg);
-void connection_handle_write_cb(struct bufferevent *bufev, void *arg);
-void connection_handle_event_cb(struct bufferevent *bufev, short event,
- void *arg);
-void connection_get_rate_limit_totals(uint64_t *read_out,
- uint64_t *written_out);
-void connection_enable_rate_limiting(connection_t *conn);
-#else
-#define connection_type_uses_bufferevent(c) (0)
-#endif
+/** Check if a connection is on the way out so the OOS handler doesn't try
+ * to kill more than it needs. */
+static inline int
+connection_is_moribund(connection_t *conn)
+{
+ if (conn != NULL &&
+ (conn->conn_array_index < 0 ||
+ conn->marked_for_close)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void connection_check_oos(int n_socks, int failed);
#ifdef CONNECTION_PRIVATE
STATIC void connection_free_(connection_t *conn);
@@ -289,6 +281,9 @@ MOCK_DECL(STATIC int,connection_connect_sockaddr,
const struct sockaddr *bindaddr,
socklen_t bindaddr_len,
int *socket_error));
+MOCK_DECL(STATIC void, kill_conn_list_for_oos, (smartlist_t *conns));
+MOCK_DECL(STATIC smartlist_t *, pick_oos_victims, (int n));
+
#endif
#endif
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 8098fb017b..12fe2f57c9 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -7,6 +7,51 @@
/**
* \file connection_edge.c
* \brief Handle edge streams.
+ *
+ * An edge_connection_t is a subtype of a connection_t, and represents two
+ * critical concepts in Tor: a stream, and an edge connection. From the Tor
+ * protocol's point of view, a stream is a bi-directional channel that is
+ * multiplexed on a single circuit. Each stream on a circuit is identified
+ * with a separate 16-bit stream ID, local to the (circuit,exit) pair.
+ * Streams are created in response to client requests.
+ *
+ * An edge connection is one thing that can implement a stream: it is either a
+ * TCP application socket that has arrived via (e.g.) a SOCKS request, or an
+ * exit connection.
+ *
+ * Not every instance of edge_connection_t truly represents an edge connction,
+ * however. (Sorry!) We also create edge_connection_t objects for streams that
+ * we will not be handling with TCP. The types of these streams are:
+ * <ul>
+ * <li>DNS lookup streams, created on the client side in response to
+ * a UDP DNS request received on a DNSPort, or a RESOLVE command
+ * on a controller.
+ * <li>DNS lookup streams, created on the exit side in response to
+ * a RELAY_RESOLVE cell from a client.
+ * <li>Tunneled directory streams, created on the directory cache side
+ * in response to a RELAY_BEGINDIR cell. These streams attach directly
+ * to a dir_connection_t object without ever using TCP.
+ * </ul>
+ *
+ * This module handles general-purpose functionality having to do with
+ * edge_connection_t. On the client side, it accepts various types of
+ * application requests on SocksPorts, TransPorts, and NATDPorts, and
+ * creates streams appropriately.
+ *
+ * This module is also responsible for implementing stream isolation:
+ * ensuring that streams that should not be linkable to one another are
+ * kept to different circuits.
+ *
+ * On the exit side, this module handles the various stream-creating
+ * type of RELAY cells by launching appropriate outgoing connections,
+ * DNS requests, or directory connection objects.
+ *
+ * And for all edge connections, this module is responsible for handling
+ * incoming and outdoing data as it arrives or leaves in the relay.c
+ * module. (Outgoing data will be packaged in
+ * connection_edge_process_inbuf() as it calls
+ * connection_edge_package_raw_inbuf(); incoming data from RELAY_DATA
+ * cells is applied in connection_edge_process_relay_cell().)
**/
#define CONNECTION_EDGE_PRIVATE
@@ -27,6 +72,7 @@
#include "control.h"
#include "dns.h"
#include "dnsserv.h"
+#include "directory.h"
#include "dirserv.h"
#include "hibernate.h"
#include "main.h"
@@ -214,6 +260,7 @@ connection_edge_process_inbuf(edge_connection_t *conn, int package_partial)
}
/* Fall through if the connection is on a circuit without optimistic
* data support. */
+ /* Falls through. */
case EXIT_CONN_STATE_CONNECTING:
case AP_CONN_STATE_RENDDESC_WAIT:
case AP_CONN_STATE_CIRCUIT_WAIT:
@@ -478,8 +525,7 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn)
rep_hist_note_exit_stream_opened(conn->port);
conn->state = EXIT_CONN_STATE_OPEN;
- IF_HAS_NO_BUFFEREVENT(conn)
- connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
+ connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */
if (connection_get_outbuf_len(conn)) /* in case there are any queued relay
* cells */
connection_start_writing(conn);
@@ -820,7 +866,9 @@ connection_ap_attach_pending(int retry)
continue;
}
if (conn->state != AP_CONN_STATE_CIRCUIT_WAIT) {
- log_warn(LD_BUG, "%p is no longer in circuit_wait. Its current state "
+ // XXXX 030 -- this is downgraded in 0.2.9, since we apparently
+ // XXXX are running into it in practice. It's harmless.
+ log_info(LD_BUG, "%p is no longer in circuit_wait. Its current state "
"is %s. Why is it on pending_entry_connections?",
entry_conn,
conn_state_to_string(conn->type, conn->state));
@@ -928,7 +976,7 @@ connection_ap_warn_and_unmark_if_pending_circ(entry_connection_t *entry_conn,
/** Tell any AP streams that are waiting for a one-hop tunnel to
* <b>failed_digest</b> that they are going to fail. */
-/* XXX024 We should get rid of this function, and instead attach
+/* XXXX We should get rid of this function, and instead attach
* one-hop streams to circ->p_streams so they get marked in
* circuit_mark_for_close like normal p_streams. */
void
@@ -1035,8 +1083,8 @@ connection_ap_detach_retriable(entry_connection_t *conn,
pathbias_mark_use_rollback(circ);
if (conn->pending_optimistic_data) {
- generic_buffer_set_to_copy(&conn->sending_optimistic_data,
- conn->pending_optimistic_data);
+ buf_set_to_copy(&conn->sending_optimistic_data,
+ conn->pending_optimistic_data);
}
if (!get_options()->LeaveStreamsUnattached || conn->use_begindir) {
@@ -1237,7 +1285,7 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
}
/* Hang on, did we find an answer saying that this is a reverse lookup for
- * an internal address? If so, we should reject it if we're condigured to
+ * an internal address? If so, we should reject it if we're configured to
* do so. */
if (options->ClientDNSRejectInternalAddresses) {
/* Don't let people try to do a reverse lookup on 10.0.0.1. */
@@ -1451,7 +1499,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
- /* XXXX024-1090 Should we also allow foo.bar.exit if ExitNodes is set and
+ /* XXXX-1090 Should we also allow foo.bar.exit if ExitNodes is set and
Bar is not listed in it? I say yes, but our revised manpage branch
implies no. */
}
@@ -1476,14 +1524,61 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* If we're running in Tor2webMode, we don't allow anything BUT .onion
* addresses. */
if (options->Tor2webMode) {
- log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname %s "
- "because tor2web mode is enabled.",
+ log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname "
+ "or IP address %s because tor2web mode is enabled.",
safe_str_client(socks->address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1;
}
#endif
+ /* socks->address is a non-onion hostname or IP address.
+ * If we can't do any non-onion requests, refuse the connection.
+ * If we have a hostname but can't do DNS, refuse the connection.
+ * If we have an IP address, but we can't use that address family,
+ * refuse the connection.
+ *
+ * If we can do DNS requests, and we can use at least one address family,
+ * then we have to resolve the address first. Then we'll know if it
+ * resolves to a usable address family. */
+
+ /* First, check if all non-onion traffic is disabled */
+ if (!conn->entry_cfg.dns_request && !conn->entry_cfg.ipv4_traffic
+ && !conn->entry_cfg.ipv6_traffic) {
+ log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname "
+ "or IP address %s because Port has OnionTrafficOnly set (or "
+ "NoDNSRequest, NoIPv4Traffic, and NoIPv6Traffic).",
+ safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ }
+
+ /* Then check if we have a hostname or IP address, and whether DNS or
+ * the IP address family are permitted */
+ tor_addr_t dummy_addr;
+ int socks_family = tor_addr_parse(&dummy_addr, socks->address);
+ /* family will be -1 for a non-onion hostname that's not an IP */
+ if (socks_family == -1 && !conn->entry_cfg.dns_request) {
+ log_warn(LD_APP, "Refusing to connect to hostname %s "
+ "because Port has NoDNSRequest set.",
+ safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ } else if (socks_family == AF_INET && !conn->entry_cfg.ipv4_traffic) {
+ log_warn(LD_APP, "Refusing to connect to IPv4 address %s because "
+ "Port has NoIPv4Traffic set.",
+ safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ } else if (socks_family == AF_INET6 && !conn->entry_cfg.ipv6_traffic) {
+ log_warn(LD_APP, "Refusing to connect to IPv6 address %s because "
+ "Port has NoIPv6Traffic set.",
+ safe_str_client(socks->address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
+ return -1;
+ }
+ /* No else, we've covered all possible returned value. */
+
/* See if this is a hostname lookup that we can answer immediately.
* (For example, an attempt to look up the IP address for an IP address.)
*/
@@ -1671,6 +1766,14 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* If we get here, it's a request for a .onion address! */
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)) {
@@ -1700,7 +1803,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
rend_service_authorization_t *client_auth =
rend_client_lookup_service_authorization(socks->address);
- const char *cookie = NULL;
+ 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 "
@@ -1712,7 +1815,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
/* 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, cookie, auth_type);
+ rend_data_client_create(socks->address, NULL, (char *) cookie,
+ auth_type);
if (rend_data == NULL) {
return -1;
}
@@ -1807,8 +1911,8 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
socklen_t orig_dst_len = sizeof(orig_dst);
tor_addr_t addr;
-#ifdef TRANS_TRPOXY
- if (options->TransProxyType_parsed == TPT_TPROXY) {
+#ifdef TRANS_TPROXY
+ if (get_options()->TransProxyType_parsed == TPT_TPROXY) {
if (getsockname(ENTRY_TO_CONN(conn)->s, (struct sockaddr*)&orig_dst,
&orig_dst_len) < 0) {
int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s);
@@ -2016,14 +2120,8 @@ connection_ap_handshake_process_socks(entry_connection_t *conn)
log_debug(LD_APP,"entered.");
- IF_HAS_BUFFEREVENT(base_conn, {
- struct evbuffer *input = bufferevent_get_input(base_conn->bufev);
- sockshere = fetch_from_evbuffer_socks(input, socks,
- options->TestSocks, options->SafeSocks);
- }) ELSE_IF_NO_BUFFEREVENT {
- sockshere = fetch_from_buf_socks(base_conn->inbuf, socks,
- options->TestSocks, options->SafeSocks);
- };
+ sockshere = fetch_from_buf_socks(base_conn->inbuf, socks,
+ options->TestSocks, options->SafeSocks);
if (socks->replylen) {
had_reply = 1;
@@ -2286,6 +2384,7 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
char payload[CELL_PAYLOAD_SIZE];
int payload_len;
int begin_type;
+ const or_options_t *options = get_options();
origin_circuit_t *circ;
edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn);
connection_t *base_conn = TO_CONN(edge_conn);
@@ -2299,7 +2398,7 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
edge_conn->stream_id = get_unique_stream_id_by_circ(circ);
if (edge_conn->stream_id==0) {
- /* XXXX024 Instead of closing this stream, we should make it get
+ /* XXXX+ Instead of closing this stream, we should make it get
* retried on another circuit. */
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
@@ -2329,10 +2428,37 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
begin_type = ap_conn->use_begindir ?
RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN;
+
+ /* Check that circuits are anonymised, based on their type. */
if (begin_type == RELAY_COMMAND_BEGIN) {
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(circ->build_state->onehop_tunnel == 0);
-#endif
+ /* This connection is a standard OR connection.
+ * Make sure its path length is anonymous, or that we're in a
+ * non-anonymous mode. */
+ assert_circ_anonymity_ok(circ, options);
+ } else if (begin_type == RELAY_COMMAND_BEGIN_DIR) {
+ /* This connection is a begindir directory connection.
+ * Look at the linked directory connection to access the directory purpose.
+ * If a BEGINDIR connection is ever not linked, that's a bug. */
+ if (BUG(!base_conn->linked)) {
+ return -1;
+ }
+ connection_t *linked_dir_conn_base = base_conn->linked_conn;
+ /* If the linked connection has been unlinked by other code, we can't send
+ * a begin cell on it. */
+ if (!linked_dir_conn_base) {
+ return -1;
+ }
+ /* Sensitive directory connections must have an anonymous path length.
+ * Otherwise, directory connections are typically one-hop.
+ * This matches the earlier check for directory connection path anonymity
+ * in directory_initiate_command_rend(). */
+ if (is_sensitive_dir_purpose(linked_dir_conn_base->purpose)) {
+ assert_circ_anonymity_ok(circ, options);
+ }
+ } else {
+ /* This code was written for the two connection types BEGIN and BEGIN_DIR
+ */
+ tor_assert_unreached();
}
if (connection_edge_send_command(edge_conn, begin_type,
@@ -2355,7 +2481,7 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn)
log_info(LD_APP, "Sending up to %ld + %ld bytes of queued-up data",
(long)connection_get_inbuf_len(base_conn),
ap_conn->sending_optimistic_data ?
- (long)generic_buffer_len(ap_conn->sending_optimistic_data) : 0);
+ (long)buf_datalen(ap_conn->sending_optimistic_data) : 0);
if (connection_edge_package_raw_inbuf(edge_conn, 1, NULL) < 0) {
connection_mark_for_close(base_conn);
}
@@ -2391,7 +2517,7 @@ connection_ap_handshake_send_resolve(entry_connection_t *ap_conn)
edge_conn->stream_id = get_unique_stream_id_by_circ(circ);
if (edge_conn->stream_id==0) {
- /* XXXX024 Instead of closing this stream, we should make it get
+ /* XXXX+ Instead of closing this stream, we should make it get
* retried on another circuit. */
connection_mark_unattached_ap(ap_conn, END_STREAM_REASON_INTERNAL);
@@ -2442,7 +2568,7 @@ connection_ap_handshake_send_resolve(entry_connection_t *ap_conn)
if (!base_conn->address) {
/* This might be unnecessary. XXXX */
- base_conn->address = tor_dup_addr(&base_conn->addr);
+ base_conn->address = tor_addr_to_str_dup(&base_conn->addr);
}
base_conn->state = AP_CONN_STATE_RESOLVE_WAIT;
log_info(LD_APP,"Address sent for resolve, ap socket "TOR_SOCKET_T_FORMAT
@@ -2889,7 +3015,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
or_circuit_t *or_circ = NULL;
const or_options_t *options = get_options();
begin_cell_t bcell;
- int r;
+ int rv;
uint8_t end_reason=0;
assert_circuit_ok(circ);
@@ -2914,10 +3040,10 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
return 0;
}
- r = begin_cell_parse(cell, &bcell, &end_reason);
- if (r < -1) {
+ rv = begin_cell_parse(cell, &bcell, &end_reason);
+ if (rv < -1) {
return -END_CIRC_REASON_TORPROTOCOL;
- } else if (r == -1) {
+ } else if (rv == -1) {
tor_free(bcell.address);
relay_send_end_cell_from_edge(rh.stream_id, circ, end_reason, NULL);
return 0;
@@ -3155,6 +3281,24 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ)
return 0;
}
+/** Helper: Return true and set *<b>why_rejected</b> to an optional clarifying
+ * message message iff we do not allow connections to <b>addr</b>:<b>port</b>.
+ */
+static int
+my_exit_policy_rejects(const tor_addr_t *addr,
+ uint16_t port,
+ const char **why_rejected)
+{
+ if (router_compare_to_my_exit_policy(addr, port)) {
+ *why_rejected = "";
+ return 1;
+ } else if (tor_addr_family(addr) == AF_INET6 && !get_options()->IPv6Exit) {
+ *why_rejected = " (IPv6 address without IPv6Exit configured)";
+ return 1;
+ }
+ return 0;
+}
+
/** Connect to conn's specified addr and port. If it worked, conn
* has now been added to the connection_array.
*
@@ -3169,14 +3313,18 @@ connection_exit_connect(edge_connection_t *edge_conn)
uint16_t port;
connection_t *conn = TO_CONN(edge_conn);
int socket_error = 0, result;
-
- if ( (!connection_edge_is_rendezvous_stream(edge_conn) &&
- router_compare_to_my_exit_policy(&edge_conn->base_.addr,
- edge_conn->base_.port)) ||
- (tor_addr_family(&conn->addr) == AF_INET6 &&
- ! get_options()->IPv6Exit)) {
- log_info(LD_EXIT,"%s:%d failed exit policy. Closing.",
- escaped_safe_str_client(conn->address), conn->port);
+ const char *why_failed_exit_policy = NULL;
+
+ /* Apply exit policy to non-rendezvous connections. */
+ if (! connection_edge_is_rendezvous_stream(edge_conn) &&
+ my_exit_policy_rejects(&edge_conn->base_.addr,
+ edge_conn->base_.port,
+ &why_failed_exit_policy)) {
+ if (BUG(!why_failed_exit_policy))
+ why_failed_exit_policy = "";
+ log_info(LD_EXIT,"%s:%d failed exit policy%s. Closing.",
+ escaped_safe_str_client(conn->address), conn->port,
+ why_failed_exit_policy);
connection_edge_end(edge_conn, END_STREAM_REASON_EXITPOLICY);
circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn);
connection_free(conn);
@@ -3233,11 +3381,9 @@ connection_exit_connect(edge_connection_t *edge_conn)
conn->state = EXIT_CONN_STATE_OPEN;
if (connection_get_outbuf_len(conn)) {
/* in case there are any queued data cells, from e.g. optimistic data */
- IF_HAS_NO_BUFFEREVENT(conn)
- connection_watch_events(conn, READ_EVENT|WRITE_EVENT);
+ connection_watch_events(conn, READ_EVENT|WRITE_EVENT);
} else {
- IF_HAS_NO_BUFFEREVENT(conn)
- connection_watch_events(conn, READ_EVENT);
+ connection_watch_events(conn, READ_EVENT);
}
/* also, deliver a 'connected' cell back through the circuit. */
@@ -3341,19 +3487,20 @@ connection_edge_is_rendezvous_stream(edge_connection_t *conn)
return 0;
}
-/** Return 1 if router <b>exit</b> is likely to allow stream <b>conn</b>
+/** Return 1 if router <b>exit_node</b> is likely to allow stream <b>conn</b>
* to exit from it, or 0 if it probably will not allow it.
* (We might be uncertain if conn's destination address has not yet been
* resolved.)
*/
int
-connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
+connection_ap_can_use_exit(const entry_connection_t *conn,
+ const node_t *exit_node)
{
const or_options_t *options = get_options();
tor_assert(conn);
tor_assert(conn->socks_request);
- tor_assert(exit);
+ tor_assert(exit_node);
/* If a particular exit node has been requested for the new connection,
* make sure the exit node of the existing circuit matches exactly.
@@ -3362,7 +3509,7 @@ connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
const node_t *chosen_exit =
node_get_by_nickname(conn->chosen_exit_name, 1);
if (!chosen_exit || tor_memneq(chosen_exit->identity,
- exit->identity, DIGEST_LEN)) {
+ exit_node->identity, DIGEST_LEN)) {
/* doesn't match */
// log_debug(LD_APP,"Requested node '%s', considering node '%s'. No.",
// conn->chosen_exit_name, exit->nickname);
@@ -3387,7 +3534,8 @@ connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
tor_addr_make_null(&addr, AF_INET);
addrp = &addr;
}
- r = compare_tor_addr_to_node_policy(addrp, conn->socks_request->port,exit);
+ r = compare_tor_addr_to_node_policy(addrp, conn->socks_request->port,
+ exit_node);
if (r == ADDR_POLICY_REJECTED)
return 0; /* We know the address, and the exit policy rejects it. */
if (r == ADDR_POLICY_PROBABLY_REJECTED && !conn->chosen_exit_name)
@@ -3396,10 +3544,10 @@ connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
* this node, err on the side of caution. */
} else if (SOCKS_COMMAND_IS_RESOLVE(conn->socks_request->command)) {
/* Don't send DNS requests to non-exit servers by default. */
- if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit))
+ if (!conn->chosen_exit_name && node_exit_policy_rejects_all(exit_node))
return 0;
}
- if (routerset_contains_node(options->ExcludeExitNodesUnion_, exit)) {
+ if (routerset_contains_node(options->ExcludeExitNodesUnion_, exit_node)) {
/* Not a suitable exit. Refuse it. */
return 0;
}
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 3892ac02fb..dadfdc4380 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -8,6 +8,17 @@
* \file connection_or.c
* \brief Functions to handle OR connections, TLS handshaking, and
* cells on the network.
+ *
+ * An or_connection_t is a subtype of connection_t (as implemented in
+ * connection.c) that uses a TLS connection to send and receive cells on the
+ * Tor network. (By sending and receiving cells connection_or.c, it cooperates
+ * with channeltls.c to implement a the channel interface of channel.c.)
+ *
+ * Every OR connection has an underlying tortls_t object (as implemented in
+ * tortls.c) which it uses as its TLS stream. It is responsible for
+ * sending and receiving cells over that TLS.
+ *
+ * This module also implements the client side of the v3 Tor link handshake,
**/
#include "or.h"
#include "buffers.h"
@@ -42,10 +53,6 @@
#include "ext_orport.h"
#include "scheduler.h"
-#ifdef USE_BUFFEREVENTS
-#include <event2/bufferevent_ssl.h>
-#endif
-
static int connection_tls_finish_handshake(or_connection_t *conn);
static int connection_or_launch_v3_or_handshake(or_connection_t *conn);
static int connection_or_process_cells_from_inbuf(or_connection_t *conn);
@@ -66,12 +73,6 @@ static void connection_or_mark_bad_for_new_circs(or_connection_t *or_conn);
static void connection_or_change_state(or_connection_t *conn, uint8_t state);
-#ifdef USE_BUFFEREVENTS
-static void connection_or_handle_event_cb(struct bufferevent *bufev,
- short event, void *arg);
-#include <event2/buffer.h>/*XXXX REMOVE */
-#endif
-
/**************************************************************/
/** Map from identity digest of connected OR or desired OR to a connection_t
@@ -404,8 +405,8 @@ connection_or_change_state(or_connection_t *conn, uint8_t state)
* be an or_connection_t field, but it got moved to channel_t and we
* shouldn't maintain two copies. */
-int
-connection_or_get_num_circuits(or_connection_t *conn)
+MOCK_IMPL(int,
+connection_or_get_num_circuits, (or_connection_t *conn))
{
tor_assert(conn);
@@ -567,13 +568,6 @@ connection_or_process_inbuf(or_connection_t *conn)
return ret;
case OR_CONN_STATE_TLS_SERVER_RENEGOTIATING:
-#ifdef USE_BUFFEREVENTS
- if (tor_tls_server_got_renegotiate(conn->tls))
- connection_or_tls_renegotiated_cb(conn->tls, conn);
- if (conn->base_.marked_for_close)
- return 0;
- /* fall through. */
-#endif
case OR_CONN_STATE_OPEN:
case OR_CONN_STATE_OR_HANDSHAKING_V2:
case OR_CONN_STATE_OR_HANDSHAKING_V3:
@@ -586,7 +580,7 @@ connection_or_process_inbuf(or_connection_t *conn)
* check would otherwise just let data accumulate. It serves no purpose
* in 0.2.3.
*
- * XXX024 Remove this check once we verify that the above paragraph is
+ * XXXX Remove this check once we verify that the above paragraph is
* 100% true. */
if (buf_datalen(conn->base_.inbuf) > MAX_OR_INBUF_WHEN_NONOPEN) {
log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated too much data (%d bytes) "
@@ -809,27 +803,6 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset,
conn->bandwidthrate = rate;
conn->bandwidthburst = burst;
-#ifdef USE_BUFFEREVENTS
- {
- const struct timeval *tick = tor_libevent_get_one_tick_timeout();
- struct ev_token_bucket_cfg *cfg, *old_cfg;
- int64_t rate64 = (((int64_t)rate) * options->TokenBucketRefillInterval)
- / 1000;
- /* This can't overflow, since TokenBucketRefillInterval <= 1000,
- * and rate started out less than INT_MAX. */
- int rate_per_tick = (int) rate64;
-
- cfg = ev_token_bucket_cfg_new(rate_per_tick, burst, rate_per_tick,
- burst, tick);
- old_cfg = conn->bucket_cfg;
- if (conn->base_.bufev)
- tor_set_bufferevent_rate_limit(conn->base_.bufev, cfg);
- if (old_cfg)
- ev_token_bucket_cfg_free(old_cfg);
- conn->bucket_cfg = cfg;
- (void) reset; /* No way to do this with libevent yet. */
- }
-#else
if (reset) { /* set up the token buckets to be full */
conn->read_bucket = conn->write_bucket = burst;
return;
@@ -840,7 +813,6 @@ connection_or_update_token_buckets_helper(or_connection_t *conn, int reset,
conn->read_bucket = burst;
if (conn->write_bucket > burst)
conn->write_bucket = burst;
-#endif
}
/** Either our set of relays or our per-conn rate limits have changed.
@@ -937,7 +909,7 @@ connection_or_init_conn_from_address(or_connection_t *conn,
}
conn->nickname = tor_strdup(node_get_nickname(r));
tor_free(conn->base_.address);
- conn->base_.address = tor_dup_addr(&node_ap.addr);
+ conn->base_.address = tor_addr_to_str_dup(&node_ap.addr);
} else {
conn->nickname = tor_malloc(HEX_DIGEST_LEN+2);
conn->nickname[0] = '$';
@@ -945,7 +917,7 @@ connection_or_init_conn_from_address(or_connection_t *conn,
conn->identity_digest, DIGEST_LEN);
tor_free(conn->base_.address);
- conn->base_.address = tor_dup_addr(addr);
+ conn->base_.address = tor_addr_to_str_dup(addr);
}
/*
@@ -1284,11 +1256,9 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port,
switch (connection_connect(TO_CONN(conn), conn->base_.address,
&addr, port, &socket_error)) {
case -1:
- /* If the connection failed immediately, and we're using
- * a proxy, our proxy is down. Don't blame the Tor server. */
- if (conn->base_.proxy_state == PROXY_INFANT)
- entry_guard_register_connect_status(conn->identity_digest,
- 0, 1, time(NULL));
+ /* We failed to establish a connection probably because of a local
+ * error. No need to blame the guard in this case. Notify the networking
+ * system of this failure. */
connection_or_connect_failed(conn,
errno_to_orconn_end_reason(socket_error),
tor_socket_strerror(socket_error));
@@ -1399,40 +1369,14 @@ connection_tls_start_handshake,(or_connection_t *conn, int receiving))
tor_tls_set_logged_address(conn->tls, // XXX client and relay?
escaped_safe_str(conn->base_.address));
-#ifdef USE_BUFFEREVENTS
- if (connection_type_uses_bufferevent(TO_CONN(conn))) {
- const int filtering = get_options()->UseFilteringSSLBufferevents;
- struct bufferevent *b =
- tor_tls_init_bufferevent(conn->tls, conn->base_.bufev, conn->base_.s,
- receiving, filtering);
- if (!b) {
- log_warn(LD_BUG,"tor_tls_init_bufferevent failed. Closing.");
- return -1;
- }
- conn->base_.bufev = b;
- if (conn->bucket_cfg)
- tor_set_bufferevent_rate_limit(conn->base_.bufev, conn->bucket_cfg);
- connection_enable_rate_limiting(TO_CONN(conn));
-
- connection_configure_bufferevent_callbacks(TO_CONN(conn));
- bufferevent_setcb(b,
- connection_handle_read_cb,
- connection_handle_write_cb,
- connection_or_handle_event_cb,/* overriding this one*/
- TO_CONN(conn));
- }
-#endif
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_HAS_BUFFEREVENT(TO_CONN(conn), {
- /* ???? */;
- }) ELSE_IF_NO_BUFFEREVENT {
- if (connection_tls_continue_handshake(conn) < 0)
- return -1;
- }
+ if (connection_tls_continue_handshake(conn) < 0)
+ return -1;
+
return 0;
}
@@ -1521,75 +1465,6 @@ connection_tls_continue_handshake(or_connection_t *conn)
return 0;
}
-#ifdef USE_BUFFEREVENTS
-static void
-connection_or_handle_event_cb(struct bufferevent *bufev, short event,
- void *arg)
-{
- struct or_connection_t *conn = TO_OR_CONN(arg);
-
- /* XXXX cut-and-paste code; should become a function. */
- if (event & BEV_EVENT_CONNECTED) {
- if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (tor_tls_finish_handshake(conn->tls) < 0) {
- log_warn(LD_OR, "Problem finishing handshake");
- connection_or_close_for_error(conn, 0);
- return;
- }
- }
-
- if (! tor_tls_used_v1_handshake(conn->tls)) {
- if (!tor_tls_is_server(conn->tls)) {
- if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) {
- if (connection_or_launch_v3_or_handshake(conn) < 0)
- connection_or_close_for_error(conn, 0);
- }
- } else {
- const int handshakes = tor_tls_get_num_server_handshakes(conn->tls);
-
- if (handshakes == 1) {
- /* v2 or v3 handshake, as a server. Only got one handshake, so
- * wait for the next one. */
- tor_tls_set_renegotiate_callback(conn->tls,
- connection_or_tls_renegotiated_cb,
- conn);
- connection_or_change_state(conn,
- OR_CONN_STATE_TLS_SERVER_RENEGOTIATING);
- } else if (handshakes == 2) {
- /* v2 handshake, as a server. Two handshakes happened already,
- * so we treat renegotiation as done.
- */
- connection_or_tls_renegotiated_cb(conn->tls, conn);
- } else if (handshakes > 2) {
- log_warn(LD_OR, "More than two handshakes done on connection. "
- "Closing.");
- connection_or_close_for_error(conn, 0);
- } else {
- log_warn(LD_BUG, "We were unexpectedly told that a connection "
- "got %d handshakes. Closing.", handshakes);
- connection_or_close_for_error(conn, 0);
- }
- return;
- }
- }
- connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
- if (connection_tls_finish_handshake(conn) < 0)
- connection_or_close_for_error(conn, 0); /* ???? */
- return;
- }
-
- if (event & BEV_EVENT_ERROR) {
- unsigned long err;
- while ((err = bufferevent_get_openssl_error(bufev))) {
- tor_tls_log_one_error(conn->tls, err, LOG_WARN, LD_OR,
- "handshaking (with bufferevent)");
- }
- }
-
- connection_handle_event_cb(bufev, event, arg);
-}
-#endif
-
/** Return 1 if we initiated this connection, or 0 if it started
* out as an incoming connection.
*/
@@ -2007,11 +1882,7 @@ connection_or_set_state_open(or_connection_t *conn)
or_handshake_state_free(conn->handshake_state);
conn->handshake_state = NULL;
- IF_HAS_BUFFEREVENT(TO_CONN(conn), {
- connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
- }) ELSE_IF_NO_BUFFEREVENT {
- connection_start_reading(TO_CONN(conn));
- }
+ connection_start_reading(TO_CONN(conn));
return 0;
}
@@ -2071,12 +1942,7 @@ static int
connection_fetch_var_cell_from_buf(or_connection_t *or_conn, var_cell_t **out)
{
connection_t *conn = TO_CONN(or_conn);
- IF_HAS_BUFFEREVENT(conn, {
- struct evbuffer *input = bufferevent_get_input(conn->bufev);
- return fetch_var_cell_from_evbuffer(input, out, or_conn->link_proto);
- }) ELSE_IF_NO_BUFFEREVENT {
- return fetch_var_cell_from_buf(conn->inbuf, out, or_conn->link_proto);
- }
+ return fetch_var_cell_from_buf(conn->inbuf, out, or_conn->link_proto);
}
/** Process cells from <b>conn</b>'s inbuf.
@@ -2273,22 +2139,29 @@ connection_or_send_netinfo,(or_connection_t *conn))
int
connection_or_send_certs_cell(or_connection_t *conn)
{
- const tor_x509_cert_t *link_cert = NULL, *id_cert = NULL;
+ const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL,
+ *using_link_cert = NULL;
+ tor_x509_cert_t *own_link_cert = NULL;
const uint8_t *link_encoded = NULL, *id_encoded = NULL;
size_t link_len, id_len;
var_cell_t *cell;
size_t cell_len;
ssize_t pos;
- int server_mode;
tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
if (! conn->handshake_state)
return -1;
- server_mode = ! conn->handshake_state->started_here;
- if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0)
+ const int conn_in_server_mode = ! conn->handshake_state->started_here;
+ if (tor_tls_get_my_certs(conn_in_server_mode,
+ &global_link_cert, &id_cert) < 0)
return -1;
- tor_x509_cert_get_der(link_cert, &link_encoded, &link_len);
+ if (conn_in_server_mode) {
+ using_link_cert = own_link_cert = tor_tls_get_own_cert(conn->tls);
+ } else {
+ using_link_cert = global_link_cert;
+ }
+ tor_x509_cert_get_der(using_link_cert, &link_encoded, &link_len);
tor_x509_cert_get_der(id_cert, &id_encoded, &id_len);
cell_len = 1 /* 1 byte: num certs in cell */ +
@@ -2299,7 +2172,7 @@ connection_or_send_certs_cell(or_connection_t *conn)
cell->payload[0] = 2;
pos = 1;
- if (server_mode)
+ if (conn_in_server_mode)
cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */
else
cell->payload[pos] = OR_CERT_TYPE_AUTH_1024; /* client authentication */
@@ -2316,6 +2189,7 @@ connection_or_send_certs_cell(or_connection_t *conn)
connection_or_write_var_cell_to_buf(cell, conn);
var_cell_free(cell);
+ tor_x509_cert_free(own_link_cert);
return 0;
}
@@ -2395,10 +2269,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
memcpy(auth1_getarray_type(auth), "AUTH0001", 8);
{
- const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL;
+ const tor_x509_cert_t *id_cert=NULL;
const common_digests_t *my_digests, *their_digests;
const uint8_t *my_id, *their_id, *client_id, *server_id;
- if (tor_tls_get_my_certs(server, &link_cert, &id_cert))
+ if (tor_tls_get_my_certs(server, NULL, &id_cert))
goto err;
my_digests = tor_x509_cert_get_id_digests(id_cert);
their_digests =
@@ -2437,13 +2311,11 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
{
/* Digest of cert used on TLS link : 32 octets. */
- const tor_x509_cert_t *cert = NULL;
- tor_x509_cert_t *freecert = NULL;
+ tor_x509_cert_t *cert = NULL;
if (server) {
- tor_tls_get_my_certs(1, &cert, NULL);
+ cert = tor_tls_get_own_cert(conn->tls);
} else {
- freecert = tor_tls_get_peer_cert(conn->tls);
- cert = freecert;
+ cert = tor_tls_get_peer_cert(conn->tls);
}
if (!cert) {
log_warn(LD_OR, "Unable to find cert when making AUTH1 data.");
@@ -2453,8 +2325,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
memcpy(auth->scert,
tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
- if (freecert)
- tor_x509_cert_free(freecert);
+ tor_x509_cert_free(cert);
}
/* HMAC of clientrandom and serverrandom using master key : 32 octets */
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index e2ec47a4f2..2e8c6066cc 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -64,7 +64,7 @@ void connection_or_init_conn_from_address(or_connection_t *conn,
int connection_or_client_learned_peer_id(or_connection_t *conn,
const uint8_t *peer_id);
time_t connection_or_client_used(or_connection_t *conn);
-int connection_or_get_num_circuits(or_connection_t *conn);
+MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn));
void or_handshake_state_free(or_handshake_state_t *state);
void or_handshake_state_record_cell(or_connection_t *conn,
or_handshake_state_t *state,
diff --git a/src/or/control.c b/src/or/control.c
index e2ad8cc6dc..03d9fcee2a 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -5,7 +5,31 @@
/**
* \file control.c
* \brief Implementation for Tor's control-socket interface.
- * See doc/spec/control-spec.txt for full details on protocol.
+ *
+ * A "controller" is an external program that monitors and controls a Tor
+ * instance via a text-based protocol. It connects to Tor via a connection
+ * to a local socket.
+ *
+ * The protocol is line-driven. The controller sends commands terminated by a
+ * CRLF. Tor sends lines that are either <em>replies</em> to what the
+ * controller has said, or <em>events</em> that Tor sends to the controller
+ * asynchronously based on occurrences in the Tor network model.
+ *
+ * See the control-spec.txt file in the torspec.git repository for full
+ * details on protocol.
+ *
+ * This module generally has two kinds of entry points: those based on having
+ * received a command on a controller socket, which are handled in
+ * connection_control_process_inbuf(), and dispatched to individual functions
+ * with names like control_handle_COMMANDNAME(); and those based on events
+ * that occur elsewhere in Tor, which are handled by functions with names like
+ * control_event_EVENTTYPE().
+ *
+ * Controller events are not sent immediately; rather, they are inserted into
+ * the queued_control_events array, and flushed later from
+ * flush_queued_events_cb(). Doing this simplifies our callgraph greatly,
+ * by limiting the number of places in Tor that can call back into the network
+ * stack.
**/
#define CONTROL_PRIVATE
@@ -51,11 +75,7 @@
#include <sys/resource.h>
#endif
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
#include "crypto_s2k.h"
#include "procmon.h"
@@ -190,6 +210,8 @@ static void set_cached_network_liveness(int liveness);
static void flush_queued_events_cb(evutil_socket_t fd, short what, void *arg);
+static char * download_status_to_string(const download_status_t *dl);
+
/** Given a control event code for a message event, return the corresponding
* log severity. */
static inline int
@@ -596,7 +618,7 @@ typedef struct queued_event_s {
/** Pointer to int. If this is greater than 0, we don't allow new events to be
* queued. */
-static tor_threadlocal_t block_event_queue;
+static tor_threadlocal_t block_event_queue_flag;
/** Holds a smartlist of queued_event_t objects that may need to be sent
* to one or more controllers */
@@ -631,17 +653,17 @@ control_initialize_event_queue(void)
if (queued_control_events_lock == NULL) {
queued_control_events_lock = tor_mutex_new();
- tor_threadlocal_init(&block_event_queue);
+ tor_threadlocal_init(&block_event_queue_flag);
}
}
static int *
get_block_event_queue(void)
{
- int *val = tor_threadlocal_get(&block_event_queue);
+ int *val = tor_threadlocal_get(&block_event_queue_flag);
if (PREDICT_UNLIKELY(val == NULL)) {
val = tor_malloc_zero(sizeof(int));
- tor_threadlocal_set(&block_event_queue, val);
+ tor_threadlocal_set(&block_event_queue_flag, val);
}
return val;
}
@@ -873,7 +895,8 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
config_line_t *lines=NULL;
char *start = body;
char *errstring = NULL;
- const int clear_first = 1;
+ const unsigned flags =
+ CAL_CLEAR_FIRST | (use_defaults ? CAL_USE_DEFAULTS : 0);
char *config;
smartlist_t *entries = smartlist_new();
@@ -933,7 +956,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body,
}
tor_free(config);
- opt_err = options_trial_assign(lines, use_defaults, clear_first, &errstring);
+ opt_err = options_trial_assign(lines, flags, &errstring);
{
const char *msg;
switch (opt_err) {
@@ -1211,7 +1234,8 @@ decode_hashed_passwords(config_line_t *passwords)
const char *hashed = cl->value;
if (!strcmpstart(hashed, "16:")) {
- if (base16_decode(decoded, sizeof(decoded), hashed+3, strlen(hashed+3))<0
+ if (base16_decode(decoded, sizeof(decoded), hashed+3, strlen(hashed+3))
+ != S2K_RFC2440_SPECIFIER_LEN + DIGEST_LEN
|| strlen(hashed+3) != (S2K_RFC2440_SPECIFIER_LEN+DIGEST_LEN)*2) {
goto err;
}
@@ -1262,7 +1286,8 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
tor_assert(i>0);
password_len = i/2;
password = tor_malloc(password_len + 1);
- if (base16_decode(password, password_len+1, body, i)<0) {
+ if (base16_decode(password, password_len+1, body, i)
+ != (int) password_len) {
connection_write_str_to_buf(
"551 Invalid hexadecimal encoding. Maybe you tried a plain text "
"password? If so, the standard requires that you put it in "
@@ -1370,7 +1395,7 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
goto err;
}
bad_password = 1;
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
sl = NULL;
} else {
@@ -1382,7 +1407,7 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
received, DIGEST_LEN))
goto ok;
});
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
sl = NULL;
@@ -1410,7 +1435,7 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
connection_printf_to_buf(conn, "515 Authentication failed: %s\r\n", errstr);
connection_mark_for_close(TO_CONN(conn));
if (sl) { /* clean up */
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
}
return 0;
@@ -1421,7 +1446,7 @@ handle_control_authenticate(control_connection_t *conn, uint32_t len,
conn->base_.state = CONTROL_CONN_STATE_OPEN;
tor_free(password);
if (sl) { /* clean up */
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
}
return 0;
@@ -1679,7 +1704,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
*answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS");
} else if (!strcmp(question, "address")) {
uint32_t addr;
- if (router_pick_published_address(get_options(), &addr) < 0) {
+ if (router_pick_published_address(get_options(), &addr, 0) < 0) {
*errmsg = "Address unknown";
return -1;
}
@@ -1724,8 +1749,6 @@ getinfo_helper_misc(control_connection_t *conn, const char *question,
} else if (!strcmp(question, "limits/max-mem-in-queues")) {
tor_asprintf(answer, U64_FORMAT,
U64_PRINTF_ARG(get_options()->MaxMemInQueues));
- } else if (!strcmp(question, "dir-usage")) {
- *answer = directory_dump_request_log();
} else if (!strcmp(question, "fingerprint")) {
crypto_pk_t *server_key;
if (!server_mode(get_options())) {
@@ -1852,11 +1875,10 @@ getinfo_helper_dir(control_connection_t *control_conn,
const char *question, char **answer,
const char **errmsg)
{
- const node_t *node;
- const routerinfo_t *ri = NULL;
(void) control_conn;
if (!strcmpstart(question, "desc/id/")) {
- node = node_get_by_hex_id(question+strlen("desc/id/"));
+ const routerinfo_t *ri = NULL;
+ const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"));
if (node)
ri = node->ri;
if (ri) {
@@ -1865,9 +1887,11 @@ getinfo_helper_dir(control_connection_t *control_conn,
*answer = tor_strndup(body, ri->cache_info.signed_descriptor_len);
}
} else if (!strcmpstart(question, "desc/name/")) {
- /* XXX023 Setting 'warn_if_unnamed' here is a bit silly -- the
+ 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. */
- node = node_get_by_nickname(question+strlen("desc/name/"), 1);
+ const node_t *node =
+ node_get_by_nickname(question+strlen("desc/name/"), 1);
if (node)
ri = node->ri;
if (ri) {
@@ -1951,7 +1975,7 @@ getinfo_helper_dir(control_connection_t *control_conn,
*answer = tor_strndup(md->body, md->bodylen);
}
} else if (!strcmpstart(question, "md/name/")) {
- /* XXX023 Setting 'warn_if_unnamed' here is a bit silly -- the
+ /* 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);
/* XXXX duplicated code */
@@ -1961,7 +1985,9 @@ getinfo_helper_dir(control_connection_t *control_conn,
*answer = tor_strndup(md->body, md->bodylen);
}
} else if (!strcmpstart(question, "desc-annotations/id/")) {
- node = node_get_by_hex_id(question+strlen("desc-annotations/id/"));
+ const routerinfo_t *ri = NULL;
+ const node_t *node =
+ node_get_by_hex_id(question+strlen("desc-annotations/id/"));
if (node)
ri = node->ri;
if (ri) {
@@ -2028,7 +2054,8 @@ getinfo_helper_dir(control_connection_t *control_conn,
if (strlen(question) == HEX_DIGEST_LEN) {
char d[DIGEST_LEN];
signed_descriptor_t *sd = NULL;
- if (base16_decode(d, sizeof(d), question, strlen(question))==0) {
+ if (base16_decode(d, sizeof(d), question, strlen(question))
+ == sizeof(d)) {
/* XXXX this test should move into extrainfo_get_by_descriptor_digest,
* but I don't want to risk affecting other parts of the code,
* especially since the rules for using our own extrainfo (including
@@ -2050,6 +2077,411 @@ getinfo_helper_dir(control_connection_t *control_conn,
return 0;
}
+/** Given a smartlist of 20-byte digests, return a newly allocated string
+ * containing each of those digests in order, formatted in HEX, and terminated
+ * with a newline. */
+static char *
+digest_list_to_string(const smartlist_t *sl)
+{
+ int len;
+ char *result, *s;
+
+ /* Allow for newlines, and a \0 at the end */
+ len = smartlist_len(sl) * (HEX_DIGEST_LEN + 1) + 1;
+ result = tor_malloc_zero(len);
+
+ s = result;
+ SMARTLIST_FOREACH_BEGIN(sl, const char *, digest) {
+ base16_encode(s, HEX_DIGEST_LEN + 1, digest, DIGEST_LEN);
+ s[HEX_DIGEST_LEN] = '\n';
+ s += HEX_DIGEST_LEN + 1;
+ } SMARTLIST_FOREACH_END(digest);
+ *s = '\0';
+
+ return result;
+}
+
+/** Turn a download_status_t into a human-readable description in a newly
+ * allocated string. The format is specified in control-spec.txt, under
+ * the documentation for "GETINFO download/..." . */
+static char *
+download_status_to_string(const download_status_t *dl)
+{
+ char *rv = NULL, *tmp;
+ char tbuf[ISO_TIME_LEN+1];
+ const char *schedule_str, *want_authority_str;
+ const char *increment_on_str, *backoff_str;
+
+ if (dl) {
+ /* Get some substrings of the eventual output ready */
+ format_iso_time(tbuf, dl->next_attempt_at);
+
+ switch (dl->schedule) {
+ case DL_SCHED_GENERIC:
+ schedule_str = "DL_SCHED_GENERIC";
+ break;
+ case DL_SCHED_CONSENSUS:
+ schedule_str = "DL_SCHED_CONSENSUS";
+ break;
+ case DL_SCHED_BRIDGE:
+ schedule_str = "DL_SCHED_BRIDGE";
+ break;
+ default:
+ schedule_str = "unknown";
+ break;
+ }
+
+ switch (dl->want_authority) {
+ case DL_WANT_ANY_DIRSERVER:
+ want_authority_str = "DL_WANT_ANY_DIRSERVER";
+ break;
+ case DL_WANT_AUTHORITY:
+ want_authority_str = "DL_WANT_AUTHORITY";
+ break;
+ default:
+ want_authority_str = "unknown";
+ break;
+ }
+
+ switch (dl->increment_on) {
+ case DL_SCHED_INCREMENT_FAILURE:
+ increment_on_str = "DL_SCHED_INCREMENT_FAILURE";
+ break;
+ case DL_SCHED_INCREMENT_ATTEMPT:
+ increment_on_str = "DL_SCHED_INCREMENT_ATTEMPT";
+ break;
+ default:
+ increment_on_str = "unknown";
+ break;
+ }
+
+ switch (dl->backoff) {
+ case DL_SCHED_DETERMINISTIC:
+ backoff_str = "DL_SCHED_DETERMINISTIC";
+ break;
+ case DL_SCHED_RANDOM_EXPONENTIAL:
+ backoff_str = "DL_SCHED_RANDOM_EXPONENTIAL";
+ break;
+ default:
+ backoff_str = "unknown";
+ break;
+ }
+
+ /* Now assemble them */
+ tor_asprintf(&tmp,
+ "next-attempt-at %s\n"
+ "n-download-failures %u\n"
+ "n-download-attempts %u\n"
+ "schedule %s\n"
+ "want-authority %s\n"
+ "increment-on %s\n"
+ "backoff %s\n",
+ tbuf,
+ dl->n_download_failures,
+ dl->n_download_attempts,
+ schedule_str,
+ want_authority_str,
+ increment_on_str,
+ backoff_str);
+
+ if (dl->backoff == DL_SCHED_RANDOM_EXPONENTIAL) {
+ /* Additional fields become relevant in random-exponential mode */
+ tor_asprintf(&rv,
+ "%s"
+ "last-backoff-position %u\n"
+ "last-delay-used %d\n",
+ tmp,
+ dl->last_backoff_position,
+ dl->last_delay_used);
+ tor_free(tmp);
+ } else {
+ /* That was it */
+ rv = tmp;
+ }
+ }
+
+ return rv;
+}
+
+/** Handle the consensus download cases for getinfo_helper_downloads() */
+STATIC void
+getinfo_helper_downloads_networkstatus(const char *flavor,
+ download_status_t **dl_to_emit,
+ const char **errmsg)
+{
+ /*
+ * We get the one for the current bootstrapped status by default, or
+ * take an extra /bootstrap or /running suffix
+ */
+ if (strcmp(flavor, "ns") == 0) {
+ *dl_to_emit = networkstatus_get_dl_status_by_flavor(FLAV_NS);
+ } else if (strcmp(flavor, "ns/bootstrap") == 0) {
+ *dl_to_emit = networkstatus_get_dl_status_by_flavor_bootstrap(FLAV_NS);
+ } else if (strcmp(flavor, "ns/running") == 0 ) {
+ *dl_to_emit = networkstatus_get_dl_status_by_flavor_running(FLAV_NS);
+ } else if (strcmp(flavor, "microdesc") == 0) {
+ *dl_to_emit = networkstatus_get_dl_status_by_flavor(FLAV_MICRODESC);
+ } else if (strcmp(flavor, "microdesc/bootstrap") == 0) {
+ *dl_to_emit =
+ networkstatus_get_dl_status_by_flavor_bootstrap(FLAV_MICRODESC);
+ } else if (strcmp(flavor, "microdesc/running") == 0) {
+ *dl_to_emit =
+ networkstatus_get_dl_status_by_flavor_running(FLAV_MICRODESC);
+ } else {
+ *errmsg = "Unknown flavor";
+ }
+}
+
+/** Handle the cert download cases for getinfo_helper_downloads() */
+STATIC void
+getinfo_helper_downloads_cert(const char *fp_sk_req,
+ download_status_t **dl_to_emit,
+ smartlist_t **digest_list,
+ const char **errmsg)
+{
+ const char *sk_req;
+ char id_digest[DIGEST_LEN];
+ char sk_digest[DIGEST_LEN];
+
+ /*
+ * We have to handle four cases; fp_sk_req is the request with
+ * a prefix of "downloads/cert/" snipped off.
+ *
+ * Case 1: fp_sk_req = "fps"
+ * - We should emit a digest_list with a list of all the identity
+ * fingerprints that can be queried for certificate download status;
+ * get it by calling list_authority_ids_with_downloads().
+ *
+ * Case 2: fp_sk_req = "fp/<fp>" for some fingerprint fp
+ * - We want the default certificate for this identity fingerprint's
+ * download status; this is the download we get from URLs starting
+ * in /fp/ on the directory server. We can get it with
+ * id_only_download_status_for_authority_id().
+ *
+ * Case 3: fp_sk_req = "fp/<fp>/sks" for some fingerprint fp
+ * - We want a list of all signing key digests for this identity
+ * fingerprint which can be queried for certificate download status.
+ * Get it with list_sk_digests_for_authority_id().
+ *
+ * Case 4: fp_sk_req = "fp/<fp>/<sk>" for some fingerprint fp and
+ * signing key digest sk
+ * - We want the download status for the certificate for this specific
+ * signing key and fingerprint. These correspond to the ones we get
+ * from URLs starting in /fp-sk/ on the directory server. Get it with
+ * list_sk_digests_for_authority_id().
+ */
+
+ if (strcmp(fp_sk_req, "fps") == 0) {
+ *digest_list = list_authority_ids_with_downloads();
+ if (!(*digest_list)) {
+ *errmsg = "Failed to get list of authority identity digests (!)";
+ }
+ } else if (!strcmpstart(fp_sk_req, "fp/")) {
+ fp_sk_req += strlen("fp/");
+ /* Okay, look for another / to tell the fp from fp-sk cases */
+ sk_req = strchr(fp_sk_req, '/');
+ if (sk_req) {
+ /* okay, split it here and try to parse <fp> */
+ if (base16_decode(id_digest, DIGEST_LEN,
+ fp_sk_req, sk_req - fp_sk_req) == DIGEST_LEN) {
+ /* Skip past the '/' */
+ ++sk_req;
+ if (strcmp(sk_req, "sks") == 0) {
+ /* We're asking for the list of signing key fingerprints */
+ *digest_list = list_sk_digests_for_authority_id(id_digest);
+ if (!(*digest_list)) {
+ *errmsg = "Failed to get list of signing key digests for this "
+ "authority identity digest";
+ }
+ } else {
+ /* We've got a signing key digest */
+ if (base16_decode(sk_digest, DIGEST_LEN,
+ sk_req, strlen(sk_req)) == DIGEST_LEN) {
+ *dl_to_emit =
+ download_status_for_authority_id_and_sk(id_digest, sk_digest);
+ if (!(*dl_to_emit)) {
+ *errmsg = "Failed to get download status for this identity/"
+ "signing key digest pair";
+ }
+ } else {
+ *errmsg = "That didn't look like a signing key digest";
+ }
+ }
+ } else {
+ *errmsg = "That didn't look like an identity digest";
+ }
+ } else {
+ /* We're either in downloads/certs/fp/<fp>, or we can't parse <fp> */
+ if (strlen(fp_sk_req) == HEX_DIGEST_LEN) {
+ if (base16_decode(id_digest, DIGEST_LEN,
+ fp_sk_req, strlen(fp_sk_req)) == DIGEST_LEN) {
+ *dl_to_emit = id_only_download_status_for_authority_id(id_digest);
+ if (!(*dl_to_emit)) {
+ *errmsg = "Failed to get download status for this authority "
+ "identity digest";
+ }
+ } else {
+ *errmsg = "That didn't look like a digest";
+ }
+ } else {
+ *errmsg = "That didn't look like a digest";
+ }
+ }
+ } else {
+ *errmsg = "Unknown certificate download status query";
+ }
+}
+
+/** Handle the routerdesc download cases for getinfo_helper_downloads() */
+STATIC void
+getinfo_helper_downloads_desc(const char *desc_req,
+ download_status_t **dl_to_emit,
+ smartlist_t **digest_list,
+ const char **errmsg)
+{
+ char desc_digest[DIGEST_LEN];
+ /*
+ * Two cases to handle here:
+ *
+ * Case 1: desc_req = "descs"
+ * - Emit a list of all router descriptor digests, which we get by
+ * calling router_get_descriptor_digests(); this can return NULL
+ * if we have no current ns-flavor consensus.
+ *
+ * Case 2: desc_req = <fp>
+ * - Check on the specified fingerprint and emit its download_status_t
+ * using router_get_dl_status_by_descriptor_digest().
+ */
+
+ if (strcmp(desc_req, "descs") == 0) {
+ *digest_list = router_get_descriptor_digests();
+ if (!(*digest_list)) {
+ *errmsg = "We don't seem to have a networkstatus-flavored consensus";
+ }
+ /*
+ * Microdescs don't use the download_status_t mechanism, so we don't
+ * answer queries about their downloads here; see microdesc.c.
+ */
+ } else if (strlen(desc_req) == HEX_DIGEST_LEN) {
+ if (base16_decode(desc_digest, DIGEST_LEN,
+ desc_req, strlen(desc_req)) == DIGEST_LEN) {
+ /* Okay we got a digest-shaped thing; try asking for it */
+ *dl_to_emit = router_get_dl_status_by_descriptor_digest(desc_digest);
+ if (!(*dl_to_emit)) {
+ *errmsg = "No such descriptor digest found";
+ }
+ } else {
+ *errmsg = "That didn't look like a digest";
+ }
+ } else {
+ *errmsg = "Unknown router descriptor download status query";
+ }
+}
+
+/** Handle the bridge download cases for getinfo_helper_downloads() */
+STATIC void
+getinfo_helper_downloads_bridge(const char *bridge_req,
+ download_status_t **dl_to_emit,
+ smartlist_t **digest_list,
+ const char **errmsg)
+{
+ char bridge_digest[DIGEST_LEN];
+ /*
+ * Two cases to handle here:
+ *
+ * Case 1: bridge_req = "bridges"
+ * - Emit a list of all bridge identity digests, which we get by
+ * calling list_bridge_identities(); this can return NULL if we are
+ * not using bridges.
+ *
+ * Case 2: bridge_req = <fp>
+ * - Check on the specified fingerprint and emit its download_status_t
+ * using get_bridge_dl_status_by_id().
+ */
+
+ if (strcmp(bridge_req, "bridges") == 0) {
+ *digest_list = list_bridge_identities();
+ if (!(*digest_list)) {
+ *errmsg = "We don't seem to be using bridges";
+ }
+ } else if (strlen(bridge_req) == HEX_DIGEST_LEN) {
+ if (base16_decode(bridge_digest, DIGEST_LEN,
+ bridge_req, strlen(bridge_req)) == DIGEST_LEN) {
+ /* Okay we got a digest-shaped thing; try asking for it */
+ *dl_to_emit = get_bridge_dl_status_by_id(bridge_digest);
+ if (!(*dl_to_emit)) {
+ *errmsg = "No such bridge identity digest found";
+ }
+ } else {
+ *errmsg = "That didn't look like a digest";
+ }
+ } else {
+ *errmsg = "Unknown bridge descriptor download status query";
+ }
+}
+
+/** Implementation helper for GETINFO: knows the answers for questions about
+ * download status information. */
+STATIC int
+getinfo_helper_downloads(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ download_status_t *dl_to_emit = NULL;
+ smartlist_t *digest_list = NULL;
+
+ /* Assert args are sane */
+ tor_assert(control_conn != NULL);
+ tor_assert(question != NULL);
+ tor_assert(answer != NULL);
+ tor_assert(errmsg != NULL);
+
+ /* We check for this later to see if we should supply a default */
+ *errmsg = NULL;
+
+ /* Are we after networkstatus downloads? */
+ if (!strcmpstart(question, "downloads/networkstatus/")) {
+ getinfo_helper_downloads_networkstatus(
+ question + strlen("downloads/networkstatus/"),
+ &dl_to_emit, errmsg);
+ /* Certificates? */
+ } else if (!strcmpstart(question, "downloads/cert/")) {
+ getinfo_helper_downloads_cert(
+ question + strlen("downloads/cert/"),
+ &dl_to_emit, &digest_list, errmsg);
+ /* Router descriptors? */
+ } else if (!strcmpstart(question, "downloads/desc/")) {
+ getinfo_helper_downloads_desc(
+ question + strlen("downloads/desc/"),
+ &dl_to_emit, &digest_list, errmsg);
+ /* Bridge descriptors? */
+ } else if (!strcmpstart(question, "downloads/bridge/")) {
+ getinfo_helper_downloads_bridge(
+ question + strlen("downloads/bridge/"),
+ &dl_to_emit, &digest_list, errmsg);
+ } else {
+ *errmsg = "Unknown download status query";
+ }
+
+ if (dl_to_emit) {
+ *answer = download_status_to_string(dl_to_emit);
+
+ return 0;
+ } else if (digest_list) {
+ *answer = digest_list_to_string(digest_list);
+ SMARTLIST_FOREACH(digest_list, void *, s, tor_free(s));
+ smartlist_free(digest_list);
+
+ return 0;
+ } else {
+ if (!(*errmsg)) {
+ *errmsg = "Unknown error";
+ }
+
+ return -1;
+ }
+}
+
/** Allocate and return a description of <b>circ</b>'s current status,
* including its path (if any). */
static char *
@@ -2489,6 +2921,49 @@ static const getinfo_item_t getinfo_items[] = {
DOC("config/defaults",
"List of default values for configuration options. "
"See also config/names"),
+ PREFIX("downloads/networkstatus/", downloads,
+ "Download statuses for networkstatus objects"),
+ DOC("downloads/networkstatus/ns",
+ "Download status for current-mode networkstatus download"),
+ DOC("downloads/networkstatus/ns/bootstrap",
+ "Download status for bootstrap-time networkstatus download"),
+ DOC("downloads/networkstatus/ns/running",
+ "Download status for run-time networkstatus download"),
+ DOC("downloads/networkstatus/microdesc",
+ "Download status for current-mode microdesc download"),
+ DOC("downloads/networkstatus/microdesc/bootstrap",
+ "Download status for bootstrap-time microdesc download"),
+ DOC("downloads/networkstatus/microdesc/running",
+ "Download status for run-time microdesc download"),
+ PREFIX("downloads/cert/", downloads,
+ "Download statuses for certificates, by id fingerprint and "
+ "signing key"),
+ DOC("downloads/cert/fps",
+ "List of authority fingerprints for which any download statuses "
+ "exist"),
+ DOC("downloads/cert/fp/<fp>",
+ "Download status for <fp> with the default signing key; corresponds "
+ "to /fp/ URLs on directory server."),
+ DOC("downloads/cert/fp/<fp>/sks",
+ "List of signing keys for which specific download statuses are "
+ "available for this id fingerprint"),
+ DOC("downloads/cert/fp/<fp>/<sk>",
+ "Download status for <fp> with signing key <sk>; corresponds "
+ "to /fp-sk/ URLs on directory server."),
+ PREFIX("downloads/desc/", downloads,
+ "Download statuses for router descriptors, by descriptor digest"),
+ DOC("downloads/desc/descs",
+ "Return a list of known router descriptor digests"),
+ DOC("downloads/desc/<desc>",
+ "Return a download status for a given descriptor digest"),
+ PREFIX("downloads/bridge/", downloads,
+ "Download statuses for bridge descriptors, by bridge identity "
+ "digest"),
+ DOC("downloads/bridge/bridges",
+ "Return a list of configured bridge identity digests with download "
+ "statuses"),
+ DOC("downloads/bridge/<desc>",
+ "Return a download status for a given bridge identity digest"),
ITEM("info/names", misc,
"List of GETINFO options, types, and documentation."),
ITEM("events/names", misc,
@@ -2561,7 +3036,6 @@ static const getinfo_item_t getinfo_items[] = {
"Username under which the tor process is running."),
ITEM("process/descriptor-limit", misc, "File descriptor limit."),
ITEM("limits/max-mem-in-queues", misc, "Actual limit on memory in queues"),
- ITEM("dir-usage", misc, "Breakdown of bytes transferred over DirPort."),
PREFIX("desc-annotations/id/", dir, "Router annotations by hexdigest."),
PREFIX("dir/server/", dir,"Router descriptors as retrieved from a DirPort."),
PREFIX("dir/status/", dir,
@@ -2575,7 +3049,7 @@ static const getinfo_item_t getinfo_items[] = {
" ExitPolicyRejectPrivate."),
ITEM("exit-policy/reject-private/relay", policies,
"The relay-specific rules appended to the configured exit policy by"
- " ExitPolicyRejectPrivate."),
+ " ExitPolicyRejectPrivate and/or ExitPolicyRejectLocalInterfaces."),
ITEM("exit-policy/full", policies, "The entire exit policy of onion router"),
ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"),
ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"),
@@ -3094,12 +3568,15 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
int cache = 0; /* eventually, we may switch this to 1 */
const char *cp = memchr(body, '\n', len);
- smartlist_t *args = smartlist_new();
- tor_assert(cp);
+
+ if (cp == NULL) {
+ connection_printf_to_buf(conn, "251 Empty body\r\n");
+ return 0;
+ }
++cp;
char *cmdline = tor_memdup_nulterm(body, cp-body);
-
+ smartlist_t *args = smartlist_new();
smartlist_split_string(args, cmdline, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(args, char *, option) {
@@ -3445,7 +3922,8 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len,
client_nonce = tor_malloc_zero(client_nonce_len);
if (base16_decode(client_nonce, client_nonce_len,
- cp, client_nonce_encoded_len) < 0) {
+ cp, client_nonce_encoded_len)
+ != (int) client_nonce_len) {
connection_write_str_to_buf("513 Invalid base16 client nonce\r\n",
conn);
connection_mark_for_close(TO_CONN(conn));
@@ -3683,14 +4161,19 @@ handle_control_hspost(control_connection_t *conn,
const char *body)
{
static const char *opt_server = "SERVER=";
- smartlist_t *args = smartlist_new();
smartlist_t *hs_dirs = NULL;
const char *encoded_desc = body;
size_t encoded_desc_len = len;
char *cp = memchr(body, '\n', len);
+ if (cp == NULL) {
+ connection_printf_to_buf(conn, "251 Empty body\r\n");
+ return 0;
+ }
char *argline = tor_strndup(body, cp-body);
+ smartlist_t *args = smartlist_new();
+
/* If any SERVER= options were specified, try parse the options line */
if (!strcasecmpstart(argline, opt_server)) {
/* encoded_desc begins after a newline character */
@@ -3791,14 +4274,20 @@ handle_control_add_onion(control_connection_t *conn,
* the other arguments are malformed.
*/
smartlist_t *port_cfgs = smartlist_new();
+ smartlist_t *auth_clients = NULL;
+ smartlist_t *auth_created_clients = NULL;
int discard_pk = 0;
int detach = 0;
int max_streams = 0;
int max_streams_close_circuit = 0;
+ rend_auth_type_t auth_type = REND_NO_AUTH;
+ /* Default to adding an anonymous hidden service if no flag is given */
+ int non_anonymous = 0;
for (size_t i = 1; i < arg_len; i++) {
static const char *port_prefix = "Port=";
static const char *flags_prefix = "Flags=";
static const char *max_s_prefix = "MaxStreams=";
+ static const char *auth_prefix = "ClientAuth=";
const char *arg = smartlist_get(args, i);
if (!strcasecmpstart(arg, port_prefix)) {
@@ -3829,10 +4318,17 @@ handle_control_add_onion(control_connection_t *conn,
* connection.
* * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
* exceeded.
+ * * 'BasicAuth' - Client authorization using the 'basic' method.
+ * * 'NonAnonymous' - Add a non-anonymous Single Onion Service. If this
+ * flag is present, tor must be in non-anonymous
+ * hidden service mode. If this flag is absent,
+ * tor must be in anonymous hidden service mode.
*/
static const char *discard_flag = "DiscardPK";
static const char *detach_flag = "Detach";
static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
+ static const char *basicauth_flag = "BasicAuth";
+ static const char *non_anonymous_flag = "NonAnonymous";
smartlist_t *flags = smartlist_new();
int bad = 0;
@@ -3851,6 +4347,10 @@ handle_control_add_onion(control_connection_t *conn,
detach = 1;
} else if (!strcasecmp(flag, max_s_close_flag)) {
max_streams_close_circuit = 1;
+ } else if (!strcasecmp(flag, basicauth_flag)) {
+ auth_type = REND_BASIC_AUTH;
+ } else if (!strcasecmp(flag, non_anonymous_flag)) {
+ non_anonymous = 1;
} else {
connection_printf_to_buf(conn,
"512 Invalid 'Flags' argument: %s\r\n",
@@ -3863,6 +4363,42 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_free(flags);
if (bad)
goto out;
+ } else if (!strcasecmpstart(arg, auth_prefix)) {
+ char *err_msg = NULL;
+ int created = 0;
+ rend_authorized_client_t *client =
+ add_onion_helper_clientauth(arg + strlen(auth_prefix),
+ &created, &err_msg);
+ if (!client) {
+ if (err_msg) {
+ connection_write_str_to_buf(err_msg, conn);
+ tor_free(err_msg);
+ }
+ goto out;
+ }
+
+ if (auth_clients != NULL) {
+ int bad = 0;
+ SMARTLIST_FOREACH_BEGIN(auth_clients, rend_authorized_client_t *, ac) {
+ if (strcmp(ac->client_name, client->client_name) == 0) {
+ bad = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ac);
+ if (bad) {
+ connection_printf_to_buf(conn,
+ "512 Duplicate name in ClientAuth\r\n");
+ rend_authorized_client_free(client);
+ goto out;
+ }
+ } else {
+ auth_clients = smartlist_new();
+ auth_created_clients = smartlist_new();
+ }
+ smartlist_add(auth_clients, client);
+ if (created) {
+ smartlist_add(auth_created_clients, client);
+ }
} else {
connection_printf_to_buf(conn, "513 Invalid argument\r\n");
goto out;
@@ -3871,6 +4407,31 @@ handle_control_add_onion(control_connection_t *conn,
if (smartlist_len(port_cfgs) == 0) {
connection_printf_to_buf(conn, "512 Missing 'Port' argument\r\n");
goto out;
+ } else if (auth_type == REND_NO_AUTH && auth_clients != NULL) {
+ connection_printf_to_buf(conn, "512 No auth type specified\r\n");
+ goto out;
+ } else if (auth_type != REND_NO_AUTH && auth_clients == NULL) {
+ connection_printf_to_buf(conn, "512 No auth clients specified\r\n");
+ goto out;
+ } else if ((auth_type == REND_BASIC_AUTH &&
+ smartlist_len(auth_clients) > 512) ||
+ (auth_type == REND_STEALTH_AUTH &&
+ smartlist_len(auth_clients) > 16)) {
+ connection_printf_to_buf(conn, "512 Too many auth clients\r\n");
+ goto out;
+ } else if (non_anonymous != rend_service_non_anonymous_mode_enabled(
+ get_options())) {
+ /* If we failed, and the non-anonymous flag is set, Tor must be in
+ * anonymous hidden service mode.
+ * The error message changes based on the current Tor config:
+ * 512 Tor is in anonymous hidden service mode
+ * 512 Tor is in non-anonymous hidden service mode
+ * (I've deliberately written them out in full here to aid searchability.)
+ */
+ connection_printf_to_buf(conn, "512 Tor is in %sanonymous hidden service "
+ "mode\r\n",
+ non_anonymous ? "" : "non-");
+ goto out;
}
/* Parse the "keytype:keyblob" argument. */
@@ -3891,35 +4452,21 @@ handle_control_add_onion(control_connection_t *conn,
}
tor_assert(!err_msg);
- /* Create the HS, using private key pk, and port config port_cfg.
+ /* Create the HS, using private key pk, client authentication auth_type,
+ * the list of auth_clients, and port config port_cfg.
* rend_service_add_ephemeral() will take ownership of pk and port_cfg,
* regardless of success/failure.
*/
char *service_id = NULL;
int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
max_streams_close_circuit,
+ auth_type, auth_clients,
&service_id);
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
+ auth_clients = NULL; /* so is auth_clients */
switch (ret) {
case RSAE_OKAY:
{
- char *buf = NULL;
- tor_assert(service_id);
- if (key_new_alg) {
- tor_assert(key_new_blob);
- tor_asprintf(&buf,
- "250-ServiceID=%s\r\n"
- "250-PrivateKey=%s:%s\r\n"
- "250 OK\r\n",
- service_id,
- key_new_alg,
- key_new_blob);
- } else {
- tor_asprintf(&buf,
- "250-ServiceID=%s\r\n"
- "250 OK\r\n",
- service_id);
- }
if (detach) {
if (!detached_onion_services)
detached_onion_services = smartlist_new();
@@ -3930,9 +4477,26 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_add(conn->ephemeral_onion_services, service_id);
}
- connection_write_str_to_buf(buf, conn);
- memwipe(buf, 0, strlen(buf));
- tor_free(buf);
+ tor_assert(service_id);
+ connection_printf_to_buf(conn, "250-ServiceID=%s\r\n", service_id);
+ if (key_new_alg) {
+ tor_assert(key_new_blob);
+ connection_printf_to_buf(conn, "250-PrivateKey=%s:%s\r\n",
+ key_new_alg, key_new_blob);
+ }
+ if (auth_created_clients) {
+ SMARTLIST_FOREACH(auth_created_clients, rend_authorized_client_t *, ac, {
+ char *encoded = rend_auth_encode_cookie(ac->descriptor_cookie,
+ auth_type);
+ tor_assert(encoded);
+ connection_printf_to_buf(conn, "250-ClientAuth=%s:%s\r\n",
+ ac->client_name, encoded);
+ memwipe(encoded, 0, strlen(encoded));
+ tor_free(encoded);
+ });
+ }
+
+ connection_printf_to_buf(conn, "250 OK\r\n");
break;
}
case RSAE_BADPRIVKEY:
@@ -3944,6 +4508,9 @@ handle_control_add_onion(control_connection_t *conn,
case RSAE_BADVIRTPORT:
connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n");
break;
+ case RSAE_BADAUTH:
+ connection_printf_to_buf(conn, "512 Invalid client authorization\r\n");
+ break;
case RSAE_INTERNAL: /* FALLSTHROUGH */
default:
connection_printf_to_buf(conn, "551 Failed to add Onion Service\r\n");
@@ -3960,6 +4527,16 @@ handle_control_add_onion(control_connection_t *conn,
smartlist_free(port_cfgs);
}
+ if (auth_clients) {
+ SMARTLIST_FOREACH(auth_clients, rend_authorized_client_t *, ac,
+ rend_authorized_client_free(ac));
+ smartlist_free(auth_clients);
+ }
+ if (auth_created_clients) {
+ // Do not free entries; they are the same as auth_clients
+ smartlist_free(auth_created_clients);
+ }
+
SMARTLIST_FOREACH(args, char *, cp, {
memwipe(cp, 0, strlen(cp));
tor_free(cp);
@@ -4068,6 +4645,65 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
return pk;
}
+/** Helper function to handle parsing a ClientAuth argument to the
+ * ADD_ONION command. Return a new rend_authorized_client_t, or NULL
+ * and an optional control protocol error message on failure. The
+ * caller is responsible for freeing the returned auth_client and err_msg.
+ *
+ * If 'created' is specified, it will be set to 1 when a new cookie has
+ * been generated.
+ */
+STATIC rend_authorized_client_t *
+add_onion_helper_clientauth(const char *arg, int *created, char **err_msg)
+{
+ int ok = 0;
+
+ tor_assert(arg);
+ tor_assert(created);
+ tor_assert(err_msg);
+ *err_msg = NULL;
+
+ smartlist_t *auth_args = smartlist_new();
+ rend_authorized_client_t *client =
+ tor_malloc_zero(sizeof(rend_authorized_client_t));
+ smartlist_split_string(auth_args, arg, ":", 0, 0);
+ if (smartlist_len(auth_args) < 1 || smartlist_len(auth_args) > 2) {
+ *err_msg = tor_strdup("512 Invalid ClientAuth syntax\r\n");
+ goto err;
+ }
+ client->client_name = tor_strdup(smartlist_get(auth_args, 0));
+ if (smartlist_len(auth_args) == 2) {
+ char *decode_err_msg = NULL;
+ if (rend_auth_decode_cookie(smartlist_get(auth_args, 1),
+ client->descriptor_cookie,
+ NULL, &decode_err_msg) < 0) {
+ tor_assert(decode_err_msg);
+ tor_asprintf(err_msg, "512 %s\r\n", decode_err_msg);
+ tor_free(decode_err_msg);
+ goto err;
+ }
+ *created = 0;
+ } else {
+ crypto_rand((char *) client->descriptor_cookie, REND_DESC_COOKIE_LEN);
+ *created = 1;
+ }
+
+ if (!rend_valid_client_name(client->client_name)) {
+ *err_msg = tor_strdup("512 Invalid name in ClientAuth\r\n");
+ goto err;
+ }
+
+ ok = 1;
+ err:
+ SMARTLIST_FOREACH(auth_args, char *, item, tor_free(item));
+ smartlist_free(auth_args);
+ if (!ok) {
+ rend_authorized_client_free(client);
+ client = NULL;
+ }
+ return client;
+}
+
/** Called when we get a DEL_ONION command; parse the body, and remove
* the existing ephemeral Onion Service. */
static int
@@ -4213,19 +4849,14 @@ is_valid_initial_command(control_connection_t *conn, const char *cmd)
* interfaces is broken. */
#define MAX_COMMAND_LINE_LENGTH (1024*1024)
-/** Wrapper around peek_(evbuffer|buf)_has_control0 command: presents the same
- * interface as those underlying functions, but takes a connection_t intead of
- * an evbuffer or a buf_t.
+/** Wrapper around peek_buf_has_control0 command: presents the same
+ * interface as that underlying functions, but takes a connection_t intead of
+ * a buf_t.
*/
static int
peek_connection_has_control0_command(connection_t *conn)
{
- IF_HAS_BUFFEREVENT(conn, {
- struct evbuffer *input = bufferevent_get_input(conn->bufev);
- return peek_evbuffer_has_control0_command(input);
- }) ELSE_IF_NO_BUFFEREVENT {
- return peek_buf_has_control0_command(conn->inbuf);
- }
+ return peek_buf_has_control0_command(conn->inbuf);
}
/** Called when data has arrived on a v1 control connection: Try to fetch
@@ -5504,14 +6135,14 @@ control_event_buildtimeout_set(buildtimeout_set_event_t type,
/** Called when a signal has been processed from signal_callback */
int
-control_event_signal(uintptr_t signal)
+control_event_signal(uintptr_t signal_num)
{
const char *signal_string = NULL;
if (!control_event_is_interesting(EVENT_GOT_SIGNAL))
return 0;
- switch (signal) {
+ switch (signal_num) {
case SIGHUP:
signal_string = "RELOAD";
break;
@@ -5532,7 +6163,7 @@ control_event_signal(uintptr_t signal)
break;
default:
log_warn(LD_BUG, "Unrecognized signal %lu in control_event_signal",
- (unsigned long)signal);
+ (unsigned long)signal_num);
return -1;
}
diff --git a/src/or/control.h b/src/or/control.h
index 008bfb1c3b..6330c85571 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -259,6 +259,33 @@ STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk,
const char **key_new_alg_out,
char **key_new_blob_out,
char **err_msg_out);
+STATIC rend_authorized_client_t *
+add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out);
+
+STATIC void getinfo_helper_downloads_networkstatus(
+ const char *flavor,
+ download_status_t **dl_to_emit,
+ const char **errmsg);
+STATIC void getinfo_helper_downloads_cert(
+ const char *fp_sk_req,
+ download_status_t **dl_to_emit,
+ smartlist_t **digest_list,
+ const char **errmsg);
+STATIC void getinfo_helper_downloads_desc(
+ const char *desc_req,
+ download_status_t **dl_to_emit,
+ smartlist_t **digest_list,
+ const char **errmsg);
+STATIC void getinfo_helper_downloads_bridge(
+ const char *bridge_req,
+ download_status_t **dl_to_emit,
+ smartlist_t **digest_list,
+ const char **errmsg);
+STATIC int getinfo_helper_downloads(
+ control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg);
+
#endif
#endif
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index 3109d5a177..fd6de6ea7c 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -8,7 +8,11 @@
* \brief Uses the workqueue/threadpool code to farm CPU-intensive activities
* out to subprocesses.
*
- * Right now, we only use this for processing onionskins.
+ * The multithreading backend for this module is in workqueue.c; this module
+ * specializes workqueue.c.
+ *
+ * Right now, we only use this for processing onionskins, and invoke it mostly
+ * from onion.c.
**/
#include "or.h"
#include "channel.h"
@@ -23,11 +27,7 @@
#include "router.h"
#include "workqueue.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
static void queue_pending_tasks(void);
@@ -168,6 +168,7 @@ update_state_threadfn(void *state_, void *work_)
server_onion_keys_free(state->onion_keys);
state->onion_keys = update->onion_keys;
update->onion_keys = NULL;
+ worker_state_free(update);
++state->generation;
return WQ_RPL_REPLY;
}
diff --git a/src/or/dircollate.c b/src/or/dircollate.c
index 3f9d78f02d..033a7afe0f 100644
--- a/src/or/dircollate.c
+++ b/src/or/dircollate.c
@@ -8,6 +8,17 @@
*
* \brief Collation code for figuring out which identities to vote for in
* the directory voting process.
+ *
+ * During the consensus calculation, when an authority is looking at the vote
+ * documents from all the authorities, it needs to compute the consensus for
+ * each relay listed by at least one authority. But the notion of "each
+ * relay" can be tricky: some relays have Ed25519 keys, and others don't.
+ *
+ * Moreover, older consensus methods did RSA-based ID collation alone, and
+ * ignored Ed25519 keys. We need to support those too until we're completely
+ * sure that authorities will never downgrade.
+ *
+ * This module is invoked exclusively from dirvote.c.
*/
#define DIRCOLLATE_PRIVATE
@@ -21,6 +32,9 @@ static void dircollator_collate_by_ed25519(dircollator_t *dc);
* RSA SHA1 digest) to an array of vote_routerstatus_t. */
typedef struct ddmap_entry_s {
HT_ENTRY(ddmap_entry_s) node;
+ /** A SHA1-RSA1024 identity digest and Ed25519 identity key,
+ * concatenated. (If there is no ed25519 identity key, there is no
+ * entry in this table.) */
uint8_t d[DIGEST_LEN + DIGEST256_LEN];
/* The nth member of this array corresponds to the vote_routerstatus_t (if
* any) received for this digest pair from the nth voter. */
@@ -43,12 +57,16 @@ ddmap_entry_new(int n_votes)
sizeof(vote_routerstatus_t *) * n_votes);
}
+/** Helper: compute a hash of a single ddmap_entry_t's identity (or
+ * identities) */
static unsigned
ddmap_entry_hash(const ddmap_entry_t *ent)
{
return (unsigned) siphash24g(ent->d, sizeof(ent->d));
}
+/** Helper: return true if <b>a</b> and <b>b</b> have the same
+ * identity/identities. */
static unsigned
ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b)
{
@@ -56,7 +74,7 @@ ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b)
}
/** Record the RSA identity of <b>ent</b> as <b>rsa_sha1</b>, and the
- * ed25519 identity as <b>ed25519</b>. */
+ * ed25519 identity as <b>ed25519</b>. Both must be provided. */
static void
ddmap_entry_set_digests(ddmap_entry_t *ent,
const uint8_t *rsa_sha1,
@@ -67,13 +85,17 @@ ddmap_entry_set_digests(ddmap_entry_t *ent,
}
HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
- ddmap_entry_eq);
+ ddmap_entry_eq)
HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
- ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_);
+ ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_)
/** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator
- * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of
- * its RSA key digest and Ed25519 key. */
+ * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of its RSA
+ * key digest and Ed25519 key. It must come from the <b>vote_num</b>th
+ * vote.
+ *
+ * Requires that the vote is well-formed -- that is, that it has no duplicate
+ * routerstatus entries. We already checked for that when parsing the vote. */
static void
dircollator_add_routerstatus(dircollator_t *dc,
int vote_num,
@@ -82,12 +104,15 @@ dircollator_add_routerstatus(dircollator_t *dc,
{
const char *id = vrs->status.identity_digest;
+ /* Clear this flag; we might set it later during the voting process */
vrs->ed25519_reflects_consensus = 0;
- (void) vote;
+ (void) vote; // We don't currently need this.
+
+ /* First, add this item to the appropriate RSA-SHA-Id array. */
vote_routerstatus_t **vrs_lst = digestmap_get(dc->by_rsa_sha1, id);
if (NULL == vrs_lst) {
- vrs_lst = tor_calloc(sizeof(vote_routerstatus_t *), dc->n_votes);
+ vrs_lst = tor_calloc(dc->n_votes, sizeof(vote_routerstatus_t *));
digestmap_set(dc->by_rsa_sha1, id, vrs_lst);
}
tor_assert(vrs_lst[vote_num] == NULL);
@@ -98,6 +123,7 @@ dircollator_add_routerstatus(dircollator_t *dc,
if (! vrs->has_ed25519_listing)
return;
+ /* Now add it to the appropriate <Ed,RSA-SHA-Id> array. */
ddmap_entry_t search, *found;
memset(&search, 0, sizeof(search));
ddmap_entry_set_digests(&search, (const uint8_t *)id, ed);
diff --git a/src/or/directory.c b/src/or/directory.c
index 89b08223d2..f285e4c6ed 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -30,6 +30,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
+#include "shared_random.h"
#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
#ifndef OPENBSD
@@ -80,7 +81,6 @@ static void dir_routerdesc_download_failed(smartlist_t *failed,
int was_descriptor_digests);
static void dir_microdesc_download_failed(smartlist_t *failed,
int status_code);
-static void note_client_request(int purpose, int compressed, size_t bytes);
static int client_likes_consensus(networkstatus_t *v, const char *want_url);
static void directory_initiate_command_rend(
@@ -123,7 +123,7 @@ static void connection_dir_close_consensus_fetches(
/** Return true iff the directory purpose <b>dir_purpose</b> (and if it's
* fetching descriptors, it's fetching them for <b>router_purpose</b>)
* must use an anonymous connection to a directory. */
-STATIC int
+int
purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose)
{
if (get_options()->AllDirActionsPrivate)
@@ -495,8 +495,6 @@ MOCK_IMPL(void, directory_get_from_dirserver, (
* sort of dir fetch we'll be doing, so it won't return a bridge
* that can't answer our question.
*/
- /* XXX024 Not all bridges handle conditional consensus downloading,
- * so, for now, never assume the server supports that. -PP */
const node_t *node = choose_random_dirguard(type);
if (node && node->ri) {
/* every bridge has a routerinfo. */
@@ -727,6 +725,10 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
node = node_get_by_id(status->identity_digest);
+ /* XXX The below check is wrong: !node means it's not in the consensus,
+ * but we haven't checked if we have a descriptor for it -- and also,
+ * we only care about the descriptor if it's a begindir-style anonymized
+ * connection. */
if (!node && anonymized_connection) {
log_info(LD_DIR, "Not sending anonymized request to directory '%s'; we "
"don't have its router descriptor.",
@@ -744,7 +746,7 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status,
return;
}
- /* At this point, if we are a clients making a direct connection to a
+ /* At this point, if we are a client making a direct connection to a
* directory server, we have selected a server that has at least one address
* allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This
* selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if
@@ -869,7 +871,7 @@ connection_dir_retry_bridges(smartlist_t *descs)
char digest[DIGEST_LEN];
SMARTLIST_FOREACH(descs, const char *, cp,
{
- if (base16_decode(digest, DIGEST_LEN, cp, strlen(cp))<0) {
+ if (base16_decode(digest, DIGEST_LEN, cp, strlen(cp)) != DIGEST_LEN) {
log_warn(LD_BUG, "Malformed fingerprint in list: %s",
escaped(cp));
continue;
@@ -1080,7 +1082,7 @@ directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
* <b>dir_purpose</b> reveals sensitive information about a Tor
* instance's client activities. (Such connections must be performed
* through normal three-hop Tor circuits.) */
-static int
+int
is_sensitive_dir_purpose(uint8_t dir_purpose)
{
return ((dir_purpose == DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2) ||
@@ -1135,12 +1137,10 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose));
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(!(is_sensitive_dir_purpose(dir_purpose) &&
- !anonymized_connection));
-#else
- (void)is_sensitive_dir_purpose;
-#endif
+ if (is_sensitive_dir_purpose(dir_purpose)) {
+ tor_assert(anonymized_connection ||
+ rend_non_anonymous_mode_enabled(options));
+ }
/* use encrypted begindir connections for everything except relays
* this provides better protection for directory fetches */
@@ -1178,7 +1178,7 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
/* set up conn so it's got all the data we need to remember */
tor_addr_copy(&conn->base_.addr, &addr);
conn->base_.port = port;
- conn->base_.address = tor_dup_addr(&addr);
+ conn->base_.address = tor_addr_to_str_dup(&addr);
memcpy(conn->identity_digest, digest, DIGEST_LEN);
conn->base_.purpose = dir_purpose;
@@ -1267,11 +1267,7 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
if_modified_since);
connection_watch_events(TO_CONN(conn), READ_EVENT|WRITE_EVENT);
- IF_HAS_BUFFEREVENT(ENTRY_TO_CONN(linked_conn), {
- connection_watch_events(ENTRY_TO_CONN(linked_conn),
- READ_EVENT|WRITE_EVENT);
- }) ELSE_IF_NO_BUFFEREVENT
- connection_start_reading(ENTRY_TO_CONN(linked_conn));
+ connection_start_reading(ENTRY_TO_CONN(linked_conn));
}
}
@@ -1306,9 +1302,9 @@ compare_strs_(const void **a, const void **b)
/** Return the URL we should use for a consensus download.
*
- * This url depends on whether or not the server we go to
- * is sufficiently new to support conditional consensus downloading,
- * i.e. GET .../consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>
+ * Use the "conditional consensus downloading" feature described in
+ * dir-spec.txt, i.e.
+ * GET .../consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b>
*
* If 'resource' is provided, it is the name of a consensus flavor to request.
*/
@@ -1839,7 +1835,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
char *body;
char *headers;
char *reason = NULL;
- size_t body_len = 0, orig_len = 0;
+ size_t body_len = 0;
int status_code;
time_t date_header = 0;
long apparent_skew;
@@ -1849,7 +1845,6 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
int allow_partial = (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC ||
conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO ||
conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC);
- int was_compressed = 0;
time_t now = time(NULL);
int src_code;
@@ -1868,7 +1863,6 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
return -1;
/* case 1, fall through */
}
- orig_len = body_len;
if (parse_http_response(headers, &status_code, &date_header,
&compression, &reason) < 0) {
@@ -1986,7 +1980,6 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
tor_free(body);
body = new_body;
body_len = new_len;
- was_compressed = 1;
}
}
@@ -2006,7 +1999,8 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
}
log_info(LD_DIR,"Received consensus directory (size %d) from server "
"'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port);
- if ((r=networkstatus_set_current_consensus(body, flavname, 0))<0) {
+ if ((r=networkstatus_set_current_consensus(body, flavname, 0,
+ conn->identity_digest))<0) {
log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR,
"Unable to load %s consensus directory downloaded from "
"server '%s:%d'. I'll try again soon.",
@@ -2024,6 +2018,10 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
update_microdescs_from_networkstatus(now);
update_microdesc_downloads(now);
directory_info_has_arrived(now, 0, 0);
+ if (authdir_mode_v3(get_options())) {
+ sr_act_post_consensus(
+ networkstatus_get_latest_consensus_by_flavor(FLAV_NS));
+ }
log_info(LD_DIR, "Successfully loaded consensus.");
}
@@ -2053,7 +2051,8 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
}
if (src_code != -1) {
- if (trusted_dirs_load_certs_from_string(body, src_code, 1)<0) {
+ if (trusted_dirs_load_certs_from_string(body, src_code, 1,
+ conn->identity_digest)<0) {
log_warn(LD_DIR, "Unable to parse fetched certificates");
/* if we fetched more than one and only some failed, the successful
* ones got flushed to disk so it's safe to call this on them */
@@ -2249,7 +2248,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
ds->nickname);
/* XXXX use this information; be sure to upload next one
* sooner. -NM */
- /* XXXX023 On further thought, the task above implies that we're
+ /* XXXX++ On further thought, the task above implies that we're
* basing our regenerate-descriptor time on when we uploaded the
* last descriptor, not on the published time of the last
* descriptor. If those are different, that's a bad thing to
@@ -2450,7 +2449,6 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
break;
}
}
- note_client_request(conn->base_.purpose, was_compressed, orig_len);
tor_free(body); tor_free(headers); tor_free(reason);
return 0;
}
@@ -2651,129 +2649,6 @@ write_http_response_header(dir_connection_t *conn, ssize_t length,
cache_lifetime);
}
-#if defined(INSTRUMENT_DOWNLOADS) || defined(RUNNING_DOXYGEN)
-/* DOCDOC */
-typedef struct request_t {
- uint64_t bytes; /**< How many bytes have we transferred? */
- uint64_t count; /**< How many requests have we made? */
-} request_t;
-
-/** Map used to keep track of how much data we've up/downloaded in what kind
- * of request. Maps from request type to pointer to request_t. */
-static strmap_t *request_map = NULL;
-
-/** Record that a client request of <b>purpose</b> was made, and that
- * <b>bytes</b> bytes of possibly <b>compressed</b> data were sent/received.
- * Used to keep track of how much we've up/downloaded in what kind of
- * request. */
-static void
-note_client_request(int purpose, int compressed, size_t bytes)
-{
- char *key;
- const char *kind = NULL;
- switch (purpose) {
- case DIR_PURPOSE_FETCH_CONSENSUS: kind = "dl/consensus"; break;
- case DIR_PURPOSE_FETCH_CERTIFICATE: kind = "dl/cert"; break;
- case DIR_PURPOSE_FETCH_STATUS_VOTE: kind = "dl/vote"; break;
- case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: kind = "dl/detached_sig";
- break;
- case DIR_PURPOSE_FETCH_SERVERDESC: kind = "dl/server"; break;
- case DIR_PURPOSE_FETCH_EXTRAINFO: kind = "dl/extra"; break;
- case DIR_PURPOSE_UPLOAD_DIR: kind = "dl/ul-dir"; break;
- case DIR_PURPOSE_UPLOAD_VOTE: kind = "dl/ul-vote"; break;
- case DIR_PURPOSE_UPLOAD_SIGNATURES: kind = "dl/ul-sig"; break;
- case DIR_PURPOSE_FETCH_RENDDESC_V2: kind = "dl/rend2"; break;
- case DIR_PURPOSE_UPLOAD_RENDDESC_V2: kind = "dl/ul-rend2"; break;
- }
- if (kind) {
- tor_asprintf(&key, "%s%s", kind, compressed?".z":"");
- } else {
- tor_asprintf(&key, "unknown purpose (%d)%s",
- purpose, compressed?".z":"");
- }
- note_request(key, bytes);
- tor_free(key);
-}
-
-/** Helper: initialize the request map to instrument downloads. */
-static void
-ensure_request_map_initialized(void)
-{
- if (!request_map)
- request_map = strmap_new();
-}
-
-/** Called when we just transmitted or received <b>bytes</b> worth of data
- * because of a request of type <b>key</b> (an arbitrary identifier): adds
- * <b>bytes</b> to the total associated with key. */
-void
-note_request(const char *key, size_t bytes)
-{
- request_t *r;
- ensure_request_map_initialized();
-
- r = strmap_get(request_map, key);
- if (!r) {
- r = tor_malloc_zero(sizeof(request_t));
- strmap_set(request_map, key, r);
- }
- r->bytes += bytes;
- r->count++;
-}
-
-/** Return a newly allocated string holding a summary of bytes used per
- * request type. */
-char *
-directory_dump_request_log(void)
-{
- smartlist_t *lines;
- char *result;
- strmap_iter_t *iter;
-
- ensure_request_map_initialized();
-
- lines = smartlist_new();
-
- for (iter = strmap_iter_init(request_map);
- !strmap_iter_done(iter);
- iter = strmap_iter_next(request_map, iter)) {
- const char *key;
- void *val;
- request_t *r;
- strmap_iter_get(iter, &key, &val);
- r = val;
- smartlist_add_asprintf(lines, "%s "U64_FORMAT" "U64_FORMAT"\n",
- key, U64_PRINTF_ARG(r->bytes), U64_PRINTF_ARG(r->count));
- }
- smartlist_sort_strings(lines);
- result = smartlist_join_strings(lines, "", 0, NULL);
- SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
- smartlist_free(lines);
- return result;
-}
-#else
-static void
-note_client_request(int purpose, int compressed, size_t bytes)
-{
- (void)purpose;
- (void)compressed;
- (void)bytes;
-}
-
-void
-note_request(const char *key, size_t bytes)
-{
- (void)key;
- (void)bytes;
-}
-
-char *
-directory_dump_request_log(void)
-{
- return tor_strdup("Not supported.");
-}
-#endif
-
/** Decide whether a client would accept the consensus we have.
*
* Clients can say they only want a consensus if it's signed by more
@@ -2803,7 +2678,8 @@ client_likes_consensus(networkstatus_t *v, const char *want_url)
if (want_len > DIGEST_LEN)
want_len = DIGEST_LEN;
- if (base16_decode(want_digest, DIGEST_LEN, d, want_len*2) < 0) {
+ if (base16_decode(want_digest, DIGEST_LEN, d, want_len*2)
+ != (int) want_len) {
log_fn(LOG_PROTOCOL_WARN, LD_DIR,
"Failed to decode requested authority digest %s.", escaped(d));
continue;
@@ -2845,18 +2721,81 @@ choose_compression_level(ssize_t n_bytes)
}
}
+/** Information passed to handle a GET request. */
+typedef struct get_handler_args_t {
+ /** True if the client asked for compressed data. */
+ int compressed;
+ /** If nonzero, the time included an if-modified-since header with this
+ * value. */
+ time_t if_modified_since;
+ /** String containing the requested URL or resource. */
+ const char *url;
+ /** String containing the HTTP headers */
+ const char *headers;
+} get_handler_args_t;
+
+/** Entry for handling an HTTP GET request.
+ *
+ * This entry matches a request if "string" is equal to the requested
+ * resource, or if "is_prefix" is true and "string" is a prefix of the
+ * requested resource.
+ *
+ * The 'handler' function is called to handle the request. It receives
+ * an arguments structure, and must return 0 on success or -1 if we should
+ * close the connection.
+ **/
+typedef struct url_table_ent_s {
+ const char *string;
+ int is_prefix;
+ int (*handler)(dir_connection_t *conn, const get_handler_args_t *args);
+} url_table_ent_t;
+
+static int handle_get_frontpage(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_current_consensus(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_status_vote(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_microdesc(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_descriptor(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_keys(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_rendezvous2(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_robots(dir_connection_t *conn,
+ const get_handler_args_t *args);
+static int handle_get_networkstatus_bridges(dir_connection_t *conn,
+ const get_handler_args_t *args);
+
+/** Table for handling GET requests. */
+static const url_table_ent_t url_table[] = {
+ { "/tor/", 0, handle_get_frontpage },
+ { "/tor/status-vote/current/consensus", 1, handle_get_current_consensus },
+ { "/tor/status-vote/current/", 1, handle_get_status_vote },
+ { "/tor/status-vote/next/", 1, handle_get_status_vote },
+ { "/tor/micro/d/", 1, handle_get_microdesc },
+ { "/tor/server/", 1, handle_get_descriptor },
+ { "/tor/extra/", 1, handle_get_descriptor },
+ { "/tor/keys/", 1, handle_get_keys },
+ { "/tor/rendezvous2/", 1, handle_get_rendezvous2 },
+ { "/tor/robots.txt", 0, handle_get_robots },
+ { "/tor/networkstatus-bridges", 0, handle_get_networkstatus_bridges },
+ { NULL, 0, NULL },
+};
+
/** Helper function: called when a dirserver gets a complete HTTP GET
* request. Look for a request for a directory or for a rendezvous
* service descriptor. On finding one, write a response into
- * conn-\>outbuf. If the request is unrecognized, send a 400.
- * Always return 0. */
+ * conn-\>outbuf. If the request is unrecognized, send a 404.
+ * Return 0 if we handled this successfully, or -1 if we need to close
+ * the connection. */
STATIC int
directory_handle_command_get(dir_connection_t *conn, const char *headers,
const char *req_body, size_t req_body_len)
{
- size_t dlen;
char *url, *url_mem, *header;
- const or_options_t *options = get_options();
time_t if_modified_since = 0;
int compressed;
size_t url_len;
@@ -2896,29 +2835,73 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
url_len -= 2;
}
- if (!strcmp(url,"/tor/")) {
- const char *frontpage = get_dirportfrontpage();
-
- if (frontpage) {
- dlen = strlen(frontpage);
- /* Let's return a disclaimer page (users shouldn't use V1 anymore,
- and caches don't fetch '/', so this is safe). */
-
- /* [We don't check for write_bucket_low here, since we want to serve
- * this page no matter what.] */
- note_request(url, dlen);
- write_http_response_header_impl(conn, dlen, "text/html", "identity",
- NULL, DIRPORTFRONTPAGE_CACHE_LIFETIME);
- connection_write_to_buf(frontpage, dlen, TO_CONN(conn));
+ get_handler_args_t args;
+ args.url = url;
+ args.headers = headers;
+ args.if_modified_since = if_modified_since;
+ args.compressed = compressed;
+
+ int i, result = -1;
+ for (i = 0; url_table[i].string; ++i) {
+ int match;
+ if (url_table[i].is_prefix) {
+ match = !strcmpstart(url, url_table[i].string);
+ } else {
+ match = !strcmp(url, url_table[i].string);
+ }
+ if (match) {
+ result = url_table[i].handler(conn, &args);
goto done;
}
- /* if no disclaimer file, fall through and continue */
}
- if (!strcmpstart(url, "/tor/status-vote/current/consensus")) {
+ /* we didn't recognize the url */
+ write_http_status_line(conn, 404, "Not found");
+ result = 0;
+
+ done:
+ tor_free(url_mem);
+ return result;
+}
+
+/** Helper function for GET / or GET /tor/
+ */
+static int
+handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
+{
+ (void) args; /* unused */
+ const char *frontpage = get_dirportfrontpage();
+
+ if (frontpage) {
+ size_t dlen;
+ dlen = strlen(frontpage);
+ /* Let's return a disclaimer page (users shouldn't use V1 anymore,
+ and caches don't fetch '/', so this is safe). */
+
+ /* [We don't check for write_bucket_low here, since we want to serve
+ * 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));
+ } else {
+ write_http_status_line(conn, 404, "Not found");
+ }
+ return 0;
+}
+
+/** Helper function for GET /tor/status-vote/current/consensus
+ */
+static int
+handle_get_current_consensus(dir_connection_t *conn,
+ const get_handler_args_t *args)
+{
+ const char *url = args->url;
+ const int compressed = args->compressed;
+ const time_t if_modified_since = args->if_modified_since;
+
+ {
/* v3 network status fetch. */
smartlist_t *dir_fps = smartlist_new();
- const char *request_type = NULL;
long lifetime = NETWORKSTATUS_CACHE_LIFETIME;
if (1) {
@@ -2967,7 +2950,6 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
tor_free(flavor);
smartlist_add(dir_fps, fp);
}
- request_type = compressed?"v3.z":"v3";
lifetime = (v && v->fresh_until > now) ? v->fresh_until - now : 0;
}
@@ -2992,7 +2974,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
goto done;
}
- dlen = dirserv_estimate_data_size(dir_fps, 0, compressed);
+ size_t dlen = dirserv_estimate_data_size(dir_fps, 0, compressed);
if (global_write_bucket_low(TO_CONN(conn), dlen, 2)) {
log_debug(LD_DIRSERV,
"Client asked for network status lists, but we've been "
@@ -3022,8 +3004,6 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
}
}
- // note_request(request_type,dlen);
- (void) request_type;
write_http_response_header(conn, -1, compressed,
smartlist_len(dir_fps) == 1 ? lifetime : 0);
conn->fingerprint_stack = dir_fps;
@@ -3036,17 +3016,24 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
goto done;
}
- if (!strcmpstart(url,"/tor/status-vote/current/") ||
- !strcmpstart(url,"/tor/status-vote/next/")) {
- /* XXXX If-modified-since is only implemented for the current
- * consensus: that's probably fine, since it's the only vote document
- * people fetch much. */
+ done:
+ return 0;
+}
+
+/** Helper function for GET /tor/status-vote/{current,next}/...
+ */
+static int
+handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
+{
+ const char *url = args->url;
+ const int compressed = args->compressed;
+ {
int current;
ssize_t body_len = 0;
ssize_t estimated_len = 0;
smartlist_t *items = smartlist_new();
smartlist_t *dir_items = smartlist_new();
- int lifetime = 60; /* XXXX023 should actually use vote intervals. */
+ int lifetime = 60; /* XXXX?? should actually use vote intervals. */
url += strlen("/tor/status-vote/");
current = !strcmpstart(url, "current/");
url = strchr(url, '/');
@@ -3136,8 +3123,18 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
smartlist_free(dir_items);
goto done;
}
+ done:
+ return 0;
+}
- if (!strcmpstart(url, "/tor/micro/d/")) {
+/** Helper function for GET /tor/micro/d/...
+ */
+static int
+handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
+{
+ const char *url = args->url;
+ const int compressed = args->compressed;
+ {
smartlist_t *fps = smartlist_new();
dir_split_resource_into_fingerprints(url+strlen("/tor/micro/d/"),
@@ -3150,7 +3147,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
smartlist_free(fps);
goto done;
}
- dlen = dirserv_estimate_microdesc_size(fps, compressed);
+ size_t dlen = dirserv_estimate_microdesc_size(fps, compressed);
if (global_write_bucket_low(TO_CONN(conn), dlen, 2)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
@@ -3173,12 +3170,24 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
goto done;
}
+ done:
+ return 0;
+}
+
+/** Helper function for GET /tor/{server,extra}/...
+ */
+static int
+handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
+{
+ const char *url = args->url;
+ const int compressed = args->compressed;
+ const or_options_t *options = get_options();
if (!strcmpstart(url,"/tor/server/") ||
(!options->BridgeAuthoritativeDir &&
!options->BridgeRelay && !strcmpstart(url,"/tor/extra/"))) {
+ size_t dlen;
int res;
const char *msg;
- const char *request_type = NULL;
int cache_lifetime = 0;
int is_extra = !strcmpstart(url,"/tor/extra/");
url += is_extra ? strlen("/tor/extra/") : strlen("/tor/server/");
@@ -3189,24 +3198,16 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
is_extra);
if (!strcmpstart(url, "fp/")) {
- request_type = compressed?"/tor/server/fp.z":"/tor/server/fp";
if (smartlist_len(conn->fingerprint_stack) == 1)
cache_lifetime = ROUTERDESC_CACHE_LIFETIME;
} else if (!strcmpstart(url, "authority")) {
- request_type = compressed?"/tor/server/authority.z":
- "/tor/server/authority";
cache_lifetime = ROUTERDESC_CACHE_LIFETIME;
} else if (!strcmpstart(url, "all")) {
- request_type = compressed?"/tor/server/all.z":"/tor/server/all";
cache_lifetime = FULL_DIR_CACHE_LIFETIME;
} else if (!strcmpstart(url, "d/")) {
- request_type = compressed?"/tor/server/d.z":"/tor/server/d";
if (smartlist_len(conn->fingerprint_stack) == 1)
cache_lifetime = ROUTERDESC_BY_DIGEST_CACHE_LIFETIME;
- } else {
- request_type = "/tor/server/?";
}
- (void) request_type; /* usable for note_request. */
if (!strcmpstart(url, "d/"))
conn->dir_spool_src =
is_extra ? DIR_SPOOL_EXTRA_BY_DIGEST : DIR_SPOOL_SERVER_BY_DIGEST;
@@ -3242,8 +3243,19 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
}
goto done;
}
+ done:
+ return 0;
+}
- if (!strcmpstart(url,"/tor/keys/")) {
+/** Helper function for GET /tor/keys/...
+ */
+static int
+handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
+{
+ const char *url = args->url;
+ const int compressed = args->compressed;
+ const time_t if_modified_since = args->if_modified_since;
+ {
smartlist_t *certs = smartlist_new();
ssize_t len = -1;
if (!strcmp(url, "/tor/keys/all")) {
@@ -3328,9 +3340,17 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
smartlist_free(certs);
goto done;
}
+ done:
+ return 0;
+}
- if (connection_dir_is_encrypted(conn) &&
- !strcmpstart(url,"/tor/rendezvous2/")) {
+/** Helper function for GET /tor/rendezvous2/
+ */
+static int
+handle_get_rendezvous2(dir_connection_t *conn, const get_handler_args_t *args)
+{
+ const char *url = args->url;
+ if (connection_dir_is_encrypted(conn)) {
/* Handle v2 rendezvous descriptor fetch request. */
const char *descp;
const char *query = url + strlen("/tor/rendezvous2/");
@@ -3353,16 +3373,30 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
write_http_status_line(conn, 400, "Bad request");
}
goto done;
+ } else {
+ /* Not encrypted! */
+ write_http_status_line(conn, 404, "Not found");
}
+ done:
+ return 0;
+}
+/** Helper function for GET /tor/networkstatus-bridges
+ */
+static int
+handle_get_networkstatus_bridges(dir_connection_t *conn,
+ const get_handler_args_t *args)
+{
+ const char *headers = args->headers;
+
+ const or_options_t *options = get_options();
if (options->BridgeAuthoritativeDir &&
options->BridgePassword_AuthDigest_ &&
- connection_dir_is_encrypted(conn) &&
- !strcmp(url,"/tor/networkstatus-bridges")) {
+ connection_dir_is_encrypted(conn)) {
char *status;
char digest[DIGEST256_LEN];
- header = http_get_header(headers, "Authorization: Basic ");
+ char *header = http_get_header(headers, "Authorization: Basic ");
if (header)
crypto_digest256(digest, header, strlen(header), DIGEST_SHA256);
@@ -3378,75 +3412,27 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
/* all happy now. send an answer. */
status = networkstatus_getinfo_by_purpose("bridge", time(NULL));
- dlen = strlen(status);
+ size_t dlen = strlen(status);
write_http_response_header(conn, dlen, 0, 0);
connection_write_to_buf(status, dlen, TO_CONN(conn));
tor_free(status);
goto done;
}
+ done:
+ return 0;
+}
- if (!strcmpstart(url,"/tor/bytes.txt")) {
- char *bytes = directory_dump_request_log();
- size_t len = strlen(bytes);
- write_http_response_header(conn, len, 0, 0);
- connection_write_to_buf(bytes, len, TO_CONN(conn));
- tor_free(bytes);
- goto done;
- }
-
- if (!strcmp(url,"/tor/robots.txt")) { /* /robots.txt will have been
- rewritten to /tor/robots.txt */
- char robots[] = "User-agent: *\r\nDisallow: /\r\n";
+/** Helper function for GET robots.txt or /tor/robots.txt */
+static int
+handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args)
+{
+ (void)args;
+ {
+ const char robots[] = "User-agent: *\r\nDisallow: /\r\n";
size_t len = strlen(robots);
write_http_response_header(conn, len, 0, ROBOTS_CACHE_LIFETIME);
connection_write_to_buf(robots, len, TO_CONN(conn));
- goto done;
- }
-
-#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
-#define ADD_MALLINFO_LINE(x) do { \
- smartlist_add_asprintf(lines, "%s %d\n", #x, mi.x); \
- }while(0);
-
- if (!strcmp(url,"/tor/mallinfo.txt") &&
- (tor_addr_eq_ipv4h(&conn->base_.addr, 0x7f000001ul))) {
- char *result;
- size_t len;
- struct mallinfo mi;
- smartlist_t *lines;
-
- memset(&mi, 0, sizeof(mi));
- mi = mallinfo();
- lines = smartlist_new();
-
- ADD_MALLINFO_LINE(arena)
- ADD_MALLINFO_LINE(ordblks)
- ADD_MALLINFO_LINE(smblks)
- ADD_MALLINFO_LINE(hblks)
- ADD_MALLINFO_LINE(hblkhd)
- ADD_MALLINFO_LINE(usmblks)
- ADD_MALLINFO_LINE(fsmblks)
- ADD_MALLINFO_LINE(uordblks)
- ADD_MALLINFO_LINE(fordblks)
- ADD_MALLINFO_LINE(keepcost)
-
- result = smartlist_join_strings(lines, "", 0, NULL);
- SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp));
- smartlist_free(lines);
-
- len = strlen(result);
- write_http_response_header(conn, len, 0, 0);
- connection_write_to_buf(result, len, TO_CONN(conn));
- tor_free(result);
- goto done;
}
-#endif
-
- /* we didn't recognize the url */
- write_http_status_line(conn, 404, "Not found");
-
- done:
- tor_free(url_mem);
return 0;
}
@@ -3646,16 +3632,8 @@ connection_dir_finished_flushing(dir_connection_t *conn)
return 0;
case DIR_CONN_STATE_SERVER_WRITING:
if (conn->dir_spool_src != DIR_SPOOL_NONE) {
-#ifdef USE_BUFFEREVENTS
- /* This can happen with paired bufferevents, since a paired connection
- * can flush immediately when you write to it, making the subsequent
- * check in connection_handle_write_cb() decide that the connection
- * is flushed. */
- log_debug(LD_DIRSERV, "Emptied a dirserv buffer, but still spooling.");
-#else
log_warn(LD_BUG, "Emptied a dirserv buffer, but it's still spooling!");
connection_mark_for_close(TO_CONN(conn));
-#endif
} else {
log_debug(LD_DIRSERV, "Finished writing server response. Closing.");
connection_mark_for_close(TO_CONN(conn));
@@ -3725,26 +3703,24 @@ connection_dir_finished_connecting(dir_connection_t *conn)
STATIC const smartlist_t *
find_dl_schedule(download_status_t *dls, const or_options_t *options)
{
- const int dir_server = dir_server_mode(options);
- const int multi_d = networkstatus_consensus_can_use_multiple_directories(
- options);
- const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
- time(NULL));
- const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(
- options);
switch (dls->schedule) {
case DL_SCHED_GENERIC:
- if (dir_server) {
+ /* Any other directory document */
+ if (dir_server_mode(options)) {
+ /* A directory authority or directory mirror */
return options->TestingServerDownloadSchedule;
} else {
return options->TestingClientDownloadSchedule;
}
case DL_SCHED_CONSENSUS:
- if (!multi_d) {
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
+ /* A public relay */
return options->TestingServerConsensusDownloadSchedule;
} else {
- if (we_are_bootstrapping) {
- if (!use_fallbacks) {
+ /* A client or bridge */
+ if (networkstatus_consensus_is_bootstrapping(time(NULL))) {
+ /* During bootstrapping */
+ if (!networkstatus_consensus_can_use_extra_fallbacks(options)) {
/* A bootstrapping client without extra fallback directories */
return
options->ClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
@@ -3760,6 +3736,8 @@ find_dl_schedule(download_status_t *dls, const or_options_t *options)
options->ClientBootstrapConsensusFallbackDownloadSchedule;
}
} else {
+ /* A client with a reasonably live consensus, with or without
+ * certificates */
return options->TestingClientConsensusDownloadSchedule;
}
}
@@ -3773,17 +3751,100 @@ find_dl_schedule(download_status_t *dls, const or_options_t *options)
return NULL;
}
-/* Find the current delay for dls based on schedule.
- * Set dls->next_attempt_at based on now, and return the delay.
+/** Decide which minimum and maximum delay step we want to use based on
+ * descriptor type in <b>dls</b> and <b>options</b>.
+ * Helper function for download_status_schedule_get_delay(). */
+STATIC void
+find_dl_min_and_max_delay(download_status_t *dls, const or_options_t *options,
+ int *min, int *max)
+{
+ tor_assert(dls);
+ tor_assert(options);
+ tor_assert(min);
+ tor_assert(max);
+
+ /*
+ * For now, just use the existing schedule config stuff and pick the
+ * first/last entries off to get min/max delay for backoff purposes
+ */
+ const smartlist_t *schedule = find_dl_schedule(dls, options);
+ tor_assert(schedule != NULL && smartlist_len(schedule) >= 2);
+ *min = *((int *)(smartlist_get(schedule, 0)));
+ 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.
+ *
+ * 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)
+{
+ /* 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 (delay && delay < (INT_MAX-1) / multiplier) {
+ max_increment = delay * multiplier;
+ } else if (delay) {
+ max_increment = INT_MAX-1;
+ } else {
+ max_increment = 1;
+ }
+
+ if (BUG(max_increment < 1))
+ max_increment = 1;
+
+ /* the + 1 here is so that we always wait longer than last time. */
+ int increment = crypto_rand_int(max_increment)+1;
+
+ if (increment < max_delay - delay)
+ return delay + increment;
+ else
+ return max_delay;
+}
+
+/** Find the current delay for dls based on schedule or min_delay/
+ * max_delay if we're using exponential backoff. If dls->backoff is
+ * DL_SCHED_RANDOM_EXPONENTIAL, we must have 0 <= min_delay <= max_delay <=
+ * INT_MAX, but schedule may be set to NULL; otherwise schedule is required.
+ * This function sets dls->next_attempt_at based on now, and returns the delay.
* Helper for download_status_increment_failure and
* download_status_increment_attempt. */
STATIC int
download_status_schedule_get_delay(download_status_t *dls,
const smartlist_t *schedule,
+ int min_delay, int max_delay,
time_t now)
{
tor_assert(dls);
- tor_assert(schedule);
+ /* We don't need a schedule if we're using random exponential backoff */
+ tor_assert(dls->backoff == DL_SCHED_RANDOM_EXPONENTIAL ||
+ schedule != NULL);
+ /* If we're using random exponential backoff, we do need min/max delay */
+ tor_assert(dls->backoff != DL_SCHED_RANDOM_EXPONENTIAL ||
+ (min_delay >= 0 && max_delay >= min_delay));
int delay = INT_MAX;
uint8_t dls_schedule_position = (dls->increment_on
@@ -3791,20 +3852,50 @@ download_status_schedule_get_delay(download_status_t *dls,
? dls->n_download_attempts
: dls->n_download_failures);
- if (dls_schedule_position < smartlist_len(schedule))
- delay = *(int *)smartlist_get(schedule, dls_schedule_position);
- else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD)
- delay = INT_MAX;
- else
- delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
+ if (dls->backoff == DL_SCHED_DETERMINISTIC) {
+ if (dls_schedule_position < smartlist_len(schedule))
+ delay = *(int *)smartlist_get(schedule, dls_schedule_position);
+ else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD)
+ delay = INT_MAX;
+ else
+ delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
+ } else if (dls->backoff == DL_SCHED_RANDOM_EXPONENTIAL) {
+ /* Check if we missed a reset somehow */
+ if (dls->last_backoff_position > dls_schedule_position) {
+ dls->last_backoff_position = 0;
+ dls->last_delay_used = 0;
+ }
+
+ if (dls_schedule_position > 0) {
+ delay = dls->last_delay_used;
+
+ while (dls->last_backoff_position < dls_schedule_position) {
+ /* Do one increment step */
+ delay = next_random_exponential_delay(delay, max_delay);
+ /* Update our position */
+ ++(dls->last_backoff_position);
+ }
+ } else {
+ /* If we're just starting out, use the minimum delay */
+ delay = min_delay;
+ }
+
+ /* Clamp it within min/max if we have them */
+ if (min_delay >= 0 && delay < min_delay) delay = min_delay;
+ if (max_delay != INT_MAX && delay > max_delay) delay = max_delay;
+
+ /* Store it for next time */
+ dls->last_backoff_position = dls_schedule_position;
+ dls->last_delay_used = delay;
+ }
/* A negative delay makes no sense. Knowing that delay is
* non-negative allows us to safely do the wrapping check below. */
tor_assert(delay >= 0);
- /* Avoid now+delay overflowing INT_MAX, by comparing with a subtraction
+ /* Avoid now+delay overflowing TIME_MAX, by comparing with a subtraction
* that won't overflow (since delay is non-negative). */
- if (delay < INT_MAX && now <= INT_MAX - delay) {
+ if (delay < INT_MAX && now <= TIME_MAX - delay) {
dls->next_attempt_at = now+delay;
} else {
dls->next_attempt_at = TIME_MAX;
@@ -3856,13 +3947,16 @@ time_t
download_status_increment_failure(download_status_t *dls, int status_code,
const char *item, int server, time_t now)
{
+ (void) status_code; // XXXX no longer used.
+ (void) server; // XXXX no longer used.
int increment = -1;
+ int min_delay = 0, max_delay = INT_MAX;
+
tor_assert(dls);
- /* only count the failure if it's permanent, or we're a server */
- if (status_code != 503 || server) {
- if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1)
- ++dls->n_download_failures;
+ /* count the failure */
+ if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1) {
+ ++dls->n_download_failures;
}
if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
@@ -3877,7 +3971,9 @@ download_status_increment_failure(download_status_t *dls, int status_code,
/* only return a failure retry time if this schedule increments on failures
*/
const smartlist_t *schedule = find_dl_schedule(dls, get_options());
- increment = download_status_schedule_get_delay(dls, schedule, now);
+ find_dl_min_and_max_delay(dls, get_options(), &min_delay, &max_delay);
+ increment = download_status_schedule_get_delay(dls, schedule,
+ min_delay, max_delay, now);
}
download_status_log_helper(item, !dls->increment_on, "failed",
@@ -3906,12 +4002,14 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
time_t now)
{
int delay = -1;
+ int min_delay = 0, max_delay = INT_MAX;
+
tor_assert(dls);
if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
/* this schedule should retry on failure, and not launch any concurrent
attempts */
- log_info(LD_BUG, "Tried to launch an attempt-based connection on a "
+ log_warn(LD_BUG, "Tried to launch an attempt-based connection on a "
"failure-based schedule.");
return TIME_MAX;
}
@@ -3920,7 +4018,9 @@ download_status_increment_attempt(download_status_t *dls, const char *item,
++dls->n_download_attempts;
const smartlist_t *schedule = find_dl_schedule(dls, get_options());
- delay = download_status_schedule_get_delay(dls, schedule, now);
+ find_dl_min_and_max_delay(dls, get_options(), &min_delay, &max_delay);
+ delay = download_status_schedule_get_delay(dls, schedule,
+ min_delay, max_delay, now);
download_status_log_helper(item, dls->increment_on, "attempted",
"on failure", dls->n_download_attempts,
@@ -3952,6 +4052,8 @@ download_status_reset(download_status_t *dls)
dls->n_download_failures = 0;
dls->n_download_attempts = 0;
dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0);
+ dls->last_backoff_position = 0;
+ dls->last_delay_used = 0;
/* Don't reset dls->want_authority or dls->increment_on */
}
@@ -4001,7 +4103,7 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code,
}
SMARTLIST_FOREACH_BEGIN(failed, const char *, cp) {
download_status_t *dls = NULL;
- if (base16_decode(digest, DIGEST_LEN, cp, strlen(cp)) < 0) {
+ if (base16_decode(digest, DIGEST_LEN, cp, strlen(cp)) != DIGEST_LEN) {
log_warn(LD_BUG, "Malformed fingerprint in list: %s", escaped(cp));
continue;
}
@@ -4098,9 +4200,10 @@ dir_split_resource_into_fingerprint_pairs(const char *res,
"Skipping digest pair %s with missing dash.", escaped(cp));
} else {
fp_pair_t pair;
- if (base16_decode(pair.first, DIGEST_LEN, cp, HEX_DIGEST_LEN)<0 ||
- base16_decode(pair.second,
- DIGEST_LEN, cp+HEX_DIGEST_LEN+1, HEX_DIGEST_LEN)<0) {
+ if (base16_decode(pair.first, DIGEST_LEN,
+ cp, HEX_DIGEST_LEN) != DIGEST_LEN ||
+ base16_decode(pair.second,DIGEST_LEN,
+ cp+HEX_DIGEST_LEN+1, HEX_DIGEST_LEN) != DIGEST_LEN) {
log_info(LD_DIR, "Skipping non-decodable digest pair %s", escaped(cp));
} else {
smartlist_add(pairs_result, tor_memdup(&pair, sizeof(pair)));
@@ -4178,8 +4281,9 @@ dir_split_resource_into_fingerprints(const char *resource,
}
d = tor_malloc_zero(digest_len);
if (decode_hex ?
- (base16_decode(d, digest_len, cp, hex_digest_len)<0) :
- (base64_decode(d, digest_len, cp, base64_digest_len)<0)) {
+ (base16_decode(d, digest_len, cp, hex_digest_len) != digest_len) :
+ (base64_decode(d, digest_len, cp, base64_digest_len)
+ != digest_len)) {
log_info(LD_DIR, "Skipping non-decodable digest %s", escaped(cp));
smartlist_del_keeporder(fp_tmp, i--);
goto again;
diff --git a/src/or/directory.h b/src/or/directory.h
index 7646cac03f..629b3ead90 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -114,9 +114,15 @@ static inline int
download_status_is_ready(download_status_t *dls, time_t now,
int max_failures)
{
- int under_failure_limit = (dls->n_download_failures <= max_failures
- && dls->n_download_attempts <= max_failures);
- return (under_failure_limit && dls->next_attempt_at <= now);
+ if (dls->backoff == DL_SCHED_DETERMINISTIC) {
+ /* Deterministic schedules can hit an endpoint; exponential backoff
+ * schedules just wait longer and longer. */
+ int under_failure_limit = (dls->n_download_failures <= max_failures
+ && dls->n_download_attempts <= max_failures);
+ if (!under_failure_limit)
+ return 0;
+ }
+ return dls->next_attempt_at <= now;
}
static void download_status_mark_impossible(download_status_t *dl);
@@ -132,12 +138,15 @@ 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);
+/* Yes, these two functions are confusingly similar.
+ * Let's sort that out in #20077. */
+int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose);
+int is_sensitive_dir_purpose(uint8_t dir_purpose);
+
#ifdef TOR_UNIT_TESTS
/* Used only by directory.c and test_dir.c */
STATIC int parse_http_url(const char *headers, char **url);
-STATIC int purpose_needs_anonymity(uint8_t dir_purpose,
- uint8_t router_purpose);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
const char *resource);
STATIC int directory_handle_command_get(dir_connection_t *conn,
@@ -146,6 +155,7 @@ STATIC int directory_handle_command_get(dir_connection_t *conn,
size_t req_body_len);
STATIC int download_status_schedule_get_delay(download_status_t *dls,
const smartlist_t *schedule,
+ int min_delay, int max_delay,
time_t now);
STATIC char* authdir_type_to_string(dirinfo_type_t auth);
@@ -154,6 +164,11 @@ STATIC int should_use_directory_guards(const or_options_t *options);
STATIC zlib_compression_level_t choose_compression_level(ssize_t n_bytes);
STATIC const smartlist_t *find_dl_schedule(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);
+
#endif
#endif
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index dafaed8bf2..da34c196f4 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -19,10 +19,12 @@
#include "dirvote.h"
#include "hibernate.h"
#include "keypin.h"
+#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "protover.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
@@ -34,6 +36,24 @@
* \file dirserv.c
* \brief Directory server core implementation. Manages directory
* contents and generates directories.
+ *
+ * This module implements most of directory cache functionality, and some of
+ * the directory authority functionality. The directory.c module delegates
+ * here in order to handle incoming requests from clients, via
+ * connection_dirserv_flushed_some() and its kin. In order to save RAM, this
+ * module is reponsible for spooling directory objects (in whole or in part)
+ * onto buf_t instances, and then closing the dir_connection_t once the
+ * objects are totally flushed.
+ *
+ * The directory.c module also delegates here for handling descriptor uploads
+ * via dirserv_add_multiple_descriptors().
+ *
+ * Additionally, this module handles some aspects of voting, including:
+ * deciding how to vote on individual flags (based on decisions reached in
+ * rephist.c), of formatting routerstatus lines, and deciding what relays to
+ * include in an authority's vote. (TODO: Those functions could profitably be
+ * split off. They only live in this file because historically they were
+ * shared among the v1, v2, and v3 directory code.)
*/
/** How far in the future do we allow a router to get? (seconds) */
@@ -44,10 +64,6 @@
* directory authorities. */
#define MAX_UNTRUSTED_NETWORKSTATUSES 16
-extern time_t time_of_process_start; /* from main.c */
-
-extern long stats_n_seconds_working; /* from main.c */
-
/** Total number of routers with measured bandwidth; this is set by
* dirserv_count_measured_bws() before the loop in
* dirserv_generate_networkstatus_vote_obj() and checked by
@@ -125,7 +141,8 @@ add_fingerprint_to_dir(const char *fp, authdir_config_t *list,
fingerprint = tor_strdup(fp);
tor_strstrip(fingerprint, " ");
- if (base16_decode(d, DIGEST_LEN, fingerprint, strlen(fingerprint))) {
+ if (base16_decode(d, DIGEST_LEN,
+ fingerprint, strlen(fingerprint)) != DIGEST_LEN) {
log_warn(LD_DIRSERV, "Couldn't decode fingerprint \"%s\"",
escaped(fp));
tor_free(fingerprint);
@@ -202,7 +219,7 @@ dirserv_load_fingerprint_file(void)
tor_strstrip(fingerprint, " "); /* remove spaces */
if (strlen(fingerprint) != HEX_DIGEST_LEN ||
base16_decode(digest_tmp, sizeof(digest_tmp),
- fingerprint, HEX_DIGEST_LEN) < 0) {
+ fingerprint, HEX_DIGEST_LEN) != sizeof(digest_tmp)) {
log_notice(LD_CONFIG,
"Invalid fingerprint (nickname '%s', "
"fingerprint %s). Skipping.",
@@ -257,6 +274,20 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
return FP_REJECT;
}
+ /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc,
+ * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha.
+ * But just in case a relay doesn't provide or lies about its version, or
+ * doesn't include an ntor key in its descriptor, check that it exists,
+ * and is non-zero (clients check that it's non-zero before using it). */
+ if (!routerinfo_has_curve25519_onion_key(router)) {
+ log_fn(severity, LD_DIR,
+ "Descriptor from router %s is missing an ntor curve25519 onion "
+ "key.", router_describe(router));
+ if (msg)
+ *msg = "Missing ntor curve25519 onion key. Please upgrade!";
+ return FP_REJECT;
+ }
+
if (router->cache_info.signing_key_cert) {
/* This has an ed25519 identity key. */
if (KEYPIN_MISMATCH ==
@@ -334,6 +365,16 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
strmap_size(fingerprint_list->fp_by_name),
digestmap_size(fingerprint_list->status_by_digest));
+ if (platform) {
+ tor_version_t ver_tmp;
+ if (tor_version_parse_platform(platform, &ver_tmp, 1) < 0) {
+ if (msg) {
+ *msg = "Malformed platform string.";
+ }
+ return FP_REJECT;
+ }
+ }
+
/* Versions before Tor 0.2.4.18-rc are too old to support, and are
* missing some important security fixes too. Disable them. */
if (platform && !tor_version_as_new_as(platform,"0.2.4.18-rc")) {
@@ -342,6 +383,17 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
return FP_REJECT;
}
+ /* Tor 0.2.9.x where x<5 suffers from bug #20499, where relays don't
+ * keep their consensus up to date so they make bad guards.
+ * The simple fix is to just drop them from the network. */
+ if (platform &&
+ tor_version_as_new_as(platform,"0.2.9.0-alpha") &&
+ !tor_version_as_new_as(platform,"0.2.9.5-alpha")) {
+ if (msg)
+ *msg = "Tor version contains bug 20499. Please upgrade!";
+ return FP_REJECT;
+ }
+
status_by_digest = digestmap_get(fingerprint_list->status_by_digest,
id_digest);
if (status_by_digest)
@@ -349,7 +401,7 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
if (result & FP_REJECT) {
if (msg)
- *msg = "Fingerprint is marked rejected";
+ *msg = "Fingerprint is marked rejected -- please contact us?";
return FP_REJECT;
} else if (result & FP_INVALID) {
if (msg)
@@ -367,7 +419,7 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname,
log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'",
nickname, fmt_addr32(addr));
if (msg)
- *msg = "Authdir is rejecting routers in this range.";
+ *msg = "Suspicious relay address range -- please contact us?";
return FP_REJECT;
}
if (!authdir_policy_valid_address(addr, or_port)) {
@@ -574,7 +626,11 @@ dirserv_add_multiple_descriptors(const char *desc, uint8_t purpose,
* passed back to the origin of this descriptor, or NULL if there is no such
* message. Use <b>source</b> to produce better log messages.
*
- * Return the status of the operation
+ * If <b>ri</b> is not added to the list of server descriptors, free it.
+ * That means the caller must not access <b>ri</b> after this function
+ * returns, since it might have been freed.
+ *
+ * Return the status of the operation.
*
* This function is only called when fresh descriptors are posted, not when
* we re-load the cache.
@@ -602,8 +658,8 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
control_event_or_authdir_new_descriptor("REJECTED",
ri->cache_info.signed_descriptor_body,
desclen, *msg);
- routerinfo_free(ri);
- return ROUTER_AUTHDIR_REJECTS;
+ r = ROUTER_AUTHDIR_REJECTS;
+ goto fail;
}
/* Check whether this descriptor is semantically identical to the last one
@@ -623,8 +679,8 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
control_event_or_authdir_new_descriptor("DROPPED",
ri->cache_info.signed_descriptor_body,
desclen, *msg);
- routerinfo_free(ri);
- return ROUTER_IS_ALREADY_KNOWN;
+ r = ROUTER_IS_ALREADY_KNOWN;
+ goto fail;
}
/* Do keypinning again ... this time, to add the pin if appropriate */
@@ -647,7 +703,8 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
"its key did not match an older RSA/Ed25519 keypair",
router_describe(ri), source);
*msg = "Looks like your keypair does not match its older value.";
- return ROUTER_AUTHDIR_REJECTS;
+ r = ROUTER_AUTHDIR_REJECTS;
+ goto fail;
}
/* Make a copy of desc, since router_add_to_routerlist might free
@@ -685,6 +742,20 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
tor_free(desc);
tor_free(nickname);
return r;
+ fail:
+ {
+ const char *desc_digest = ri->cache_info.signed_descriptor_digest;
+ download_status_t *dls =
+ router_get_dl_status_by_descriptor_digest(desc_digest);
+ if (dls) {
+ log_info(LD_GENERAL, "Marking router with descriptor %s as rejected, "
+ "and therefore undownloadable",
+ hex_str(desc_digest, DIGEST_LEN));
+ download_status_mark_impossible(dls);
+ }
+ routerinfo_free(ri);
+ }
+ return r;
}
/** As dirserv_add_descriptor, but for an extrainfo_t <b>ei</b>. */
@@ -693,6 +764,7 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
{
routerinfo_t *ri;
int r;
+ was_router_added_t rv;
tor_assert(msg);
*msg = NULL;
@@ -701,8 +773,8 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
ri = router_get_mutable_by_digest(ei->cache_info.identity_digest);
if (!ri) {
*msg = "No corresponding router descriptor for extra-info descriptor";
- extrainfo_free(ei);
- return ROUTER_BAD_EI;
+ rv = ROUTER_BAD_EI;
+ goto fail;
}
/* If it's too big, refuse it now. Otherwise we'll cache it all over the
@@ -714,17 +786,34 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
(int)ei->cache_info.signed_descriptor_len,
MAX_EXTRAINFO_UPLOAD_SIZE);
*msg = "Extrainfo document was too large";
- extrainfo_free(ei);
- return ROUTER_BAD_EI;
+ rv = ROUTER_BAD_EI;
+ goto fail;
}
if ((r = routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
&ri->cache_info, msg))) {
- extrainfo_free(ei);
- return r < 0 ? ROUTER_IS_ALREADY_KNOWN : ROUTER_BAD_EI;
+ if (r<0) {
+ extrainfo_free(ei);
+ return ROUTER_IS_ALREADY_KNOWN;
+ }
+ rv = ROUTER_BAD_EI;
+ goto fail;
}
router_add_extrainfo_to_routerlist(ei, msg, 0, 0);
return ROUTER_ADDED_SUCCESSFULLY;
+ fail:
+ {
+ const char *d = ei->cache_info.signed_descriptor_digest;
+ signed_descriptor_t *sd = router_get_by_extrainfo_digest((char*)d);
+ if (sd) {
+ log_info(LD_GENERAL, "Marking extrainfo with descriptor %s as "
+ "rejected, and therefore undownloadable",
+ hex_str((char*)d,DIGEST_LEN));
+ download_status_mark_impossible(&sd->ei_dl_status);
+ }
+ extrainfo_free(ei);
+ }
+ return rv;
}
/** Remove all descriptors whose nicknames or fingerprints no longer
@@ -823,7 +912,7 @@ running_long_enough_to_decide_unreachable(void)
void
dirserv_set_router_is_running(routerinfo_t *router, time_t now)
{
- /*XXXX024 This function is a mess. Separate out the part that calculates
+ /*XXXX This function is a mess. Separate out the part that calculates
whether it's reachable and the part that tells rephist that the router was
unreachable.
*/
@@ -985,94 +1074,6 @@ router_is_active(const routerinfo_t *ri, const node_t *node, time_t now)
return 1;
}
-/** Generate a new v1 directory and write it into a newly allocated string.
- * Point *<b>dir_out</b> to the allocated string. Sign the
- * directory with <b>private_key</b>. Return 0 on success, -1 on
- * failure. If <b>complete</b> is set, give us all the descriptors;
- * otherwise leave out non-running and non-valid ones.
- */
-int
-dirserv_dump_directory_to_string(char **dir_out,
- crypto_pk_t *private_key)
-{
- /* XXXX 024 Get rid of this function if we can confirm that nobody's
- * fetching these any longer */
- char *cp;
- char *identity_pkey; /* Identity key, DER64-encoded. */
- char *recommended_versions;
- char digest[DIGEST_LEN];
- char published[ISO_TIME_LEN+1];
- char *buf = NULL;
- size_t buf_len;
- size_t identity_pkey_len;
- time_t now = time(NULL);
-
- tor_assert(dir_out);
- *dir_out = NULL;
-
- if (crypto_pk_write_public_key_to_string(private_key,&identity_pkey,
- &identity_pkey_len)<0) {
- log_warn(LD_BUG,"write identity_pkey to string failed!");
- return -1;
- }
-
- recommended_versions =
- format_versions_list(get_options()->RecommendedVersions);
-
- format_iso_time(published, now);
-
- buf_len = 2048+strlen(recommended_versions);
-
- buf = tor_malloc(buf_len);
- /* We'll be comparing against buf_len throughout the rest of the
- function, though strictly speaking we shouldn't be able to exceed
- it. This is C, after all, so we may as well check for buffer
- overruns.*/
-
- tor_snprintf(buf, buf_len,
- "signed-directory\n"
- "published %s\n"
- "recommended-software %s\n"
- "router-status %s\n"
- "dir-signing-key\n%s\n",
- published, recommended_versions, "",
- identity_pkey);
-
- tor_free(recommended_versions);
- tor_free(identity_pkey);
-
- cp = buf + strlen(buf);
- *cp = '\0';
-
- /* These multiple strlcat calls are inefficient, but dwarfed by the RSA
- signature. */
- if (strlcat(buf, "directory-signature ", buf_len) >= buf_len)
- goto truncated;
- if (strlcat(buf, get_options()->Nickname, buf_len) >= buf_len)
- goto truncated;
- if (strlcat(buf, "\n", buf_len) >= buf_len)
- goto truncated;
-
- if (router_get_dir_hash(buf,digest)) {
- log_warn(LD_BUG,"couldn't compute digest");
- tor_free(buf);
- return -1;
- }
- note_crypto_pk_op(SIGN_DIR);
- if (router_append_dirobj_signature(buf,buf_len,digest,DIGEST_LEN,
- private_key)<0) {
- tor_free(buf);
- return -1;
- }
-
- *dir_out = buf;
- return 0;
- truncated:
- log_warn(LD_BUG,"tried to exceed string length.");
- tor_free(buf);
- return -1;
-}
-
/********************************************************************/
/* A set of functions to answer questions about how we'd like to behave
@@ -1090,7 +1091,8 @@ directory_fetches_from_authorities(const or_options_t *options)
return 1;
if (options->BridgeRelay == 1)
return 0;
- if (server_mode(options) && router_pick_published_address(options, &addr)<0)
+ if (server_mode(options) &&
+ router_pick_published_address(options, &addr, 1) < 0)
return 1; /* we don't know our IP address; ask an authority. */
refuseunknown = ! router_my_exit_policy_is_reject_star() &&
should_refuse_unknown_exits(options);
@@ -1329,7 +1331,7 @@ dirserv_thinks_router_is_unreliable(time_t now,
{
if (need_uptime) {
if (!enough_mtbf_info) {
- /* XXX024 Once most authorities are on v3, we should change the rule from
+ /* XXXX We should change the rule from
* "use uptime if we don't have mtbf data" to "don't advertise Stable on
* v3 if we don't have enough mtbf data." Or maybe not, since if we ever
* hit a point where we need to reset a lot of authorities at once,
@@ -1871,6 +1873,7 @@ version_from_platform(const char *platform)
*/
char *
routerstatus_format_entry(const routerstatus_t *rs, const char *version,
+ const char *protocols,
routerstatus_format_type_t format,
const vote_routerstatus_t *vrs)
{
@@ -1934,6 +1937,9 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) {
smartlist_add_asprintf(chunks, "v %s\n", version);
}
+ if (protocols) {
+ smartlist_add_asprintf(chunks, "pr %s\n", protocols);
+ }
if (format != NS_V2) {
const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest);
@@ -2152,8 +2158,8 @@ routers_make_ed_keys_unique(smartlist_t *routers)
const time_t ri2_pub = ri2->cache_info.published_on;
if (ri2_pub < ri_pub ||
(ri2_pub == ri_pub &&
- memcmp(ri->cache_info.signed_descriptor_digest,
- ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) {
+ fast_memcmp(ri->cache_info.signed_descriptor_digest,
+ ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) {
digest256map_set(by_ed_key, pk, ri);
ri2->omit_from_vote = 1;
} else {
@@ -2206,7 +2212,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs,
rs->is_valid = node->is_valid;
- if (node->is_fast &&
+ if (node->is_fast && node->is_stable &&
((options->AuthDirGuardBWGuarantee &&
routerbw_kb >= options->AuthDirGuardBWGuarantee/1000) ||
routerbw_kb >= MIN(guard_bandwidth_including_exits_kb,
@@ -2285,13 +2291,17 @@ dirserv_set_routerstatus_testing(routerstatus_t *rs)
/** Routerstatus <b>rs</b> is part of a group of routers that are on
* too narrow an IP-space. Clear out its flags: we don't want people
* using it.
+ *
+ * Leave its BadExit flag alone though, since if we think it's a bad exit,
+ * we want to vote that way in case all the other authorities are voting
+ * Running and Exit.
*/
static void
clear_status_flags_on_sybil(routerstatus_t *rs)
{
rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast =
rs->is_flagged_running = rs->is_named = rs->is_valid =
- rs->is_hs_dir = rs->is_possible_guard = rs->is_bad_exit = 0;
+ rs->is_hs_dir = rs->is_v2_dir = rs->is_possible_guard = 0;
/* FFFF we might want some mechanism to check later on if we
* missed zeroing any flags: it's easy to add a new flag but
* forget to add it to this clause. */
@@ -2365,7 +2375,8 @@ guardfraction_file_parse_guard_line(const char *guard_line,
inputs_tmp = smartlist_get(sl, 0);
if (strlen(inputs_tmp) != HEX_DIGEST_LEN ||
- base16_decode(guard_id, DIGEST_LEN, inputs_tmp, HEX_DIGEST_LEN)) {
+ base16_decode(guard_id, DIGEST_LEN,
+ inputs_tmp, HEX_DIGEST_LEN) != DIGEST_LEN) {
tor_asprintf(err_msg, "bad digest '%s'", inputs_tmp);
goto done;
}
@@ -2669,7 +2680,8 @@ measured_bw_line_parse(measured_bw_line_t *out, const char *orig_line)
cp+=strlen("node_id=$");
if (strlen(cp) != HEX_DIGEST_LEN ||
- base16_decode(out->node_id, DIGEST_LEN, cp, HEX_DIGEST_LEN)) {
+ base16_decode(out->node_id, DIGEST_LEN,
+ cp, HEX_DIGEST_LEN) != DIGEST_LEN) {
log_warn(LD_DIRSERV, "Invalid node_id in bandwidth file line: %s",
escaped(orig_line));
tor_free(line);
@@ -2910,6 +2922,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
rs->is_flagged_running = 0;
vrs->version = version_from_platform(ri->platform);
+ if (ri->protocol_list) {
+ vrs->protocols = tor_strdup(ri->protocol_list);
+ } else {
+ vrs->protocols = tor_strdup(
+ protover_compute_for_old_tor(vrs->version));
+ }
vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now,
microdescriptors);
@@ -2982,6 +3000,31 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
v3_out->client_versions = client_versions;
v3_out->server_versions = server_versions;
+
+ /* These are hardwired, to avoid disaster. */
+ v3_out->recommended_relay_protocols =
+ tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2");
+ v3_out->recommended_client_protocols =
+ tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2");
+ v3_out->required_client_protocols =
+ tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2");
+ v3_out->required_relay_protocols =
+ tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2");
+
+ /* We are not allowed to vote to require anything we don't have. */
+ tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL));
+ tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL));
+
+ /* We should not recommend anything we don't have. */
+ tor_assert_nonfatal(protover_all_supported(
+ v3_out->recommended_relay_protocols, NULL));
+ tor_assert_nonfatal(protover_all_supported(
+ v3_out->recommended_client_protocols, NULL));
+
v3_out->package_lines = smartlist_new();
{
config_line_t *cl;
@@ -3340,7 +3383,7 @@ lookup_cached_dir_by_fp(const char *fp)
d = strmap_get(cached_consensuses, "ns");
} else if (memchr(fp, '\0', DIGEST_LEN) && cached_consensuses &&
(d = strmap_get(cached_consensuses, fp))) {
- /* this here interface is a nasty hack XXXX024 */;
+ /* this here interface is a nasty hack XXXX */;
}
return d;
}
diff --git a/src/or/dirserv.h b/src/or/dirserv.h
index 9a9725ad6f..1e4f27e3d7 100644
--- a/src/or/dirserv.h
+++ b/src/or/dirserv.h
@@ -47,8 +47,6 @@ enum was_router_added_t dirserv_add_descriptor(routerinfo_t *ri,
void dirserv_set_router_is_running(routerinfo_t *router, time_t now);
int list_server_status_v1(smartlist_t *routers, char **router_status_out,
int for_controller);
-int dirserv_dump_directory_to_string(char **dir_out,
- crypto_pk_t *private_key);
char *dirserv_get_flag_thresholds_line(void);
void dirserv_compute_bridge_flag_thresholds(void);
@@ -98,7 +96,9 @@ size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
size_t dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed);
char *routerstatus_format_entry(
- const routerstatus_t *rs, const char *platform,
+ const routerstatus_t *rs,
+ const char *version,
+ const char *protocols,
routerstatus_format_type_t format,
const vote_routerstatus_t *vrs);
void dirserv_free_all(void);
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 62f85877fe..738ab35bc1 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -13,12 +13,15 @@
#include "microdesc.h"
#include "networkstatus.h"
#include "policies.h"
+#include "protover.h"
#include "rephist.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "entrynodes.h" /* needed for guardfraction methods */
#include "torcert.h"
+#include "shared_random_state.h"
/**
* \file dirvote.c
@@ -59,6 +62,58 @@ static int dirvote_publish_consensus(void);
* Voting
* =====*/
+/* If <b>opt_value</b> is non-NULL, return "keyword opt_value\n" in a new
+ * string. Otherwise return a new empty string. */
+static char *
+format_line_if_present(const char *keyword, const char *opt_value)
+{
+ if (opt_value) {
+ char *result = NULL;
+ tor_asprintf(&result, "%s %s\n", keyword, opt_value);
+ return result;
+ } else {
+ return tor_strdup("");
+ }
+}
+
+/** Format the recommended/required-relay-client protocols lines for a vote in
+ * a newly allocated string, and return that string. */
+static char *
+format_protocols_lines_for_vote(const networkstatus_t *v3_ns)
+{
+ char *recommended_relay_protocols_line = NULL;
+ char *recommended_client_protocols_line = NULL;
+ char *required_relay_protocols_line = NULL;
+ char *required_client_protocols_line = NULL;
+
+ recommended_relay_protocols_line =
+ format_line_if_present("recommended-relay-protocols",
+ v3_ns->recommended_relay_protocols);
+ recommended_client_protocols_line =
+ format_line_if_present("recommended-client-protocols",
+ v3_ns->recommended_client_protocols);
+ required_relay_protocols_line =
+ format_line_if_present("required-relay-protocols",
+ v3_ns->required_relay_protocols);
+ required_client_protocols_line =
+ format_line_if_present("required-client-protocols",
+ v3_ns->required_client_protocols);
+
+ char *result = NULL;
+ tor_asprintf(&result, "%s%s%s%s",
+ recommended_relay_protocols_line,
+ recommended_client_protocols_line,
+ required_relay_protocols_line,
+ required_client_protocols_line);
+
+ tor_free(recommended_relay_protocols_line);
+ tor_free(recommended_client_protocols_line);
+ tor_free(required_relay_protocols_line);
+ tor_free(required_client_protocols_line);
+
+ return result;
+}
+
/** Return a new string containing the string representation of the vote in
* <b>v3_ns</b>, signed with our v3 signing key <b>private_signing_key</b>.
* For v3 authorities. */
@@ -67,12 +122,13 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
networkstatus_t *v3_ns)
{
smartlist_t *chunks = smartlist_new();
- const char *client_versions = NULL, *server_versions = NULL;
char *packages = NULL;
char fingerprint[FINGERPRINT_LEN+1];
char digest[DIGEST_LEN];
uint32_t addr;
+ char *protocols_lines = NULL;
char *client_versions_line = NULL, *server_versions_line = NULL;
+ char *shared_random_vote_str = NULL;
networkstatus_voter_info_t *voter;
char *status = NULL;
@@ -85,27 +141,19 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
base16_encode(fingerprint, sizeof(fingerprint),
v3_ns->cert->cache_info.identity_digest, DIGEST_LEN);
- client_versions = v3_ns->client_versions;
- server_versions = v3_ns->server_versions;
- if (client_versions) {
- tor_asprintf(&client_versions_line, "client-versions %s\n",
- client_versions);
- } else {
- client_versions_line = tor_strdup("");
- }
- if (server_versions) {
- tor_asprintf(&server_versions_line, "server-versions %s\n",
- server_versions);
- } else {
- server_versions_line = tor_strdup("");
- }
+ client_versions_line = format_line_if_present("client-versions",
+ v3_ns->client_versions);
+ server_versions_line = format_line_if_present("server-versions",
+ v3_ns->server_versions);
+ protocols_lines = format_protocols_lines_for_vote(v3_ns);
if (v3_ns->package_lines) {
smartlist_t *tmp = smartlist_new();
SMARTLIST_FOREACH(v3_ns->package_lines, const char *, p,
if (validate_recommended_package_line(p))
smartlist_add_asprintf(tmp, "package %s\n", p));
+ smartlist_sort_strings(tmp);
packages = smartlist_join_strings(tmp, "", 0, NULL);
SMARTLIST_FOREACH(tmp, char *, cp, tor_free(cp));
smartlist_free(tmp);
@@ -113,6 +161,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
packages = tor_strdup("");
}
+ /* Get shared random commitments/reveals line(s). */
+ shared_random_vote_str = sr_get_string_for_vote();
+
{
char published[ISO_TIME_LEN+1];
char va[ISO_TIME_LEN+1];
@@ -147,30 +198,36 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
"valid-until %s\n"
"voting-delay %d %d\n"
"%s%s" /* versions */
+ "%s" /* protocols */
"%s" /* packages */
"known-flags %s\n"
"flag-thresholds %s\n"
"params %s\n"
"dir-source %s %s %s %s %d %d\n"
- "contact %s\n",
+ "contact %s\n"
+ "%s", /* shared randomness information */
v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion",
methods,
published, va, fu, vu,
v3_ns->vote_seconds, v3_ns->dist_seconds,
client_versions_line,
server_versions_line,
+ protocols_lines,
packages,
flags,
flag_thresholds,
params,
voter->nickname, fingerprint, voter->address,
fmt_addr32(addr), voter->dir_port, voter->or_port,
- voter->contact);
+ voter->contact,
+ shared_random_vote_str ?
+ shared_random_vote_str : "");
tor_free(params);
tor_free(flags);
tor_free(flag_thresholds);
tor_free(methods);
+ tor_free(shared_random_vote_str);
if (!tor_digest_is_zero(voter->legacy_id_digest)) {
char fpbuf[HEX_DIGEST_LEN+1];
@@ -187,7 +244,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
char *rsf;
vote_microdesc_hash_t *h;
rsf = routerstatus_format_entry(&vrs->status,
- vrs->version, NS_V3_VOTE, vrs);
+ vrs->version, vrs->protocols,
+ NS_V3_VOTE, vrs);
if (rsf)
smartlist_add(chunks, rsf);
@@ -247,6 +305,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
done:
tor_free(client_versions_line);
tor_free(server_versions_line);
+ tor_free(protocols_lines);
tor_free(packages);
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
@@ -362,16 +421,30 @@ compare_vote_rs(const vote_routerstatus_t *a, const vote_routerstatus_t *b)
b->status.descriptor_digest,
DIGEST_LEN)))
return r;
- if ((r = (int)(b->status.published_on - a->status.published_on)))
- return r;
+ /* If we actually reached this point, then the identities and
+ * the descriptor digests matched, so somebody is making SHA1 collisions.
+ */
+#define CMP_FIELD(utype, itype, field) do { \
+ utype aval = (utype) (itype) a->status.field; \
+ utype bval = (utype) (itype) b->status.field; \
+ utype u = bval - aval; \
+ itype r2 = (itype) u; \
+ if (r2 < 0) { \
+ return -1; \
+ } else if (r2 > 0) { \
+ return 1; \
+ } \
+ } while (0)
+
+ CMP_FIELD(uint64_t, int64_t, published_on);
+
if ((r = strcmp(b->status.nickname, a->status.nickname)))
return r;
- if ((r = (((int)b->status.addr) - ((int)a->status.addr))))
- return r;
- if ((r = (((int)b->status.or_port) - ((int)a->status.or_port))))
- return r;
- if ((r = (((int)b->status.dir_port) - ((int)a->status.dir_port))))
- return r;
+
+ CMP_FIELD(unsigned, int, addr);
+ CMP_FIELD(unsigned, int, or_port);
+ CMP_FIELD(unsigned, int, dir_port);
+
return 0;
}
@@ -607,15 +680,47 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning)
return result;
}
+/** Given a list of K=V values, return the int32_t value corresponding to
+ * KEYWORD=, or default_val if no such value exists, or if the value is
+ * corrupt.
+ */
+STATIC int32_t
+dirvote_get_intermediate_param_value(const smartlist_t *param_list,
+ const char *keyword,
+ int32_t default_val)
+{
+ unsigned int n_found = 0;
+ int32_t value = default_val;
+
+ SMARTLIST_FOREACH_BEGIN(param_list, const char *, k_v_pair) {
+ if (!strcmpstart(k_v_pair, keyword) && k_v_pair[strlen(keyword)] == '=') {
+ const char *integer_str = &k_v_pair[strlen(keyword)+1];
+ int ok;
+ value = (int32_t)
+ tor_parse_long(integer_str, 10, INT32_MIN, INT32_MAX, &ok, NULL);
+ if (BUG(! ok))
+ return default_val;
+ ++n_found;
+ }
+ } SMARTLIST_FOREACH_END(k_v_pair);
+
+ if (n_found == 1)
+ return value;
+ else if (BUG(n_found > 1))
+ return default_val;
+ else
+ return default_val;
+}
+
/** Minimum number of directory authorities voting for a parameter to
* include it in the consensus, if consensus method 12 or later is to be
* used. See proposal 178 for details. */
#define MIN_VOTES_FOR_PARAM 3
-/** Helper: given a list of valid networkstatus_t, return a new string
+/** Helper: given a list of valid networkstatus_t, return a new smartlist
* containing the contents of the consensus network parameter set.
*/
-STATIC char *
+STATIC smartlist_t *
dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
{
int i;
@@ -624,7 +729,6 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
int cur_param_len;
const char *cur_param;
const char *eq;
- char *result;
const int n_votes = smartlist_len(votes);
smartlist_t *output;
@@ -646,8 +750,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
if (smartlist_len(param_list) == 0) {
tor_free(vals);
- smartlist_free(param_list);
- return NULL;
+ return param_list;
}
smartlist_sort_strings(param_list);
@@ -695,12 +798,9 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
}
} SMARTLIST_FOREACH_END(param);
- result = smartlist_join_strings(output, " ", 0, NULL);
- SMARTLIST_FOREACH(output, char *, cp, tor_free(cp));
- smartlist_free(output);
smartlist_free(param_list);
tor_free(vals);
- return result;
+ return output;
}
#define RANGE_CHECK(a,b,c,d,e,f,g,mx) \
@@ -815,7 +915,7 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G,
}
/*
- * Computed from cases in 3.4.3 of dir-spec.txt
+ * Computed from cases in 3.8.3 of dir-spec.txt
*
* 1. Neither are scarce
* 2. Both Guard and Exit are scarce
@@ -1114,6 +1214,72 @@ update_total_bandwidth_weights(const routerstatus_t *rs,
}
}
+/** Considering the different recommended/required protocols sets as a
+ * 4-element array, return the element from <b>vote</b> for that protocol
+ * set.
+ */
+static const char *
+get_nth_protocol_set_vote(int n, const networkstatus_t *vote)
+{
+ switch (n) {
+ case 0: return vote->recommended_client_protocols;
+ case 1: return vote->recommended_relay_protocols;
+ case 2: return vote->required_client_protocols;
+ case 3: return vote->required_relay_protocols;
+ default:
+ tor_assert_unreached();
+ return NULL;
+ }
+}
+
+/** Considering the different recommended/required protocols sets as a
+ * 4-element array, return a newly allocated string for the consensus value
+ * for the n'th set.
+ */
+static char *
+compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes)
+{
+ const char *keyword;
+ smartlist_t *proto_votes = smartlist_new();
+ int threshold;
+ switch (n) {
+ case 0:
+ keyword = "recommended-client-protocols";
+ threshold = CEIL_DIV(n_voters, 2);
+ break;
+ case 1:
+ keyword = "recommended-relay-protocols";
+ threshold = CEIL_DIV(n_voters, 2);
+ break;
+ case 2:
+ keyword = "required-client-protocols";
+ threshold = CEIL_DIV(n_voters * 2, 3);
+ break;
+ case 3:
+ keyword = "required-relay-protocols";
+ threshold = CEIL_DIV(n_voters * 2, 3);
+ break;
+ default:
+ tor_assert_unreached();
+ return NULL;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(votes, const networkstatus_t *, ns) {
+ const char *v = get_nth_protocol_set_vote(n, ns);
+ if (v)
+ smartlist_add(proto_votes, (void*)v);
+ } SMARTLIST_FOREACH_END(ns);
+
+ char *protocols = protover_compute_vote(proto_votes, threshold);
+ smartlist_free(proto_votes);
+
+ char *result = NULL;
+ tor_asprintf(&result, "%s %s\n", keyword, protocols);
+ tor_free(protocols);
+
+ return result;
+}
+
/** Given a list of vote networkstatus_t in <b>votes</b>, our public
* authority <b>identity_key</b>, our private authority <b>signing_key</b>,
* and the number of <b>total_authorities</b> that we believe exist in our
@@ -1147,6 +1313,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
char *packages = NULL;
int added_weights = 0;
dircollator_t *collator = NULL;
+ smartlist_t *param_list = NULL;
+
tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC);
tor_assert(total_authorities >= smartlist_len(votes));
tor_assert(total_authorities > 0);
@@ -1291,14 +1459,42 @@ networkstatus_compute_consensus(smartlist_t *votes,
tor_free(flaglist);
}
- params = dirvote_compute_params(votes, consensus_method,
- total_authorities);
- if (params) {
+ if (consensus_method >= MIN_METHOD_FOR_RECOMMENDED_PROTOCOLS) {
+ int num_dirauth = get_n_authorities(V3_DIRINFO);
+ int idx;
+ for (idx = 0; idx < 4; ++idx) {
+ char *proto_line = compute_nth_protocol_set(idx, num_dirauth, votes);
+ if (BUG(!proto_line))
+ continue;
+ smartlist_add(chunks, proto_line);
+ }
+ }
+
+ param_list = dirvote_compute_params(votes, consensus_method,
+ total_authorities);
+ if (smartlist_len(param_list)) {
+ params = smartlist_join_strings(param_list, " ", 0, NULL);
smartlist_add(chunks, tor_strdup("params "));
smartlist_add(chunks, params);
smartlist_add(chunks, tor_strdup("\n"));
}
+ if (consensus_method >= MIN_METHOD_FOR_SHARED_RANDOM) {
+ int num_dirauth = get_n_authorities(V3_DIRINFO);
+ /* Default value of this is 2/3 of the total number of authorities. For
+ * instance, if we have 9 dirauth, the default value is 6. The following
+ * calculation will round it down. */
+ int32_t num_srv_agreements =
+ dirvote_get_intermediate_param_value(param_list,
+ "AuthDirNumSRVAgreements",
+ (num_dirauth * 2) / 3);
+ /* Add the shared random value. */
+ char *srv_lines = sr_get_string_for_consensus(votes, num_srv_agreements);
+ if (srv_lines != NULL) {
+ smartlist_add(chunks, srv_lines);
+ }
+ }
+
/* Sort the votes. */
smartlist_sort(votes, compare_votes_by_authority_id_);
/* Add the authority sections. */
@@ -1350,7 +1546,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW) {
char *max_unmeasured_param = NULL;
- /* XXXX Extract this code into a common function */
+ /* XXXX Extract this code into a common function. Or don't! see #19011 */
if (params) {
if (strcmpstart(params, "maxunmeasuredbw=") == 0)
max_unmeasured_param = params;
@@ -1374,7 +1570,6 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Add the actual router entries. */
{
- int *index; /* index[j] is the current index into votes[j]. */
int *size; /* size[j] is the number of routerstatuses in votes[j]. */
int *flag_counts; /* The number of voters that list flag[j] for the
* currently considered router. */
@@ -1382,6 +1577,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_t *matching_descs = smartlist_new();
smartlist_t *chosen_flags = smartlist_new();
smartlist_t *versions = smartlist_new();
+ smartlist_t *protocols = smartlist_new();
smartlist_t *exitsummaries = smartlist_new();
uint32_t *bandwidths_kb = tor_calloc(smartlist_len(votes),
sizeof(uint32_t));
@@ -1409,7 +1605,6 @@ networkstatus_compute_consensus(smartlist_t *votes,
memset(conflict, 0, sizeof(conflict));
memset(unknown, 0xff, sizeof(conflict));
- index = tor_calloc(smartlist_len(votes), sizeof(int));
size = tor_calloc(smartlist_len(votes), sizeof(int));
n_voter_flags = tor_calloc(smartlist_len(votes), sizeof(int));
n_flag_voters = tor_calloc(smartlist_len(flags), sizeof(int));
@@ -1525,9 +1720,10 @@ networkstatus_compute_consensus(smartlist_t *votes,
routerstatus_t rs_out;
const char *current_rsa_id = NULL;
const char *chosen_version;
+ const char *chosen_protocol_list;
const char *chosen_name = NULL;
int exitsummary_disagreement = 0;
- int is_named = 0, is_unnamed = 0, is_running = 0;
+ int is_named = 0, is_unnamed = 0, is_running = 0, is_valid = 0;
int is_guard = 0, is_exit = 0, is_bad_exit = 0;
int naming_conflict = 0;
int n_listing = 0;
@@ -1538,6 +1734,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_clear(matching_descs);
smartlist_clear(chosen_flags);
smartlist_clear(versions);
+ smartlist_clear(protocols);
num_bandwidths = 0;
num_mbws = 0;
num_guardfraction_inputs = 0;
@@ -1557,6 +1754,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (rs->version && rs->version[0])
smartlist_add(versions, rs->version);
+ if (rs->protocols) {
+ /* We include this one even if it's empty: voting for an
+ * empty protocol list actually is meaningful. */
+ smartlist_add(protocols, rs->protocols);
+ }
+
/* Tally up all the flags. */
for (int flag = 0; flag < n_voter_flags[voter_idx]; ++flag) {
if (rs->flags & (U64_LITERAL(1) << flag))
@@ -1678,6 +1881,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
is_running = 1;
else if (!strcmp(fl, "BadExit"))
is_bad_exit = 1;
+ else if (!strcmp(fl, "Valid"))
+ is_valid = 1;
}
}
} SMARTLIST_FOREACH_END(fl);
@@ -1687,6 +1892,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (!is_running)
continue;
+ /* Starting with consensus method 24, we don't list servers
+ * that are not valid in a consensus. See Proposal 272 */
+ if (!is_valid &&
+ consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES)
+ continue;
+
/* Pick the version. */
if (smartlist_len(versions)) {
sort_version_list(versions, 0);
@@ -1695,6 +1906,14 @@ networkstatus_compute_consensus(smartlist_t *votes,
chosen_version = NULL;
}
+ /* Pick the protocol list */
+ if (smartlist_len(protocols)) {
+ smartlist_sort_strings(protocols);
+ chosen_protocol_list = get_most_frequent_member(protocols);
+ } else {
+ chosen_protocol_list = NULL;
+ }
+
/* If it's a guard and we have enough guardfraction votes,
calculate its consensus guardfraction value. */
if (is_guard && num_guardfraction_inputs > 2 &&
@@ -1828,7 +2047,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
char *buf;
/* Okay!! Now we can write the descriptor... */
/* First line goes into "buf". */
- buf = routerstatus_format_entry(&rs_out, NULL, rs_format, NULL);
+ buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL);
if (buf)
smartlist_add(chunks, buf);
}
@@ -1848,6 +2067,10 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_add(chunks, tor_strdup(chosen_version));
}
smartlist_add(chunks, tor_strdup("\n"));
+ if (chosen_protocol_list &&
+ consensus_method >= MIN_METHOD_FOR_RS_PROTOCOLS) {
+ smartlist_add_asprintf(chunks, "pr %s\n", chosen_protocol_list);
+ }
/* Now the weight line. */
if (rs_out.has_bandwidth) {
char *guardfraction_str = NULL;
@@ -1875,7 +2098,6 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* And the loop is over and we move on to the next router */
}
- tor_free(index);
tor_free(size);
tor_free(n_voter_flags);
tor_free(n_flag_voters);
@@ -1889,6 +2111,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_free(matching_descs);
smartlist_free(chosen_flags);
smartlist_free(versions);
+ smartlist_free(protocols);
smartlist_free(exitsummaries);
tor_free(bandwidths_kb);
tor_free(measured_bws_kb);
@@ -1905,7 +2128,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
// Parse params, extract BW_WEIGHT_SCALE if present
// DO NOT use consensus_param_bw_weight_scale() in this code!
// The consensus is not formed yet!
- /* XXXX Extract this code into a common function */
+ /* XXXX Extract this code into a common function. Or not: #19011. */
if (params) {
if (strcmpstart(params, "bwweightscale=") == 0)
bw_weight_param = params;
@@ -2025,6 +2248,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_free(flags);
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
smartlist_free(chunks);
+ SMARTLIST_FOREACH(param_list, char *, cp, tor_free(cp));
+ smartlist_free(param_list);
return result;
}
@@ -2168,7 +2393,7 @@ networkstatus_add_detached_signatures(networkstatus_t *target,
}
}
if (!n_matches) {
- *msg_out = "No regognized digests for given consensus flavor";
+ *msg_out = "No recognized digests for given consensus flavor";
}
}
@@ -2363,15 +2588,15 @@ networkstatus_get_detached_signatures(smartlist_t *consensuses)
/* Now get all the sigs for non-FLAV_NS consensuses */
SMARTLIST_FOREACH_BEGIN(consensuses, networkstatus_t *, ns) {
- char *sigs;
+ char *sigs_on_this_consensus;
if (ns->flavor == FLAV_NS)
continue;
- sigs = networkstatus_format_signatures(ns, 1);
- if (!sigs) {
+ sigs_on_this_consensus = networkstatus_format_signatures(ns, 1);
+ if (!sigs_on_this_consensus) {
log_warn(LD_DIR, "Couldn't format signatures");
goto err;
}
- smartlist_add(elements, sigs);
+ smartlist_add(elements, sigs_on_this_consensus);
} SMARTLIST_FOREACH_END(ns);
/* Now add the FLAV_NS consensus signatrures. */
@@ -2510,50 +2735,60 @@ dirvote_get_start_of_next_interval(time_t now, int interval, int offset)
return next;
}
-/** Scheduling information for a voting interval. */
-static struct {
- /** When do we generate and distribute our vote for this interval? */
- time_t voting_starts;
- /** When do we send an HTTP request for any votes that we haven't
- * been posted yet?*/
- time_t fetch_missing_votes;
- /** When do we give up on getting more votes and generate a consensus? */
- time_t voting_ends;
- /** When do we send an HTTP request for any signatures we're expecting to
- * see on the consensus? */
- time_t fetch_missing_signatures;
- /** When do we publish the consensus? */
- time_t interval_starts;
-
- /* True iff we have generated and distributed our vote. */
- int have_voted;
- /* True iff we've requested missing votes. */
- int have_fetched_missing_votes;
- /* True iff we have built a consensus and sent the signatures around. */
- int have_built_consensus;
- /* True iff we've fetched missing signatures. */
- int have_fetched_missing_signatures;
- /* True iff we have published our consensus. */
- int have_published_consensus;
-} voting_schedule = {0,0,0,0,0,0,0,0,0,0};
+/* 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 *
+get_voting_schedule(const or_options_t *options, time_t now, int severity)
+{
int interval, vote_delay, dist_delay;
time_t start;
time_t end;
networkstatus_t *consensus;
+ voting_schedule_t *new_voting_schedule;
- if (!authdir_mode_v3(options))
- return;
+ new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t));
consensus = networkstatus_get_live_consensus(now);
- memset(&voting_schedule, 0, sizeof(voting_schedule));
-
if (consensus) {
interval = (int)( consensus->fresh_until - consensus->valid_after );
vote_delay = consensus->vote_seconds;
@@ -2569,7 +2804,7 @@ dirvote_recalculate_timing(const or_options_t *options, time_t now)
if (vote_delay + dist_delay > interval/2)
vote_delay = dist_delay = interval / 4;
- start = voting_schedule.interval_starts =
+ start = new_voting_schedule->interval_starts =
dirvote_get_start_of_next_interval(now,interval,
options->TestingV3AuthVotingStartOffset);
end = dirvote_get_start_of_next_interval(start+1, interval,
@@ -2577,18 +2812,31 @@ dirvote_recalculate_timing(const or_options_t *options, time_t now)
tor_assert(end > start);
- voting_schedule.fetch_missing_signatures = start - (dist_delay/2);
- voting_schedule.voting_ends = start - dist_delay;
- voting_schedule.fetch_missing_votes = start - dist_delay - (vote_delay/2);
- voting_schedule.voting_starts = start - dist_delay - vote_delay;
+ new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2);
+ new_voting_schedule->voting_ends = start - dist_delay;
+ new_voting_schedule->fetch_missing_votes =
+ start - dist_delay - (vote_delay/2);
+ new_voting_schedule->voting_starts = start - dist_delay - vote_delay;
{
char tbuf[ISO_TIME_LEN+1];
- format_iso_time(tbuf, voting_schedule.interval_starts);
- log_notice(LD_DIR,"Choosing expected valid-after time as %s: "
- "consensus_set=%d, interval=%d",
- tbuf, consensus?1:0, interval);
+ format_iso_time(tbuf, new_voting_schedule->interval_starts);
+ tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: "
+ "consensus_set=%d, interval=%d",
+ tbuf, consensus?1:0, interval);
}
+
+ return new_voting_schedule;
+}
+
+/** Frees a voting_schedule_t. This should be used instead of the generic
+ * tor_free. */
+void
+voting_schedule_free(voting_schedule_t *voting_schedule_to_free)
+{
+ if (!voting_schedule_to_free)
+ return;
+ tor_free(voting_schedule_to_free);
}
/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */
@@ -2637,6 +2885,9 @@ dirvote_act(const or_options_t *options, time_t now)
dirvote_publish_consensus();
dirvote_clear_votes(0);
voting_schedule.have_published_consensus = 1;
+ /* Update our shared random state with the consensus just published. */
+ sr_act_post_consensus(
+ networkstatus_get_latest_consensus_by_flavor(FLAV_NS));
/* XXXX We will want to try again later if we haven't got enough
* signatures yet. Implement this if it turns out to ever happen. */
dirvote_recalculate_timing(options, now);
@@ -2916,7 +3167,8 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
/* Hey, it's a new cert! */
trusted_dirs_load_certs_from_string(
vote->cert->cache_info.signed_descriptor_body,
- TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/);
+ TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/,
+ NULL);
if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest,
vote->cert->signing_key_digest)) {
log_warn(LD_BUG, "We added a cert, but still couldn't find it.");
@@ -2975,6 +3227,10 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
}
} SMARTLIST_FOREACH_END(v);
+ /* This a valid vote, update our shared random state. */
+ sr_handle_received_commits(vote->sr_info.commits,
+ vote->cert->identity_key);
+
pending_vote = tor_malloc_zero(sizeof(pending_vote_t));
pending_vote->vote_body = new_cached_dir(tor_strndup(vote_body,
end_of_vote-vote_body),
@@ -3019,6 +3275,30 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
return any_failed ? NULL : pending_vote;
}
+/* Write the votes in <b>pending_vote_list</b> to disk. */
+static void
+write_v3_votes_to_disk(const smartlist_t *pending_votes)
+{
+ smartlist_t *votestrings = smartlist_new();
+ char *votefile = NULL;
+
+ SMARTLIST_FOREACH(pending_votes, pending_vote_t *, v,
+ {
+ sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
+ c->bytes = v->vote_body->dir;
+ c->len = v->vote_body->dir_len;
+ smartlist_add(votestrings, c); /* collect strings to write to disk */
+ });
+
+ votefile = get_datadir_fname("v3-status-votes");
+ write_chunks_to_file(votefile, votestrings, 0, 0);
+ log_debug(LD_DIR, "Wrote votes to disk (%s)!", votefile);
+
+ tor_free(votefile);
+ SMARTLIST_FOREACH(votestrings, sized_chunk_t *, c, tor_free(c));
+ smartlist_free(votestrings);
+}
+
/** Try to compute a v3 networkstatus consensus from the currently pending
* votes. Return 0 on success, -1 on failure. Store the consensus in
* pending_consensus: it won't be ready to be published until we have
@@ -3028,8 +3308,8 @@ dirvote_compute_consensuses(void)
{
/* Have we got enough votes to try? */
int n_votes, n_voters, n_vote_running = 0;
- smartlist_t *votes = NULL, *votestrings = NULL;
- char *consensus_body = NULL, *signatures = NULL, *votefile;
+ smartlist_t *votes = NULL;
+ char *consensus_body = NULL, *signatures = NULL;
networkstatus_t *consensus = NULL;
authority_cert_t *my_cert;
pending_consensus_t pending[N_CONSENSUS_FLAVORS];
@@ -3040,6 +3320,17 @@ dirvote_compute_consensuses(void)
if (!pending_vote_list)
pending_vote_list = smartlist_new();
+ /* Write votes to disk */
+ write_v3_votes_to_disk(pending_vote_list);
+
+ /* Setup votes smartlist */
+ votes = smartlist_new();
+ SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v,
+ {
+ smartlist_add(votes, v->vote); /* collect votes to compute consensus */
+ });
+
+ /* See if consensus managed to achieve majority */
n_voters = get_n_authorities(V3_DIRINFO);
n_votes = smartlist_len(pending_vote_list);
if (n_votes <= n_voters/2) {
@@ -3066,24 +3357,6 @@ dirvote_compute_consensuses(void)
goto err;
}
- votes = smartlist_new();
- votestrings = smartlist_new();
- SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v,
- {
- sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
- c->bytes = v->vote_body->dir;
- c->len = v->vote_body->dir_len;
- smartlist_add(votestrings, c); /* collect strings to write to disk */
-
- smartlist_add(votes, v->vote); /* collect votes to compute consensus */
- });
-
- votefile = get_datadir_fname("v3-status-votes");
- write_chunks_to_file(votefile, votestrings, 0, 0);
- tor_free(votefile);
- SMARTLIST_FOREACH(votestrings, sized_chunk_t *, c, tor_free(c));
- smartlist_free(votestrings);
-
{
char legacy_dbuf[DIGEST_LEN];
crypto_pk_t *legacy_sign=NULL;
@@ -3373,7 +3646,7 @@ dirvote_publish_consensus(void)
continue;
}
- if (networkstatus_set_current_consensus(pending->body, name, 0))
+ if (networkstatus_set_current_consensus(pending->body, name, 0, NULL))
log_warn(LD_DIR, "Error publishing %s consensus", name);
else
log_notice(LD_DIR, "Published %s consensus", name);
@@ -3515,7 +3788,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
if (consensus_method >= MIN_METHOD_FOR_P6_LINES &&
ri->ipv6_exit_policy) {
- /* XXXX024 This doesn't match proposal 208, which says these should
+ /* XXXX+++ This doesn't match proposal 208, which says these should
* be taken unchanged from the routerinfo. That's bogosity, IMO:
* the proposal should have said to do this instead.*/
char *p6 = write_short_policy(ri->ipv6_exit_policy);
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index 0b1d284060..efd233ef5f 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -55,7 +55,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 22
+#define MAX_SUPPORTED_CONSENSUS_METHOD 25
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@@ -90,10 +90,27 @@
* ed25519 identities in microdescriptors. (Broken; see
* consensus_method_is_supported() for more info.) */
#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21
+
/** Lowest consensus method where authorities vote on ed25519 ids and ensure
* ed25519 id consistency. */
#define MIN_METHOD_FOR_ED25519_ID_VOTING 22
+/** Lowest consensus method where authorities may include a shared random
+ * value(s). */
+#define MIN_METHOD_FOR_SHARED_RANDOM 23
+
+/** Lowest consensus method where authorities drop all nodes that don't get
+ * the Valid flag. */
+#define MIN_METHOD_FOR_EXCLUDING_INVALID_NODES 24
+
+/** Lowest consensus method where authorities vote on required/recommended
+ * protocols. */
+#define MIN_METHOD_FOR_RECOMMENDED_PROTOCOLS 25
+
+/** Lowest consensus method where authorities add protocols to routerstatus
+ * entries. */
+#define MIN_METHOD_FOR_RS_PROTOCOLS 25
+
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
@@ -121,12 +138,46 @@ void ns_detached_signatures_free(ns_detached_signatures_t *s);
authority_cert_t *authority_cert_dup(authority_cert_t *cert);
/* vote scheduling */
+
+/** Scheduling information for a voting interval. */
+typedef struct {
+ /** When do we generate and distribute our vote for this interval? */
+ time_t voting_starts;
+ /** When do we send an HTTP request for any votes that we haven't
+ * been posted yet?*/
+ time_t fetch_missing_votes;
+ /** When do we give up on getting more votes and generate a consensus? */
+ time_t voting_ends;
+ /** When do we send an HTTP request for any signatures we're expecting to
+ * see on the consensus? */
+ time_t fetch_missing_signatures;
+ /** When do we publish the consensus? */
+ time_t interval_starts;
+
+ /* True iff we have generated and distributed our vote. */
+ int have_voted;
+ /* True iff we've requested missing votes. */
+ int have_fetched_missing_votes;
+ /* True iff we have built a consensus and sent the signatures around. */
+ int have_built_consensus;
+ /* True iff we've fetched missing signatures. */
+ 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);
+
void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
time_t dirvote_get_start_of_next_interval(time_t now,
int interval,
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);
/* invoked on timers and by outside triggers. */
struct pending_vote_t * dirvote_add_vote(const char *vote_body,
@@ -173,9 +224,13 @@ document_signature_t *voter_get_sig_by_algorithm(
digest_algorithm_t alg);
#ifdef DIRVOTE_PRIVATE
+STATIC int32_t dirvote_get_intermediate_param_value(
+ const smartlist_t *param_list,
+ const char *keyword,
+ int32_t default_val);
STATIC char *format_networkstatus_vote(crypto_pk_t *private_key,
networkstatus_t *v3_ns);
-STATIC char *dirvote_compute_params(smartlist_t *votes, int method,
+STATIC smartlist_t *dirvote_compute_params(smartlist_t *votes, int method,
int total_authorities);
STATIC char *compute_consensus_package_lines(smartlist_t *votes);
STATIC char *make_consensus_method_list(int low, int high, const char *sep);
diff --git a/src/or/dns.c b/src/or/dns.c
index c7adfbc971..c1e3c3256e 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -9,6 +9,42 @@
* This is implemented as a wrapper around Adam Langley's eventdns.c code.
* (We can't just use gethostbyname() and friends because we really need to
* be nonblocking.)
+ *
+ * There are three main cases when a Tor relay uses dns.c to launch a DNS
+ * request:
+ * <ol>
+ * <li>To check whether the DNS server is working more or less correctly.
+ * This happens via dns_launch_correctness_checks(). The answer is
+ * reported in the return value from later calls to
+ * dns_seems_to_be_broken().
+ * <li>When a client has asked the relay, in a RELAY_BEGIN cell, to connect
+ * to a given server by hostname. This happens via dns_resolve().
+ * <li>When a client has asked the rela, in a RELAY_RESOLVE cell, to look
+ * up a given server's IP address(es) by hostname. This also happens via
+ * dns_resolve().
+ * </ol>
+ *
+ * Each of these gets handled a little differently.
+ *
+ * To check for correctness, we look up some hostname we expect to exist and
+ * have real entries, some hostnames which we expect to definitely not exist,
+ * and some hostnames that we expect to probably not exist. If too many of
+ * the hostnames that shouldn't exist do exist, that's a DNS hijacking
+ * attempt. If too many of the hostnames that should exist have the same
+ * addresses as the ones that shouldn't exist, that's a very bad DNS hijacking
+ * attempt, or a very naughty captive portal. And if the hostnames that
+ * should exist simply don't exist, we probably have a broken nameserver.
+ *
+ * To handle client requests, we first check our cache for answers. If there
+ * isn't something up-to-date, we've got to launch A or AAAA requests as
+ * appropriate. How we handle responses to those in particular is a bit
+ * complex; see dns_lookup() and set_exitconn_info_from_resolve().
+ *
+ * When a lookup is finally complete, the inform_pending_connections()
+ * function will tell all of the streams that have been waiting for the
+ * resolve, by calling connection_exit_connect() if the client sent a
+ * RELAY_BEGIN cell, and by calling send_resolved_cell() or
+ * send_hostname_cell() if the client sent a RELAY_RESOLVE cell.
**/
#define DNS_PRIVATE
@@ -27,61 +63,8 @@
#include "router.h"
#include "ht.h"
#include "sandbox.h"
-#ifdef HAVE_EVENT2_DNS_H
#include <event2/event.h>
#include <event2/dns.h>
-#else
-#include <event.h>
-#include "eventdns.h"
-#ifndef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
-#define HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
-#endif
-#endif
-
-#ifndef HAVE_EVENT2_DNS_H
-struct evdns_base;
-struct evdns_request;
-#define evdns_base_new(x,y) tor_malloc(1)
-#define evdns_base_clear_nameservers_and_suspend(base) \
- evdns_clear_nameservers_and_suspend()
-#define evdns_base_search_clear(base) evdns_search_clear()
-#define evdns_base_set_default_outgoing_bind_address(base, a, len) \
- evdns_set_default_outgoing_bind_address((a),(len))
-#define evdns_base_resolv_conf_parse(base, options, fname) \
- evdns_resolv_conf_parse((options), (fname))
-#define evdns_base_count_nameservers(base) \
- evdns_count_nameservers()
-#define evdns_base_resume(base) \
- evdns_resume()
-#define evdns_base_config_windows_nameservers(base) \
- evdns_config_windows_nameservers()
-#define evdns_base_set_option_(base, opt, val) \
- evdns_set_option((opt),(val),DNS_OPTIONS_ALL)
-/* Note: our internal eventdns.c, plus Libevent 1.4, used a 1 return to
- * signify failure to launch a resolve. Libevent 2.0 uses a -1 return to
- * signify a failure on a resolve, though if we're on Libevent 2.0, we should
- * have event2/dns.h and never hit these macros. Regardless, 0 is success. */
-#define evdns_base_resolve_ipv4(base, addr, options, cb, ptr) \
- ((evdns_resolve_ipv4((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-#define evdns_base_resolve_ipv6(base, addr, options, cb, ptr) \
- ((evdns_resolve_ipv6((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-#define evdns_base_resolve_reverse(base, addr, options, cb, ptr) \
- ((evdns_resolve_reverse((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-#define evdns_base_resolve_reverse_ipv6(base, addr, options, cb, ptr) \
- ((evdns_resolve_reverse_ipv6((addr), (options), (cb), (ptr))!=0) \
- ? NULL : ((void*)1))
-
-#elif defined(LIBEVENT_VERSION_NUMBER) && LIBEVENT_VERSION_NUMBER < 0x02000303
-#define evdns_base_set_option_(base, opt, val) \
- evdns_base_set_option((base), (opt),(val),DNS_OPTIONS_ALL)
-
-#else
-#define evdns_base_set_option_ evdns_base_set_option
-
-#endif
/** How long will we wait for an answer from the resolver before we decide
* that the resolver is wedged? */
@@ -260,29 +243,19 @@ has_dns_init_failed(void)
}
/** Helper: Given a TTL from a DNS response, determine what TTL to give the
- * OP that asked us to resolve it. */
+ * OP that asked us to resolve it, and how long to cache that record
+ * ourselves. */
uint32_t
dns_clip_ttl(uint32_t ttl)
{
- if (ttl < MIN_DNS_TTL)
- return MIN_DNS_TTL;
- else if (ttl > MAX_DNS_TTL)
- return MAX_DNS_TTL;
- else
- return ttl;
-}
-
-/** Helper: Given a TTL from a DNS response, determine how long to hold it in
- * our cache. */
-STATIC uint32_t
-dns_get_expiry_ttl(uint32_t ttl)
-{
- if (ttl < MIN_DNS_TTL)
- return MIN_DNS_TTL;
- else if (ttl > MAX_DNS_ENTRY_AGE)
- return MAX_DNS_ENTRY_AGE;
+ /* This logic is a defense against "DefectTor" DNS-based traffic
+ * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf .
+ * We only give two values: a "low" value and a "high" value.
+ */
+ if (ttl < MIN_DNS_TTL_AT_EXIT)
+ return MIN_DNS_TTL_AT_EXIT;
else
- return ttl;
+ return MAX_DNS_TTL_AT_EXIT;
}
/** Helper: free storage held by an entry in the DNS cache. */
@@ -353,7 +326,7 @@ cached_resolve_add_answer(cached_resolve_t *resolve,
resolve->result_ipv4.err_ipv4 = dns_result;
resolve->res_status_ipv4 = RES_STATUS_DONE_ERR;
}
-
+ resolve->ttl_ipv4 = ttl;
} else if (query_type == DNS_IPv6_AAAA) {
if (resolve->res_status_ipv6 != RES_STATUS_INFLIGHT)
return;
@@ -368,6 +341,7 @@ cached_resolve_add_answer(cached_resolve_t *resolve,
resolve->result_ipv6.err_ipv6 = dns_result;
resolve->res_status_ipv6 = RES_STATUS_DONE_ERR;
}
+ resolve->ttl_ipv6 = ttl;
}
}
@@ -548,6 +522,7 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type,
answer_type = RESOLVED_TYPE_ERROR;
/* fall through. */
}
+ /* Falls through. */
case RESOLVED_TYPE_ERROR_TRANSIENT:
case RESOLVED_TYPE_ERROR:
{
@@ -846,8 +821,14 @@ dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve,
}
/** Given an exit connection <b>exitconn</b>, and a cached_resolve_t
- * <b>resolve</b> whose DNS lookups have all succeeded or failed, update the
- * appropriate fields (address_ttl and addr) of <b>exitconn</b>.
+ * <b>resolve</b> whose DNS lookups have all either succeeded or failed,
+ * update the appropriate fields (address_ttl and addr) of <b>exitconn</b>.
+ *
+ * The logic can be complicated here, since we might have launched both
+ * an A lookup and an AAAA lookup, and since either of those might have
+ * succeeded or failed, and since we want to answer a RESOLVE cell with
+ * a full answer but answer a BEGIN cell with whatever answer the client
+ * would accept <i>and</i> we could still connect to.
*
* If this is a reverse lookup, set *<b>hostname_out</b> to a newly allocated
* copy of the name resulting hostname.
@@ -1190,7 +1171,12 @@ dns_found_answer(const char *address, uint8_t query_type,
/** Given a pending cached_resolve_t that we just finished resolving,
* inform every connection that was waiting for the outcome of that
- * resolution. */
+ * resolution.
+ *
+ * Do this by sending a RELAY_RESOLVED cell (if the pending stream had sent us
+ * RELAY_RESOLVE cell), or by launching an exit connection (if the pending
+ * stream had send us a RELAY_BEGIN cell).
+ */
static void
inform_pending_connections(cached_resolve_t *resolve)
{
@@ -1323,7 +1309,7 @@ make_pending_resolve_cached(cached_resolve_t *resolve)
resolve->ttl_hostname < ttl)
ttl = resolve->ttl_hostname;
- set_expiry(new_resolve, time(NULL) + dns_get_expiry_ttl(ttl));
+ set_expiry(new_resolve, time(NULL) + dns_clip_ttl(ttl));
}
assert_cache_ok();
@@ -1373,23 +1359,6 @@ configure_nameservers(int force)
}
}
-#ifdef HAVE_EVDNS_SET_DEFAULT_OUTGOING_BIND_ADDRESS
- if (! tor_addr_is_null(&options->OutboundBindAddressIPv4_)) {
- int socklen;
- struct sockaddr_storage ss;
- socklen = tor_addr_to_sockaddr(&options->OutboundBindAddressIPv4_, 0,
- (struct sockaddr *)&ss, sizeof(ss));
- if (socklen <= 0) {
- log_warn(LD_BUG, "Couldn't convert outbound bind address to sockaddr."
- " Ignoring.");
- } else {
- evdns_base_set_default_outgoing_bind_address(the_evdns_base,
- (struct sockaddr *)&ss,
- socklen);
- }
- }
-#endif
-
evdns_set_log_fn(evdns_log_cb);
if (conf_fname) {
log_debug(LD_FS, "stat()ing %s", conf_fname);
@@ -1454,16 +1423,33 @@ configure_nameservers(int force)
}
#endif
-#define SET(k,v) evdns_base_set_option_(the_evdns_base, (k), (v))
+#define SET(k,v) evdns_base_set_option(the_evdns_base, (k), (v))
+ // If we only have one nameserver, it does not make sense to back off
+ // from it for a timeout. Unfortunately, the value for max-timeouts is
+ // currently clamped by libevent to 255, but it does not hurt to set
+ // it higher in case libevent gets a patch for this.
+ // Reducing attempts in the case of just one name server too, because
+ // it is very likely to be a local one where a network connectivity
+ // issue should not cause an attempt to fail.
if (evdns_base_count_nameservers(the_evdns_base) == 1) {
- SET("max-timeouts:", "16");
- SET("timeout:", "10");
+ SET("max-timeouts:", "1000000");
+ SET("attempts:", "1");
} else {
SET("max-timeouts:", "3");
- SET("timeout:", "5");
}
+ // Elongate the queue of maximum inflight dns requests, so if a bunch
+ // time out at the resolver (happens commonly with unbound) we won't
+ // stall every other DNS request. This potentially means some wasted
+ // CPU as there's a walk over a linear queue involved, but this is a
+ // much better tradeoff compared to just failing DNS requests because
+ // of a full queue.
+ SET("max-inflight:", "8192");
+
+ // Time out after 5 seconds if no reply.
+ SET("timeout:", "5");
+
if (options->ServerDNSRandomizeCase)
SET("randomize-case:", "1");
else
diff --git a/src/or/dns.h b/src/or/dns.h
index b14f7dd29c..951a2a3467 100644
--- a/src/or/dns.h
+++ b/src/or/dns.h
@@ -12,6 +12,18 @@
#ifndef TOR_DNS_H
#define TOR_DNS_H
+/** Lowest value for DNS ttl that a server will give. */
+#define MIN_DNS_TTL_AT_EXIT (5*60)
+/** Highest value for DNS ttl that a server will give. */
+#define MAX_DNS_TTL_AT_EXIT (60*60)
+
+/** How long do we keep DNS cache entries before purging them (regardless of
+ * their TTL)? */
+#define MAX_DNS_ENTRY_AGE (3*60*60)
+/** How long do we cache/tell clients to cache DNS records when no TTL is
+ * known? */
+#define DEFAULT_DNS_TTL (30*60)
+
int dns_init(void);
int has_dns_init_failed(void);
void dns_free_all(void);
@@ -31,8 +43,6 @@ void dump_dns_mem_usage(int severity);
#ifdef DNS_PRIVATE
#include "dns_structs.h"
-STATIC uint32_t dns_get_expiry_ttl(uint32_t ttl);
-
MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn,
int is_resolve,or_circuit_t *oncirc, char **hostname_out,
int *made_connection_pending_out, cached_resolve_t **resolve_out));
diff --git a/src/or/dns_structs.h b/src/or/dns_structs.h
index bb67459d7b..bc6067213d 100644
--- a/src/or/dns_structs.h
+++ b/src/or/dns_structs.h
@@ -1,3 +1,15 @@
+/* Copyright (c) 2003-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dns_structs.h
+ *
+ * \brief Structures used in dns.c. Exposed to dns.c, and to the unit tests
+ * that declare DNS_PRIVATE.
+ */
+
#ifndef TOR_DNS_STRUCTS_H
#define TOR_DNS_STRUCTS_H
diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c
index 74f17ce78c..f5a4f2ac0f 100644
--- a/src/or/dnsserv.c
+++ b/src/or/dnsserv.c
@@ -17,14 +17,10 @@
#include "control.h"
#include "main.h"
#include "policies.h"
-#ifdef HAVE_EVENT2_DNS_H
#include <event2/dns.h>
#include <event2/dns_compat.h>
/* XXXX this implies we want an improved evdns */
#include <event2/dns_struct.h>
-#else
-#include "eventdns.h"
-#endif
/** Helper function: called by evdns whenever the client sends a request to our
* DNSPort. We need to eventually answer the request <b>req</b>.
@@ -130,7 +126,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
tor_addr_copy(&TO_CONN(conn)->addr, &tor_addr);
TO_CONN(conn)->port = port;
- TO_CONN(conn)->address = tor_dup_addr(&tor_addr);
+ TO_CONN(conn)->address = tor_addr_to_str_dup(&tor_addr);
if (q->type == EVDNS_TYPE_A || q->type == EVDNS_TYPE_AAAA ||
q->type == EVDNS_QTYPE_ALL) {
@@ -140,6 +136,8 @@ evdns_server_callback(struct evdns_server_request *req, void *data_)
entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR;
}
+ /* This serves our DNS port so enable DNS request by default. */
+ entry_conn->entry_cfg.dns_request = 1;
if (q->type == EVDNS_TYPE_A || q->type == EVDNS_QTYPE_ALL) {
entry_conn->entry_cfg.ipv4_traffic = 1;
entry_conn->entry_cfg.ipv6_traffic = 0;
@@ -205,7 +203,7 @@ dnsserv_launch_request(const char *name, int reverse,
tor_addr_copy(&TO_CONN(conn)->addr, &control_conn->base_.addr);
#ifdef AF_UNIX
/*
- * The control connection can be AF_UNIX and if so tor_dup_addr will
+ * The control connection can be AF_UNIX and if so tor_addr_to_str_dup will
* unhelpfully say "<unknown address type>"; say "(Tor_internal)"
* instead.
*/
@@ -214,11 +212,11 @@ dnsserv_launch_request(const char *name, int reverse,
TO_CONN(conn)->address = tor_strdup("(Tor_internal)");
} else {
TO_CONN(conn)->port = control_conn->base_.port;
- TO_CONN(conn)->address = tor_dup_addr(&control_conn->base_.addr);
+ TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
}
#else
TO_CONN(conn)->port = control_conn->base_.port;
- TO_CONN(conn)->address = tor_dup_addr(&control_conn->base_.addr);
+ TO_CONN(conn)->address = tor_addr_to_str_dup(&control_conn->base_.addr);
#endif
if (reverse)
@@ -292,6 +290,10 @@ evdns_get_orig_address(const struct evdns_server_request *req,
case RESOLVED_TYPE_IPV6:
type = EVDNS_TYPE_AAAA;
break;
+ case RESOLVED_TYPE_ERROR:
+ case RESOLVED_TYPE_ERROR_TRANSIENT:
+ /* Addr doesn't matter, since we're not sending it back in the reply.*/
+ return addr;
default:
tor_fragile_assert();
return addr;
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 310a948b35..265b6dcda1 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -76,6 +76,14 @@ static const node_t *choose_random_entry_impl(cpath_build_state_t *state,
int *n_options_out);
static int num_bridges_usable(void);
+/* Default number of entry guards in the case where the NumEntryGuards
+ * consensus parameter is not set */
+#define DEFAULT_N_GUARDS 1
+/* Minimum and maximum number of entry guards (in case the NumEntryGuards
+ * consensus parameter is set). */
+#define MIN_N_GUARDS 1
+#define MAX_N_GUARDS 10
+
/** Return the list of entry guards, creating it if necessary. */
const smartlist_t *
get_entry_guards(void)
@@ -488,7 +496,8 @@ decide_num_guards(const or_options_t *options, int for_directory)
return options->NumEntryGuards;
/* Use the value from the consensus, or 3 if no guidance. */
- return networkstatus_get_param(NULL, "NumEntryGuards", 3, 1, 10);
+ return networkstatus_get_param(NULL, "NumEntryGuards", DEFAULT_N_GUARDS,
+ MIN_N_GUARDS, MAX_N_GUARDS);
}
/** If the use of entry guards is configured, choose more entry guards
@@ -722,8 +731,9 @@ entry_guards_compute_status(const or_options_t *options, time_t now)
*
* If <b>mark_relay_status</b>, also call router_set_status() on this
* relay.
- *
- * XXX024 change succeeded and mark_relay_status into 'int flags'.
+ */
+/* XXX We could change succeeded and mark_relay_status into 'int flags'.
+ * Too many boolean arguments is a recipe for confusion.
*/
int
entry_guard_register_connect_status(const char *digest, int succeeded,
@@ -1243,7 +1253,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
} else {
strlcpy(node->nickname, smartlist_get(args,0), MAX_NICKNAME_LEN+1);
if (base16_decode(node->identity, DIGEST_LEN, smartlist_get(args,1),
- strlen(smartlist_get(args,1)))<0) {
+ strlen(smartlist_get(args,1))) != DIGEST_LEN) {
*msg = tor_strdup("Unable to parse entry nodes: "
"Bad hex digest for EntryGuard");
}
@@ -1299,8 +1309,9 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
log_warn(LD_BUG, "EntryGuardAddedBy line is not long enough.");
continue;
}
- if (base16_decode(d, sizeof(d), line->value, HEX_DIGEST_LEN)<0 ||
- line->value[HEX_DIGEST_LEN] != ' ') {
+ if (base16_decode(d, sizeof(d),
+ line->value, HEX_DIGEST_LEN) != sizeof(d) ||
+ line->value[HEX_DIGEST_LEN] != ' ') {
log_warn(LD_BUG, "EntryGuardAddedBy line %s does not begin with "
"hex digest", escaped(line->value));
continue;
@@ -1444,7 +1455,6 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
}
} else {
if (state_version) {
- time_t now = time(NULL);
e->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now);
e->chosen_by_version = tor_strdup(state_version);
}
@@ -1466,7 +1476,7 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
}
entry_guards = new_entry_guards;
entry_guards_dirty = 0;
- /* XXX024 hand new_entry_guards to this func, and move it up a
+ /* XXX hand new_entry_guards to this func, and move it up a
* few lines, so we don't have to re-dirty it */
if (remove_obsolete_entry_guards(now))
entry_guards_dirty = 1;
@@ -2022,6 +2032,7 @@ bridge_add_from_config(bridge_line_t *bridge_line)
if (bridge_line->transport_name)
b->transport_name = bridge_line->transport_name;
b->fetch_status.schedule = DL_SCHED_BRIDGE;
+ b->fetch_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL;
b->socks_args = bridge_line->socks_args;
if (!bridge_list)
bridge_list = smartlist_new();
@@ -2412,6 +2423,44 @@ num_bridges_usable(void)
return n_options;
}
+/** Return a smartlist containing all bridge identity digests */
+MOCK_IMPL(smartlist_t *,
+list_bridge_identities, (void))
+{
+ smartlist_t *result = NULL;
+ char *digest_tmp;
+
+ if (get_options()->UseBridges && bridge_list) {
+ result = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
+ digest_tmp = tor_malloc(DIGEST_LEN);
+ memcpy(digest_tmp, b->identity, DIGEST_LEN);
+ smartlist_add(result, digest_tmp);
+ } SMARTLIST_FOREACH_END(b);
+ }
+
+ return result;
+}
+
+/** Get the download status for a bridge descriptor given its identity */
+MOCK_IMPL(download_status_t *,
+get_bridge_dl_status_by_id, (const char *digest))
+{
+ download_status_t *dl = NULL;
+
+ if (digest && get_options()->UseBridges && bridge_list) {
+ SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
+ if (tor_memeq(digest, b->identity, DIGEST_LEN)) {
+ dl = &(b->fetch_status);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(b);
+ }
+
+ return dl;
+}
+
/** Return 1 if we have at least one descriptor for an entry guard
* (bridge or member of EntryNodes) and all descriptors we know are
* down. Else return 0. If <b>act</b> is 1, then mark the down guards
diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h
index 247c80940e..1021e67d43 100644
--- a/src/or/entrynodes.h
+++ b/src/or/entrynodes.h
@@ -179,5 +179,9 @@ guard_get_guardfraction_bandwidth(guardfraction_bandwidth_t *guardfraction_bw,
int orig_bandwidth,
uint32_t guardfraction_percentage);
+MOCK_DECL(smartlist_t *, list_bridge_identities, (void));
+MOCK_DECL(download_status_t *, get_bridge_dl_status_by_id,
+ (const char *digest));
+
#endif
diff --git a/src/or/eventdns_tor.h b/src/or/eventdns_tor.h
deleted file mode 100644
index 5db09ae043..0000000000
--- a/src/or/eventdns_tor.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Copyright (c) 2007-2016, The Tor Project, Inc. */
-/* See LICENSE for licensing information */
-
-#ifndef TOR_EVENTDNS_TOR_H
-#define TOR_EVENTDNS_TOR_H
-
-#include "orconfig.h"
-#define DNS_USE_OPENSSL_FOR_ID
-#ifndef HAVE_UINT
-typedef unsigned int uint;
-#endif
-#ifndef HAVE_U_CHAR
-typedef unsigned char u_char;
-#endif
-#include "torint.h"
-
-/* These are for debugging possible memory leaks. */
-#include "util.h"
-#include "compat.h"
-
-#endif
-
diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c
index aa1b3e26fe..676adfd8bf 100644
--- a/src/or/ext_orport.c
+++ b/src/or/ext_orport.c
@@ -4,7 +4,17 @@
/**
* \file ext_orport.c
* \brief Code implementing the Extended ORPort.
-*/
+ *
+ * The Extended ORPort interface is used by pluggable transports to
+ * communicate additional information to a Tor bridge, including
+ * address information. For more information on this interface,
+ * see pt-spec.txt in torspec.git.
+ *
+ * There is no separate structure for extended ORPort connections; they use
+ * or_connection_t objects, and share most of their implementation with
+ * connection_or.c. Once the handshake is done, an extended ORPort connection
+ * turns into a regular OR connection, using connection_ext_or_transition().
+ */
#define EXT_ORPORT_PRIVATE
#include "or.h"
@@ -41,12 +51,7 @@ ext_or_cmd_free(ext_or_cmd_t *cmd)
static int
connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out)
{
- IF_HAS_BUFFEREVENT(conn, {
- struct evbuffer *input = bufferevent_get_input(conn->bufev);
- return fetch_ext_or_command_from_evbuffer(input, out);
- }) ELSE_IF_NO_BUFFEREVENT {
- return fetch_ext_or_command_from_buf(conn->inbuf, out);
- }
+ return fetch_ext_or_command_from_buf(conn->inbuf, out);
}
/** Write an Extended ORPort message to <b>conn</b>. Use
@@ -461,8 +466,8 @@ connection_ext_or_handle_cmd_useraddr(connection_t *conn,
return -1;
{ /* do some logging */
- char *old_address = tor_dup_addr(&conn->addr);
- char *new_address = tor_dup_addr(&addr);
+ char *old_address = tor_addr_to_str_dup(&conn->addr);
+ char *new_address = tor_addr_to_str_dup(&addr);
log_debug(LD_NET, "Received USERADDR."
"We rewrite our address from '%s:%u' to '%s:%u'.",
@@ -478,7 +483,7 @@ connection_ext_or_handle_cmd_useraddr(connection_t *conn,
if (conn->address) {
tor_free(conn->address);
}
- conn->address = tor_dup_addr(&addr);
+ conn->address = tor_addr_to_str_dup(&addr);
return 0;
}
diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c
index 53b311e580..eeeb0f1de3 100644
--- a/src/or/fp_pair.c
+++ b/src/or/fp_pair.c
@@ -7,6 +7,14 @@
* \brief Manages data structures for associating pairs of fingerprints. Used
* to handle combinations of identity/signing-key fingerprints for
* authorities.
+ *
+ * This is a nice, simple, compact data structure module that handles a map
+ * from (signing key fingerprint, identity key fingerprint) to void *. The
+ * fingerprints here are SHA1 digests of RSA keys.
+ *
+ * This structure is used in directory.c and in routerlist.c for handling
+ * handling authority certificates, since we never want more than a single
+ * certificate for any (ID key, signing key) pair.
**/
#include "or.h"
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 681cb900f2..00c055bbe7 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -7,6 +7,24 @@
* to summarizing client connections by country to entry guards, bridges,
* and directory servers; and for statistics on answering network status
* requests.
+ *
+ * There are two main kinds of functions in this module: geoip functions,
+ * which map groups of IPv4 and IPv6 addresses to country codes, and
+ * statistical functions, which collect statistics about different kinds of
+ * per-country usage.
+ *
+ * The geoip lookup tables are implemented as sorted lists of disjoint address
+ * ranges, each mapping to a singleton geoip_country_t. These country objects
+ * are also indexed by their names in a hashtable.
+ *
+ * The tables are populated from disk at startup by the geoip_load_file()
+ * function. For more information on the file format they read, see that
+ * function. See the scripts and the README file in src/config for more
+ * information about how those files are generated.
+ *
+ * Tor uses GeoIP information in order to implement user requests (such as
+ * ExcludeNodes {cc}), and to keep track of how much usage relays are getting
+ * for each country.
*/
#define GEOIP_PRIVATE
@@ -80,9 +98,9 @@ geoip_add_entry(const tor_addr_t *low, const tor_addr_t *high,
intptr_t idx;
void *idxplus1_;
- if (tor_addr_family(low) != tor_addr_family(high))
+ IF_BUG_ONCE(tor_addr_family(low) != tor_addr_family(high))
return;
- if (tor_addr_compare(high, low, CMP_EXACT) < 0)
+ IF_BUG_ONCE(tor_addr_compare(high, low, CMP_EXACT) < 0)
return;
idxplus1_ = strmap_get_lc(country_idxplus1_by_lc_code, country);
@@ -110,8 +128,8 @@ geoip_add_entry(const tor_addr_t *low, const tor_addr_t *high,
smartlist_add(geoip_ipv4_entries, ent);
} else if (tor_addr_family(low) == AF_INET6) {
geoip_ipv6_entry_t *ent = tor_malloc_zero(sizeof(geoip_ipv6_entry_t));
- ent->ip_low = *tor_addr_to_in6(low);
- ent->ip_high = *tor_addr_to_in6(high);
+ ent->ip_low = *tor_addr_to_in6_assert(low);
+ ent->ip_high = *tor_addr_to_in6_assert(high);
ent->country = idx;
smartlist_add(geoip_ipv6_entries, ent);
}
@@ -504,7 +522,7 @@ clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
}
HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
- clientmap_entries_eq);
+ clientmap_entries_eq)
HT_GENERATE2(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
clientmap_entries_eq, 0.6, tor_reallocarray_, tor_free_)
@@ -718,7 +736,7 @@ dirreq_map_ent_hash(const dirreq_map_entry_t *entry)
}
HT_PROTOTYPE(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash,
- dirreq_map_ent_eq);
+ dirreq_map_ent_eq)
HT_GENERATE2(dirreqmap, dirreq_map_entry_t, node, dirreq_map_ent_hash,
dirreq_map_ent_eq, 0.6, tor_reallocarray_, tor_free_)
@@ -824,7 +842,6 @@ geoip_get_transport_history(void)
static const char* no_transport_str = "<OR>";
clientmap_entry_t **ent;
- const char *transport_name = NULL;
smartlist_t *string_chunks = smartlist_new();
char *the_string = NULL;
@@ -850,7 +867,7 @@ geoip_get_transport_history(void)
HT_FOREACH(ent, clientmap, &client_history) {
uintptr_t val;
void *ptr;
- transport_name = (*ent)->transport_name;
+ const char *transport_name = (*ent)->transport_name;
if (!transport_name)
transport_name = no_transport_str;
@@ -916,13 +933,13 @@ geoip_get_dirreq_history(dirreq_type_t type)
smartlist_t *dirreq_completed = NULL;
uint32_t complete = 0, timeouts = 0, running = 0;
int bufsize = 1024, written;
- dirreq_map_entry_t **ptr, **next, *ent;
+ dirreq_map_entry_t **ptr, **next;
struct timeval now;
tor_gettimeofday(&now);
dirreq_completed = smartlist_new();
for (ptr = HT_START(dirreqmap, &dirreq_map); ptr; ptr = next) {
- ent = *ptr;
+ dirreq_map_entry_t *ent = *ptr;
if (ent->type != type) {
next = HT_NEXT(dirreqmap, &dirreq_map, ptr);
continue;
@@ -1024,7 +1041,7 @@ geoip_get_client_history(geoip_client_action_t action,
smartlist_t *entries = NULL;
int n_countries = geoip_get_n_countries();
int i;
- clientmap_entry_t **ent;
+ clientmap_entry_t **cm_ent;
unsigned *counts = NULL;
unsigned total = 0;
unsigned ipv4_count = 0, ipv6_count = 0;
@@ -1033,17 +1050,17 @@ geoip_get_client_history(geoip_client_action_t action,
return -1;
counts = tor_calloc(n_countries, sizeof(unsigned));
- HT_FOREACH(ent, clientmap, &client_history) {
+ HT_FOREACH(cm_ent, clientmap, &client_history) {
int country;
- if ((*ent)->action != (int)action)
+ if ((*cm_ent)->action != (int)action)
continue;
- country = geoip_get_country_by_addr(&(*ent)->addr);
+ country = geoip_get_country_by_addr(&(*cm_ent)->addr);
if (country < 0)
country = 0; /** unresolved requests are stored at index 0. */
tor_assert(0 <= country && country < n_countries);
++counts[country];
++total;
- switch (tor_addr_family(&(*ent)->addr)) {
+ switch (tor_addr_family(&(*cm_ent)->addr)) {
case AF_INET:
ipv4_count++;
break;
diff --git a/src/or/hibernate.c b/src/or/hibernate.c
index 9408925d96..e3c80b5f14 100644
--- a/src/or/hibernate.c
+++ b/src/or/hibernate.c
@@ -8,6 +8,12 @@
* etc in preparation for closing down or going dormant; and to track
* bandwidth and time intervals to know when to hibernate and when to
* stop hibernating.
+ *
+ * Ordinarily a Tor relay is "Live".
+ *
+ * A live relay can stop accepting connections for one of two reasons: either
+ * it is trying to conserve bandwidth because of bandwidth accounting rules
+ * ("soft hibernation"), or it is about to shut down ("exiting").
**/
/*
@@ -28,13 +34,12 @@ hibernating, phase 2:
#include "config.h"
#include "connection.h"
#include "connection_edge.h"
+#include "control.h"
#include "hibernate.h"
#include "main.h"
#include "router.h"
#include "statefile.h"
-extern long stats_n_seconds_working; /* published uptime */
-
/** Are we currently awake, asleep, running out of bandwidth, or shutting
* down? */
static hibernate_state_t hibernate_state = HIBERNATE_STATE_INITIAL;
@@ -50,8 +55,10 @@ typedef enum {
UNIT_MONTH=1, UNIT_WEEK=2, UNIT_DAY=3,
} time_unit_t;
-/* Fields for accounting logic. Accounting overview:
+/*
+ * @file hibernate.c
*
+ * <h4>Accounting</h4>
* Accounting is designed to ensure that no more than N bytes are sent in
* either direction over a given interval (currently, one month, one week, or
* one day) We could
@@ -65,17 +72,21 @@ typedef enum {
*
* Each interval runs as follows:
*
- * 1. We guess our bandwidth usage, based on how much we used
+ * <ol>
+ * <li>We guess our bandwidth usage, based on how much we used
* last time. We choose a "wakeup time" within the interval to come up.
- * 2. Until the chosen wakeup time, we hibernate.
- * 3. We come up at the wakeup time, and provide bandwidth until we are
+ * <li>Until the chosen wakeup time, we hibernate.
+ * <li> We come up at the wakeup time, and provide bandwidth until we are
* "very close" to running out.
- * 4. Then we go into low-bandwidth mode, and stop accepting new
+ * <li> Then we go into low-bandwidth mode, and stop accepting new
* connections, but provide bandwidth until we run out.
- * 5. Then we hibernate until the end of the interval.
+ * <li> Then we hibernate until the end of the interval.
*
* If the interval ends before we run out of bandwidth, we go back to
* step one.
+ *
+ * Accounting is controlled by the AccountingMax, AccountingRule, and
+ * AccountingStart options.
*/
/** How many bytes have we read in this accounting interval? */
@@ -111,11 +122,34 @@ static int cfg_start_day = 0,
cfg_start_min = 0;
/** @} */
+static const char *hibernate_state_to_string(hibernate_state_t state);
static void reset_accounting(time_t now);
static int read_bandwidth_usage(void);
static time_t start_of_accounting_period_after(time_t now);
static time_t start_of_accounting_period_containing(time_t now);
static void accounting_set_wakeup_time(void);
+static void on_hibernate_state_change(hibernate_state_t prev_state);
+
+/**
+ * Return the human-readable name for the hibernation state <b>state</b>
+ */
+static const char *
+hibernate_state_to_string(hibernate_state_t state)
+{
+ static char buf[64];
+ switch (state) {
+ case HIBERNATE_STATE_EXITING: return "EXITING";
+ case HIBERNATE_STATE_LOWBANDWIDTH: return "SOFT";
+ case HIBERNATE_STATE_DORMANT: return "HARD";
+ case HIBERNATE_STATE_INITIAL:
+ case HIBERNATE_STATE_LIVE:
+ return "AWAKE";
+ default:
+ log_warn(LD_BUG, "unknown hibernate state %d", state);
+ tor_snprintf(buf, sizeof(buf), "unknown [%d]", state);
+ return buf;
+ }
+}
/* ************
* Functions for bandwidth accounting.
@@ -297,7 +331,7 @@ edge_of_accounting_period_containing(time_t now, int get_end)
case UNIT_MONTH: {
/* If this is before the Nth, we want the Nth of last month. */
if (tm.tm_mday < cfg_start_day ||
- (tm.tm_mday < cfg_start_day && before)) {
+ (tm.tm_mday == cfg_start_day && before)) {
--tm.tm_mon;
}
/* Otherwise, the month is correct. */
@@ -670,7 +704,7 @@ read_bandwidth_usage(void)
int res;
res = unlink(fname);
- if (res != 0) {
+ if (res != 0 && errno != ENOENT) {
log_warn(LD_FS,
"Failed to unlink %s: %s",
fname, strerror(errno));
@@ -935,6 +969,7 @@ consider_hibernation(time_t now)
{
int accounting_enabled = get_options()->AccountingMax != 0;
char buf[ISO_TIME_LEN+1];
+ hibernate_state_t prev_state = hibernate_state;
/* If we're in 'exiting' mode, then we just shut down after the interval
* elapses. */
@@ -990,6 +1025,10 @@ consider_hibernation(time_t now)
hibernate_end_time_elapsed(now);
}
}
+
+ /* Dispatch a controller event if the hibernation state changed. */
+ if (hibernate_state != prev_state)
+ on_hibernate_state_change(prev_state);
}
/** Helper function: called when we get a GETINFO request for an
@@ -1007,12 +1046,8 @@ getinfo_helper_accounting(control_connection_t *conn,
if (!strcmp(question, "accounting/enabled")) {
*answer = tor_strdup(accounting_is_enabled(get_options()) ? "1" : "0");
} else if (!strcmp(question, "accounting/hibernating")) {
- if (hibernate_state == HIBERNATE_STATE_DORMANT)
- *answer = tor_strdup("hard");
- else if (hibernate_state == HIBERNATE_STATE_LOWBANDWIDTH)
- *answer = tor_strdup("soft");
- else
- *answer = tor_strdup("awake");
+ *answer = tor_strdup(hibernate_state_to_string(hibernate_state));
+ tor_strlower(*answer);
} else if (!strcmp(question, "accounting/bytes")) {
tor_asprintf(answer, U64_FORMAT" "U64_FORMAT,
U64_PRINTF_ARG(n_bytes_read_in_interval),
@@ -1062,6 +1097,20 @@ getinfo_helper_accounting(control_connection_t *conn,
return 0;
}
+/**
+ * Helper function: called when the hibernation state changes, and sends a
+ * SERVER_STATUS event to notify interested controllers of the accounting
+ * state change.
+ */
+static void
+on_hibernate_state_change(hibernate_state_t prev_state)
+{
+ (void)prev_state; /* Should we do something with this? */
+ control_event_server_status(LOG_NOTICE,
+ "HIBERNATION_STATUS STATUS=%s",
+ hibernate_state_to_string(hibernate_state));
+}
+
#ifdef TOR_UNIT_TESTS
/**
* Manually change the hibernation state. Private; used only by the unit
diff --git a/src/or/include.am b/src/or/include.am
index 712ae18406..ae493b7225 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -17,12 +17,6 @@ endif
EXTRA_DIST+= src/or/ntmain.c src/or/Makefile.nmake
-if USE_EXTERNAL_EVDNS
-evdns_source=
-else
-evdns_source=src/ext/eventdns.c
-endif
-
LIBTOR_A_SOURCES = \
src/or/addressmap.c \
src/or/buffers.c \
@@ -62,8 +56,11 @@ LIBTOR_A_SOURCES = \
src/or/onion.c \
src/or/onion_fast.c \
src/or/onion_tap.c \
+ src/or/shared_random.c \
+ src/or/shared_random_state.c \
src/or/transports.c \
src/or/periodic.c \
+ src/or/protover.c \
src/or/policies.c \
src/or/reasons.c \
src/or/relay.c \
@@ -84,7 +81,6 @@ LIBTOR_A_SOURCES = \
src/or/status.c \
src/or/torcert.c \
src/or/onion_ntor.c \
- $(evdns_source) \
$(tor_platform_source)
src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES)
@@ -109,7 +105,7 @@ src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
-src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \
+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 \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@@ -121,6 +117,7 @@ src_or_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
+ src/common/libor-ctime-testing.a \
src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@@ -154,7 +151,6 @@ ORHEADERS = \
src/or/dns.h \
src/or/dns_structs.h \
src/or/dnsserv.h \
- src/or/eventdns_tor.h \
src/or/ext_orport.h \
src/or/fallback_dirs.inc \
src/or/fp_pair.h \
@@ -172,9 +168,12 @@ ORHEADERS = \
src/or/onion_ntor.h \
src/or/onion_tap.h \
src/or/or.h \
+ src/or/shared_random.h \
+ src/or/shared_random_state.h \
src/or/transports.h \
src/or/periodic.h \
src/or/policies.h \
+ src/or/protover.h \
src/or/reasons.h \
src/or/relay.h \
src/or/rendcache.h \
@@ -199,7 +198,7 @@ noinst_HEADERS+= $(ORHEADERS) micro-revision.i
micro-revision.i: FORCE
$(AM_V_at)rm -f micro-revision.tmp; \
- if test -d "$(top_srcdir)/.git" && \
+ if test -r "$(top_srcdir)/.git" && \
test -x "`which git 2>&1;true`"; then \
HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
echo \"$$HASH\" > micro-revision.tmp; \
diff --git a/src/or/keypin.c b/src/or/keypin.c
index 1f82eccf86..2d4c4e92d2 100644
--- a/src/or/keypin.c
+++ b/src/or/keypin.c
@@ -39,16 +39,28 @@
* @brief Key-pinning for RSA and Ed25519 identity keys at directory
* authorities.
*
+ * Many older clients, and many internal interfaces, still refer to relays by
+ * their RSA1024 identity keys. We can make this more secure, however:
+ * authorities use this module to track which RSA keys have been used along
+ * with which Ed25519 keys, and force such associations to be permanent.
+ *
* This module implements a key-pinning mechanism to ensure that it's safe
* to use RSA keys as identitifers even as we migrate to Ed25519 keys. It
* remembers, for every Ed25519 key we've seen, what the associated Ed25519
* key is. This way, if we see a different Ed25519 key with that RSA key,
* we'll know that there's a mismatch.
*
+ * (As of this writing, these key associations are advisory only, mostly
+ * because some relay operators kept mishandling their Ed25519 keys during
+ * the initial Ed25519 rollout. We should fix this problem, and then toggle
+ * the AuthDirPinKeys option.)
+ *
* We persist these entries to disk using a simple format, where each line
* has a base64-encoded RSA SHA1 hash, then a base64-endoded Ed25519 key.
* Empty lines, misformed lines, and lines beginning with # are
* ignored. Lines beginning with @ are reserved for future extensions.
+ *
+ * The dirserv.c module is the main user of these functions.
*/
static int keypin_journal_append_entry(const uint8_t *rsa_id_digest,
@@ -93,14 +105,14 @@ return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key));
}
HT_PROTOTYPE(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa,
- keypin_ents_eq_rsa);
+ keypin_ents_eq_rsa)
HT_GENERATE2(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa,
- keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_);
+ keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_)
HT_PROTOTYPE(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed,
- keypin_ents_eq_ed);
+ keypin_ents_eq_ed)
HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed,
- keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_);
+ keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_)
/**
* Check whether we already have an entry in the key pinning table for a
@@ -479,7 +491,7 @@ keypin_clear(void)
HT_CLEAR(rsamap,&the_rsa_map);
if (bad_entries) {
- log_warn(LD_BUG, "Found %d discrepencies in the the keypin database.",
+ log_warn(LD_BUG, "Found %d discrepencies in the keypin database.",
bad_entries);
}
}
diff --git a/src/or/main.c b/src/or/main.c
index d4d98ee317..187b255bfb 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -46,6 +46,7 @@
#include "onion.h"
#include "periodic.h"
#include "policies.h"
+#include "protover.h"
#include "transports.h"
#include "relay.h"
#include "rendclient.h"
@@ -57,6 +58,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "scheduler.h"
+#include "shared_random.h"
#include "statefile.h"
#include "status.h"
#include "util_process.h"
@@ -68,15 +70,7 @@
#include "memarea.h"
#include "sandbox.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
-
-#ifdef USE_BUFFEREVENTS
-#include <event2/bufferevent.h>
-#endif
#ifdef HAVE_SYSTEMD
# if defined(__COVERITY__) && !defined(__INCLUDE_LEVEL__)
@@ -104,8 +98,6 @@ static int run_main_loop_until_done(void);
static void process_signal(int sig);
/********* START VARIABLES **********/
-
-#ifndef USE_BUFFEREVENTS
int global_read_bucket; /**< Max number of bytes I can read this second. */
int global_write_bucket; /**< Max number of bytes I can write this second. */
@@ -119,7 +111,6 @@ static int stats_prev_global_read_bucket;
/** What was the write bucket before the last second_elapsed_callback() call?
* (used to determine how many bytes we've written). */
static int stats_prev_global_write_bucket;
-#endif
/* DOCDOC stats_prev_n_read */
static uint64_t stats_prev_n_read = 0;
@@ -172,9 +163,6 @@ static int can_complete_circuits = 0;
/** How often do we check for router descriptors that we should download
* when we have enough directory info? */
#define LAZY_DESCRIPTOR_RETRY_INTERVAL (60)
-/** How often do we 'forgive' undownloadable router descriptors and attempt
- * to download them again? */
-#define DESCRIPTOR_FAILURE_RESET_INTERVAL (60*60)
/** Decides our behavior when no logs are configured/before any
* logs have been configured. For 0, we log notice to stdout as normal.
@@ -191,28 +179,6 @@ int quiet_level = 0;
*
****************************************************************************/
-#if defined(_WIN32) && defined(USE_BUFFEREVENTS)
-/** Remove the kernel-space send and receive buffers for <b>s</b>. For use
- * with IOCP only. */
-static int
-set_buffer_lengths_to_zero(tor_socket_t s)
-{
- int zero = 0;
- int r = 0;
- if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero,
- (socklen_t)sizeof(zero))) {
- log_warn(LD_NET, "Unable to clear SO_SNDBUF");
- r = -1;
- }
- if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero,
- (socklen_t)sizeof(zero))) {
- log_warn(LD_NET, "Unable to clear SO_RCVBUF");
- r = -1;
- }
- return r;
-}
-#endif
-
/** Return 1 if we have successfully built a circuit, and nothing has changed
* to make us think that maybe we can't.
*/
@@ -255,66 +221,9 @@ connection_add_impl(connection_t *conn, int is_connecting)
conn->conn_array_index = smartlist_len(connection_array);
smartlist_add(connection_array, conn);
-#ifdef USE_BUFFEREVENTS
- if (connection_type_uses_bufferevent(conn)) {
- if (SOCKET_OK(conn->s) && !conn->linked) {
-
-#ifdef _WIN32
- if (tor_libevent_using_iocp_bufferevents() &&
- get_options()->UserspaceIOCPBuffers) {
- set_buffer_lengths_to_zero(conn->s);
- }
-#endif
-
- conn->bufev = bufferevent_socket_new(
- tor_libevent_get_base(),
- conn->s,
- BEV_OPT_DEFER_CALLBACKS);
- if (!conn->bufev) {
- log_warn(LD_BUG, "Unable to create socket bufferevent");
- smartlist_del(connection_array, conn->conn_array_index);
- conn->conn_array_index = -1;
- return -1;
- }
- if (is_connecting) {
- /* Put the bufferevent into a "connecting" state so that we'll get
- * a "connected" event callback on successful write. */
- bufferevent_socket_connect(conn->bufev, NULL, 0);
- }
- connection_configure_bufferevent_callbacks(conn);
- } else if (conn->linked && conn->linked_conn &&
- connection_type_uses_bufferevent(conn->linked_conn)) {
- tor_assert(!(SOCKET_OK(conn->s)));
- if (!conn->bufev) {
- struct bufferevent *pair[2] = { NULL, NULL };
- if (bufferevent_pair_new(tor_libevent_get_base(),
- BEV_OPT_DEFER_CALLBACKS,
- pair) < 0) {
- log_warn(LD_BUG, "Unable to create bufferevent pair");
- smartlist_del(connection_array, conn->conn_array_index);
- conn->conn_array_index = -1;
- return -1;
- }
- tor_assert(pair[0]);
- conn->bufev = pair[0];
- conn->linked_conn->bufev = pair[1];
- } /* else the other side already was added, and got a bufferevent_pair */
- connection_configure_bufferevent_callbacks(conn);
- } else {
- tor_assert(!conn->linked);
- }
-
- if (conn->bufev)
- tor_assert(conn->inbuf == NULL);
-
- if (conn->linked_conn && conn->linked_conn->bufev)
- tor_assert(conn->linked_conn->inbuf == NULL);
- }
-#else
(void) is_connecting;
-#endif
- if (!HAS_BUFFEREVENT(conn) && (SOCKET_OK(conn->s) || conn->linked)) {
+ if (SOCKET_OK(conn->s) || conn->linked) {
conn->read_event = tor_event_new(tor_libevent_get_base(),
conn->s, EV_READ|EV_PERSIST, conn_read_callback, conn);
conn->write_event = tor_event_new(tor_libevent_get_base(),
@@ -343,12 +252,6 @@ connection_unregister_events(connection_t *conn)
log_warn(LD_BUG, "Error removing write event for %d", (int)conn->s);
tor_free(conn->write_event);
}
-#ifdef USE_BUFFEREVENTS
- if (conn->bufev) {
- bufferevent_free(conn->bufev);
- conn->bufev = NULL;
- }
-#endif
if (conn->type == CONN_TYPE_AP_DNS_LISTENER) {
dnsserv_close_listener(conn);
}
@@ -479,8 +382,8 @@ connection_in_array(connection_t *conn)
/** Set <b>*array</b> to an array of all connections. <b>*array</b> must not
* be modified.
*/
-smartlist_t *
-get_connection_array(void)
+MOCK_IMPL(smartlist_t *,
+get_connection_array, (void))
{
if (!connection_array)
connection_array = smartlist_new();
@@ -508,17 +411,6 @@ get_bytes_written,(void))
void
connection_watch_events(connection_t *conn, watchable_events_t events)
{
- IF_HAS_BUFFEREVENT(conn, {
- short ev = ((short)events) & (EV_READ|EV_WRITE);
- short old_ev = bufferevent_get_enabled(conn->bufev);
- if ((ev & ~old_ev) != 0) {
- bufferevent_enable(conn->bufev, ev);
- }
- if ((old_ev & ~ev) != 0) {
- bufferevent_disable(conn->bufev, old_ev & ~ev);
- }
- return;
- });
if (events & READ_EVENT)
connection_start_reading(conn);
else
@@ -536,9 +428,6 @@ connection_is_reading(connection_t *conn)
{
tor_assert(conn);
- IF_HAS_BUFFEREVENT(conn,
- return (bufferevent_get_enabled(conn->bufev) & EV_READ) != 0;
- );
return conn->reading_from_linked_conn ||
(conn->read_event && event_pending(conn->read_event, EV_READ, NULL));
}
@@ -589,11 +478,6 @@ connection_stop_reading,(connection_t *conn))
{
tor_assert(conn);
- IF_HAS_BUFFEREVENT(conn, {
- bufferevent_disable(conn->bufev, EV_READ);
- return;
- });
-
if (connection_check_event(conn, conn->read_event) < 0) {
return;
}
@@ -616,11 +500,6 @@ connection_start_reading,(connection_t *conn))
{
tor_assert(conn);
- IF_HAS_BUFFEREVENT(conn, {
- bufferevent_enable(conn->bufev, EV_READ);
- return;
- });
-
if (connection_check_event(conn, conn->read_event) < 0) {
return;
}
@@ -644,10 +523,6 @@ connection_is_writing(connection_t *conn)
{
tor_assert(conn);
- IF_HAS_BUFFEREVENT(conn,
- return (bufferevent_get_enabled(conn->bufev) & EV_WRITE) != 0;
- );
-
return conn->writing_to_linked_conn ||
(conn->write_event && event_pending(conn->write_event, EV_WRITE, NULL));
}
@@ -658,11 +533,6 @@ connection_stop_writing,(connection_t *conn))
{
tor_assert(conn);
- IF_HAS_BUFFEREVENT(conn, {
- bufferevent_disable(conn->bufev, EV_WRITE);
- return;
- });
-
if (connection_check_event(conn, conn->write_event) < 0) {
return;
}
@@ -686,11 +556,6 @@ connection_start_writing,(connection_t *conn))
{
tor_assert(conn);
- IF_HAS_BUFFEREVENT(conn, {
- bufferevent_enable(conn->bufev, EV_WRITE);
- return;
- });
-
if (connection_check_event(conn, conn->write_event) < 0) {
return;
}
@@ -796,6 +661,23 @@ close_closeable_connections(void)
}
}
+/** Count moribund connections for the OOS handler */
+MOCK_IMPL(int,
+connection_count_moribund, (void))
+{
+ int moribund = 0;
+
+ /*
+ * Count things we'll try to kill when close_closeable_connections()
+ * runs next.
+ */
+ SMARTLIST_FOREACH_BEGIN(closeable_connection_lst, connection_t *, conn) {
+ if (SOCKET_OK(conn->s) && connection_is_moribund(conn)) ++moribund;
+ } SMARTLIST_FOREACH_END(conn);
+
+ return moribund;
+}
+
/** Libevent callback: this gets invoked when (connection_t*)<b>conn</b> has
* some data to read. */
static void
@@ -888,21 +770,6 @@ conn_close_if_marked(int i)
assert_connection_ok(conn, now);
/* assert_all_pending_dns_resolves_ok(); */
-#ifdef USE_BUFFEREVENTS
- if (conn->bufev) {
- if (conn->hold_open_until_flushed &&
- evbuffer_get_length(bufferevent_get_output(conn->bufev))) {
- /* don't close yet. */
- return 0;
- }
- if (conn->linked_conn && ! conn->linked_conn->marked_for_close) {
- /* We need to do this explicitly so that the linked connection
- * notices that there was an EOF. */
- bufferevent_flush(conn->bufev, EV_WRITE, BEV_FINISHED);
- }
- }
-#endif
-
log_debug(LD_NET,"Cleaning up connection (fd "TOR_SOCKET_T_FORMAT").",
conn->s);
@@ -912,7 +779,6 @@ conn_close_if_marked(int i)
if (conn->proxy_state == PROXY_INFANT)
log_failed_proxy_connection(conn);
- IF_HAS_BUFFEREVENT(conn, goto unlink);
if ((SOCKET_OK(conn->s) || conn->linked_conn) &&
connection_wants_to_flush(conn)) {
/* s == -1 means it's an incomplete edge connection, or that the socket
@@ -971,7 +837,7 @@ conn_close_if_marked(int i)
connection_stop_writing(conn);
}
if (connection_is_reading(conn)) {
- /* XXXX024 We should make this code unreachable; if a connection is
+ /* XXXX+ We should make this code unreachable; if a connection is
* marked for close and flushing, there is no point in reading to it
* at all. Further, checking at this point is a bit of a hack: it
* would make much more sense to react in
@@ -997,9 +863,6 @@ conn_close_if_marked(int i)
}
}
-#ifdef USE_BUFFEREVENTS
- unlink:
-#endif
connection_unlink(conn); /* unlink, remove, free */
return 1;
}
@@ -1145,11 +1008,7 @@ run_connection_housekeeping(int i, time_t now)
the connection or send a keepalive, depending. */
or_conn = TO_OR_CONN(conn);
-#ifdef USE_BUFFEREVENTS
- tor_assert(conn->bufev);
-#else
tor_assert(conn->outbuf);
-#endif
chan = TLS_CHAN_TO_BASE(or_conn->chan);
tor_assert(chan);
@@ -1259,7 +1118,6 @@ static int periodic_events_initialized = 0;
CALLBACK(rotate_onion_key);
CALLBACK(check_ed_keys);
CALLBACK(launch_descriptor_fetches);
-CALLBACK(reset_descriptor_failures);
CALLBACK(rotate_x509_certificate);
CALLBACK(add_entropy);
CALLBACK(launch_reachability_tests);
@@ -1291,7 +1149,6 @@ static periodic_event_item_t periodic_events[] = {
CALLBACK(rotate_onion_key),
CALLBACK(check_ed_keys),
CALLBACK(launch_descriptor_fetches),
- CALLBACK(reset_descriptor_failures),
CALLBACK(rotate_x509_certificate),
CALLBACK(add_entropy),
CALLBACK(launch_reachability_tests),
@@ -1624,15 +1481,6 @@ launch_descriptor_fetches_callback(time_t now, const or_options_t *options)
}
static int
-reset_descriptor_failures_callback(time_t now, const or_options_t *options)
-{
- (void)now;
- (void)options;
- router_reset_descriptor_download_failures();
- return DESCRIPTOR_FAILURE_RESET_INTERVAL;
-}
-
-static int
rotate_x509_certificate_callback(time_t now, const or_options_t *options)
{
static int first = 1;
@@ -1647,8 +1495,8 @@ rotate_x509_certificate_callback(time_t now, const or_options_t *options)
* TLS context. */
log_info(LD_GENERAL,"Rotating tls context.");
if (router_initialize_tls_context() < 0) {
- log_warn(LD_BUG, "Error reinitializing TLS context");
- tor_assert(0);
+ log_err(LD_BUG, "Error reinitializing TLS context");
+ tor_assert_unreached();
}
/* We also make sure to rotate the TLS connections themselves if they've
@@ -2069,25 +1917,10 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
/* the second has rolled over. check more stuff. */
seconds_elapsed = current_second ? (int)(now - current_second) : 0;
-#ifdef USE_BUFFEREVENTS
- {
- uint64_t cur_read,cur_written;
- connection_get_rate_limit_totals(&cur_read, &cur_written);
- bytes_written = (size_t)(cur_written - stats_prev_n_written);
- bytes_read = (size_t)(cur_read - stats_prev_n_read);
- stats_n_bytes_read += bytes_read;
- stats_n_bytes_written += bytes_written;
- if (accounting_is_enabled(options) && seconds_elapsed >= 0)
- accounting_add_bytes(bytes_read, bytes_written, seconds_elapsed);
- stats_prev_n_written = cur_written;
- stats_prev_n_read = cur_read;
- }
-#else
bytes_read = (size_t)(stats_n_bytes_read - stats_prev_n_read);
bytes_written = (size_t)(stats_n_bytes_written - stats_prev_n_written);
stats_prev_n_read = stats_n_bytes_read;
stats_prev_n_written = stats_n_bytes_written;
-#endif
control_event_bandwidth_used((uint32_t)bytes_read,(uint32_t)bytes_written);
control_event_stream_bandwidth_used();
@@ -2159,12 +1992,11 @@ systemd_watchdog_callback(periodic_timer_t *timer, void *arg)
}
#endif
-#ifndef USE_BUFFEREVENTS
/** Timer: used to invoke refill_callback(). */
static periodic_timer_t *refill_timer = NULL;
/** Libevent callback: invoked periodically to refill token buckets
- * and count r/w bytes. It is only used when bufferevents are disabled. */
+ * and count r/w bytes. */
static void
refill_callback(periodic_timer_t *timer, void *arg)
{
@@ -2208,7 +2040,6 @@ refill_callback(periodic_timer_t *timer, void *arg)
current_millisecond = now; /* remember what time it is, for next time */
}
-#endif
#ifndef _WIN32
/** Called when a possibly ignorable libevent error occurs; ensures that we
@@ -2235,8 +2066,8 @@ ip_address_changed(int at_interface)
{
const or_options_t *options = get_options();
int server = server_mode(options);
- int exit_reject_private = (server && options->ExitRelay
- && options->ExitPolicyRejectPrivate);
+ int exit_reject_interfaces = (server && options->ExitRelay
+ && options->ExitPolicyRejectLocalInterfaces);
if (at_interface) {
if (! server) {
@@ -2254,8 +2085,8 @@ ip_address_changed(int at_interface)
}
/* Exit relays incorporate interface addresses in their exit policies when
- * ExitPolicyRejectPrivate is set */
- if (exit_reject_private || (server && !at_interface)) {
+ * ExitPolicyRejectLocalInterfaces is set */
+ if (exit_reject_interfaces || (server && !at_interface)) {
mark_my_descriptor_dirty("IP address changed");
}
@@ -2384,13 +2215,6 @@ do_main_loop(void)
}
}
-#ifdef USE_BUFFEREVENTS
- log_warn(LD_GENERAL, "Tor was compiled with the --enable-bufferevents "
- "option. This is still experimental, and might cause strange "
- "bugs. If you want a more stable Tor, be sure to build without "
- "--enable-bufferevents.");
-#endif
-
handle_signals(1);
/* load the private keys, if we're supposed to have them, and set up the
@@ -2404,10 +2228,8 @@ do_main_loop(void)
/* Set up our buckets */
connection_bucket_init();
-#ifndef USE_BUFFEREVENTS
stats_prev_global_read_bucket = global_read_bucket;
stats_prev_global_write_bucket = global_write_bucket;
-#endif
/* initialize the bootstrap status events to know we're starting up */
control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
@@ -2462,6 +2284,13 @@ do_main_loop(void)
cpu_init();
}
+ /* Setup shared random protocol subsystem. */
+ if (authdir_mode_publishes_statuses(get_options())) {
+ if (sr_init(1) < 0) {
+ return -1;
+ }
+ }
+
/* set up once-a-second callback. */
if (! second_timer) {
struct timeval one_second;
@@ -2497,7 +2326,6 @@ do_main_loop(void)
}
#endif
-#ifndef USE_BUFFEREVENTS
if (!refill_timer) {
struct timeval refill_interval;
int msecs = get_options()->TokenBucketRefillInterval;
@@ -2511,7 +2339,6 @@ do_main_loop(void)
NULL);
tor_assert(refill_timer);
}
-#endif
#ifdef HAVE_SYSTEMD
{
@@ -2573,9 +2400,7 @@ run_main_loop_once(void)
return -1;
#endif
} else {
- if (ERRNO_IS_EINPROGRESS(e))
- log_warn(LD_BUG,
- "libevent call returned EINPROGRESS? Please report.");
+ tor_assert_nonfatal_once(! ERRNO_IS_EINPROGRESS(e));
log_debug(LD_NET,"libevent call interrupted.");
/* You can't trust the results of this poll(). Go back to the
* top of the big for loop. */
@@ -2706,9 +2531,6 @@ get_uptime,(void))
return stats_n_seconds_working;
}
-extern uint64_t rephist_total_alloc;
-extern uint32_t rephist_total_num;
-
/**
* Write current memory usage information to the log.
*/
@@ -3010,14 +2832,9 @@ tor_init(int argc, char *argv[])
{
const char *version = get_version();
- const char *bev_str =
-#ifdef USE_BUFFEREVENTS
- "(with bufferevents) ";
-#else
- "";
-#endif
- log_notice(LD_GENERAL, "Tor v%s %srunning on %s with Libevent %s, "
- "OpenSSL %s and Zlib %s.", version, bev_str,
+
+ log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, "
+ "OpenSSL %s and Zlib %s.", version,
get_uname(),
tor_libevent_get_version_str(),
crypto_openssl_get_version_str(),
@@ -3032,11 +2849,6 @@ tor_init(int argc, char *argv[])
"Expect more bugs than usual.");
}
-#ifdef NON_ANONYMOUS_MODE_ENABLED
- log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a "
- "non-anonymous mode. It will provide NO ANONYMITY.");
-#endif
-
if (network_init()<0) {
log_err(LD_BUG,"Error initializing network; exiting.");
return -1;
@@ -3048,15 +2860,18 @@ tor_init(int argc, char *argv[])
return -1;
}
+ /* The options are now initialised */
+ const or_options_t *options = get_options();
+
#ifndef _WIN32
if (geteuid()==0)
log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, "
"and you probably shouldn't.");
#endif
- if (crypto_global_init(get_options()->HardwareAccel,
- get_options()->AccelName,
- get_options()->AccelDir)) {
+ if (crypto_global_init(options->HardwareAccel,
+ options->AccelName,
+ options->AccelDir)) {
log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting.");
return -1;
}
@@ -3065,6 +2880,9 @@ tor_init(int argc, char *argv[])
log_warn(LD_NET, "Problem initializing libevent RNG.");
}
+ /* Scan/clean unparseable descroptors; after reading config */
+ routerparse_init();
+
return 0;
}
@@ -3166,9 +2984,11 @@ tor_free_all(int postfork)
scheduler_free_all();
nodelist_free_all();
microdesc_free_all();
+ routerparse_free_all();
ext_orport_free_all();
control_free_all();
sandbox_free_getaddrinfo_cache();
+ protover_free_all();
if (!postfork) {
config_free_all();
or_state_free_all();
@@ -3189,9 +3009,7 @@ tor_free_all(int postfork)
smartlist_free(active_linked_connection_lst);
periodic_timer_free(second_timer);
teardown_periodic_events();
-#ifndef USE_BUFFEREVENTS
periodic_timer_free(refill_timer);
-#endif
if (!postfork) {
release_lockfile();
@@ -3230,6 +3048,9 @@ tor_cleanup(void)
accounting_record_bandwidth_usage(now, get_or_state());
or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
or_state_save(now);
+ if (authdir_mode(options)) {
+ sr_save_and_cleanup();
+ }
if (authdir_mode_tests_reachability(options))
rep_hist_record_mtbf_data(now, 0);
keypin_close_journal();
@@ -3388,6 +3209,7 @@ sandbox_init_filter(void)
OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp");
OPEN_DATADIR("cached-extrainfo.tmp.tmp");
OPEN_DATADIR_SUFFIX("state", ".tmp");
+ OPEN_DATADIR_SUFFIX("sr-state", ".tmp");
OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp");
OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp");
OPEN_DATADIR("key-pinning-journal");
@@ -3440,6 +3262,7 @@ sandbox_init_filter(void)
RENAME_SUFFIX("cached-extrainfo", ".new");
RENAME_SUFFIX("cached-extrainfo.new", ".tmp");
RENAME_SUFFIX("state", ".tmp");
+ RENAME_SUFFIX("sr-state", ".tmp");
RENAME_SUFFIX("unparseable-desc", ".tmp");
RENAME_SUFFIX("v3-status-votes", ".tmp");
@@ -3603,6 +3426,11 @@ tor_main(int argc, char *argv[])
int result = 0;
#ifdef _WIN32
+#ifndef HeapEnableTerminationOnCorruption
+#define HeapEnableTerminationOnCorruption 1
+#endif
+ /* On heap corruption, just give up; don't try to play along. */
+ HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
/* Call SetProcessDEPPolicy to permanently enable DEP.
The function will not resolve on earlier versions of Windows,
and failure is not dangerous. */
@@ -3611,7 +3439,10 @@ tor_main(int argc, char *argv[])
typedef BOOL (WINAPI *PSETDEP)(DWORD);
PSETDEP setdeppolicy = (PSETDEP)GetProcAddress(hMod,
"SetProcessDEPPolicy");
- if (setdeppolicy) setdeppolicy(1); /* PROCESS_DEP_ENABLE */
+ if (setdeppolicy) {
+ /* PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION */
+ setdeppolicy(3);
+ }
}
#endif
@@ -3653,6 +3484,8 @@ tor_main(int argc, char *argv[])
#endif
}
+ monotime_init();
+
switch (get_options()->command) {
case CMD_RUN_TOR:
#ifdef NT_SERVICE
diff --git a/src/or/main.h b/src/or/main.h
index 6949376f3e..07b22598b1 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -25,7 +25,7 @@ int connection_in_array(connection_t *conn);
void add_connection_to_closeable_list(connection_t *conn);
int connection_is_on_closeable_list(connection_t *conn);
-smartlist_t *get_connection_array(void);
+MOCK_DECL(smartlist_t *, get_connection_array, (void));
MOCK_DECL(uint64_t,get_bytes_read,(void));
MOCK_DECL(uint64_t,get_bytes_written,(void));
@@ -49,6 +49,8 @@ void tell_event_loop_to_finish(void);
void connection_stop_reading_from_linked_conn(connection_t *conn);
+MOCK_DECL(int, connection_count_moribund, (void));
+
void directory_all_unreachable(time_t now);
void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs);
@@ -77,6 +79,14 @@ int tor_main(int argc, char *argv[]);
int do_main_loop(void);
int tor_init(int argc, char **argv);
+extern time_t time_of_process_start;
+extern long stats_n_seconds_working;
+extern int quiet_level;
+extern int global_read_bucket;
+extern int global_write_bucket;
+extern int global_relayed_read_bucket;
+extern int global_relayed_write_bucket;
+
#ifdef MAIN_PRIVATE
STATIC void init_connection_lists(void);
STATIC void close_closeable_connections(void);
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 5b5c29a6d2..a81dc54628 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -69,7 +69,7 @@ microdesc_eq_(microdesc_t *a, microdesc_t *b)
}
HT_PROTOTYPE(microdesc_map, microdesc_t, node,
- microdesc_hash_, microdesc_eq_);
+ microdesc_hash_, microdesc_eq_)
HT_GENERATE2(microdesc_map, microdesc_t, node,
microdesc_hash_, microdesc_eq_, 0.6,
tor_reallocarray_, tor_free_)
@@ -108,6 +108,7 @@ dump_microdescriptor(int fd, microdesc_t *md, size_t *annotation_len_out)
md->off = tor_fd_getpos(fd);
written = write_all(fd, md->body, md->bodylen, 0);
if (written != (ssize_t)md->bodylen) {
+ written = written < 0 ? 0 : written;
log_warn(LD_DIR,
"Couldn't dump microdescriptor (wrote %ld out of %lu): %s",
(long)written, (unsigned long)md->bodylen,
@@ -925,7 +926,7 @@ we_use_microdescriptors_for_circuits(const or_options_t *options)
return 0;
/* Otherwise, we decide that we'll use microdescriptors iff we are
* not a server, and we're not autofetching everything. */
- /* XXX023 what does not being a server have to do with it? also there's
+ /* XXXX++ what does not being a server have to do with it? also there's
* a partitioning issue here where bridges differ from clients. */
ret = !server_mode(options) && !options->FetchUselessDescriptors;
}
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 1cedfef9b7..991cf80121 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -28,11 +28,14 @@
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
+#include "protover.h"
#include "relay.h"
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "shared_random.h"
#include "transports.h"
+#include "torcert.h"
/** Map from lowercase nickname to identity digest of named server, if any. */
static strmap_t *named_server_map = NULL;
@@ -40,14 +43,6 @@ static strmap_t *named_server_map = NULL;
* as unnamed for some server in the consensus. */
static strmap_t *unnamed_server_map = NULL;
-/** Most recently received and validated v3 consensus network status,
- * of whichever type we are using for our own circuits. This will be the same
- * as one of current_ns_consensus or current_md_consensus.
- */
-#define current_consensus \
- (we_use_microdescriptors_for_circuits(get_options()) ? \
- current_md_consensus : current_ns_consensus)
-
/** Most recently received and validated v3 "ns"-flavored consensus network
* status. */
static networkstatus_t *current_ns_consensus = NULL;
@@ -86,9 +81,9 @@ static time_t time_to_download_next_consensus[N_CONSENSUS_FLAVORS];
static download_status_t consensus_dl_status[N_CONSENSUS_FLAVORS] =
{
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE },
+ DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_FAILURE },
+ DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
};
#define N_CONSENSUS_BOOTSTRAP_SCHEDULES 2
@@ -105,10 +100,10 @@ static download_status_t
consensus_bootstrap_dl_status[N_CONSENSUS_BOOTSTRAP_SCHEDULES] =
{
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_ATTEMPT },
+ DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
/* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
{ 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_ATTEMPT },
+ DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 },
};
/** True iff we have logged a warning about this OR's version being older than
@@ -122,16 +117,17 @@ static void routerstatus_list_update_named_server_map(void);
static void update_consensus_bootstrap_multiple_downloads(
time_t now,
const or_options_t *options);
+static int networkstatus_check_required_protocols(const networkstatus_t *ns,
+ int client_mode,
+ char **warning_out);
/** Forget that we've warned about anything networkstatus-related, so we will
* give fresh warnings if the same behavior happens again. */
void
networkstatus_reset_warnings(void)
{
- if (current_consensus) {
- SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
- node->name_lookup_warned = 0);
- }
+ SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node,
+ node->name_lookup_warned = 0);
have_warned_about_old_version = 0;
have_warned_about_new_version = 0;
@@ -145,6 +141,9 @@ networkstatus_reset_download_failures(void)
{
int i;
+ log_debug(LD_GENERAL,
+ "In networkstatus_reset_download_failures()");
+
for (i=0; i < N_CONSENSUS_FLAVORS; ++i)
download_status_reset(&consensus_dl_status[i]);
@@ -173,7 +172,7 @@ router_reload_consensus_networkstatus(void)
}
s = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
if (s) {
- if (networkstatus_set_current_consensus(s, flavor, flags) < -1) {
+ if (networkstatus_set_current_consensus(s, flavor, flags, NULL) < -1) {
log_warn(LD_FS, "Couldn't load consensus %s networkstatus from \"%s\"",
flavor, filename);
}
@@ -191,7 +190,8 @@ router_reload_consensus_networkstatus(void)
s = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
if (s) {
if (networkstatus_set_current_consensus(s, flavor,
- flags|NSSET_WAS_WAITING_FOR_CERTS)) {
+ flags|NSSET_WAS_WAITING_FOR_CERTS,
+ NULL)) {
log_info(LD_FS, "Couldn't load consensus %s networkstatus from \"%s\"",
flavor, filename);
}
@@ -200,7 +200,7 @@ router_reload_consensus_networkstatus(void)
tor_free(filename);
}
- if (!current_consensus) {
+ if (!networkstatus_get_latest_consensus()) {
if (!named_server_map)
named_server_map = strmap_new();
if (!unnamed_server_map)
@@ -223,6 +223,7 @@ vote_routerstatus_free(vote_routerstatus_t *rs)
if (!rs)
return;
tor_free(rs->version);
+ tor_free(rs->protocols);
tor_free(rs->status.exitsummary);
for (h = rs->microdesc; h; h = next) {
tor_free(h->microdesc_hash_line);
@@ -269,6 +270,11 @@ networkstatus_vote_free(networkstatus_t *ns)
tor_free(ns->client_versions);
tor_free(ns->server_versions);
+ tor_free(ns->recommended_client_protocols);
+ tor_free(ns->recommended_relay_protocols);
+ tor_free(ns->required_client_protocols);
+ tor_free(ns->required_relay_protocols);
+
if (ns->known_flags) {
SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c));
smartlist_free(ns->known_flags);
@@ -319,6 +325,14 @@ networkstatus_vote_free(networkstatus_t *ns)
digestmap_free(ns->desc_digest_map, NULL);
+ if (ns->sr_info.commits) {
+ SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c,
+ sr_commit_free(c));
+ smartlist_free(ns->sr_info.commits);
+ }
+ tor_free(ns->sr_info.previous_srv);
+ tor_free(ns->sr_info.current_srv);
+
memwipe(ns, 11, sizeof(*ns));
tor_free(ns);
}
@@ -633,7 +647,7 @@ router_get_mutable_consensus_status_by_descriptor_digest,(
const char *digest))
{
if (!consensus)
- consensus = current_consensus;
+ consensus = networkstatus_get_latest_consensus();
if (!consensus)
return NULL;
if (!consensus->desc_digest_map) {
@@ -658,6 +672,43 @@ router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
consensus, digest);
}
+/** Return a smartlist of all router descriptor digests in a consensus */
+static smartlist_t *
+router_get_descriptor_digests_in_consensus(networkstatus_t *consensus)
+{
+ smartlist_t *result = smartlist_new();
+ digestmap_iter_t *i;
+ const char *digest;
+ void *rs;
+ char *digest_tmp;
+
+ for (i = digestmap_iter_init(consensus->desc_digest_map);
+ !(digestmap_iter_done(i));
+ i = digestmap_iter_next(consensus->desc_digest_map, i)) {
+ digestmap_iter_get(i, &digest, &rs);
+ digest_tmp = tor_malloc(DIGEST_LEN);
+ memcpy(digest_tmp, digest, DIGEST_LEN);
+ smartlist_add(result, digest_tmp);
+ }
+
+ return result;
+}
+
+/** Return a smartlist of all router descriptor digests in the current
+ * consensus */
+MOCK_IMPL(smartlist_t *,
+router_get_descriptor_digests,(void))
+{
+ smartlist_t *result = NULL;
+
+ if (current_ns_consensus) {
+ result =
+ router_get_descriptor_digests_in_consensus(current_ns_consensus);
+ }
+
+ return result;
+}
+
/** Given the digest of a router descriptor, return its current download
* status, or NULL if the digest is unrecognized. */
MOCK_IMPL(download_status_t *,
@@ -677,9 +728,11 @@ router_get_dl_status_by_descriptor_digest,(const char *d))
routerstatus_t *
router_get_mutable_consensus_status_by_id(const char *digest)
{
- if (!current_consensus)
+ const networkstatus_t *ns = networkstatus_get_latest_consensus();
+ if (!ns)
return NULL;
- return smartlist_bsearch(current_consensus->routerstatus_list, digest,
+ smartlist_t *rslist = ns->routerstatus_list;
+ return smartlist_bsearch(rslist, digest,
compare_digest_to_routerstatus_entry);
}
@@ -762,9 +815,15 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor)
* fetching certs before we check whether there is a better one? */
#define DELAY_WHILE_FETCHING_CERTS (20*60)
+/** What is the minimum time we need to have waited fetching certs, before we
+ * increment the consensus download schedule on failure? */
+#define MIN_DELAY_FOR_FETCH_CERT_STATUS_FAILURE (1*60)
+
/* Check if a downloaded consensus flavor should still wait for certificates
- * to download now.
- * If so, return 1. If not, fail dls and return 0. */
+ * to download now. If we decide not to wait, check if enough time has passed
+ * to consider the certificate download failure a separate failure. If so,
+ * fail dls.
+ * If waiting for certificates to download, return 1. If not, return 0. */
static int
check_consensus_waiting_for_certs(int flavor, time_t now,
download_status_t *dls)
@@ -778,11 +837,14 @@ check_consensus_waiting_for_certs(int flavor, time_t now,
waiting = &consensus_waiting_for_certs[flavor];
if (waiting->consensus) {
/* XXXX make sure this doesn't delay sane downloads. */
- if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
+ if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now &&
+ waiting->consensus->valid_until > now) {
return 1;
} else {
if (!waiting->dl_failed) {
- download_status_failed(dls, 0);
+ if (waiting->set_at + MIN_DELAY_FOR_FETCH_CERT_STATUS_FAILURE > now) {
+ download_status_failed(dls, 0);
+ }
waiting->dl_failed=1;
}
}
@@ -827,7 +889,7 @@ update_consensus_networkstatus_downloads(time_t now)
resource = networkstatus_get_flavor_name(i);
/* Check if we already have enough connections in progress */
- if (we_are_bootstrapping) {
+ if (we_are_bootstrapping && use_multi_conn) {
max_in_progress_conns =
options->ClientBootstrapConsensusMaxInProgressTries;
}
@@ -1160,13 +1222,13 @@ update_certificate_downloads(time_t now)
for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) {
if (consensus_waiting_for_certs[i].consensus)
authority_certs_fetch_missing(consensus_waiting_for_certs[i].consensus,
- now);
+ now, NULL);
}
if (current_ns_consensus)
- authority_certs_fetch_missing(current_ns_consensus, now);
+ authority_certs_fetch_missing(current_ns_consensus, now, NULL);
if (current_md_consensus)
- authority_certs_fetch_missing(current_md_consensus, now);
+ authority_certs_fetch_missing(current_md_consensus, now, NULL);
}
/** Return 1 if we have a consensus but we don't have enough certificates
@@ -1178,12 +1240,61 @@ consensus_is_waiting_for_certs(void)
? 1 : 0;
}
+/** Look up the currently active (depending on bootstrap status) download
+ * status for this consensus flavor and return a pointer to it.
+ */
+MOCK_IMPL(download_status_t *,
+networkstatus_get_dl_status_by_flavor,(consensus_flavor_t flavor))
+{
+ download_status_t *dl = NULL;
+ const int we_are_bootstrapping =
+ networkstatus_consensus_is_bootstrapping(time(NULL));
+
+ if ((int)flavor <= N_CONSENSUS_FLAVORS) {
+ dl = &((we_are_bootstrapping ?
+ consensus_bootstrap_dl_status : consensus_dl_status)[flavor]);
+ }
+
+ return dl;
+}
+
+/** Look up the bootstrap download status for this consensus flavor
+ * and return a pointer to it. */
+MOCK_IMPL(download_status_t *,
+networkstatus_get_dl_status_by_flavor_bootstrap,(consensus_flavor_t flavor))
+{
+ download_status_t *dl = NULL;
+
+ if ((int)flavor <= N_CONSENSUS_FLAVORS) {
+ dl = &(consensus_bootstrap_dl_status[flavor]);
+ }
+
+ return dl;
+}
+
+/** Look up the running (non-bootstrap) download status for this consensus
+ * flavor and return a pointer to it. */
+MOCK_IMPL(download_status_t *,
+networkstatus_get_dl_status_by_flavor_running,(consensus_flavor_t flavor))
+{
+ download_status_t *dl = NULL;
+
+ if ((int)flavor <= N_CONSENSUS_FLAVORS) {
+ dl = &(consensus_dl_status[flavor]);
+ }
+
+ return dl;
+}
+
/** Return the most recent consensus that we have downloaded, or NULL if we
* don't have one. */
-networkstatus_t *
-networkstatus_get_latest_consensus(void)
+MOCK_IMPL(networkstatus_t *,
+networkstatus_get_latest_consensus,(void))
{
- return current_consensus;
+ if (we_use_microdescriptors_for_circuits(get_options()))
+ return current_md_consensus;
+ else
+ return current_ns_consensus;
}
/** Return the latest consensus we have whose flavor matches <b>f</b>, or NULL
@@ -1203,13 +1314,13 @@ networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f))
/** Return the most recent consensus that we have downloaded, or NULL if it is
* no longer live. */
-networkstatus_t *
-networkstatus_get_live_consensus(time_t now)
+MOCK_IMPL(networkstatus_t *,
+networkstatus_get_live_consensus,(time_t now))
{
- if (current_consensus &&
- current_consensus->valid_after <= now &&
- now <= current_consensus->valid_until)
- return current_consensus;
+ if (networkstatus_get_latest_consensus() &&
+ networkstatus_get_latest_consensus()->valid_after <= now &&
+ now <= networkstatus_get_latest_consensus()->valid_until)
+ return networkstatus_get_latest_consensus();
else
return NULL;
}
@@ -1349,8 +1460,9 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b)
a->is_valid != b->is_valid ||
a->is_possible_guard != b->is_possible_guard ||
a->is_bad_exit != b->is_bad_exit ||
- a->is_hs_dir != b->is_hs_dir ||
- a->version_known != b->version_known;
+ a->is_hs_dir != b->is_hs_dir;
+ // XXXX this function needs a huge refactoring; it has gotten out
+ // XXXX of sync with routerstatus_t, and it will do so again.
}
/** Notify controllers of any router status entries that changed between
@@ -1449,6 +1561,66 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
}
#endif //TOR_UNIT_TESTS
+/**
+ * Return true if any option is set in <b>options</b> to make us behave
+ * as a client.
+ *
+ * XXXX If we need this elsewhere at any point, we should make it nonstatic
+ * XXXX and move it into another file.
+ */
+static int
+any_client_port_set(const or_options_t *options)
+{
+ return (options->SocksPort_set ||
+ options->TransPort_set ||
+ options->NATDPort_set ||
+ options->ControlPort_set ||
+ options->DNSPort_set);
+}
+
+/**
+ * Helper for handle_missing_protocol_warning: handles either the
+ * client case (if <b>is_client</b> is set) or the server case otherwise.
+ */
+static void
+handle_missing_protocol_warning_impl(const networkstatus_t *c,
+ int is_client)
+{
+ char *protocol_warning = NULL;
+
+ int should_exit = networkstatus_check_required_protocols(c,
+ is_client,
+ &protocol_warning);
+ if (protocol_warning) {
+ tor_log(should_exit ? LOG_ERR : LOG_WARN,
+ LD_GENERAL,
+ "%s", protocol_warning);
+ }
+ if (should_exit) {
+ tor_assert_nonfatal(protocol_warning);
+ }
+ tor_free(protocol_warning);
+ if (should_exit)
+ exit(1);
+}
+
+/** Called when we have received a networkstatus <b>c</b>. If there are
+ * any _required_ protocols we are missing, log an error and exit
+ * immediately. If there are any _recommended_ protocols we are missing,
+ * warn. */
+static void
+handle_missing_protocol_warning(const networkstatus_t *c,
+ const or_options_t *options)
+{
+ const int is_server = server_mode(options);
+ const int is_client = any_client_port_set(options) || !is_server;
+
+ if (is_server)
+ handle_missing_protocol_warning_impl(c, 0);
+ if (is_client)
+ handle_missing_protocol_warning_impl(c, 1);
+}
+
/** Try to replace the current cached v3 networkstatus with the one in
* <b>consensus</b>. If we don't have enough certificates to validate it,
* store it in consensus_waiting_for_certs and launch a certificate fetch.
@@ -1460,6 +1632,10 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
* If flags & NSSET_ACCEPT_OBSOLETE, then we should be willing to take this
* consensus, even if it comes from many days in the past.
*
+ * If source_dir is non-NULL, it's the identity digest for a directory that
+ * we've just successfully retrieved a consensus or certificates from, so try
+ * it first to fetch any missing certificates.
+ *
* Return 0 on success, <0 on failure. On failure, caller should increment
* the failure count as appropriate.
*
@@ -1469,7 +1645,8 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c,
int
networkstatus_set_current_consensus(const char *consensus,
const char *flavor,
- unsigned flags)
+ unsigned flags,
+ const char *source_dir)
{
networkstatus_t *c=NULL;
int r, result = -1;
@@ -1487,6 +1664,7 @@ networkstatus_set_current_consensus(const char *consensus,
time_t current_valid_after = 0;
int free_consensus = 1; /* Free 'c' at the end of the function */
int old_ewma_enabled;
+ int checked_protocols_already = 0;
if (flav < 0) {
/* XXXX we don't handle unrecognized flavors yet. */
@@ -1502,6 +1680,16 @@ networkstatus_set_current_consensus(const char *consensus,
goto done;
}
+ 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
+ * 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. */
+ checked_protocols_already = 1;
+ handle_missing_protocol_warning(c, options);
+ }
+
if ((int)c->flavor != flav) {
/* This wasn't the flavor we thought we were getting. */
if (require_flavor) {
@@ -1591,7 +1779,7 @@ networkstatus_set_current_consensus(const char *consensus,
write_str_to_file(unverified_fname, consensus, 0);
}
if (dl_certs)
- authority_certs_fetch_missing(c, now);
+ authority_certs_fetch_missing(c, now, source_dir);
/* This case is not a success or a failure until we get the certs
* or fail to get the certs. */
result = 0;
@@ -1627,20 +1815,25 @@ networkstatus_set_current_consensus(const char *consensus,
if (!from_cache && flav == usable_consensus_flavor())
control_event_client_status(LOG_NOTICE, "CONSENSUS_ARRIVED");
+ if (!checked_protocols_already) {
+ handle_missing_protocol_warning(c, options);
+ }
+
/* Are we missing any certificates at all? */
if (r != 1 && dl_certs)
- authority_certs_fetch_missing(c, now);
+ authority_certs_fetch_missing(c, now, source_dir);
const int is_usable_flavor = flav == usable_consensus_flavor();
if (is_usable_flavor) {
- notify_control_networkstatus_changed(current_consensus, c);
+ notify_control_networkstatus_changed(
+ networkstatus_get_latest_consensus(), c);
}
if (flav == FLAV_NS) {
if (current_ns_consensus) {
networkstatus_copy_old_consensus_info(c, current_ns_consensus);
networkstatus_vote_free(current_ns_consensus);
- /* Defensive programming : we should set current_consensus very soon,
+ /* Defensive programming : we should set current_ns_consensus very soon
* but we're about to call some stuff in the meantime, and leaving this
* dangling pointer around has proven to be trouble. */
current_ns_consensus = NULL;
@@ -1688,7 +1881,7 @@ networkstatus_set_current_consensus(const char *consensus,
/* Update ewma and adjust policy if needed; first cache the old value */
old_ewma_enabled = cell_ewma_enabled();
/* Change the cell EWMA settings */
- cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus());
+ cell_ewma_set_scale_factor(options, c);
/* If we just enabled ewma, set the cmux policy on all active channels */
if (cell_ewma_enabled() && !old_ewma_enabled) {
channel_set_cmux_policy_everywhere(&ewma_policy);
@@ -1697,12 +1890,12 @@ networkstatus_set_current_consensus(const char *consensus,
channel_set_cmux_policy_everywhere(NULL);
}
- /* XXXX024 this call might be unnecessary here: can changing the
+ /* XXXX this call might be unnecessary here: can changing the
* current consensus really alter our view of any OR's rate limits? */
connection_or_update_token_buckets(get_connection_array(), options);
- circuit_build_times_new_consensus_params(get_circuit_build_times_mutable(),
- current_consensus);
+ circuit_build_times_new_consensus_params(
+ get_circuit_build_times_mutable(), c);
}
/* Reset the failure count only if this consensus is actually valid. */
@@ -1754,9 +1947,14 @@ networkstatus_set_current_consensus(const char *consensus,
}
/** Called when we have gotten more certificates: see whether we can
- * now verify a pending consensus. */
+ * now verify a pending consensus.
+ *
+ * If source_dir is non-NULL, it's the identity digest for a directory that
+ * we've just successfully retrieved certificates from, so try it first to
+ * fetch any missing certificates.
+ */
void
-networkstatus_note_certs_arrived(void)
+networkstatus_note_certs_arrived(const char *source_dir)
{
int i;
for (i=0; i<N_CONSENSUS_FLAVORS; ++i) {
@@ -1768,7 +1966,8 @@ networkstatus_note_certs_arrived(void)
if (!networkstatus_set_current_consensus(
waiting_body,
networkstatus_get_flavor_name(i),
- NSSET_WAS_WAITING_FOR_CERTS)) {
+ NSSET_WAS_WAITING_FOR_CERTS,
+ source_dir)) {
tor_free(waiting_body);
}
}
@@ -1846,15 +2045,16 @@ routers_update_all_from_networkstatus(time_t now, int dir_version)
static void
routerstatus_list_update_named_server_map(void)
{
- if (!current_consensus)
+ 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_FOREACH_BEGIN(current_consensus->routerstatus_list,
- const routerstatus_t *, rs) {
+ 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));
@@ -1874,7 +2074,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
{
const or_options_t *options = get_options();
int authdir = authdir_mode_v3(options);
- networkstatus_t *ns = current_consensus;
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
if (!ns || !smartlist_len(ns->routerstatus_list))
return;
@@ -1943,7 +2143,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
char *
networkstatus_getinfo_helper_single(const routerstatus_t *rs)
{
- return routerstatus_format_entry(rs, NULL, NS_CONTROL_PORT, NULL);
+ return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL);
}
/** Alloc and return a string describing routerstatuses for the most
@@ -2160,15 +2360,16 @@ client_would_use_router(const routerstatus_t *rs, time_t now,
* But, if we want to have a complete list, fetch it anyway. */
return 0;
}
- if (rs->published_on + options->TestingEstimatedDescriptorPropagationTime
- > now) {
- /* Most caches probably don't have this descriptor yet. */
- return 0;
- }
if (rs->published_on + OLD_ROUTER_DESC_MAX_AGE < now) {
/* We'd drop it immediately for being too old. */
return 0;
}
+ if (!routerstatus_version_supports_extend2_cells(rs, 1)) {
+ /* We'd ignore it because it doesn't support EXTEND2 cells.
+ * If we don't know the version, download the descriptor so we can
+ * check if it supports EXTEND2 cells and ntor. */
+ return 0;
+ }
return 1;
}
@@ -2184,14 +2385,14 @@ getinfo_helper_networkstatus(control_connection_t *conn,
const routerstatus_t *status;
(void) conn;
- if (!current_consensus) {
+ if (!networkstatus_get_latest_consensus()) {
*answer = tor_strdup("");
return 0;
}
if (!strcmp(question, "ns/all")) {
smartlist_t *statuses = smartlist_new();
- SMARTLIST_FOREACH(current_consensus->routerstatus_list,
+ SMARTLIST_FOREACH(networkstatus_get_latest_consensus()->routerstatus_list,
const routerstatus_t *, rs,
{
smartlist_add(statuses, networkstatus_getinfo_helper_single(rs));
@@ -2206,7 +2407,7 @@ getinfo_helper_networkstatus(control_connection_t *conn,
if (*q == '$')
++q;
- if (base16_decode(d, DIGEST_LEN, q, strlen(q))) {
+ if (base16_decode(d, DIGEST_LEN, q, strlen(q)) != DIGEST_LEN) {
*errmsg = "Data not decodeable as hex";
return -1;
}
@@ -2252,6 +2453,56 @@ getinfo_helper_networkstatus(control_connection_t *conn,
return 0;
}
+/** Check whether the networkstatus <b>ns</b> lists any protocol
+ * versions as "required" or "recommended" that we do not support. If
+ * so, set *<b>warning_out</b> to a newly allocated string describing
+ * the problem.
+ *
+ * Return 1 if we should exit, 0 if we should not. */
+int
+networkstatus_check_required_protocols(const networkstatus_t *ns,
+ int client_mode,
+ char **warning_out)
+{
+ const char *func = client_mode ? "client" : "relay";
+ const char *required, *recommended;
+ char *missing = NULL;
+
+ tor_assert(warning_out);
+
+ if (client_mode) {
+ required = ns->required_client_protocols;
+ recommended = ns->recommended_client_protocols;
+ } else {
+ required = ns->required_relay_protocols;
+ recommended = ns->recommended_relay_protocols;
+ }
+
+ if (!protover_all_supported(required, &missing)) {
+ tor_asprintf(warning_out, "At least one protocol listed as required in "
+ "the consensus is not supported by this version of Tor. "
+ "You should upgrade. This version of Tor will not work as a "
+ "%s on the Tor network. The missing protocols are: %s",
+ func, missing);
+ tor_free(missing);
+ return 1;
+ }
+
+ if (! protover_all_supported(recommended, &missing)) {
+ tor_asprintf(warning_out, "At least one protocol listed as recommended in "
+ "the consensus is not supported by this version of Tor. "
+ "You should upgrade. This version of Tor will eventually "
+ "stop working as a %s on the Tor network. The missing "
+ "protocols are: %s",
+ func, missing);
+ tor_free(missing);
+ }
+
+ tor_assert_nonfatal(missing == NULL);
+
+ return 0;
+}
+
/** Free all storage held locally in this module. */
void
networkstatus_free_all(void)
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index ac93e5de91..71f36b69ed 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -38,6 +38,17 @@ routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns,
int networkstatus_vote_find_entry_idx(networkstatus_t *ns,
const char *digest, int *found_out);
+MOCK_DECL(download_status_t *,
+ networkstatus_get_dl_status_by_flavor,
+ (consensus_flavor_t flavor));
+MOCK_DECL(download_status_t *,
+ networkstatus_get_dl_status_by_flavor_bootstrap,
+ (consensus_flavor_t flavor));
+MOCK_DECL(download_status_t *,
+ networkstatus_get_dl_status_by_flavor_running,
+ (consensus_flavor_t flavor));
+
+MOCK_DECL(smartlist_t *, router_get_descriptor_digests, (void));
MOCK_DECL(download_status_t *,router_get_dl_status_by_descriptor_digest,
(const char *d));
@@ -64,10 +75,10 @@ void update_certificate_downloads(time_t now);
int consensus_is_waiting_for_certs(void);
int client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options);
-networkstatus_t *networkstatus_get_latest_consensus(void);
+MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus,(void));
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
(consensus_flavor_t f));
-networkstatus_t *networkstatus_get_live_consensus(time_t now);
+MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now));
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor);
MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));
@@ -84,8 +95,9 @@ int networkstatus_consensus_is_already_downloading(const char *resource);
#define NSSET_REQUIRE_FLAVOR 16
int networkstatus_set_current_consensus(const char *consensus,
const char *flavor,
- unsigned flags);
-void networkstatus_note_certs_arrived(void);
+ unsigned flags,
+ const char *source_dir);
+void networkstatus_note_certs_arrived(const char *source_dir);
void routers_update_all_from_networkstatus(time_t now, int dir_version);
void routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
int reset_failures);
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 89b5355c8d..0e9a651818 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -77,7 +77,7 @@ node_id_eq(const node_t *node1, const node_t *node2)
return tor_memeq(node1->identity, node2->identity, DIGEST_LEN);
}
-HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq);
+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_)
@@ -542,13 +542,15 @@ node_get_by_hex_id(const char *hex_id)
MOCK_IMPL(const node_t *,
node_get_by_nickname,(const char *nickname, int warn_if_unnamed))
{
- const node_t *node;
if (!the_nodelist)
return NULL;
/* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */
- if ((node = node_get_by_hex_id(nickname)) != NULL)
+ {
+ const node_t *node;
+ if ((node = node_get_by_hex_id(nickname)) != NULL)
return node;
+ }
if (!strcasecmp(nickname, UNNAMED_ROUTER_NICKNAME))
return NULL;
@@ -1029,6 +1031,14 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out)
node_assert_ok(node);
tor_assert(ap_out);
+ /* Clear the address, as a safety precaution if calling functions ignore the
+ * return value */
+ tor_addr_make_null(&ap_out->addr, AF_INET);
+ ap_out->port = 0;
+
+ /* Check ri first, because rewrite_node_address_for_bridge() updates
+ * node->ri with the configured bridge address. */
+
RETURN_IPV4_AP(node->ri, or_port, ap_out);
RETURN_IPV4_AP(node->rs, or_port, ap_out);
/* Microdescriptors only have an IPv6 address */
@@ -1171,14 +1181,38 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out)
}
}
+/** Return true iff <b>md</b> has a curve25519 onion key.
+ * Use node_has_curve25519_onion_key() instead of calling this directly. */
+static int
+microdesc_has_curve25519_onion_key(const microdesc_t *md)
+{
+ if (!md) {
+ return 0;
+ }
+
+ if (!md->onion_curve25519_pkey) {
+ return 0;
+ }
+
+ if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN)) {
+ return 0;
+ }
+
+ return 1;
+}
+
/** Return true iff <b>node</b> has a curve25519 onion key. */
int
node_has_curve25519_onion_key(const node_t *node)
{
+ if (!node)
+ return 0;
+
if (node->ri)
- return node->ri->onion_curve25519_pkey != NULL;
+ return routerinfo_has_curve25519_onion_key(node->ri);
else if (node->md)
- return node->md->onion_curve25519_pkey != NULL;
+ return microdesc_has_curve25519_onion_key(node->md);
else
return 0;
}
diff --git a/src/or/ntmain.c b/src/or/ntmain.c
index ded0e0d307..0e6f296d24 100644
--- a/src/or/ntmain.c
+++ b/src/or/ntmain.c
@@ -6,7 +6,15 @@
/**
* \file ntmain.c
*
- * \brief Entry points for running/configuring Tor as Windows Service.
+ * \brief Entry points for running/configuring Tor as a Windows Service.
+ *
+ * Windows Services expect to be registered with the operating system, and to
+ * have entry points for starting, stopping, and monitoring them. This module
+ * implements those entry points so that a tor relay or client or hidden
+ * service can run as a Windows service. Therefore, this module
+ * is only compiled when building for Windows.
+ *
+ * Warning: this module is not very well tested or very well maintained.
*/
#ifdef _WIN32
@@ -16,11 +24,7 @@
#include "main.h"
#include "ntmain.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
#include <windows.h>
#define GENSRV_SERVICENAME "tor"
@@ -289,6 +293,7 @@ nt_service_body(int argc, char **argv)
* event loop */
service_status.dwCurrentState = SERVICE_RUNNING;
service_fns.SetServiceStatus_fn(hStatus, &service_status);
+ set_main_thread();
do_main_loop();
tor_cleanup();
}
diff --git a/src/or/onion.c b/src/or/onion.c
index d6ef3673dd..4b803a785c 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -8,9 +8,62 @@
* \file onion.c
* \brief Functions to queue create cells, wrap the various onionskin types,
* and parse and create the CREATE cell and its allies.
+ *
+ * This module has a few functions, all related to the CREATE/CREATED
+ * handshake that we use on links in order to create a circuit, and the
+ * related EXTEND/EXTENDED handshake that we use over circuits in order to
+ * extend them an additional hop.
+ *
+ * In this module, we provide a set of abstractions to create a uniform
+ * interface over the three circuit extension handshakes that Tor has used
+ * over the years (TAP, CREATE_FAST, and ntor). These handshakes are
+ * implemented in onion_tap.c, onion_fast.c, and onion_ntor.c respectively.
+ *
+ * All[*] of these handshakes follow a similar pattern: a client, knowing
+ * some key from the relay it wants to extend through, generates the
+ * first part of a handshake. A relay receives that handshake, and sends
+ * a reply. Once the client handles the reply, it knows that it is
+ * talking to the right relay, and it shares some freshly negotiated key
+ * material with that relay.
+ *
+ * We sometimes call the client's part of the handshake an "onionskin".
+ * We do this because historically, Onion Routing used a multi-layer
+ * structure called an "onion" to construct circuits. Each layer of the
+ * onion contained key material chosen by the client, the identity of
+ * the next relay in the circuit, and a smaller onion, encrypted with
+ * the key of the next relay. When we changed Tor to use a telescoping
+ * circuit extension design, it corresponded to sending each layer of the
+ * onion separately -- as a series of onionskins.
+ *
+ * Clients invoke these functions when creating or extending a circuit,
+ * from circuitbuild.c.
+ *
+ * Relays invoke these functions when they receive a CREATE or EXTEND
+ * cell in command.c or relay.c, in order to queue the pending request.
+ * They also invoke them from cpuworker.c, which handles dispatching
+ * onionskin requests to different worker threads.
+ *
+ * <br>
+ *
+ * This module also handles:
+ * <ul>
+ * <li> Queueing incoming onionskins on the relay side before passing
+ * them to worker threads.
+ * <li>Expiring onionskins on the relay side if they have waited for
+ * too long.
+ * <li>Packaging private keys on the server side in order to pass
+ * them to worker threads.
+ * <li>Encoding and decoding CREATE, CREATED, CREATE2, and CREATED2 cells.
+ * <li>Encoding and decodign EXTEND, EXTENDED, EXTEND2, and EXTENDED2
+ * relay cells.
+ * </ul>
+ *
+ * [*] The CREATE_FAST handshake is weaker than described here; see
+ * onion_fast.c for more information.
**/
#include "or.h"
+#include "circuitbuild.h"
#include "circuitlist.h"
#include "config.h"
#include "cpuworker.h"
@@ -38,9 +91,9 @@ typedef struct onion_queue_t {
/** Array of queues of circuits waiting for CPU workers. An element is NULL
* if that queue is empty.*/
-TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t)
- ol_list[MAX_ONION_HANDSHAKE_TYPE+1] = {
- TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */
+static TOR_TAILQ_HEAD(onion_queue_head_t, onion_queue_t)
+ ol_list[MAX_ONION_HANDSHAKE_TYPE+1] =
+{ TOR_TAILQ_HEAD_INITIALIZER(ol_list[0]), /* tap */
TOR_TAILQ_HEAD_INITIALIZER(ol_list[1]), /* fast */
TOR_TAILQ_HEAD_INITIALIZER(ol_list[2]), /* ntor */
};
@@ -51,7 +104,7 @@ static int ol_entries[MAX_ONION_HANDSHAKE_TYPE+1];
static int num_ntors_per_tap(void);
static void onion_queue_entry_remove(onion_queue_t *victim);
-/* XXXX024 Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
+/* XXXX Check lengths vs MAX_ONIONSKIN_{CHALLENGE,REPLY}_LEN.
*
* (By which I think I meant, "make sure that no
* X_ONIONSKIN_CHALLENGE/REPLY_LEN is greater than
@@ -130,9 +183,12 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
time_t now = time(NULL);
if (onionskin->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
+ /* LCOV_EXCL_START
+ * We should have rejected this far before this point */
log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
onionskin->handshake_type);
return -1;
+ /* LCOV_EXCL_STOP */
}
tmp = tor_malloc_zero(sizeof(onion_queue_t));
@@ -179,7 +235,9 @@ onion_pending_add(or_circuit_t *circ, create_cell_t *onionskin)
onion_queue_entry_remove(head);
log_info(LD_CIRC,
"Circuit create request is too old; canceling due to overload.");
- circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
+ if (! TO_CIRCUIT(circ)->marked_for_close) {
+ circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_RESOURCELIMIT);
+ }
}
return 0;
}
@@ -305,10 +363,13 @@ static void
onion_queue_entry_remove(onion_queue_t *victim)
{
if (victim->handshake_type > MAX_ONION_HANDSHAKE_TYPE) {
+ /* LCOV_EXCL_START
+ * We should have rejected this far before this point */
log_warn(LD_BUG, "Handshake %d out of range! Dropping.",
victim->handshake_type);
/* XXX leaks */
return;
+ /* LCOV_EXCL_STOP */
}
TOR_TAILQ_REMOVE(&ol_list[victim->handshake_type], victim, next);
@@ -391,9 +452,12 @@ onion_handshake_state_release(onion_handshake_state_t *state)
state->u.ntor = NULL;
break;
default:
+ /* LCOV_EXCL_START
+ * This state should not even exist. */
log_warn(LD_BUG, "called with unknown handshake state type %d",
(int)state->tag);
tor_fragile_assert();
+ /* LCOV_EXCL_STOP */
}
}
@@ -429,8 +493,7 @@ onion_skin_create(int type,
r = CREATE_FAST_LEN;
break;
case ONION_HANDSHAKE_TYPE_NTOR:
- if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key,
- CURVE25519_PUBKEY_LEN))
+ if (!extend_info_supports_ntor(node))
return -1;
if (onion_skin_ntor_create((const uint8_t*)node->identity_digest,
&node->curve25519_onion_key,
@@ -441,9 +504,12 @@ onion_skin_create(int type,
r = NTOR_ONIONSKIN_LEN;
break;
default:
+ /* LCOV_EXCL_START
+ * We should never try to create an impossible handshake type. */
log_warn(LD_BUG, "called with unknown handshake state type %d", type);
tor_fragile_assert();
r = -1;
+ /* LCOV_EXCL_STOP */
}
if (r > 0)
@@ -512,9 +578,12 @@ onion_skin_server_handshake(int type,
}
break;
default:
+ /* LCOV_EXCL_START
+ * We should have rejected this far before this point */
log_warn(LD_BUG, "called with unknown handshake state type %d", type);
tor_fragile_assert();
return -1;
+ /* LCOV_EXCL_STOP */
}
return r;
@@ -527,7 +596,7 @@ onion_skin_server_handshake(int type,
* <b>rend_authenticator_out</b> to the "KH" field that can be used to
* establish introduction points at this hop, and return 0. On failure,
* return -1, and set *msg_out to an error message if this is worth
- * complaining to the usre about. */
+ * complaining to the user about. */
int
onion_skin_client_handshake(int type,
const onion_handshake_state_t *handshake_state,
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
index 1f79860596..8dcbfe22d8 100644
--- a/src/or/onion_fast.c
+++ b/src/or/onion_fast.c
@@ -7,6 +7,24 @@
/**
* \file onion_fast.c
* \brief Functions implement the CREATE_FAST circuit handshake.
+ *
+ * The "CREATE_FAST" handshake is an unauthenticated, non-forward-secure
+ * key derivation mechanism based on SHA1. We used to use it for the
+ * first hop of each circuit, since the TAP handshake provided no
+ * additional security beyond the security already provided by the TLS
+ * handshake [*].
+ *
+ * When we switched to ntor, we deprecated CREATE_FAST, since ntor is
+ * stronger than our TLS handshake was, and fast enough to not be worrisome.
+ *
+ * This handshake, like the other circuit-extension handshakes, is
+ * invoked from onion.c.
+ *
+ * [*]Actually, it's possible that TAP _was_ a little better than TLS with
+ * RSA1024 certificates and EDH1024 for forward secrecy, if you
+ * hypothesize an adversary who can compute discrete logarithms on a
+ * small number of targetted DH1024 fields, but who can't break all that
+ * many RSA1024 keys.
**/
#include "or.h"
@@ -59,8 +77,8 @@ fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */
memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
out_len = key_out_len+DIGEST_LEN;
out = tor_malloc(out_len);
- if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) {
- goto done;
+ if (BUG(crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len))) {
+ goto done; // LCOV_EXCL_LINE
}
memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN);
memcpy(key_out, out+DIGEST_LEN, key_out_len);
@@ -100,10 +118,12 @@ fast_client_handshake(const fast_handshake_state_t *handshake_state,
memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
out_len = key_out_len+DIGEST_LEN;
out = tor_malloc(out_len);
- if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) {
+ if (BUG(crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len))) {
+ /* LCOV_EXCL_START */
if (msg_out)
*msg_out = "Failed to expand key material";
goto done;
+ /* LCOV_EXCL_STOP */
}
if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) {
/* H(K) does *not* match. Something fishy. */
diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c
index 9f97a4cfbe..ded97ee73d 100644
--- a/src/or/onion_ntor.c
+++ b/src/or/onion_ntor.c
@@ -5,6 +5,17 @@
* \file onion_ntor.c
*
* \brief Implementation for the ntor handshake.
+ *
+ * The ntor circuit-extension handshake was developed as a replacement
+ * for the old TAP handshake. It uses Elliptic-curve Diffie-Hellman and
+ * a hash function in order to perform a one-way authenticated key
+ * exchange. The ntor handshake is meant to replace the old "TAP"
+ * handshake.
+ *
+ * We instantiate ntor with curve25519, HMAC-SHA256, and HKDF.
+ *
+ * This handshake, like the other circuit-extension handshakes, is
+ * invoked from onion.c.
*/
#include "orconfig.h"
@@ -47,7 +58,7 @@ typedef struct tweakset_t {
} tweakset_t;
/** The tweaks to be used with our handshake. */
-const tweakset_t proto1_tweaks = {
+static const tweakset_t proto1_tweaks = {
#define PROTOID "ntor-curve25519-sha256-1"
#define PROTOID_LEN 24
PROTOID ":mac",
@@ -85,8 +96,13 @@ onion_skin_ntor_create(const uint8_t *router_id,
memcpy(state->router_id, router_id, DIGEST_LEN);
memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t));
if (curve25519_secret_key_generate(&state->seckey_x, 0) < 0) {
+ /* LCOV_EXCL_START
+ * Secret key generation should be unable to fail when the key isn't
+ * marked as "extra-strong" */
+ tor_assert_nonfatal_unreached();
tor_free(state);
return -1;
+ /* LCOV_EXCL_STOP */
}
curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x);
diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c
index bfd472351f..2769300945 100644
--- a/src/or/onion_tap.c
+++ b/src/or/onion_tap.c
@@ -9,10 +9,22 @@
* \brief Functions to implement the original Tor circuit extension handshake
* (a.k.a TAP).
*
+ * The "TAP" handshake is the first one that was widely used in Tor: It
+ * combines RSA1024-OAEP and AES128-CTR to perform a hybrid encryption over
+ * the first message DH1024 key exchange. (The RSA-encrypted part of the
+ * encryption is authenticated; the AES-encrypted part isn't. This was
+ * not a smart choice.)
+ *
* We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the
* Security of the Tor Authentication Protocol". (Spoiler: it's secure, but
* its security is kind of fragile and implementation dependent. Never modify
* this implementation without reading and understanding that paper at least.)
+ *
+ * We have deprecated TAP since the ntor handshake came into general use. It
+ * is still used for hidden service IP and RP connections, however.
+ *
+ * This handshake, like the other circuit-extension handshakes, is
+ * invoked from onion.c.
**/
#include "or.h"
@@ -74,9 +86,13 @@ onion_skin_TAP_create(crypto_pk_t *dest_router_key,
return 0;
err:
+ /* LCOV_EXCL_START
+ * We only get here if RSA encryption fails or DH keygen fails. Those
+ * shouldn't be possible. */
memwipe(challenge, 0, sizeof(challenge));
if (dh) crypto_dh_free(dh);
return -1;
+ /* LCOV_EXCL_STOP */
}
/** Given an encrypted DH public key as generated by onion_skin_create,
@@ -130,12 +146,20 @@ onion_skin_TAP_server_handshake(
dh = crypto_dh_new(DH_TYPE_CIRCUIT);
if (!dh) {
+ /* LCOV_EXCL_START
+ * Failure to allocate a DH key should be impossible.
+ */
log_warn(LD_BUG, "Couldn't allocate DH key");
goto err;
+ /* LCOV_EXCL_STOP */
}
if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) {
+ /* LCOV_EXCL_START
+ * This can only fail if the length of the key we just allocated is too
+ * big. That should be impossible. */
log_info(LD_GENERAL, "crypto_dh_get_public failed.");
goto err;
+ /* LCOV_EXCP_STOP */
}
key_material_len = DIGEST_LEN+key_out_len;
diff --git a/src/or/or.h b/src/or/or.h
index da84128530..33fe8b96c4 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -14,14 +14,6 @@
#include "orconfig.h"
-#if defined(__clang_analyzer__) || defined(__COVERITY__)
-/* If we're building for a static analysis, turn on all the off-by-default
- * features. */
-#ifndef INSTRUMENT_DOWNLOADS
-#define INSTRUMENT_DOWNLOADS 1
-#endif
-#endif
-
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -74,12 +66,6 @@
#include <windows.h>
#endif
-#ifdef USE_BUFFEREVENTS
-#include <event2/bufferevent.h>
-#include <event2/buffer.h>
-#include <event2/util.h>
-#endif
-
#include "crypto.h"
#include "crypto_format.h"
#include "tortls.h"
@@ -157,18 +143,6 @@
/** Maximum size of a single extrainfo document, as above. */
#define MAX_EXTRAINFO_UPLOAD_SIZE 50000
-/** How long do we keep DNS cache entries before purging them (regardless of
- * their TTL)? */
-#define MAX_DNS_ENTRY_AGE (30*60)
-/** How long do we cache/tell clients to cache DNS records when no TTL is
- * known? */
-#define DEFAULT_DNS_TTL (30*60)
-/** How long can a TTL be before we stop believing it? */
-#define MAX_DNS_TTL (3*60*60)
-/** How small can a TTL be before we stop believing it? Provides rudimentary
- * pinning. */
-#define MIN_DNS_TTL 60
-
/** How often do we rotate onion keys? */
#define MIN_ONION_KEY_LIFETIME (7*24*60*60)
/** How often do we rotate TLS contexts? */
@@ -784,7 +758,7 @@ typedef enum rend_auth_type_t {
/** Client-side configuration of authorization for a hidden service. */
typedef struct rend_service_authorization_t {
- char descriptor_cookie[REND_DESC_COOKIE_LEN];
+ uint8_t descriptor_cookie[REND_DESC_COOKIE_LEN];
char onion_address[REND_SERVICE_ADDRESS_LEN+1];
rend_auth_type_t auth_type;
} rend_service_authorization_t;
@@ -1145,11 +1119,8 @@ typedef struct {
typedef struct buf_t buf_t;
typedef struct socks_request_t socks_request_t;
-#ifdef USE_BUFFEREVENTS
-#define generic_buffer_t struct evbuffer
-#else
-#define generic_buffer_t buf_t
-#endif
+
+#define buf_t buf_t
typedef struct entry_port_cfg_t {
/* Client port types (socks, dns, trans, natd) only: */
@@ -1168,6 +1139,8 @@ typedef struct entry_port_cfg_t {
unsigned int ipv4_traffic : 1;
unsigned int ipv6_traffic : 1;
unsigned int prefer_ipv6 : 1;
+ unsigned int dns_request : 1;
+ unsigned int onion_traffic : 1;
/** For a socks listener: should we cache IPv4/IPv6 DNS information that
* exit nodes tell us?
@@ -1288,27 +1261,28 @@ typedef struct connection_t {
time_t timestamp_lastwritten; /**< When was the last time libevent said we
* could write? */
-#ifdef USE_BUFFEREVENTS
- struct bufferevent *bufev; /**< A Libevent buffered IO structure. */
-#endif
-
time_t timestamp_created; /**< When was this connection_t created? */
- /* XXXX_IP6 make this IPv6-capable */
int socket_family; /**< Address family of this connection's socket. Usually
- * AF_INET, but it can also be AF_UNIX, or in the future
- * AF_INET6 */
- tor_addr_t addr; /**< IP of the other side of the connection; used to
- * identify routers, along with port. */
- uint16_t port; /**< If non-zero, port on the other end
- * of the connection. */
+ * AF_INET, but it can also be AF_UNIX, or AF_INET6 */
+ tor_addr_t addr; /**< IP that socket "s" is directly connected to;
+ * may be the IP address for a proxy or pluggable transport,
+ * see "address" for the address of the final destination.
+ */
+ uint16_t port; /**< If non-zero, port that socket "s" is directly connected
+ * to; may be the port for a proxy or pluggable transport,
+ * see "address" for the port at the final destination. */
uint16_t marked_for_close; /**< Should we close this conn on the next
* iteration of the main loop? (If true, holds
* the line number where this connection was
* marked.) */
const char *marked_for_close_file; /**< For debugging: in which file were
* we marked for close? */
- char *address; /**< FQDN (or IP) of the other end.
+ char *address; /**< FQDN (or IP) and port of the final destination for this
+ * connection; this is always the remote address, it is
+ * passed to a proxy or pluggable transport if one in use.
+ * See "addr" and "port" for the address that socket "s" is
+ * directly connected to.
* strdup into this, because free_connection() frees it. */
/** Another connection that's connected to this one in lieu of a socket. */
struct connection_t *linked_conn;
@@ -1526,17 +1500,10 @@ typedef struct or_connection_t {
/* bandwidth* and *_bucket only used by ORs in OPEN state: */
int bandwidthrate; /**< Bytes/s added to the bucket. (OPEN ORs only.) */
int bandwidthburst; /**< Max bucket size for this conn. (OPEN ORs only.) */
-#ifndef USE_BUFFEREVENTS
int read_bucket; /**< When this hits 0, stop receiving. Every second we
* add 'bandwidthrate' to this, capping it at
* bandwidthburst. (OPEN ORs only) */
int write_bucket; /**< When this hits 0, stop writing. Like read_bucket. */
-#else
- /** A rate-limiting configuration object to determine how this connection
- * set its read- and write- limits. */
- /* XXXX we could share this among all connections. */
- struct ev_token_bucket_cfg *bucket_cfg;
-#endif
struct or_connection_t *next_with_same_id; /**< Next connection with same
* identity digest as this one. */
@@ -1642,11 +1609,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. */
- generic_buffer_t *pending_optimistic_data;
+ 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. */
- generic_buffer_t *sending_optimistic_data;
+ 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. */
@@ -1849,51 +1816,6 @@ static inline listener_connection_t *TO_LISTENER_CONN(connection_t *c)
return DOWNCAST(listener_connection_t, c);
}
-/* Conditional macros to help write code that works whether bufferevents are
- disabled or not.
-
- We can't just write:
- if (conn->bufev) {
- do bufferevent stuff;
- } else {
- do other stuff;
- }
- because the bufferevent stuff won't even compile unless we have a fairly
- new version of Libevent. Instead, we say:
- IF_HAS_BUFFEREVENT(conn, { do_bufferevent_stuff } );
- or:
- IF_HAS_BUFFEREVENT(conn, {
- do bufferevent stuff;
- }) ELSE_IF_NO_BUFFEREVENT {
- do non-bufferevent stuff;
- }
- If we're compiling with bufferevent support, then the macros expand more or
- less to:
- if (conn->bufev) {
- do_bufferevent_stuff;
- } else {
- do non-bufferevent stuff;
- }
- and if we aren't using bufferevents, they expand more or less to:
- { do non-bufferevent stuff; }
-*/
-#ifdef USE_BUFFEREVENTS
-#define HAS_BUFFEREVENT(c) (((c)->bufev) != NULL)
-#define IF_HAS_BUFFEREVENT(c, stmt) \
- if ((c)->bufev) do { \
- stmt ; \
- } while (0)
-#define ELSE_IF_NO_BUFFEREVENT ; else
-#define IF_HAS_NO_BUFFEREVENT(c) \
- if (NULL == (c)->bufev)
-#else
-#define HAS_BUFFEREVENT(c) (0)
-#define IF_HAS_BUFFEREVENT(c, stmt) (void)0
-#define ELSE_IF_NO_BUFFEREVENT ;
-#define IF_HAS_NO_BUFFEREVENT(c) \
- if (1)
-#endif
-
/** What action type does an address policy indicate: accept or reject? */
typedef enum {
ADDR_POLICY_ACCEPT=1,
@@ -1990,6 +1912,15 @@ typedef enum {
#define download_schedule_increment_bitfield_t \
ENUM_BF(download_schedule_increment_t)
+/** Enumeration: do we want to use the random exponential backoff
+ * mechanism? */
+typedef enum {
+ DL_SCHED_DETERMINISTIC = 0,
+ DL_SCHED_RANDOM_EXPONENTIAL = 1,
+} download_schedule_backoff_t;
+#define download_schedule_backoff_bitfield_t \
+ ENUM_BF(download_schedule_backoff_t)
+
/** Information about our plans for retrying downloads for a downloadable
* directory object.
* Each type of downloadable directory object has a corresponding retry
@@ -2036,6 +1967,15 @@ typedef struct download_status_t {
download_schedule_increment_bitfield_t increment_on : 1; /**< does this
* schedule increment on each attempt,
* or after each failure? */
+ download_schedule_backoff_bitfield_t backoff : 1; /**< do we use the
+ * deterministic schedule, or random
+ * exponential backoffs? */
+ uint8_t last_backoff_position; /**< number of attempts/failures, depending
+ * on increment_on, when we last recalculated
+ * the delay. Only updated if backoff
+ * == 1. */
+ int last_delay_used; /**< last delay used for random exponential backoff;
+ * only updated if backoff == 1 */
} download_status_t;
/** If n_download_failures is this high, the download can never happen. */
@@ -2123,6 +2063,9 @@ typedef struct {
char *platform; /**< What software/operating system is this OR using? */
+ char *protocol_list; /**< Encoded list of subprotocol versions supported
+ * by this OR */
+
/* link info */
uint32_t bandwidthrate; /**< How many bytes does this OR add to its token
* bucket per second? */
@@ -2240,14 +2183,13 @@ typedef struct routerstatus_t {
unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort
* or it claims to accept tunnelled dir requests.
*/
- /** True iff we know version info for this router. (i.e., a "v" entry was
- * included.) We'll replace all these with a big tor_version_t or a char[]
- * if the number of traits we care about ever becomes incredibly big. */
- unsigned int version_known:1;
+ /** True iff we have a proto line for this router, or a versions line
+ * from which we could infer the protocols. */
+ unsigned int protocols_known:1;
- /** True iff this router has a version that allows it to accept EXTEND2
- * cells */
- unsigned int version_supports_extend2_cells:1;
+ /** True iff this router has a version or protocol list that allows it to
+ * accept EXTEND2 cells */
+ unsigned int supports_extend2_cells:1;
unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */
unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */
@@ -2455,6 +2397,8 @@ typedef struct vote_routerstatus_t {
* networkstatus_t.known_flags. */
char *version; /**< The version that the authority says this router is
* running. */
+ char *protocols; /**< The protocols that this authority says this router
+ * provides. */
unsigned int has_measured_bw:1; /**< The vote had a measured bw */
/** True iff the vote included an entry for ed25519 ID, or included
* "id ed25519 none" to indicate that there was no ed25519 ID. */
@@ -2508,6 +2452,18 @@ typedef struct networkstatus_voter_info_t {
smartlist_t *sigs;
} networkstatus_voter_info_t;
+typedef struct networkstatus_sr_info_t {
+ /* Indicate if the dirauth partitipates in the SR protocol with its vote.
+ * This is tied to the SR flag in the vote. */
+ unsigned int participate:1;
+ /* Both vote and consensus: Current and previous SRV. If list is empty,
+ * this means none were found in either the consensus or vote. */
+ struct sr_srv_t *previous_srv;
+ struct sr_srv_t *current_srv;
+ /* Vote only: List of commitments. */
+ smartlist_t *commits;
+} networkstatus_sr_info_t;
+
/** Enumerates the possible seriousness values of a networkstatus document. */
typedef enum {
NS_TYPE_VOTE,
@@ -2560,6 +2516,16 @@ typedef struct networkstatus_t {
* voter has no opinion. */
char *client_versions;
char *server_versions;
+
+ /** Lists of subprotocol versions which are _recommended_ for relays and
+ * clients, or which are _require_ for relays and clients. Tor shouldn't
+ * make any more network connections if a required protocol is missing.
+ */
+ char *recommended_relay_protocols;
+ char *recommended_client_protocols;
+ char *required_relay_protocols;
+ char *required_client_protocols;
+
/** List of flags that this vote/consensus applies to routers. If a flag is
* not listed here, the voter has no opinion on what its value should be. */
smartlist_t *known_flags;
@@ -2590,6 +2556,9 @@ typedef struct networkstatus_t {
/** If present, a map from descriptor digest to elements of
* routerstatus_list. */
digestmap_t *desc_digest_map;
+
+ /** Contains the shared random protocol data from a vote or consensus. */
+ networkstatus_sr_info_t sr_info;
} networkstatus_t;
/** A set of signatures for a networkstatus consensus. Unless otherwise
@@ -2958,17 +2927,17 @@ typedef struct circuit_t {
/** When the circuit was first used, or 0 if the circuit is clean.
*
- * XXXX023 Note that some code will artifically adjust this value backward
+ * XXXX Note that some code will artifically adjust this value backward
* in time in order to indicate that a circuit shouldn't be used for new
* streams, but that it can stay alive as long as it has streams on it.
* That's a kludge we should fix.
*
- * XXX023 The CBT code uses this field to record when HS-related
+ * XXX The CBT code uses this field to record when HS-related
* circuits entered certain states. This usage probably won't
* interfere with this field's primary purpose, but we should
* document it more thoroughly to make sure of that.
*
- * XXX027 The SocksPort option KeepaliveIsolateSOCKSAuth will artificially
+ * XXX The SocksPort option KeepaliveIsolateSOCKSAuth will artificially
* adjust this value forward each time a suitable stream is attached to an
* already constructed circuit, potentially keeping the circuit alive
* indefinitely.
@@ -2983,11 +2952,11 @@ typedef struct circuit_t {
/** For what reason (See END_CIRC_REASON...) is this circuit being closed?
* This field is set in circuit_mark_for_close and used later in
* circuit_about_to_free. */
- uint16_t marked_for_close_reason;
+ int marked_for_close_reason;
/** As marked_for_close_reason, but reflects the underlying reason for
* closing this circuit.
*/
- uint16_t marked_for_close_orig_reason;
+ int marked_for_close_orig_reason;
/** Unique ID for measuring tunneled network status requests. */
uint64_t dirreq_id;
@@ -3558,7 +3527,13 @@ typedef struct {
/** Bitmask; derived from AllowInvalidNodes. */
invalid_router_usage_t AllowInvalid_;
config_line_t *ExitPolicy; /**< Lists of exit policy components. */
- int ExitPolicyRejectPrivate; /**< Should we not exit to local addresses? */
+ int ExitPolicyRejectPrivate; /**< Should we not exit to reserved private
+ * addresses, and our own published addresses?
+ */
+ int ExitPolicyRejectLocalInterfaces; /**< Should we not exit to local
+ * interface addresses?
+ * Includes OutboundBindAddresses and
+ * configured ports. */
config_line_t *SocksPolicy; /**< Lists of socks policy components */
config_line_t *DirPolicy; /**< Lists of dir policy components */
/** Addresses to bind for listening for SOCKS connections. */
@@ -3633,9 +3608,13 @@ typedef struct {
/** @name port booleans
*
- * Derived booleans: True iff there is a non-listener port on an AF_INET or
- * AF_INET6 address of the given type configured in one of the _lines
- * options above.
+ * Derived booleans: For server ports and ControlPort, true iff there is a
+ * non-listener port on an AF_INET or AF_INET6 address of the given type
+ * 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
+ * rather than SocksPort_set.
*
* @{
*/
@@ -3726,8 +3705,32 @@ typedef struct {
* they reach the normal circuit-build timeout. */
int CloseHSServiceRendCircuitsImmediatelyOnTimeout;
+ /** Onion Services in HiddenServiceSingleHopMode make one-hop (direct)
+ * circuits between the onion service server, and the introduction and
+ * rendezvous points. (Onion service descriptors are still posted using
+ * 3-hop paths, to avoid onion service directories blocking the service.)
+ * This option makes every hidden service instance hosted by
+ * this tor instance a Single Onion Service.
+ * HiddenServiceSingleHopMode requires HiddenServiceNonAnonymousMode to be
+ * set to 1.
+ * Use rend_service_allow_non_anonymous_connection() or
+ * rend_service_reveal_startup_time() instead of using this option directly.
+ */
+ int HiddenServiceSingleHopMode;
+ /* Makes hidden service clients and servers non-anonymous on this tor
+ * instance. Allows the non-anonymous HiddenServiceSingleHopMode. Enables
+ * non-anonymous behaviour in the hidden service protocol.
+ * Use rend_service_non_anonymous_mode_enabled() instead of using this option
+ * directly.
+ */
+ int HiddenServiceNonAnonymousMode;
+
int ConnLimit; /**< Demanded minimum number of simultaneous connections. */
int ConnLimit_; /**< Maximum allowed number of simultaneous connections. */
+ int ConnLimit_high_thresh; /**< start trying to lower socket usage if we
+ * have this many. */
+ int ConnLimit_low_thresh; /**< try to get down to here after socket
+ * exhaustion. */
int RunAsDaemon; /**< If true, run in the background. (Unix only) */
int FascistFirewall; /**< Whether to prefer ORs reachable on open ports. */
smartlist_t *FirewallPorts; /**< Which ports our firewall allows
@@ -3777,7 +3780,8 @@ typedef struct {
* unattached before we fail it? */
int LearnCircuitBuildTimeout; /**< If non-zero, we attempt to learn a value
* for CircuitBuildTimeout based on timeout
- * history */
+ * history. Use circuit_build_times_disabled()
+ * rather than checking this value directly. */
int CircuitBuildTimeout; /**< Cull non-open circuits that were born at
* least this many seconds ago. Used until
* adaptive algorithm learns a new value. */
@@ -3963,8 +3967,16 @@ typedef struct {
int TokenBucketRefillInterval;
char *AccelName; /**< Optional hardware acceleration engine name. */
char *AccelDir; /**< Optional hardware acceleration engine search dir. */
- int UseEntryGuards; /**< Boolean: Do we try to enter from a smallish number
- * of fixed nodes? */
+
+ /** Boolean: Do we try to enter from a smallish number
+ * of fixed nodes? */
+ int UseEntryGuards_option;
+ /** Internal variable to remember whether we're actually acting on
+ * UseEntryGuards_option -- when we're a non-anonymous Tor2web client or
+ * Single Onion Service, it is alwasy false, otherwise we use the value of
+ * UseEntryGuards_option. */
+ int UseEntryGuards;
+
int NumEntryGuards; /**< How many entry guards do we try to establish? */
int UseEntryGuardsAsDirGuards; /** Boolean: Do we try to get directory info
* from a smallish number of fixed nodes? */
@@ -4321,12 +4333,6 @@ typedef struct {
*/
double CircuitPriorityHalflife;
- /** If true, do not enable IOCP on windows with bufferevents, even if
- * we think we could. */
- int DisableIOCP;
- /** For testing only: will go away eventually. */
- int UseFilteringSSLBufferevents;
-
/** Set to true if the TestingTorNetwork configuration option is set.
* This is used so that options_validate() has a chance to realize that
* the defaults have changed. */
@@ -4350,11 +4356,6 @@ typedef struct {
* never use it. If -1, we do what the consensus says. */
int OptimisticData;
- /** If 1, and we are using IOCP, we set the kernel socket SNDBUF and RCVBUF
- * to 0 to try to save kernel memory and avoid the dread "Out of buffers"
- * issue. */
- int UserspaceIOCPBuffers;
-
/** If 1, we accept and launch no external network connections, except on
* control ports. */
int DisableNetwork;
@@ -4420,9 +4421,6 @@ typedef struct {
char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */
- /** Autobool: should we use the ntor handshake if we can? */
- int UseNTorHandshake;
-
/** Fraction: */
double PathsNeededToBuildCircuits;
@@ -4483,6 +4481,20 @@ typedef struct {
/** Autobool: Do we try to retain capabilities if we can? */
int KeepBindCapabilities;
+
+ /** Maximum total size of unparseable descriptors to log during the
+ * lifetime of this Tor process.
+ */
+ uint64_t MaxUnparseableDescSizeToLog;
+
+ /** Bool (default: 1): Switch for the shared random protocol. Only
+ * relevant to a directory authority. If off, the authority won't
+ * participate in the protocol. If on (default), a flag is added to the
+ * vote indicating participation. */
+ int AuthDirSharedRandomness;
+
+ /** If 1, we skip all OOS checks. */
+ int DisableOOSCheck;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -5032,7 +5044,7 @@ typedef enum {
/** Hidden-service side configuration of client authorization. */
typedef struct rend_authorized_client_t {
char *client_name;
- char descriptor_cookie[REND_DESC_COOKIE_LEN];
+ uint8_t descriptor_cookie[REND_DESC_COOKIE_LEN];
crypto_pk_t *client_key;
} rend_authorized_client_t;
@@ -5060,12 +5072,12 @@ typedef struct rend_encoded_v2_service_descriptor_t {
* INTRO_POINT_LIFETIME_INTRODUCTIONS INTRODUCE2 cells, it may expire
* sooner.)
*
- * XXX023 Should this be configurable? */
+ * XXX Should this be configurable? */
#define INTRO_POINT_LIFETIME_MIN_SECONDS (18*60*60)
/** The maximum number of seconds that an introduction point will last
* before expiring due to old age.
*
- * XXX023 Should this be configurable? */
+ * XXX Should this be configurable? */
#define INTRO_POINT_LIFETIME_MAX_SECONDS (24*60*60)
/** The maximum number of circuit creation retry we do to an intro point
@@ -5077,7 +5089,8 @@ typedef struct rend_encoded_v2_service_descriptor_t {
* the service side) and in rend_service_descriptor_t (on both the
* client and service side). */
typedef struct rend_intro_point_t {
- extend_info_t *extend_info; /**< Extend info of this introduction point. */
+ extend_info_t *extend_info; /**< Extend info for connecting to this
+ * introduction point via a multi-hop path. */
crypto_pk_t *intro_key; /**< Introduction key that replaces the service
* key, if this descriptor is V2. */
diff --git a/src/or/periodic.c b/src/or/periodic.c
index 057fcf672e..d02d4a7bbb 100644
--- a/src/or/periodic.c
+++ b/src/or/periodic.c
@@ -5,6 +5,10 @@
* \file periodic.c
*
* \brief Generic backend for handling periodic events.
+ *
+ * The events in this module are used by main.c to track items that need
+ * to fire once every N seconds, possibly picking a new interval each time
+ * that they fire. See periodic_events[] in main.c for examples.
*/
#include "or.h"
@@ -12,11 +16,7 @@
#include "config.h"
#include "periodic.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
/** We disable any interval greater than this number of seconds, on the
* grounds that it is probably an absolute time mistakenly passed in as a
diff --git a/src/or/policies.c b/src/or/policies.c
index 50fec3a773..28770bb38d 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -103,7 +103,7 @@ policy_expand_private(smartlist_t **policy)
if (tor_addr_parse_mask_ports(private_nets[i], 0,
&newpolicy.addr,
&newpolicy.maskbits, &port_min, &port_max)<0) {
- tor_assert(0);
+ tor_assert_unreached();
}
smartlist_add(tmp, addr_policy_get_canonical_entry(&newpolicy));
}
@@ -274,28 +274,22 @@ parse_reachable_addresses(void)
/* We ignore ReachableAddresses for relays */
if (!server_mode(options)) {
- if ((reachable_or_addr_policy
- && policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC))
- || (reachable_dir_addr_policy
- && policy_is_reject_star(reachable_dir_addr_policy, AF_UNSPEC))) {
+ if (policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC, 0)
+ || policy_is_reject_star(reachable_dir_addr_policy, AF_UNSPEC,0)) {
log_warn(LD_CONFIG, "Tor cannot connect to the Internet if "
"ReachableAddresses, ReachableORAddresses, or "
"ReachableDirAddresses reject all addresses. Please accept "
"some addresses in these options.");
} else if (options->ClientUseIPv4 == 1
- && ((reachable_or_addr_policy
- && policy_is_reject_star(reachable_or_addr_policy, AF_INET))
- || (reachable_dir_addr_policy
- && policy_is_reject_star(reachable_dir_addr_policy, AF_INET)))) {
+ && (policy_is_reject_star(reachable_or_addr_policy, AF_INET, 0)
+ || policy_is_reject_star(reachable_dir_addr_policy, AF_INET, 0))) {
log_warn(LD_CONFIG, "You have set ClientUseIPv4 1, but "
"ReachableAddresses, ReachableORAddresses, or "
"ReachableDirAddresses reject all IPv4 addresses. "
"Tor will not connect using IPv4.");
} else if (fascist_firewall_use_ipv6(options)
- && ((reachable_or_addr_policy
- && policy_is_reject_star(reachable_or_addr_policy, AF_INET6))
- || (reachable_dir_addr_policy
- && policy_is_reject_star(reachable_dir_addr_policy, AF_INET6)))) {
+ && (policy_is_reject_star(reachable_or_addr_policy, AF_INET6, 0)
+ || policy_is_reject_star(reachable_dir_addr_policy, AF_INET6, 0))) {
log_warn(LD_CONFIG, "You have configured tor to use IPv6 "
"(ClientUseIPv6 1 or UseBridges 1), but "
"ReachableAddresses, ReachableORAddresses, or "
@@ -1084,8 +1078,8 @@ validate_addr_policies(const or_options_t *options, char **msg)
const int exitrelay_setting_is_auto = options->ExitRelay == -1;
const int policy_accepts_something =
- ! (policy_is_reject_star(addr_policy, AF_INET) &&
- policy_is_reject_star(addr_policy, AF_INET6));
+ ! (policy_is_reject_star(addr_policy, AF_INET, 1) &&
+ policy_is_reject_star(addr_policy, AF_INET6, 1));
if (server_mode(options) &&
! warned_about_exitrelay &&
@@ -1204,48 +1198,48 @@ policies_parse_from_options(const or_options_t *options)
return ret;
}
-/** Compare two provided address policy items, and return -1, 0, or 1
+/** Compare two provided address policy items, and renturn -1, 0, or 1
* if the first is less than, equal to, or greater than the second. */
static int
-cmp_single_addr_policy(addr_policy_t *a, addr_policy_t *b)
+single_addr_policy_eq(const addr_policy_t *a, const addr_policy_t *b)
{
int r;
- if ((r=((int)a->policy_type - (int)b->policy_type)))
- return r;
- if ((r=((int)a->is_private - (int)b->is_private)))
- return r;
+#define CMP_FIELD(field) do { \
+ if (a->field != b->field) { \
+ return 0; \
+ } \
+ } while (0)
+ CMP_FIELD(policy_type);
+ CMP_FIELD(is_private);
/* refcnt and is_canonical are irrelevant to equality,
* they are hash table implementation details */
if ((r=tor_addr_compare(&a->addr, &b->addr, CMP_EXACT)))
- return r;
- if ((r=((int)a->maskbits - (int)b->maskbits)))
- return r;
- if ((r=((int)a->prt_min - (int)b->prt_min)))
- return r;
- if ((r=((int)a->prt_max - (int)b->prt_max)))
- return r;
- return 0;
+ return 0;
+ CMP_FIELD(maskbits);
+ CMP_FIELD(prt_min);
+ CMP_FIELD(prt_max);
+#undef CMP_FIELD
+ return 1;
}
-/** Like cmp_single_addr_policy() above, but looks at the
- * whole set of policies in each case. */
+/** As single_addr_policy_eq, but compare every element of two policies.
+ */
int
-cmp_addr_policies(smartlist_t *a, smartlist_t *b)
+addr_policies_eq(const smartlist_t *a, const smartlist_t *b)
{
- int r, i;
+ int i;
int len_a = a ? smartlist_len(a) : 0;
int len_b = b ? smartlist_len(b) : 0;
- for (i = 0; i < len_a && i < len_b; ++i) {
- if ((r = cmp_single_addr_policy(smartlist_get(a, i), smartlist_get(b, i))))
- return r;
- }
- if (i == len_a && i == len_b)
+ if (len_a != len_b)
return 0;
- if (i < len_a)
- return -1;
- else
- return 1;
+
+ for (i = 0; i < len_a; ++i) {
+ if (! single_addr_policy_eq(smartlist_get(a, i), smartlist_get(b, i)))
+ return 0;
+ }
+
+ return 1;
}
/** Node in hashtable used to store address policy entries. */
@@ -1261,7 +1255,7 @@ static HT_HEAD(policy_map, policy_map_ent_t) policy_root = HT_INITIALIZER();
static inline int
policy_eq(policy_map_ent_t *a, policy_map_ent_t *b)
{
- return cmp_single_addr_policy(a->policy, b->policy) == 0;
+ return single_addr_policy_eq(a->policy, b->policy);
}
/** Return a hashcode for <b>ent</b> */
@@ -1312,7 +1306,7 @@ addr_policy_get_canonical_entry(addr_policy_t *e)
HT_INSERT(policy_map, &policy_root, found);
}
- tor_assert(!cmp_single_addr_policy(found->policy, e));
+ tor_assert(single_addr_policy_eq(found->policy, e));
++found->policy->refcnt;
return found->policy;
}
@@ -1837,10 +1831,18 @@ policies_log_first_redundant_entry(const smartlist_t *policy)
*
* If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy.
*
+ * If <b>configured_addresses</b> contains addresses:
+ * - prepend entries that reject the addresses in this list. These may be the
+ * advertised relay addresses and/or the outbound bind addresses,
+ * depending on the ExitPolicyRejectPrivate and
+ * ExitPolicyRejectLocalInterfaces settings.
* If <b>rejectprivate</b> is true:
* - prepend "reject private:*" to the policy.
- * - prepend entries that reject publicly routable addresses on this exit
- * relay by calling policies_parse_exit_policy_reject_private
+ * If <b>reject_interface_addresses</b> is true:
+ * - prepend entries that reject publicly routable interface addresses on
+ * this exit relay by calling policies_parse_exit_policy_reject_private
+ * If <b>reject_configured_port_addresses</b> is true:
+ * - prepend entries that reject all configured port addresses
*
* If cfg doesn't end in an absolute accept or reject and if
* <b>add_default_policy</b> is true, add the default exit
@@ -1868,13 +1870,16 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
if (rejectprivate) {
/* Reject IPv4 and IPv6 reserved private netblocks */
append_exit_policy_string(dest, "reject private:*");
- /* Reject IPv4 and IPv6 publicly routable addresses on this exit relay */
- policies_parse_exit_policy_reject_private(
- dest, ipv6_exit,
+ }
+
+ /* Consider rejecting IPv4 and IPv6 advertised relay addresses, outbound bind
+ * addresses, publicly routable addresses, and configured port addresses
+ * on this exit relay */
+ policies_parse_exit_policy_reject_private(dest, ipv6_exit,
configured_addresses,
reject_interface_addresses,
reject_configured_port_addresses);
- }
+
if (parse_addr_policy(cfg, dest, -1))
return -1;
@@ -1902,8 +1907,14 @@ policies_parse_exit_policy_internal(config_line_t *cfg,
* If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
* - prepend an entry that rejects all destinations in all netblocks
* reserved for private use.
+ * - prepend entries that reject the advertised relay addresses in
+ * configured_addresses
+ * If <b>EXIT_POLICY_REJECT_LOCAL_INTERFACES</b> bit is set in <b>options</b>:
* - prepend entries that reject publicly routable addresses on this exit
* relay by calling policies_parse_exit_policy_internal
+ * - prepend entries that reject the outbound bind addresses in
+ * configured_addresses
+ * - prepend entries that reject all configured port addresses
*
* If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
* default exit policy entries to <b>result</b> smartlist.
@@ -1916,12 +1927,14 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
int add_default = (options & EXIT_POLICY_ADD_DEFAULT) ? 1 : 0;
+ int reject_local_interfaces = (options &
+ EXIT_POLICY_REJECT_LOCAL_INTERFACES) ? 1 : 0;
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
configured_addresses,
- reject_private,
- reject_private,
+ reject_local_interfaces,
+ reject_local_interfaces,
add_default);
}
@@ -1987,6 +2000,7 @@ policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list,
* add it to the list of configured addresses.
* - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it
* to the list of configured addresses.
+ * If <b>or_options->ExitPolicyRejectLocalInterfaces</b> is true:
* - if or_options->OutboundBindAddressIPv4_ is not the null tor_addr_t, add
* it to the list of configured addresses.
* - if or_options->OutboundBindAddressIPv6_ is not the null tor_addr_t, add
@@ -2030,11 +2044,20 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
parser_cfg |= EXIT_POLICY_ADD_DEFAULT;
}
+ if (or_options->ExitPolicyRejectLocalInterfaces) {
+ parser_cfg |= EXIT_POLICY_REJECT_LOCAL_INTERFACES;
+ }
+
/* Copy the configured addresses into the tor_addr_t* list */
- policies_copy_ipv4h_to_smartlist(configured_addresses, local_address);
- policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address);
- policies_copy_outbound_addresses_to_smartlist(configured_addresses,
- or_options);
+ if (or_options->ExitPolicyRejectPrivate) {
+ policies_copy_ipv4h_to_smartlist(configured_addresses, local_address);
+ policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address);
+ }
+
+ if (or_options->ExitPolicyRejectLocalInterfaces) {
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ or_options);
+ }
rv = policies_parse_exit_policy(or_options->ExitPolicy, result, parser_cfg,
configured_addresses);
@@ -2090,8 +2113,10 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port)
if (subnet_status[i] != 0)
continue; /* We already reject some part of this /8 */
tor_addr_from_ipv4h(&addr, i<<24);
- if (tor_addr_is_internal(&addr, 0))
+ if (tor_addr_is_internal(&addr, 0) &&
+ !get_options()->DirAllowPrivateAddresses) {
continue; /* Local or non-routable addresses */
+ }
if (p->policy_type == ADDR_POLICY_ACCEPT) {
if (p->maskbits > 8)
continue; /* Narrower than a /8. */
@@ -2125,13 +2150,16 @@ exit_policy_is_general_exit(smartlist_t *policy)
}
/** Return false if <b>policy</b> might permit access to some addr:port;
- * otherwise if we are certain it rejects everything, return true. */
+ * otherwise if we are certain it rejects everything, return true. If no
+ * part of <b>policy</b> matches, return <b>default_reject</b>.
+ * NULL policies are allowed, and treated as empty. */
int
-policy_is_reject_star(const smartlist_t *policy, sa_family_t family)
+policy_is_reject_star(const smartlist_t *policy, sa_family_t family,
+ int default_reject)
{
- if (!policy) /*XXXX disallow NULL policies? */
- return 1;
- SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) {
+ if (!policy)
+ return default_reject;
+ SMARTLIST_FOREACH_BEGIN(policy, const addr_policy_t *, p) {
if (p->policy_type == ADDR_POLICY_ACCEPT &&
(tor_addr_family(&p->addr) == family ||
tor_addr_family(&p->addr) == AF_UNSPEC)) {
@@ -2144,7 +2172,7 @@ policy_is_reject_star(const smartlist_t *policy, sa_family_t family)
return 1;
}
} SMARTLIST_FOREACH_END(p);
- return 1;
+ return default_reject;
}
/** Write a single address policy to the buf_len byte buffer at buf. Return
@@ -2271,7 +2299,26 @@ policy_summary_item_split(policy_summary_item_t* old, uint16_t new_starts)
* my immortal soul, he can clean it up himself. */
#define AT(x) ((policy_summary_item_t*)smartlist_get(summary, x))
-#define REJECT_CUTOFF_COUNT (1<<25)
+#define IPV4_BITS (32)
+/* Every IPv4 address is counted as one rejection */
+#define REJECT_CUTOFF_SCALE_IPV4 (0)
+/* Ports are rejected in an IPv4 summary if they are rejected in more than two
+ * IPv4 /8 address blocks */
+#define REJECT_CUTOFF_COUNT_IPV4 (U64_LITERAL(1) << \
+ (IPV4_BITS - REJECT_CUTOFF_SCALE_IPV4 - 7))
+
+#define IPV6_BITS (128)
+/* IPv6 /64s are counted as one rejection, anything smaller is ignored */
+#define REJECT_CUTOFF_SCALE_IPV6 (64)
+/* Ports are rejected in an IPv6 summary if they are rejected in more than one
+ * IPv6 /16 address block.
+ * This is rougly equivalent to the IPv4 cutoff, as only five IPv6 /12s (and
+ * some scattered smaller blocks) have been allocated to the RIRs.
+ * Network providers are typically allocated one or more IPv6 /32s.
+ */
+#define REJECT_CUTOFF_COUNT_IPV6 (U64_LITERAL(1) << \
+ (IPV6_BITS - REJECT_CUTOFF_SCALE_IPV6 - 16))
+
/** Split an exit policy summary so that prt_min and prt_max
* fall at exactly the start and end of an item respectively.
*/
@@ -2304,53 +2351,102 @@ policy_summary_split(smartlist_t *summary,
return start_at_index;
}
-/** Mark port ranges as accepted if they are below the reject_count */
+/** Mark port ranges as accepted if they are below the reject_count for family
+ */
static void
policy_summary_accept(smartlist_t *summary,
- uint16_t prt_min, uint16_t prt_max)
+ uint16_t prt_min, uint16_t prt_max,
+ sa_family_t family)
{
+ tor_assert_nonfatal_once(family == AF_INET || family == AF_INET6);
+ uint64_t family_reject_count = ((family == AF_INET) ?
+ REJECT_CUTOFF_COUNT_IPV4 :
+ REJECT_CUTOFF_COUNT_IPV6);
+
int i = policy_summary_split(summary, prt_min, prt_max);
while (i < smartlist_len(summary) &&
AT(i)->prt_max <= prt_max) {
if (!AT(i)->accepted &&
- AT(i)->reject_count <= REJECT_CUTOFF_COUNT)
+ AT(i)->reject_count <= family_reject_count)
AT(i)->accepted = 1;
i++;
}
tor_assert(i < smartlist_len(summary) || prt_max==65535);
}
-/** Count the number of addresses in a network with prefixlen maskbits
- * against the given portrange. */
+/** Count the number of addresses in a network in family with prefixlen
+ * maskbits against the given portrange. */
static void
policy_summary_reject(smartlist_t *summary,
maskbits_t maskbits,
- uint16_t prt_min, uint16_t prt_max)
+ uint16_t prt_min, uint16_t prt_max,
+ sa_family_t family)
{
+ tor_assert_nonfatal_once(family == AF_INET || family == AF_INET6);
+
int i = policy_summary_split(summary, prt_min, prt_max);
- /* XXX: ipv4 specific */
- uint64_t count = (U64_LITERAL(1) << (32-maskbits));
+
+ /* The length of a single address mask */
+ int addrbits = (family == AF_INET) ? IPV4_BITS : IPV6_BITS;
+ tor_assert_nonfatal_once(addrbits >= maskbits);
+
+ /* We divide IPv6 address counts by (1 << scale) to keep them in a uint64_t
+ */
+ int scale = ((family == AF_INET) ?
+ REJECT_CUTOFF_SCALE_IPV4 :
+ REJECT_CUTOFF_SCALE_IPV6);
+
+ tor_assert_nonfatal_once(addrbits >= scale);
+ if (maskbits > (addrbits - scale)) {
+ tor_assert_nonfatal_once(family == AF_INET6);
+ /* The address range is so small, we'd need billions of them to reach the
+ * rejection limit. So we ignore this range in the reject count. */
+ return;
+ }
+
+ uint64_t count = 0;
+ if (addrbits - scale - maskbits >= 64) {
+ tor_assert_nonfatal_once(family == AF_INET6);
+ /* The address range is so large, it's an automatic rejection for all ports
+ * in the range. */
+ count = UINT64_MAX;
+ } else {
+ count = (U64_LITERAL(1) << (addrbits - scale - maskbits));
+ }
+ tor_assert_nonfatal_once(count > 0);
while (i < smartlist_len(summary) &&
AT(i)->prt_max <= prt_max) {
- AT(i)->reject_count += count;
+ if (AT(i)->reject_count <= UINT64_MAX - count) {
+ AT(i)->reject_count += count;
+ } else {
+ /* IPv4 would require a 4-billion address redundant policy to get here,
+ * but IPv6 just needs to have ::/0 */
+ if (family == AF_INET) {
+ tor_assert_nonfatal_unreached_once();
+ }
+ /* If we do get here, use saturating arithmetic */
+ AT(i)->reject_count = UINT64_MAX;
+ }
i++;
}
tor_assert(i < smartlist_len(summary) || prt_max==65535);
}
/** Add a single exit policy item to our summary:
- * If it is an accept ignore it unless it is for all IP addresses
- * ("*"), i.e. it's prefixlen/maskbits is 0, else call
+ *
+ * If it is an accept, ignore it unless it is for all IP addresses
+ * ("*", i.e. its prefixlen/maskbits is 0). Otherwise call
* policy_summary_accept().
- * If it's a reject ignore it if it is about one of the private
- * networks, else call policy_summary_reject().
+ *
+ * If it is a reject, ignore it if it is about one of the private
+ * networks. Otherwise call policy_summary_reject().
*/
static void
policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
{
if (p->policy_type == ADDR_POLICY_ACCEPT) {
if (p->maskbits == 0) {
- policy_summary_accept(summary, p->prt_min, p->prt_max);
+ policy_summary_accept(summary, p->prt_min, p->prt_max, p->addr.family);
}
} else if (p->policy_type == ADDR_POLICY_REJECT) {
@@ -2371,7 +2467,8 @@ policy_summary_add_item(smartlist_t *summary, addr_policy_t *p)
}
if (!is_private) {
- policy_summary_reject(summary, p->maskbits, p->prt_min, p->prt_max);
+ policy_summary_reject(summary, p->maskbits, p->prt_min, p->prt_max,
+ p->addr.family);
}
} else
tor_assert(0);
@@ -2405,7 +2502,6 @@ policy_summarize(smartlist_t *policy, sa_family_t family)
}
if (f != family)
continue;
- /* XXXX-ipv6 More family work is needed */
policy_summary_add_item(summary, p);
} SMARTLIST_FOREACH_END(p);
@@ -2594,8 +2690,7 @@ parse_short_policy(const char *summary)
return result;
}
-/** Write <b>policy</b> back out into a string. Used only for unit tests
- * currently. */
+/** Write <b>policy</b> back out into a string. */
char *
write_short_policy(const short_policy_t *policy)
{
@@ -2638,7 +2733,7 @@ compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port,
{
int i;
int found_match = 0;
- int accept;
+ int accept_;
tor_assert(port != 0);
@@ -2658,9 +2753,9 @@ compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port,
}
if (found_match)
- accept = policy->is_accept;
+ accept_ = policy->is_accept;
else
- accept = ! policy->is_accept;
+ accept_ = ! policy->is_accept;
/* ???? are these right? -NM */
/* We should be sure not to return ADDR_POLICY_ACCEPTED in the accept
@@ -2673,7 +2768,7 @@ compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port,
*
* Once microdescriptors can handle addresses in special cases (e.g. if
* we ever solve ticket 1774), we can provide certainty here. -RD */
- if (accept)
+ if (accept_)
return ADDR_POLICY_PROBABLY_ACCEPTED;
else
return ADDR_POLICY_REJECTED;
@@ -2814,7 +2909,8 @@ getinfo_helper_policies(control_connection_t *conn,
return -1;
}
- if (!options->ExitPolicyRejectPrivate) {
+ if (!options->ExitPolicyRejectPrivate &&
+ !options->ExitPolicyRejectLocalInterfaces) {
*answer = tor_strdup("");
return 0;
}
@@ -2823,16 +2919,22 @@ getinfo_helper_policies(control_connection_t *conn,
smartlist_t *configured_addresses = smartlist_new();
/* Copy the configured addresses into the tor_addr_t* list */
- policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr);
- policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr);
- policies_copy_outbound_addresses_to_smartlist(configured_addresses,
- options);
+ if (options->ExitPolicyRejectPrivate) {
+ policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr);
+ policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr);
+ }
+
+ if (options->ExitPolicyRejectLocalInterfaces) {
+ policies_copy_outbound_addresses_to_smartlist(configured_addresses,
+ options);
+ }
policies_parse_exit_policy_reject_private(
- &private_policy_list,
- options->IPv6Exit,
- configured_addresses,
- 1, 1);
+ &private_policy_list,
+ options->IPv6Exit,
+ configured_addresses,
+ options->ExitPolicyRejectLocalInterfaces,
+ options->ExitPolicyRejectLocalInterfaces);
*answer = policy_dump_to_string(private_policy_list, 1, 1);
addr_policy_list_free(private_policy_list);
diff --git a/src/or/policies.h b/src/or/policies.h
index aaa6fa0a4e..f73f850c21 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -18,9 +18,13 @@
*/
#define POLICY_BUF_LEN 72
-#define EXIT_POLICY_IPV6_ENABLED (1 << 0)
-#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
-#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
+#define EXIT_POLICY_IPV6_ENABLED (1 << 0)
+#define EXIT_POLICY_REJECT_PRIVATE (1 << 1)
+#define EXIT_POLICY_ADD_DEFAULT (1 << 2)
+#define EXIT_POLICY_REJECT_LOCAL_INTERFACES (1 << 3)
+#define EXIT_POLICY_OPTION_MAX EXIT_POLICY_REJECT_LOCAL_INTERFACES
+/* All options set: used for unit testing */
+#define EXIT_POLICY_OPTION_ALL ((EXIT_POLICY_OPTION_MAX << 1) - 1)
typedef enum firewall_connection_t {
FIREWALL_OR_CONNECTION = 0,
@@ -72,7 +76,7 @@ void policy_expand_unspec(smartlist_t **policy);
int policies_parse_from_options(const or_options_t *options);
addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent);
-int cmp_addr_policies(smartlist_t *a, smartlist_t *b);
+int addr_policies_eq(const smartlist_t *a, const smartlist_t *b);
MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
(const tor_addr_t *addr, uint16_t port, const smartlist_t *policy));
addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
@@ -99,7 +103,8 @@ void addr_policy_append_reject_addr_list(smartlist_t **dest,
const smartlist_t *addrs);
void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter);
int exit_policy_is_general_exit(smartlist_t *policy);
-int policy_is_reject_star(const smartlist_t *policy, sa_family_t family);
+int policy_is_reject_star(const smartlist_t *policy, sa_family_t family,
+ int reject_by_default);
char * policy_dump_to_string(const smartlist_t *policy_list,
int include_ipv4,
int include_ipv6);
diff --git a/src/or/protover.c b/src/or/protover.c
new file mode 100644
index 0000000000..98957cabdf
--- /dev/null
+++ b/src/or/protover.c
@@ -0,0 +1,742 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file protover.c
+ * \brief Versioning information for different pieces of the Tor protocol.
+ *
+ * Starting in version 0.2.9.3-alpha, Tor places separate version numbers on
+ * each of the different components of its protocol. Relays use these numbers
+ * to advertise what versions of the protocols they can support, and clients
+ * use them to find what they can ask a given relay to do. Authorities vote
+ * on the supported protocol versions for each relay, and also vote on the
+ * which protocols you should have to support in order to be on the Tor
+ * network. All Tor instances use these required/recommended protocol versions
+ * to tell what level of support for recent protocols each relay has, and
+ * to decide whether they should be running given their current protocols.
+ *
+ * The main advantage of these protocol versions numbers over using Tor
+ * version numbers is that they allow different implementations of the Tor
+ * protocols to develop independently, without having to claim compatibility
+ * with specific versions of Tor.
+ **/
+
+#define PROTOVER_PRIVATE
+
+#include "or.h"
+#include "protover.h"
+#include "routerparse.h"
+
+static const smartlist_t *get_supported_protocol_list(void);
+static int protocol_list_contains(const smartlist_t *protos,
+ protocol_type_t pr, uint32_t ver);
+
+/** Mapping between protocol type string and protocol type. */
+static const struct {
+ protocol_type_t protover_type;
+ const char *name;
+} PROTOCOL_NAMES[] = {
+ { PRT_LINK, "Link" },
+ { PRT_LINKAUTH, "LinkAuth" },
+ { PRT_RELAY, "Relay" },
+ { PRT_DIRCACHE, "DirCache" },
+ { PRT_HSDIR, "HSDir" },
+ { PRT_HSINTRO, "HSIntro" },
+ { PRT_HSREND, "HSRend" },
+ { PRT_DESC, "Desc" },
+ { PRT_MICRODESC, "Microdesc"},
+ { PRT_CONS, "Cons" }
+};
+
+#define N_PROTOCOL_NAMES ARRAY_LENGTH(PROTOCOL_NAMES)
+
+/**
+ * Given a protocol_type_t, return the corresponding string used in
+ * descriptors.
+ */
+STATIC const char *
+protocol_type_to_str(protocol_type_t pr)
+{
+ unsigned i;
+ for (i=0; i < N_PROTOCOL_NAMES; ++i) {
+ if (PROTOCOL_NAMES[i].protover_type == pr)
+ return PROTOCOL_NAMES[i].name;
+ }
+ /* LCOV_EXCL_START */
+ tor_assert_nonfatal_unreached_once();
+ return "UNKNOWN";
+ /* LCOV_EXCL_STOP */
+}
+
+/**
+ * Given a string, find the corresponding protocol type and store it in
+ * <b>pr_out</b>. Return 0 on success, -1 on failure.
+ */
+STATIC int
+str_to_protocol_type(const char *s, protocol_type_t *pr_out)
+{
+ if (BUG(!pr_out))
+ return -1;
+
+ unsigned i;
+ for (i=0; i < N_PROTOCOL_NAMES; ++i) {
+ if (0 == strcmp(s, PROTOCOL_NAMES[i].name)) {
+ *pr_out = PROTOCOL_NAMES[i].protover_type;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Release all space held by a single proto_entry_t structure
+ */
+STATIC void
+proto_entry_free(proto_entry_t *entry)
+{
+ if (!entry)
+ return;
+ tor_free(entry->name);
+ SMARTLIST_FOREACH(entry->ranges, proto_range_t *, r, tor_free(r));
+ smartlist_free(entry->ranges);
+ tor_free(entry);
+}
+
+/**
+ * Given a string <b>s</b> and optional end-of-string pointer
+ * <b>end_of_range</b>, parse the protocol range and store it in
+ * <b>low_out</b> and <b>high_out</b>. A protocol range has the format U, or
+ * U-U, where U is an unsigned 32-bit integer.
+ */
+static int
+parse_version_range(const char *s, const char *end_of_range,
+ uint32_t *low_out, uint32_t *high_out)
+{
+ uint32_t low, high;
+ char *next = NULL;
+ int ok;
+
+ tor_assert(high_out);
+ tor_assert(low_out);
+
+ if (BUG(!end_of_range))
+ end_of_range = s + strlen(s); // LCOV_EXCL_LINE
+
+ /* Note that this wouldn't be safe if we didn't know that eventually,
+ * we'd hit a NUL */
+ low = (uint32_t) tor_parse_ulong(s, 10, 0, UINT32_MAX, &ok, &next);
+ if (!ok)
+ goto error;
+ if (next > end_of_range)
+ goto error;
+ if (next == end_of_range) {
+ high = low;
+ goto done;
+ }
+
+ if (*next != '-')
+ goto error;
+ s = next+1;
+ /* ibid */
+ high = (uint32_t) tor_parse_ulong(s, 10, 0, UINT32_MAX, &ok, &next);
+ if (!ok)
+ goto error;
+ if (next != end_of_range)
+ goto error;
+
+ done:
+ *high_out = high;
+ *low_out = low;
+ return 0;
+
+ error:
+ return -1;
+}
+
+/** Parse a single protocol entry from <b>s</b> up to an optional
+ * <b>end_of_entry</b> pointer, and return that protocol entry. Return NULL
+ * on error.
+ *
+ * A protocol entry has a keyword, an = sign, and zero or more ranges. */
+static proto_entry_t *
+parse_single_entry(const char *s, const char *end_of_entry)
+{
+ proto_entry_t *out = tor_malloc_zero(sizeof(proto_entry_t));
+ const char *equals;
+
+ out->ranges = smartlist_new();
+
+ if (BUG (!end_of_entry))
+ end_of_entry = s + strlen(s); // LCOV_EXCL_LINE
+
+ /* There must be an =. */
+ equals = memchr(s, '=', end_of_entry - s);
+ if (!equals)
+ goto error;
+
+ /* The name must be nonempty */
+ if (equals == s)
+ goto error;
+
+ out->name = tor_strndup(s, equals-s);
+
+ tor_assert(equals < end_of_entry);
+
+ s = equals + 1;
+ while (s < end_of_entry) {
+ const char *comma = memchr(s, ',', end_of_entry-s);
+ proto_range_t *range = tor_malloc_zero(sizeof(proto_range_t));
+ if (! comma)
+ comma = end_of_entry;
+
+ smartlist_add(out->ranges, range);
+ if (parse_version_range(s, comma, &range->low, &range->high) < 0) {
+ goto error;
+ }
+
+ if (range->low > range->high) {
+ goto error;
+ }
+
+ s = comma;
+ while (*s == ',' && s < end_of_entry)
+ ++s;
+ }
+
+ return out;
+
+ error:
+ proto_entry_free(out);
+ return NULL;
+}
+
+/**
+ * Parse the protocol list from <b>s</b> and return it as a smartlist of
+ * proto_entry_t
+ */
+STATIC smartlist_t *
+parse_protocol_list(const char *s)
+{
+ smartlist_t *entries = smartlist_new();
+
+ while (*s) {
+ /* Find the next space or the NUL. */
+ const char *end_of_entry = strchr(s, ' ');
+ proto_entry_t *entry;
+ if (!end_of_entry)
+ end_of_entry = s + strlen(s);
+
+ entry = parse_single_entry(s, end_of_entry);
+
+ if (! entry)
+ goto error;
+
+ smartlist_add(entries, entry);
+
+ s = end_of_entry;
+ while (*s == ' ')
+ ++s;
+ }
+
+ return entries;
+
+ error:
+ SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(entries);
+ return NULL;
+}
+
+/**
+ * Given a protocol type and version number, return true iff we know
+ * how to speak that protocol.
+ */
+int
+protover_is_supported_here(protocol_type_t pr, uint32_t ver)
+{
+ const smartlist_t *ours = get_supported_protocol_list();
+ return protocol_list_contains(ours, pr, ver);
+}
+
+/**
+ * Return true iff "list" encodes a protocol list that includes support for
+ * the indicated protocol and version.
+ */
+int
+protocol_list_supports_protocol(const char *list, protocol_type_t tp,
+ uint32_t version)
+{
+ /* NOTE: This is a pretty inefficient implementation. If it ever shows
+ * up in profiles, we should memoize it.
+ */
+ smartlist_t *protocols = parse_protocol_list(list);
+ if (!protocols) {
+ return 0;
+ }
+ int contains = protocol_list_contains(protocols, tp, version);
+
+ SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(protocols);
+ return contains;
+}
+
+/** Return the canonical string containing the list of protocols
+ * that we support. */
+const char *
+protover_get_supported_protocols(void)
+{
+ return
+ "Cons=1-2 "
+ "Desc=1-2 "
+ "DirCache=1 "
+ "HSDir=1 "
+ "HSIntro=3 "
+ "HSRend=1-2 "
+ "Link=1-4 "
+ "LinkAuth=1 "
+ "Microdesc=1-2 "
+ "Relay=1-2";
+}
+
+/** The protocols from protover_get_supported_protocols(), as parsed into a
+ * list of proto_entry_t values. Access this via
+ * get_supported_protocol_list. */
+static smartlist_t *supported_protocol_list = NULL;
+
+/** Return a pointer to a smartlist of proto_entry_t for the protocols
+ * we support. */
+static const smartlist_t *
+get_supported_protocol_list(void)
+{
+ if (PREDICT_UNLIKELY(supported_protocol_list == NULL)) {
+ supported_protocol_list =
+ parse_protocol_list(protover_get_supported_protocols());
+ }
+ return supported_protocol_list;
+}
+
+/**
+ * Given a protocol entry, encode it at the end of the smartlist <b>chunks</b>
+ * as one or more newly allocated strings.
+ */
+static void
+proto_entry_encode_into(smartlist_t *chunks, const proto_entry_t *entry)
+{
+ smartlist_add_asprintf(chunks, "%s=", entry->name);
+
+ SMARTLIST_FOREACH_BEGIN(entry->ranges, proto_range_t *, range) {
+ const char *comma = "";
+ if (range_sl_idx != 0)
+ comma = ",";
+
+ if (range->low == range->high) {
+ smartlist_add_asprintf(chunks, "%s%lu",
+ comma, (unsigned long)range->low);
+ } else {
+ smartlist_add_asprintf(chunks, "%s%lu-%lu",
+ comma, (unsigned long)range->low,
+ (unsigned long)range->high);
+ }
+ } SMARTLIST_FOREACH_END(range);
+}
+
+/** Given a list of space-separated proto_entry_t items,
+ * encode it into a newly allocated space-separated string. */
+STATIC char *
+encode_protocol_list(const smartlist_t *sl)
+{
+ const char *separator = "";
+ smartlist_t *chunks = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(sl, const proto_entry_t *, ent) {
+ smartlist_add(chunks, tor_strdup(separator));
+
+ proto_entry_encode_into(chunks, ent);
+
+ separator = " ";
+ } SMARTLIST_FOREACH_END(ent);
+
+ char *result = smartlist_join_strings(chunks, "", 0, NULL);
+
+ SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+ smartlist_free(chunks);
+
+ return result;
+}
+
+/* We treat any protocol list with more than this many subprotocols in it
+ * as a DoS attempt. */
+static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16);
+
+/** Voting helper: Given a list of proto_entry_t, return a newly allocated
+ * smartlist of newly allocated strings, one for each included protocol
+ * version. (So 'Foo=3,5-7' expands to a list of 'Foo=3', 'Foo=5', 'Foo=6',
+ * 'Foo=7'.)
+ *
+ * Do not list any protocol version more than once.
+ *
+ * Return NULL if the list would be too big.
+ */
+static smartlist_t *
+expand_protocol_list(const smartlist_t *protos)
+{
+ smartlist_t *expanded = smartlist_new();
+ if (!protos)
+ return expanded;
+
+ SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) {
+ const char *name = ent->name;
+ SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
+ uint32_t u;
+ for (u = range->low; u <= range->high; ++u) {
+ smartlist_add_asprintf(expanded, "%s=%lu", name, (unsigned long)u);
+ if (smartlist_len(expanded) > MAX_PROTOCOLS_TO_EXPAND)
+ goto too_many;
+ }
+ } SMARTLIST_FOREACH_END(range);
+ } SMARTLIST_FOREACH_END(ent);
+
+ smartlist_sort_strings(expanded);
+ smartlist_uniq_strings(expanded); // This makes voting work. do not remove
+ return expanded;
+
+ too_many:
+ SMARTLIST_FOREACH(expanded, char *, cp, tor_free(cp));
+ smartlist_free(expanded);
+ return NULL;
+}
+
+/** Voting helper: compare two singleton proto_entry_t items by version
+ * alone. (A singleton item is one with a single range entry where
+ * low==high.) */
+static int
+cmp_single_ent_by_version(const void **a_, const void **b_)
+{
+ const proto_entry_t *ent_a = *a_;
+ const proto_entry_t *ent_b = *b_;
+
+ tor_assert(smartlist_len(ent_a->ranges) == 1);
+ tor_assert(smartlist_len(ent_b->ranges) == 1);
+
+ const proto_range_t *a = smartlist_get(ent_a->ranges, 0);
+ const proto_range_t *b = smartlist_get(ent_b->ranges, 0);
+
+ tor_assert(a->low == a->high);
+ tor_assert(b->low == b->high);
+
+ if (a->low < b->low) {
+ return -1;
+ } else if (a->low == b->low) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/** Voting helper: Given a list of singleton protocol strings (of the form
+ * Foo=7), return a canonical listing of all the protocol versions listed,
+ * with as few ranges as possible, with protocol versions sorted lexically and
+ * versions sorted in numerically increasing order, using as few range entries
+ * as possible.
+ **/
+static char *
+contract_protocol_list(const smartlist_t *proto_strings)
+{
+ // map from name to list of single-version entries
+ strmap_t *entry_lists_by_name = strmap_new();
+ // list of protocol names
+ smartlist_t *all_names = smartlist_new();
+ // list of strings for the output we're building
+ smartlist_t *chunks = smartlist_new();
+
+ // Parse each item and stick it entry_lists_by_name. Build
+ // 'all_names' at the same time.
+ SMARTLIST_FOREACH_BEGIN(proto_strings, const char *, s) {
+ if (BUG(!s))
+ continue;// LCOV_EXCL_LINE
+ proto_entry_t *ent = parse_single_entry(s, s+strlen(s));
+ if (BUG(!ent))
+ continue; // LCOV_EXCL_LINE
+ smartlist_t *lst = strmap_get(entry_lists_by_name, ent->name);
+ if (!lst) {
+ smartlist_add(all_names, ent->name);
+ lst = smartlist_new();
+ strmap_set(entry_lists_by_name, ent->name, lst);
+ }
+ smartlist_add(lst, ent);
+ } SMARTLIST_FOREACH_END(s);
+
+ // We want to output the protocols sorted by their name.
+ smartlist_sort_strings(all_names);
+
+ SMARTLIST_FOREACH_BEGIN(all_names, const char *, name) {
+ const int first_entry = (name_sl_idx == 0);
+ smartlist_t *lst = strmap_get(entry_lists_by_name, name);
+ tor_assert(lst);
+ // Sort every entry with this name by version. They are
+ // singletons, so there can't be overlap.
+ smartlist_sort(lst, cmp_single_ent_by_version);
+
+ if (! first_entry)
+ smartlist_add(chunks, tor_strdup(" "));
+
+ /* We're going to construct this entry from the ranges. */
+ proto_entry_t *entry = tor_malloc_zero(sizeof(proto_entry_t));
+ entry->ranges = smartlist_new();
+ entry->name = tor_strdup(name);
+
+ // Now, find all the ranges of versions start..end where
+ // all of start, start+1, start+2, ..end are included.
+ int start_of_cur_series = 0;
+ while (start_of_cur_series < smartlist_len(lst)) {
+ const proto_entry_t *ent = smartlist_get(lst, start_of_cur_series);
+ const proto_range_t *range = smartlist_get(ent->ranges, 0);
+ const uint32_t ver_low = range->low;
+ uint32_t ver_high = ver_low;
+
+ int idx;
+ for (idx = start_of_cur_series+1; idx < smartlist_len(lst); ++idx) {
+ ent = smartlist_get(lst, idx);
+ range = smartlist_get(ent->ranges, 0);
+ if (range->low != ver_high + 1)
+ break;
+ ver_high += 1;
+ }
+
+ // Now idx is either off the end of the list, or the first sequence
+ // break in the list.
+ start_of_cur_series = idx;
+
+ proto_range_t *new_range = tor_malloc_zero(sizeof(proto_range_t));
+ new_range->low = ver_low;
+ new_range->high = ver_high;
+ smartlist_add(entry->ranges, new_range);
+ }
+ proto_entry_encode_into(chunks, entry);
+ proto_entry_free(entry);
+
+ } SMARTLIST_FOREACH_END(name);
+
+ // Build the result...
+ char *result = smartlist_join_strings(chunks, "", 0, NULL);
+
+ // And free all the stuff we allocated.
+ SMARTLIST_FOREACH_BEGIN(all_names, const char *, name) {
+ smartlist_t *lst = strmap_get(entry_lists_by_name, name);
+ tor_assert(lst);
+ SMARTLIST_FOREACH(lst, proto_entry_t *, e, proto_entry_free(e));
+ smartlist_free(lst);
+ } SMARTLIST_FOREACH_END(name);
+
+ strmap_free(entry_lists_by_name, NULL);
+ smartlist_free(all_names);
+ SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+ smartlist_free(chunks);
+
+ return result;
+}
+
+/**
+ * Protocol voting implementation.
+ *
+ * Given a list of strings describing protocol versions, return a newly
+ * allocated string encoding all of the protocols that are listed by at
+ * least <b>threshold</b> of the inputs.
+ *
+ * The string is minimal and sorted according to the rules of
+ * contract_protocol_list above.
+ */
+char *
+protover_compute_vote(const smartlist_t *list_of_proto_strings,
+ int threshold)
+{
+ smartlist_t *all_entries = smartlist_new();
+
+ // First, parse the inputs and break them into singleton entries.
+ SMARTLIST_FOREACH_BEGIN(list_of_proto_strings, const char *, vote) {
+ smartlist_t *unexpanded = parse_protocol_list(vote);
+ smartlist_t *this_vote = expand_protocol_list(unexpanded);
+ if (this_vote == NULL) {
+ log_warn(LD_NET, "When expanding a protocol list from an authority, I "
+ "got too many protocols. This is possibly an attack or a bug, "
+ "unless the Tor network truly has expanded to support over %d "
+ "different subprotocol versions. The offending string was: %s",
+ MAX_PROTOCOLS_TO_EXPAND, escaped(vote));
+ } else {
+ smartlist_add_all(all_entries, this_vote);
+ smartlist_free(this_vote);
+ }
+ SMARTLIST_FOREACH(unexpanded, proto_entry_t *, e, proto_entry_free(e));
+ smartlist_free(unexpanded);
+ } SMARTLIST_FOREACH_END(vote);
+
+ // Now sort the singleton entries
+ smartlist_sort_strings(all_entries);
+
+ // Now find all the strings that appear at least 'threshold' times.
+ smartlist_t *include_entries = smartlist_new();
+ const char *cur_entry = smartlist_get(all_entries, 0);
+ int n_times = 0;
+ SMARTLIST_FOREACH_BEGIN(all_entries, const char *, ent) {
+ if (!strcmp(ent, cur_entry)) {
+ n_times++;
+ } else {
+ if (n_times >= threshold && cur_entry)
+ smartlist_add(include_entries, (void*)cur_entry);
+ cur_entry = ent;
+ n_times = 1 ;
+ }
+ } SMARTLIST_FOREACH_END(ent);
+
+ if (n_times >= threshold && cur_entry)
+ smartlist_add(include_entries, (void*)cur_entry);
+
+ // Finally, compress that list.
+ char *result = contract_protocol_list(include_entries);
+ smartlist_free(include_entries);
+ SMARTLIST_FOREACH(all_entries, char *, cp, tor_free(cp));
+ smartlist_free(all_entries);
+
+ return result;
+}
+
+/** Return true if every protocol version described in the string <b>s</b> is
+ * one that we support, and false otherwise. If <b>missing_out</b> is
+ * provided, set it to the list of protocols we do not support.
+ *
+ * NOTE: This is quadratic, but we don't do it much: only a few times per
+ * consensus. Checking signatures should be way more expensive than this
+ * ever would be.
+ **/
+int
+protover_all_supported(const char *s, char **missing_out)
+{
+ int all_supported = 1;
+ smartlist_t *missing;
+
+ if (!s) {
+ return 1;
+ }
+
+ smartlist_t *entries = parse_protocol_list(s);
+
+ missing = smartlist_new();
+
+ SMARTLIST_FOREACH_BEGIN(entries, const proto_entry_t *, ent) {
+ protocol_type_t tp;
+ if (str_to_protocol_type(ent->name, &tp) < 0) {
+ if (smartlist_len(ent->ranges)) {
+ goto unsupported;
+ }
+ continue;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
+ uint32_t i;
+ for (i = range->low; i <= range->high; ++i) {
+ if (!protover_is_supported_here(tp, i)) {
+ goto unsupported;
+ }
+ }
+ } SMARTLIST_FOREACH_END(range);
+
+ continue;
+
+ unsupported:
+ all_supported = 0;
+ smartlist_add(missing, (void*) ent);
+ } SMARTLIST_FOREACH_END(ent);
+
+ if (missing_out && !all_supported) {
+ tor_assert(0 != smartlist_len(missing));
+ *missing_out = encode_protocol_list(missing);
+ }
+ smartlist_free(missing);
+
+ SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(entries);
+
+ return all_supported;
+}
+
+/** Helper: Given a list of proto_entry_t, return true iff
+ * <b>pr</b>=<b>ver</b> is included in that list. */
+static int
+protocol_list_contains(const smartlist_t *protos,
+ protocol_type_t pr, uint32_t ver)
+{
+ if (BUG(protos == NULL)) {
+ return 0; // LCOV_EXCL_LINE
+ }
+ const char *pr_name = protocol_type_to_str(pr);
+ if (BUG(pr_name == NULL)) {
+ return 0; // LCOV_EXCL_LINE
+ }
+
+ SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) {
+ if (strcasecmp(ent->name, pr_name))
+ continue;
+ /* name matches; check the ranges */
+ SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) {
+ if (ver >= range->low && ver <= range->high)
+ return 1;
+ } SMARTLIST_FOREACH_END(range);
+ } SMARTLIST_FOREACH_END(ent);
+
+ return 0;
+}
+
+/** Return a string describing the protocols supported by tor version
+ * <b>version</b>, or an empty string if we cannot tell.
+ *
+ * Note that this is only used to infer protocols for Tor versions that
+ * can't declare their own.
+ **/
+const char *
+protover_compute_for_old_tor(const char *version)
+{
+ if (version == NULL) {
+ /* No known version; guess the oldest series that is still supported. */
+ version = "0.2.5.15";
+ }
+
+ if (tor_version_as_new_as(version,
+ FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS)) {
+ return "";
+ } else if (tor_version_as_new_as(version, "0.2.9.1-alpha")) {
+ /* 0.2.9.1-alpha HSRend=2 */
+ return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 "
+ "Link=1-4 LinkAuth=1 "
+ "Microdesc=1-2 Relay=1-2";
+ } else if (tor_version_as_new_as(version, "0.2.7.5")) {
+ /* 0.2.7-stable added Desc=2, Microdesc=2, Cons=2, which indicate
+ * ed25519 support. We'll call them present only in "stable" 027,
+ * though. */
+ return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=1-4 LinkAuth=1 "
+ "Microdesc=1-2 Relay=1-2";
+ } else if (tor_version_as_new_as(version, "0.2.4.19")) {
+ /* No currently supported Tor server versions are older than this, or
+ * lack these protocols. */
+ return "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 "
+ "Link=1-4 LinkAuth=1 "
+ "Microdesc=1 Relay=1-2";
+ } else {
+ /* Cannot infer protocols. */
+ return "";
+ }
+}
+
+/**
+ * Release all storage held by static fields in protover.c
+ */
+void
+protover_free_all(void)
+{
+ if (supported_protocol_list) {
+ smartlist_t *entries = supported_protocol_list;
+ SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(entries);
+ supported_protocol_list = NULL;
+ }
+}
+
diff --git a/src/or/protover.h b/src/or/protover.h
new file mode 100644
index 0000000000..5c658931ea
--- /dev/null
+++ b/src/or/protover.h
@@ -0,0 +1,74 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file protover.h
+ * \brief Headers and type declarations for protover.c
+ **/
+
+#ifndef TOR_PROTOVER_H
+#define TOR_PROTOVER_H
+
+#include "container.h"
+
+/** The first version of Tor that included "proto" entries in its
+ * descriptors. Authorities should use this to decide whether to
+ * guess proto lines. */
+/* This is a guess. */
+#define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha"
+
+/** List of recognized subprotocols. */
+typedef enum protocol_type_t {
+ PRT_LINK,
+ PRT_LINKAUTH,
+ PRT_RELAY,
+ PRT_DIRCACHE,
+ PRT_HSDIR,
+ PRT_HSINTRO,
+ PRT_HSREND,
+ PRT_DESC,
+ PRT_MICRODESC,
+ PRT_CONS,
+} protocol_type_t;
+
+int protover_all_supported(const char *s, char **missing);
+int protover_is_supported_here(protocol_type_t pr, uint32_t ver);
+const char *protover_get_supported_protocols(void);
+
+char *protover_compute_vote(const smartlist_t *list_of_proto_strings,
+ int threshold);
+const char *protover_compute_for_old_tor(const char *version);
+int protocol_list_supports_protocol(const char *list, protocol_type_t tp,
+ uint32_t version);
+
+void protover_free_all(void);
+
+#ifdef PROTOVER_PRIVATE
+/** Represents a range of subprotocols of a given type. All subprotocols
+ * between <b>low</b> and <b>high</b> inclusive are included. */
+typedef struct proto_range_t {
+ uint32_t low;
+ uint32_t high;
+} proto_range_t;
+
+/** Represents a set of ranges of subprotocols of a given type. */
+typedef struct proto_entry_t {
+ /** The name of the protocol.
+ *
+ * (This needs to handle voting on protocols which
+ * we don't recognize yet, so it's a char* rather than a protocol_type_t.)
+ */
+ char *name;
+ /** Smartlist of proto_range_t */
+ smartlist_t *ranges;
+} proto_entry_t;
+
+STATIC smartlist_t *parse_protocol_list(const char *s);
+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
+
diff --git a/src/or/reasons.c b/src/or/reasons.c
index 36921cafcd..a1566e2299 100644
--- a/src/or/reasons.c
+++ b/src/or/reasons.c
@@ -6,6 +6,12 @@
* \file reasons.c
* \brief Convert circuit, stream, and orconn error reasons to and/or from
* strings and errno values.
+ *
+ * This module is just a bunch of functions full of case statements that
+ * convert from one representation of our error codes to another. These are
+ * mainly used in generating log messages, in sending messages to the
+ * controller in control.c, and in converting errors from one protocol layer
+ * to another.
**/
#include "or.h"
diff --git a/src/or/relay.c b/src/or/relay.c
index 9cd68cc440..e7f99fda08 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -255,12 +255,12 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ,
if (! CIRCUIT_IS_ORIGIN(circ) &&
TO_OR_CIRCUIT(circ)->rend_splice &&
cell_direction == CELL_DIRECTION_OUT) {
- or_circuit_t *splice = TO_OR_CIRCUIT(circ)->rend_splice;
+ or_circuit_t *splice_ = TO_OR_CIRCUIT(circ)->rend_splice;
tor_assert(circ->purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
- tor_assert(splice->base_.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
- cell->circ_id = splice->p_circ_id;
+ tor_assert(splice_->base_.purpose == CIRCUIT_PURPOSE_REND_ESTABLISHED);
+ cell->circ_id = splice_->p_circ_id;
cell->command = CELL_RELAY; /* can't be relay_early anyway */
- if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice),
+ if ((reason = circuit_receive_relay_cell(cell, TO_CIRCUIT(splice_),
CELL_DIRECTION_IN)) < 0) {
log_warn(LD_REND, "Error relaying cell across rendezvous; closing "
"circuits");
@@ -383,6 +383,11 @@ circuit_package_relay_cell(cell_t *cell, circuit_t *circ,
{
channel_t *chan; /* where to send the cell */
+ if (circ->marked_for_close) {
+ /* Circuit is marked; send nothing. */
+ return 0;
+ }
+
if (cell_direction == CELL_DIRECTION_OUT) {
crypt_path_t *thishop; /* counter for repeated crypts */
chan = circ->n_chan;
@@ -696,6 +701,12 @@ connection_edge_send_command(edge_connection_t *fromconn,
return -1;
}
+ if (circ->marked_for_close) {
+ /* The circuit has been marked, but not freed yet. When it's freed, it
+ * will mark this connection for close. */
+ return -1;
+ }
+
return relay_send_command_from_edge(fromconn->stream_id, circ,
relay_command, payload,
payload_len, cpath_layer);
@@ -859,6 +870,7 @@ connection_ap_process_end_not_open(
break; /* break means it'll close, below */
/* Else fall through: expire this circuit, clear the
* chosen_exit_name field, and try again. */
+ /* Falls through. */
case END_STREAM_REASON_RESOLVEFAILED:
case END_STREAM_REASON_TIMEOUT:
case END_STREAM_REASON_MISC:
@@ -1374,7 +1386,7 @@ connection_edge_process_relay_cell_not_open(
/* This is definitely a success, so forget about any pending data we
* had sent. */
if (entry_conn->pending_optimistic_data) {
- generic_buffer_free(entry_conn->pending_optimistic_data);
+ buf_free(entry_conn->pending_optimistic_data);
entry_conn->pending_optimistic_data = NULL;
}
@@ -1877,7 +1889,7 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
entry_conn->sending_optimistic_data != NULL;
if (PREDICT_UNLIKELY(sending_from_optimistic)) {
- bytes_to_process = generic_buffer_len(entry_conn->sending_optimistic_data);
+ bytes_to_process = buf_datalen(entry_conn->sending_optimistic_data);
if (PREDICT_UNLIKELY(!bytes_to_process)) {
log_warn(LD_BUG, "sending_optimistic_data was non-NULL but empty");
bytes_to_process = connection_get_inbuf_len(TO_CONN(conn));
@@ -1905,9 +1917,9 @@ 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. */
- generic_buffer_get(entry_conn->sending_optimistic_data, payload, length);
- if (!generic_buffer_len(entry_conn->sending_optimistic_data)) {
- generic_buffer_free(entry_conn->sending_optimistic_data);
+ fetch_from_buf(payload, length, entry_conn->sending_optimistic_data);
+ if (!buf_datalen(entry_conn->sending_optimistic_data)) {
+ buf_free(entry_conn->sending_optimistic_data);
entry_conn->sending_optimistic_data = NULL;
}
} else {
@@ -1922,8 +1934,8 @@ connection_edge_package_raw_inbuf(edge_connection_t *conn, int package_partial,
/* This is new optimistic data; remember it in case we need to detach and
retry */
if (!entry_conn->pending_optimistic_data)
- entry_conn->pending_optimistic_data = generic_buffer_new();
- generic_buffer_add(entry_conn->pending_optimistic_data, payload, length);
+ entry_conn->pending_optimistic_data = buf_new();
+ write_to_buf(payload, length, entry_conn->pending_optimistic_data);
}
if (connection_edge_send_command(conn, RELAY_COMMAND_DATA,
@@ -2321,14 +2333,12 @@ cell_queue_append_packed_copy(circuit_t *circ, cell_queue_t *queue,
int exitward, const cell_t *cell,
int wide_circ_ids, int use_stats)
{
- struct timeval now;
packed_cell_t *copy = packed_cell_copy(cell, wide_circ_ids);
(void)circ;
(void)exitward;
(void)use_stats;
- tor_gettimeofday_cached_monotonic(&now);
- copy->inserted_time = (uint32_t)tv_to_msec(&now);
+ copy->inserted_time = (uint32_t) monotime_coarse_absolute_msec();
cell_queue_append(queue, copy);
}
@@ -2457,7 +2467,7 @@ update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction,
/* Cmux sanity check */
if (! circuitmux_is_circuit_attached(cmux, circ)) {
- log_warn(LD_BUG, "called on non-attachd circuit from %s:%d",
+ log_warn(LD_BUG, "called on non-attached circuit from %s:%d",
file, lineno);
return;
}
@@ -2526,7 +2536,7 @@ set_streams_blocked_on_circ(circuit_t *circ, channel_t *chan,
edge->edge_blocked_on_circ = block;
}
- if (!conn->read_event && !HAS_BUFFEREVENT(conn)) {
+ if (!conn->read_event) {
/* This connection is a placeholder for something; probably a DNS
* request. It can't actually stop or start reading.*/
continue;
@@ -2638,9 +2648,8 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max))
if (get_options()->CellStatistics ||
get_options()->TestingEnableCellStatsEvent) {
uint32_t msec_waiting;
- struct timeval tvnow;
- tor_gettimeofday_cached(&tvnow);
- msec_waiting = ((uint32_t)tv_to_msec(&tvnow)) - cell->inserted_time;
+ uint32_t msec_now = (uint32_t)monotime_coarse_absolute_msec();
+ msec_waiting = msec_now - cell->inserted_time;
if (get_options()->CellStatistics && !CIRCUIT_IS_ORIGIN(circ)) {
or_circ = TO_OR_CIRCUIT(circ);
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index f9ae6d1173..aa69d735fe 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -958,25 +958,25 @@ rend_cache_store_v2_desc_as_client(const char *desc,
* avoid an evil HSDir serving old descriptor. We validate if the
* timestamp is greater than and not equal because it's a rounded down
* timestamp to the hour so if the descriptor changed in the same hour,
- * the rend cache failure will tells us if we have a new descriptor. */
+ * the rend cache failure will tell us if we have a new descriptor. */
if (e && e->parsed->timestamp > parsed->timestamp) {
log_info(LD_REND, "We already have a new enough service descriptor for "
"service ID %s with the same desc ID and version.",
safe_str_client(service_id));
goto okay;
}
- /* Lookup our failure cache for intro point that might be unsuable. */
+ /* Lookup our failure cache for intro point that might be unusable. */
validate_intro_point_failure(parsed, service_id);
- /* It's now possible that our intro point list is empty, this means that
+ /* It's now possible that our intro point list is empty, which means that
* this descriptor is useless to us because intro points have all failed
* somehow before. Discard the descriptor. */
if (smartlist_len(parsed->intro_nodes) == 0) {
- log_info(LD_REND, "Service descriptor with service ID %s, every "
- "intro points are unusable. Discarding it.",
+ log_info(LD_REND, "Service descriptor with service ID %s has no "
+ "usable intro points. Discarding it.",
safe_str_client(service_id));
goto err;
}
- /* Now either purge the current one and replace it's content or create a
+ /* Now either purge the current one and replace its content or create a
* new one and add it to the rend cache. */
if (!e) {
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 0e8b918753..270b614c38 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -102,6 +102,13 @@ STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc,
const char *service_id);
STATIC void rend_cache_failure_entry_free_(void *entry);
+
+#ifdef TOR_UNIT_TESTS
+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 /* TOR_RENDCACHE_H */
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 609c45c71d..a93bc94a9c 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -134,6 +134,7 @@ int
rend_client_send_introduction(origin_circuit_t *introcirc,
origin_circuit_t *rendcirc)
{
+ const or_options_t *options = get_options();
size_t payload_len;
int r, v3_shift = 0;
char payload[RELAY_PAYLOAD_SIZE];
@@ -150,10 +151,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
tor_assert(rendcirc->rend_data);
tor_assert(!rend_cmp_service_ids(introcirc->rend_data->onion_address,
rendcirc->rend_data->onion_address));
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(!(introcirc->build_state->onehop_tunnel));
- tor_assert(!(rendcirc->build_state->onehop_tunnel));
-#endif
+ assert_circ_anonymity_ok(introcirc, options);
+ assert_circ_anonymity_ok(rendcirc, options);
r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
&entry);
@@ -387,6 +386,7 @@ int
rend_client_introduction_acked(origin_circuit_t *circ,
const uint8_t *request, size_t request_len)
{
+ const or_options_t *options = get_options();
origin_circuit_t *rendcirc;
(void) request; // XXXX Use this.
@@ -398,10 +398,9 @@ rend_client_introduction_acked(origin_circuit_t *circ,
return -1;
}
+ tor_assert(circ->build_state);
tor_assert(circ->build_state->chosen_exit);
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(!(circ->build_state->onehop_tunnel));
-#endif
+ assert_circ_anonymity_ok(circ, options);
tor_assert(circ->rend_data);
/* For path bias: This circuit was used successfully. Valid
@@ -416,9 +415,7 @@ rend_client_introduction_acked(origin_circuit_t *circ,
log_info(LD_REND,"Received ack. Telling rend circ...");
rendcirc = circuit_get_ready_rend_circ_by_rend_data(circ->rend_data);
if (rendcirc) { /* remember the ack */
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(!(rendcirc->build_state->onehop_tunnel));
-#endif
+ assert_circ_anonymity_ok(rendcirc, options);
circuit_change_purpose(TO_CIRCUIT(rendcirc),
CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
/* Set timestamp_dirty, because circuit_expire_building expects
@@ -469,6 +466,23 @@ rend_client_introduction_acked(origin_circuit_t *circ,
/** 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
@@ -510,7 +524,7 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir,
tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
hsdir_id_base32,
desc_id_base32);
- /* XXX023 tor_assert(strlen(hsdir_desc_comb_id) ==
+ /* XXX++?? tor_assert(strlen(hsdir_desc_comb_id) ==
LAST_HID_SERV_REQUEST_KEY_LEN); */
if (set) {
time_t *oldptr;
@@ -532,7 +546,7 @@ static void
directory_clean_last_hid_serv_requests(time_t now)
{
strmap_iter_t *iter;
- time_t cutoff = now - REND_HID_SERV_DIR_REQUERY_PERIOD;
+ 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); ) {
@@ -572,7 +586,7 @@ purge_hid_serv_from_last_hid_serv_requests(const char *desc_id)
const char *key;
void *val;
strmap_iter_get(iter, &key, &val);
- /* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */
+ /* 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,
@@ -635,7 +649,7 @@ pick_hsdir(const char *desc_id, const char *desc_id_base32)
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 + REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
+ if (last + hsdir_requery_period(options) >= now ||
!node || !node_has_descriptor(node)) {
SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
continue;
@@ -813,9 +827,9 @@ fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs)
tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
while (tries_left > 0) {
- int rand = crypto_rand_int(tries_left);
- int chosen_replica = replicas_left_to_try[rand];
- replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
+ int rand_val = crypto_rand_int(tries_left);
+ int chosen_replica = replicas_left_to_try[rand_val];
+ replicas_left_to_try[rand_val] = replicas_left_to_try[--tries_left];
ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address,
query->auth_type == REND_STEALTH_AUTH ?
@@ -895,12 +909,6 @@ rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
rend_cache_entry_t *e = NULL;
tor_assert(rend_query);
- /* Are we configured to fetch descriptors? */
- if (!get_options()->FetchHidServDescriptors) {
- log_warn(LD_REND, "We received an onion address for a v2 rendezvous "
- "service descriptor, but are not fetching service descriptors.");
- return;
- }
/* Before fetching, check if we already have a usable descriptor here. */
if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) == 0 &&
rend_client_any_intro_points_usable(e)) {
@@ -908,6 +916,12 @@ rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
"already have a usable descriptor here. Not fetching.");
return;
}
+ /* Are we configured to fetch descriptors? */
+ if (!get_options()->FetchHidServDescriptors) {
+ log_warn(LD_REND, "We received an onion address for a v2 rendezvous "
+ "service descriptor, but are not fetching service descriptors.");
+ return;
+ }
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
safe_str_client(rend_query->onion_address));
@@ -1099,7 +1113,7 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request,
* 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
+ /* 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
@@ -1351,40 +1365,20 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry,
i = crypto_rand_int(smartlist_len(usable_nodes));
intro = smartlist_get(usable_nodes, i);
- /* Do we need to look up the router or is the extend info complete? */
- if (!intro->extend_info->onion_key) {
- const node_t *node;
- extend_info_t *new_extend_info;
- if (tor_digest_is_zero(intro->extend_info->identity_digest))
- node = node_get_by_hex_id(intro->extend_info->nickname);
- else
- node = node_get_by_id(intro->extend_info->identity_digest);
- if (!node) {
- log_info(LD_REND, "Unknown router with nickname '%s'; trying another.",
- intro->extend_info->nickname);
- smartlist_del(usable_nodes, i);
- goto again;
- }
-#ifdef ENABLE_TOR2WEB_MODE
- new_extend_info = extend_info_from_node(node, options->Tor2webMode);
-#else
- new_extend_info = extend_info_from_node(node, 0);
-#endif
- if (!new_extend_info) {
- const char *alternate_reason = "";
-#ifdef ENABLE_TOR2WEB_MODE
- alternate_reason = ", or we cannot connect directly to it";
-#endif
- log_info(LD_REND, "We don't have a descriptor for the intro-point relay "
- "'%s'%s; trying another.",
- extend_info_describe(intro->extend_info), alternate_reason);
- smartlist_del(usable_nodes, i);
- goto again;
- } else {
- extend_info_free(intro->extend_info);
- intro->extend_info = new_extend_info;
- }
- tor_assert(intro->extend_info != NULL);
+ if (BUG(!intro->extend_info)) {
+ /* This should never happen, but it isn't fatal, just try another */
+ smartlist_del(usable_nodes, i);
+ goto again;
+ }
+ /* All version 2 HS descriptors come with a TAP onion key.
+ * Clients used to try to get the TAP onion key from the consensus, but this
+ * meant that hidden services could discover which consensus clients have. */
+ if (!extend_info_supports_tap(intro->extend_info)) {
+ log_info(LD_REND, "The HS descriptor is missing a TAP onion key for the "
+ "intro-point relay '%s'; trying another.",
+ safe_str_client(extend_info_describe(intro->extend_info)));
+ smartlist_del(usable_nodes, i);
+ goto again;
}
/* Check if we should refuse to talk to this router. */
if (strict &&
@@ -1466,12 +1460,10 @@ rend_parse_service_authorization(const or_options_t *options,
strmap_t *parsed = strmap_new();
smartlist_t *sl = smartlist_new();
rend_service_authorization_t *auth = NULL;
- char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
- char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_BASE64+2+1];
+ char *err_msg = NULL;
for (line = options->HidServAuth; line; line = line->next) {
char *onion_address, *descriptor_cookie;
- int auth_type_val = 0;
auth = NULL;
SMARTLIST_FOREACH(sl, char *, c, tor_free(c););
smartlist_clear(sl);
@@ -1500,31 +1492,13 @@ rend_parse_service_authorization(const or_options_t *options,
}
/* Parse descriptor cookie. */
descriptor_cookie = smartlist_get(sl, 1);
- if (strlen(descriptor_cookie) != REND_DESC_COOKIE_LEN_BASE64) {
- log_warn(LD_CONFIG, "Authorization cookie has wrong length: '%s'",
- descriptor_cookie);
- goto err;
- }
- /* Add trailing zero bytes (AA) to make base64-decoding happy. */
- tor_snprintf(descriptor_cookie_base64ext,
- REND_DESC_COOKIE_LEN_BASE64+2+1,
- "%sAA", descriptor_cookie);
- if (base64_decode(descriptor_cookie_tmp, sizeof(descriptor_cookie_tmp),
- descriptor_cookie_base64ext,
- strlen(descriptor_cookie_base64ext)) < 0) {
- log_warn(LD_CONFIG, "Decoding authorization cookie failed: '%s'",
- descriptor_cookie);
+ if (rend_auth_decode_cookie(descriptor_cookie, auth->descriptor_cookie,
+ &auth->auth_type, &err_msg) < 0) {
+ tor_assert(err_msg);
+ log_warn(LD_CONFIG, "%s", err_msg);
+ tor_free(err_msg);
goto err;
}
- auth_type_val = (((uint8_t)descriptor_cookie_tmp[16]) >> 4) + 1;
- if (auth_type_val < 1 || auth_type_val > 2) {
- log_warn(LD_CONFIG, "Authorization cookie has unknown authorization "
- "type encoded.");
- goto err;
- }
- auth->auth_type = auth_type_val == 1 ? REND_BASIC_AUTH : REND_STEALTH_AUTH;
- memcpy(auth->descriptor_cookie, descriptor_cookie_tmp,
- REND_DESC_COOKIE_LEN);
if (strmap_get(parsed, auth->onion_address)) {
log_warn(LD_CONFIG, "Duplicate authorization for the same hidden "
"service.");
@@ -1547,8 +1521,38 @@ rend_parse_service_authorization(const or_options_t *options,
} else {
strmap_free(parsed, rend_service_authorization_strmap_item_free);
}
- memwipe(descriptor_cookie_tmp, 0, sizeof(descriptor_cookie_tmp));
- memwipe(descriptor_cookie_base64ext, 0, sizeof(descriptor_cookie_base64ext));
return res;
}
+/* Can Tor client code make direct (non-anonymous) connections to introduction
+ * or rendezvous points?
+ * Returns true if tor was compiled with NON_ANONYMOUS_MODE_ENABLED, and is
+ * configured in Tor2web mode. */
+int
+rend_client_allow_non_anonymous_connection(const or_options_t *options)
+{
+ /* Tor2web support needs to be compiled in to a tor binary. */
+#ifdef NON_ANONYMOUS_MODE_ENABLED
+ /* Tor2web */
+ return options->Tor2webMode ? 1 : 0;
+#else
+ (void)options;
+ return 0;
+#endif
+}
+
+/* At compile-time, was non-anonymous mode enabled via
+ * NON_ANONYMOUS_MODE_ENABLED ? */
+int
+rend_client_non_anonymous_mode_enabled(const or_options_t *options)
+{
+ (void)options;
+ /* Tor2web support needs to be compiled in to a tor binary. */
+#ifdef NON_ANONYMOUS_MODE_ENABLED
+ /* Tor2web */
+ return 1;
+#else
+ return 0;
+#endif
+}
+
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index e90dac07ab..b8f8c2f871 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -51,5 +51,8 @@ rend_service_authorization_t *rend_client_lookup_service_authorization(
const char *onion_address);
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
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 438fbc4d9a..d9d39b1f19 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -211,7 +211,7 @@ rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc)
goto done;
}
/* Assemble everything for this introduction point. */
- address = tor_dup_addr(&info->addr);
+ address = tor_addr_to_str_dup(&info->addr);
res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written,
"introduction-point %s\n"
"ip-address %s\n"
@@ -720,6 +720,22 @@ rend_valid_descriptor_id(const char *query)
return 0;
}
+/** Return true iff <b>client_name</b> is a syntactically valid name
+ * for rendezvous client authentication. */
+int
+rend_valid_client_name(const char *client_name)
+{
+ size_t len = strlen(client_name);
+ if (len < 1 || len > REND_CLIENTNAME_MAX_LEN) {
+ return 0;
+ }
+ if (strspn(client_name, REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
+ return 0;
+ }
+
+ return 1;
+}
+
/** Called when we get a rendezvous-related relay cell on circuit
* <b>circ</b>. Dispatch on rendezvous relay command. */
void
@@ -941,3 +957,162 @@ hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
return smartlist_len(responsible_dirs) ? 0 : -1;
}
+/* Length of the 'extended' auth cookie used to encode auth type before
+ * base64 encoding. */
+#define REND_DESC_COOKIE_LEN_EXT (REND_DESC_COOKIE_LEN + 1)
+/* Length of the zero-padded auth cookie when base64 encoded. These two
+ * padding bytes always (A=) are stripped off of the returned cookie. */
+#define REND_DESC_COOKIE_LEN_EXT_BASE64 (REND_DESC_COOKIE_LEN_BASE64 + 2)
+
+/** Encode a client authorization descriptor cookie.
+ * The result of this function is suitable for use in the HidServAuth
+ * option. The trailing padding characters are removed, and the
+ * auth type is encoded into the cookie.
+ *
+ * Returns a new base64-encoded cookie. This function cannot fail.
+ * The caller is responsible for freeing the returned value.
+ */
+char *
+rend_auth_encode_cookie(const uint8_t *cookie_in, rend_auth_type_t auth_type)
+{
+ uint8_t extended_cookie[REND_DESC_COOKIE_LEN_EXT];
+ char *cookie_out = tor_malloc_zero(REND_DESC_COOKIE_LEN_EXT_BASE64 + 1);
+ int re;
+
+ tor_assert(cookie_in);
+
+ memcpy(extended_cookie, cookie_in, REND_DESC_COOKIE_LEN);
+ extended_cookie[REND_DESC_COOKIE_LEN] = ((int)auth_type - 1) << 4;
+ re = base64_encode(cookie_out, REND_DESC_COOKIE_LEN_EXT_BASE64 + 1,
+ (const char *) extended_cookie, REND_DESC_COOKIE_LEN_EXT,
+ 0);
+ tor_assert(re == REND_DESC_COOKIE_LEN_EXT_BASE64);
+
+ /* Remove the trailing 'A='. Auth type is encoded in the high bits
+ * of the last byte, so the last base64 character will always be zero
+ * (A). This is subtly different behavior from base64_encode_nopad. */
+ cookie_out[REND_DESC_COOKIE_LEN_BASE64] = '\0';
+ memwipe(extended_cookie, 0, sizeof(extended_cookie));
+ return cookie_out;
+}
+
+/** Decode a base64-encoded client authorization descriptor cookie.
+ * The descriptor_cookie can be truncated to REND_DESC_COOKIE_LEN_BASE64
+ * characters (as given to clients), or may include the two padding
+ * characters (as stored by the service).
+ *
+ * The result is stored in REND_DESC_COOKIE_LEN bytes of cookie_out.
+ * The rend_auth_type_t decoded from the cookie is stored in the
+ * optional auth_type_out parameter.
+ *
+ * Return 0 on success, or -1 on error. The caller is responsible for
+ * freeing the returned err_msg.
+ */
+int
+rend_auth_decode_cookie(const char *cookie_in, uint8_t *cookie_out,
+ rend_auth_type_t *auth_type_out, char **err_msg_out)
+{
+ uint8_t descriptor_cookie_decoded[REND_DESC_COOKIE_LEN_EXT + 1] = { 0 };
+ char descriptor_cookie_base64ext[REND_DESC_COOKIE_LEN_EXT_BASE64 + 1];
+ const char *descriptor_cookie = cookie_in;
+ char *err_msg = NULL;
+ int auth_type_val = 0;
+ int res = -1;
+ int decoded_len;
+
+ size_t len = strlen(descriptor_cookie);
+ if (len == REND_DESC_COOKIE_LEN_BASE64) {
+ /* Add a trailing zero byte to make base64-decoding happy. */
+ tor_snprintf(descriptor_cookie_base64ext,
+ sizeof(descriptor_cookie_base64ext),
+ "%sA=", descriptor_cookie);
+ descriptor_cookie = descriptor_cookie_base64ext;
+ } else if (len != REND_DESC_COOKIE_LEN_EXT_BASE64) {
+ tor_asprintf(&err_msg, "Authorization cookie has wrong length: %s",
+ escaped(cookie_in));
+ goto err;
+ }
+
+ decoded_len = base64_decode((char *) descriptor_cookie_decoded,
+ sizeof(descriptor_cookie_decoded),
+ descriptor_cookie,
+ REND_DESC_COOKIE_LEN_EXT_BASE64);
+ if (decoded_len != REND_DESC_COOKIE_LEN &&
+ decoded_len != REND_DESC_COOKIE_LEN_EXT) {
+ tor_asprintf(&err_msg, "Authorization cookie has invalid characters: %s",
+ escaped(cookie_in));
+ goto err;
+ }
+
+ if (auth_type_out) {
+ auth_type_val = (descriptor_cookie_decoded[REND_DESC_COOKIE_LEN] >> 4) + 1;
+ if (auth_type_val < 1 || auth_type_val > 2) {
+ tor_asprintf(&err_msg, "Authorization cookie type is unknown: %s",
+ escaped(cookie_in));
+ goto err;
+ }
+ *auth_type_out = auth_type_val == 1 ? REND_BASIC_AUTH : REND_STEALTH_AUTH;
+ }
+
+ memcpy(cookie_out, descriptor_cookie_decoded, REND_DESC_COOKIE_LEN);
+ res = 0;
+ err:
+ if (err_msg_out) {
+ *err_msg_out = err_msg;
+ } else {
+ tor_free(err_msg);
+ }
+ memwipe(descriptor_cookie_decoded, 0, sizeof(descriptor_cookie_decoded));
+ memwipe(descriptor_cookie_base64ext, 0, sizeof(descriptor_cookie_base64ext));
+ return res;
+}
+
+/* Is this a rend client or server that allows direct (non-anonymous)
+ * connections?
+ * Clients must be specifically compiled and configured in this mode.
+ * Onion services can be configured to start in this mode.
+ * Prefer rend_client_allow_non_anonymous_connection() or
+ * rend_service_allow_non_anonymous_connection() whenever possible, so that
+ * checks are specific to Single Onion Services or Tor2web. */
+int
+rend_allow_non_anonymous_connection(const or_options_t* options)
+{
+ return (rend_client_allow_non_anonymous_connection(options)
+ || rend_service_allow_non_anonymous_connection(options));
+}
+
+/* Is this a rend client or server in non-anonymous mode?
+ * Clients must be specifically compiled in this mode.
+ * Onion services can be configured to start in this mode.
+ * Prefer rend_client_non_anonymous_mode_enabled() or
+ * rend_service_non_anonymous_mode_enabled() whenever possible, so that checks
+ * are specific to Single Onion Services or Tor2web. */
+int
+rend_non_anonymous_mode_enabled(const or_options_t *options)
+{
+ return (rend_client_non_anonymous_mode_enabled(options)
+ || rend_service_non_anonymous_mode_enabled(options));
+}
+
+/* Make sure that tor only builds one-hop circuits when they would not
+ * compromise user anonymity.
+ *
+ * One-hop circuits are permitted in Tor2web or Single Onion modes.
+ *
+ * Tor2web or Single Onion modes are also allowed to make multi-hop circuits.
+ * For example, single onion HSDir circuits are 3-hop to prevent denial of
+ * service.
+ */
+void
+assert_circ_anonymity_ok(origin_circuit_t *circ,
+ const or_options_t *options)
+{
+ tor_assert(options);
+ tor_assert(circ);
+ tor_assert(circ->build_state);
+
+ if (circ->build_state->onehop_tunnel) {
+ tor_assert(rend_allow_non_anonymous_connection(options));
+ }
+}
+
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index d67552e405..090e6f25e0 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -45,6 +45,7 @@ void rend_intro_point_free(rend_intro_point_t *intro);
int rend_valid_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,
rend_service_descriptor_t *desc, time_t now,
uint8_t period, rend_auth_type_t auth_type,
@@ -68,5 +69,19 @@ rend_data_t *rend_data_service_create(const char *onion_address,
const char *pk_digest,
const uint8_t *cookie,
rend_auth_type_t auth_type);
+
+char *rend_auth_encode_cookie(const uint8_t *cookie_in,
+ rend_auth_type_t auth_type);
+int rend_auth_decode_cookie(const char *cookie_in,
+ uint8_t *cookie_out,
+ rend_auth_type_t *auth_type_out,
+ char **err_msg_out);
+
+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,
+ const or_options_t *options);
+
#endif
diff --git a/src/or/rendmid.c b/src/or/rendmid.c
index a33ad92966..ca0ad7b0d4 100644
--- a/src/or/rendmid.c
+++ b/src/or/rendmid.c
@@ -309,7 +309,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request,
goto err;
}
- if (request_len != REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN) {
+ if (request_len < REND_COOKIE_LEN) {
log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
"Rejecting RENDEZVOUS1 cell with bad length (%d) on circuit %u.",
(int)request_len, (unsigned)circ->p_circ_id);
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 037c5fadd9..a8c383444d 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -20,6 +20,7 @@
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
+#include "policies.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendservice.h"
@@ -71,6 +72,13 @@ static ssize_t rend_service_parse_intro_for_v3(
size_t plaintext_len,
char **err_msg_out);
+static int rend_service_check_private_dir(const or_options_t *options,
+ const rend_service_t *s,
+ int create);
+static int rend_service_check_private_dir_impl(const or_options_t *options,
+ const rend_service_t *s,
+ int create);
+
/** Represents the mapping from a virtual port of a rendezvous service to
* a real port on some IP.
*/
@@ -107,59 +115,13 @@ struct rend_service_port_config_s {
* rendezvous point before giving up? */
#define MAX_REND_TIMEOUT 30
-/** Represents a single hidden service running at this OP. */
-typedef struct rend_service_t {
- /* Fields specified in config file */
- char *directory; /**< where in the filesystem it stores it. Will be NULL if
- * this service is ephemeral. */
- int dir_group_readable; /**< if 1, allow group read
- permissions on directory */
- smartlist_t *ports; /**< List of rend_service_port_config_t */
- rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
- * authorization is performed. */
- smartlist_t *clients; /**< List of rend_authorized_client_t's of
- * clients that may access our service. Can be NULL
- * if no client authorization is performed. */
- /* Other fields */
- crypto_pk_t *private_key; /**< Permanent hidden-service key. */
- char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
- * '.onion' */
- char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */
- smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have,
- * or are trying to establish. */
- /** List of rend_intro_point_t that are expiring. They are removed once
- * the new descriptor is successfully uploaded. A node in this list CAN
- * NOT appear in the intro_nodes list. */
- smartlist_t *expiring_nodes;
- time_t intro_period_started; /**< Start of the current period to build
- * introduction points. */
- int n_intro_circuits_launched; /**< Count of intro circuits we have
- * established in this period. */
- unsigned int n_intro_points_wanted; /**< Number of intro points this
- * service wants to have open. */
- rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
- time_t desc_is_dirty; /**< Time at which changes to the hidden service
- * descriptor content occurred, or 0 if it's
- * up-to-date. */
- time_t next_upload_time; /**< Scheduled next hidden service descriptor
- * upload time. */
- /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, 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 *accepted_intro_dh_parts;
- /** If true, we don't close circuits for making requests to unsupported
- * ports. */
- int allow_unknown_ports;
- /** The maximum number of simultanious streams-per-circuit that are allowed
- * to be established, or 0 if no limit is set.
- */
- int max_streams_per_circuit;
- /** If true, we close circuits that exceed the max_streams_per_circuit
- * limit. */
- int max_streams_close_circuit;
-} rend_service_t;
+/* Hidden service directory file names:
+ * new file names should be added to rend_service_add_filenames_to_list()
+ * for sandboxing purposes. */
+static const char *private_key_fname = "private_key";
+static const char *hostname_fname = "hostname";
+static const char *client_keys_fname = "client_keys";
+static const char *sos_poison_fname = "onion_service_non_anonymous";
/** Returns a escaped string representation of the service, <b>s</b>.
*/
@@ -183,14 +145,15 @@ num_rend_services(void)
}
/** Helper: free storage held by a single service authorized client entry. */
-static void
+void
rend_authorized_client_free(rend_authorized_client_t *client)
{
if (!client)
return;
if (client->client_key)
crypto_pk_free(client->client_key);
- memwipe(client->client_name, 0, strlen(client->client_name));
+ if (client->client_name)
+ memwipe(client->client_name, 0, strlen(client->client_name));
tor_free(client->client_name);
memwipe(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie));
tor_free(client);
@@ -205,16 +168,18 @@ rend_authorized_client_strmap_item_free(void *authorized_client)
/** Release the storage held by <b>service</b>.
*/
-static void
+STATIC void
rend_service_free(rend_service_t *service)
{
if (!service)
return;
tor_free(service->directory);
- SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p,
- rend_service_port_config_free(p));
- smartlist_free(service->ports);
+ if (service->ports) {
+ SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p,
+ rend_service_port_config_free(p));
+ smartlist_free(service->ports);
+ }
if (service->private_key)
crypto_pk_free(service->private_key);
if (service->intro_nodes) {
@@ -254,15 +219,30 @@ rend_service_free_all(void)
rend_service_list = NULL;
}
-/** Validate <b>service</b> and add it to rend_service_list if possible.
+/** Validate <b>service</b> and add it to <b>service_list</b>, or to
+ * the global rend_service_list if <b>service_list</b> is NULL.
* Return 0 on success. On failure, free <b>service</b> and return -1.
+ * Takes ownership of <b>service</b>.
*/
static int
-rend_add_service(rend_service_t *service)
+rend_add_service(smartlist_t *service_list, rend_service_t *service)
{
int i;
rend_service_port_config_t *p;
+ smartlist_t *s_list;
+ /* If no special service list is provided, then just use the global one. */
+ if (!service_list) {
+ if (BUG(!rend_service_list)) {
+ /* No global HS list, which is a failure. */
+ return -1;
+ }
+
+ s_list = rend_service_list;
+ } else {
+ s_list = service_list;
+ }
+
service->intro_nodes = smartlist_new();
service->expiring_nodes = smartlist_new();
@@ -284,7 +264,8 @@ rend_add_service(rend_service_t *service)
}
if (service->auth_type != REND_NO_AUTH &&
- smartlist_len(service->clients) == 0) {
+ (!service->clients ||
+ smartlist_len(service->clients) == 0)) {
log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no "
"clients; ignoring.",
rend_service_escaped_dir(service));
@@ -292,7 +273,7 @@ rend_add_service(rend_service_t *service)
return -1;
}
- if (!smartlist_len(service->ports)) {
+ if (!service->ports || !smartlist_len(service->ports)) {
log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; "
"ignoring.",
rend_service_escaped_dir(service));
@@ -315,8 +296,9 @@ rend_add_service(rend_service_t *service)
* lock file. But this is enough to detect a simple mistake that
* at least one person has actually made.
*/
- if (service->directory != NULL) { /* Skip dupe for ephemeral services. */
- SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
+ if (service->directory != NULL) {
+ /* Skip dupe for ephemeral services. */
+ SMARTLIST_FOREACH(s_list, rend_service_t*, ptr,
dupe = dupe ||
!strcmp(ptr->directory, service->directory));
if (dupe) {
@@ -327,7 +309,7 @@ rend_add_service(rend_service_t *service)
return -1;
}
}
- smartlist_add(rend_service_list, service);
+ smartlist_add(s_list, service);
log_debug(LD_REND,"Configuring service with directory \"%s\"",
service->directory);
for (i = 0; i < smartlist_len(service->ports); ++i) {
@@ -389,22 +371,20 @@ rend_service_parse_port_config(const char *string, const char *sep,
int realport = 0;
uint16_t p;
tor_addr_t addr;
- const char *addrport;
rend_service_port_config_t *result = NULL;
unsigned int is_unix_addr = 0;
- char *socket_path = NULL;
+ const char *socket_path = NULL;
char *err_msg = NULL;
+ char *addrport = NULL;
sl = smartlist_new();
smartlist_split_string(sl, string, sep,
- SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
- if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) {
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2);
+ if (smartlist_len(sl) < 1 || BUG(smartlist_len(sl) > 2)) {
if (err_msg_out)
err_msg = tor_strdup("Bad syntax in hidden service port configuration.");
-
goto err;
}
-
virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL);
if (!virtport) {
if (err_msg_out)
@@ -413,7 +393,6 @@ rend_service_parse_port_config(const char *string, const char *sep,
goto err;
}
-
if (smartlist_len(sl) == 1) {
/* No addr:port part; use default. */
realport = virtport;
@@ -421,17 +400,18 @@ rend_service_parse_port_config(const char *string, const char *sep,
} else {
int ret;
- addrport = smartlist_get(sl,1);
- ret = config_parse_unix_port(addrport, &socket_path);
- if (ret < 0 && ret != -ENOENT) {
- if (ret == -EINVAL)
- if (err_msg_out)
- err_msg = tor_strdup("Empty socket path in hidden service port "
- "configuration.");
-
+ const char *addrport_element = smartlist_get(sl,1);
+ const char *rest = NULL;
+ int is_unix;
+ ret = port_cfg_line_extract_addrport(addrport_element, &addrport,
+ &is_unix, &rest);
+ if (ret < 0) {
+ tor_asprintf(&err_msg, "Couldn't process address <%s> from hidden "
+ "service configuration", addrport_element);
goto err;
}
- if (socket_path) {
+ if (is_unix) {
+ socket_path = addrport;
is_unix_addr = 1;
} else if (strchr(addrport, ':') || strchr(addrport, '.')) {
/* else try it as an IP:port pair if it has a : or . in it */
@@ -469,10 +449,10 @@ rend_service_parse_port_config(const char *string, const char *sep,
}
err:
+ tor_free(addrport);
if (err_msg_out) *err_msg_out = err_msg;
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
- if (socket_path) tor_free(socket_path);
return result;
}
@@ -484,6 +464,61 @@ 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)
+{
+ 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;
+ }
+
+ if (validate_only) {
+ rend_service_free(service);
+ return 0;
+ } else {
+ /* Use service_list for unit tests */
+ smartlist_t *s_list = NULL;
+ /* If no special service list is provided, then just use the global one. */
+ if (!service_list) {
+ if (BUG(!rend_service_list)) {
+ /* No global HS list, which is a failure, because we plan on adding to
+ * it */
+ return -1;
+ }
+ s_list = rend_service_list;
+ } else {
+ s_list = service_list;
+ }
+ /* s_list can not be NULL here - if both service_list and rend_service_list
+ * are NULL, and validate_only is false, we exit earlier in the function
+ */
+ if (BUG(!s_list)) {
+ return -1;
+ }
+ /* Ignore service failures until 030 */
+ rend_add_service(s_list, service);
+ return 0;
+ }
+}
+
/** 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
@@ -505,11 +540,12 @@ rend_config_services(const or_options_t *options, int validate_only)
for (line = options->RendConfigLines; line; line = line->next) {
if (!strcasecmp(line->key, "HiddenServiceDir")) {
- if (service) { /* register the one we just finished parsing */
- if (validate_only)
- rend_service_free(service);
- else
- rend_add_service(service);
+ /* register the service we just finished parsing
+ * this code registers every service except the last one parsed,
+ * which is registered below the loop */
+ if (rend_service_check_dir_and_add(NULL, options, service,
+ validate_only) < 0) {
+ return -1;
}
service = tor_malloc_zero(sizeof(rend_service_t));
service->directory = tor_strdup(line->value);
@@ -671,27 +707,17 @@ rend_config_services(const or_options_t *options, int validate_only)
SMARTLIST_FOREACH_BEGIN(clients, const char *, client_name)
{
rend_authorized_client_t *client;
- size_t len = strlen(client_name);
- if (len < 1 || len > REND_CLIENTNAME_MAX_LEN) {
+ if (!rend_valid_client_name(client_name)) {
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
- "illegal client name: '%s'. Length must be "
- "between 1 and %d characters.",
+ "illegal client name: '%s'. Names must be "
+ "between 1 and %d characters and contain "
+ "only [A-Za-z0-9+_-].",
client_name, REND_CLIENTNAME_MAX_LEN);
SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
smartlist_free(clients);
rend_service_free(service);
return -1;
}
- if (strspn(client_name, REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
- log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains an "
- "illegal client name: '%s'. Valid "
- "characters are [A-Za-z0-9+_-].",
- client_name);
- SMARTLIST_FOREACH(clients, char *, cp, tor_free(cp));
- smartlist_free(clients);
- rend_service_free(service);
- return -1;
- }
client = tor_malloc_zero(sizeof(rend_authorized_client_t));
client->client_name = tor_strdup(client_name);
smartlist_add(service->clients, client);
@@ -725,22 +751,12 @@ rend_config_services(const or_options_t *options, int validate_only)
}
}
}
- if (service) {
- cpd_check_t check_opts = CPD_CHECK_MODE_ONLY|CPD_CHECK;
- if (service->dir_group_readable) {
- check_opts |= CPD_GROUP_READ;
- }
-
- if (check_private_dir(service->directory, check_opts, options->User) < 0) {
- rend_service_free(service);
- return -1;
- }
-
- if (validate_only) {
- rend_service_free(service);
- } else {
- rend_add_service(service);
- }
+ /* 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(NULL, options, service,
+ validate_only) < 0) {
+ return -1;
}
/* If this is a reload and there were hidden services configured before,
@@ -827,14 +843,17 @@ rend_config_services(const or_options_t *options, int validate_only)
return 0;
}
-/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, with
+/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, using
+ * client authorization <b>auth_type</b> and an optional list of
+ * rend_authorized_client_t in <b>auth_clients</b>, with
* <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit,
* and circuit closure on max streams being exceeded set by
* <b>max_streams_close_circuit</b>.
*
- * Regardless of sucess/failure, callers should not touch pk/ports after
- * calling this routine, and may assume that correct cleanup has been done
- * on failure.
+ * Ownership of pk, ports, and auth_clients is passed to this routine.
+ * Regardless of success/failure, callers should not touch these values
+ * after calling this routine, and may assume that correct cleanup has
+ * been done on failure.
*
* Return an appropriate rend_service_add_ephemeral_status_t.
*/
@@ -843,6 +862,8 @@ rend_service_add_ephemeral(crypto_pk_t *pk,
smartlist_t *ports,
int max_streams_per_circuit,
int max_streams_close_circuit,
+ rend_auth_type_t auth_type,
+ smartlist_t *auth_clients,
char **service_id_out)
{
*service_id_out = NULL;
@@ -852,7 +873,8 @@ rend_service_add_ephemeral(crypto_pk_t *pk,
rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t));
s->directory = NULL; /* This indicates the service is ephemeral. */
s->private_key = pk;
- s->auth_type = REND_NO_AUTH;
+ s->auth_type = auth_type;
+ s->clients = auth_clients;
s->ports = ports;
s->intro_period_started = time(NULL);
s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
@@ -868,6 +890,12 @@ rend_service_add_ephemeral(crypto_pk_t *pk,
rend_service_free(s);
return RSAE_BADVIRTPORT;
}
+ if (s->auth_type != REND_NO_AUTH &&
+ (!s->clients || smartlist_len(s->clients) == 0)) {
+ log_warn(LD_CONFIG, "At least one authorized client must be specified.");
+ rend_service_free(s);
+ return RSAE_BADAUTH;
+ }
/* Enforcing pk/id uniqueness should be done by rend_service_load_keys(), but
* it's not, see #14828.
@@ -885,7 +913,7 @@ rend_service_add_ephemeral(crypto_pk_t *pk,
}
/* Initialize the service. */
- if (rend_add_service(s)) {
+ if (rend_add_service(NULL, s)) {
return RSAE_INTERNAL;
}
*service_id_out = tor_strdup(s->service_id);
@@ -923,7 +951,6 @@ rend_service_del_ephemeral(const char *service_id)
*/
SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
if (!circ->marked_for_close &&
- circ->state == CIRCUIT_STATE_OPEN &&
(circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
@@ -999,13 +1026,241 @@ rend_service_update_descriptor(rend_service_t *service)
}
}
+/* Allocate and return a string containing the path to file_name in
+ * service->directory. Asserts that service has a directory.
+ * This function will never return NULL.
+ * The caller must free this path. */
+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;
+}
+
+/* Allocate and return a string containing the path to the single onion
+ * service poison file in service->directory. Asserts that service has a
+ * directory.
+ * The caller must free this path. */
+STATIC char *
+rend_service_sos_poison_path(const rend_service_t *service)
+{
+ return rend_service_path(service, sos_poison_fname);
+}
+
+/** Return True if hidden services <b>service> has been poisoned by single
+ * onion mode. */
+static int
+service_is_single_onion_poisoned(const rend_service_t *service)
+{
+ char *poison_fname = NULL;
+ file_status_t fstatus;
+
+ /* Passing a NULL service is a bug */
+ if (BUG(!service)) {
+ return 0;
+ }
+
+ if (!service->directory) {
+ return 0;
+ }
+
+ poison_fname = rend_service_sos_poison_path(service);
+
+ fstatus = file_status(poison_fname);
+ tor_free(poison_fname);
+
+ /* If this fname is occupied, the hidden service has been poisoned.
+ * fstatus can be FN_ERROR if the service directory does not exist, in that
+ * case, there is obviously no private key. */
+ if (fstatus == FN_FILE || fstatus == FN_EMPTY) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return 1 if the private key file for service exists and has a non-zero size,
+ * and 0 otherwise. */
+static int
+rend_service_private_key_exists(const rend_service_t *service)
+{
+ char *private_key_path = rend_service_path(service, private_key_fname);
+ const file_status_t private_key_status = file_status(private_key_path);
+ tor_free(private_key_path);
+ /* Only non-empty regular private key files could have been used before.
+ * fstatus can be FN_ERROR if the service directory does not exist, in that
+ * case, there is obviously no private key. */
+ return private_key_status == FN_FILE;
+}
+
+/** Check the single onion service poison state of the directory for s:
+ * - If the service is poisoned, and we are in Single Onion Mode,
+ * return 0,
+ * - If the service is not poisoned, and we are not in Single Onion Mode,
+ * return 0,
+ * - Otherwise, the poison state is invalid: the service was created in one
+ * mode, and is being used in the other, return -1.
+ * Hidden service directories without keys are always considered consistent.
+ * They will be poisoned after their directory is created (if needed). */
+STATIC int
+rend_service_verify_single_onion_poison(const rend_service_t* s,
+ const or_options_t* options)
+{
+ /* Passing a NULL service is a bug */
+ if (BUG(!s)) {
+ return -1;
+ }
+
+ /* Ephemeral services are checked at ADD_ONION time */
+ if (!s->directory) {
+ return 0;
+ }
+
+ /* Services without keys are always ok - their keys will only ever be used
+ * in the current mode */
+ if (!rend_service_private_key_exists(s)) {
+ return 0;
+ }
+
+ /* The key has been used before in a different mode */
+ if (service_is_single_onion_poisoned(s) !=
+ rend_service_non_anonymous_mode_enabled(options)) {
+ return -1;
+ }
+
+ /* The key exists and is consistent with the current mode */
+ return 0;
+}
+
+/*** Helper for rend_service_poison_new_single_onion_dir(). Add a file to
+ * the hidden service directory for s that marks it as a single onion service.
+ * Tor must be in single onion mode before calling this function, and the
+ * service directory must already have been created.
+ * Returns 0 when a directory is successfully poisoned, or if it is already
+ * poisoned. Returns -1 on a failure to read the directory or write the poison
+ * file, or if there is an existing private key file in the directory. (The
+ * service should have been poisoned when the key was created.) */
+static int
+poison_new_single_onion_hidden_service_dir_impl(const rend_service_t *service,
+ const or_options_t* options)
+{
+ /* Passing a NULL service is a bug */
+ if (BUG(!service)) {
+ return -1;
+ }
+
+ /* We must only poison directories if we're in Single Onion mode */
+ tor_assert(rend_service_non_anonymous_mode_enabled(options));
+
+ int fd;
+ int retval = -1;
+ char *poison_fname = NULL;
+
+ if (!service->directory) {
+ log_info(LD_REND, "Ephemeral HS started in non-anonymous mode.");
+ return 0;
+ }
+
+ /* Make sure we're only poisoning new hidden service directories */
+ if (rend_service_private_key_exists(service)) {
+ log_warn(LD_BUG, "Tried to single onion poison a service directory after "
+ "the private key was created.");
+ return -1;
+ }
+
+ /* Make sure the directory was created before calling this function. */
+ if (BUG(rend_service_check_private_dir_impl(options, service, 0) < 0))
+ return -1;
+
+ poison_fname = rend_service_sos_poison_path(service);
+
+ switch (file_status(poison_fname)) {
+ case FN_DIR:
+ case FN_ERROR:
+ log_warn(LD_FS, "Can't read single onion poison file \"%s\"",
+ poison_fname);
+ goto done;
+ case FN_FILE: /* single onion poison file already exists. NOP. */
+ case FN_EMPTY: /* single onion poison file already exists. NOP. */
+ log_debug(LD_FS, "Tried to re-poison a single onion poisoned file \"%s\"",
+ poison_fname);
+ break;
+ case FN_NOENT:
+ fd = tor_open_cloexec(poison_fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ if (fd < 0) {
+ log_warn(LD_FS, "Could not create single onion poison file %s",
+ poison_fname);
+ goto done;
+ }
+ close(fd);
+ break;
+ default:
+ tor_assert(0);
+ }
+
+ retval = 0;
+
+ done:
+ tor_free(poison_fname);
+
+ return retval;
+}
+
+/** We just got launched in Single Onion Mode. That's a non-anoymous mode for
+ * hidden services. If s is new, we should mark its hidden service
+ * directory appropriately so that it is never launched as a location-private
+ * hidden service. (New directories don't have private key files.)
+ * Return 0 on success, -1 on fail. */
+STATIC int
+rend_service_poison_new_single_onion_dir(const rend_service_t *s,
+ const or_options_t* options)
+{
+ /* Passing a NULL service is a bug */
+ if (BUG(!s)) {
+ return -1;
+ }
+
+ /* We must only poison directories if we're in Single Onion mode */
+ tor_assert(rend_service_non_anonymous_mode_enabled(options));
+
+ if (!rend_service_private_key_exists(s)) {
+ if (poison_new_single_onion_hidden_service_dir_impl(s, options)
+ < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
/** Load and/or generate private keys for all hidden services, possibly
- * including keys for client authorization. Return 0 on success, -1 on
- * failure. */
+ * including keys for client authorization.
+ * If a <b>service_list</b> is provided, treat it as the list of hidden
+ * services (used in unittests). Otherwise, require that rend_service_list is
+ * not NULL.
+ * Return 0 on success, -1 on failure. */
int
-rend_service_load_all_keys(void)
+rend_service_load_all_keys(const smartlist_t *service_list)
{
- SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
+ const smartlist_t *s_list = NULL;
+ /* If no special service list is provided, then just use the global one. */
+ if (!service_list) {
+ if (BUG(!rend_service_list)) {
+ return -1;
+ }
+ s_list = rend_service_list;
+ } else {
+ s_list = service_list;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(s_list, rend_service_t *, s) {
if (s->private_key)
continue;
log_info(LD_REND, "Loading hidden-service keys from \"%s\"",
@@ -1025,12 +1280,10 @@ rend_service_add_filenames_to_list(smartlist_t *lst, const rend_service_t *s)
tor_assert(lst);
tor_assert(s);
tor_assert(s->directory);
- smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"private_key",
- s->directory);
- smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"hostname",
- s->directory);
- smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"client_keys",
- s->directory);
+ smartlist_add(lst, rend_service_path(s, private_key_fname));
+ smartlist_add(lst, rend_service_path(s, hostname_fname));
+ smartlist_add(lst, rend_service_path(s, client_keys_fname));
+ smartlist_add(lst, rend_service_sos_poison_path(s));
}
/** Add to <b>open_lst</b> every filename used by a configured hidden service,
@@ -1068,61 +1321,133 @@ rend_service_derive_key_digests(struct rend_service_t *s)
return 0;
}
-/** Load and/or generate private keys for the hidden service <b>s</b>,
- * possibly including keys for client authorization. Return 0 on success, -1
- * on failure. */
+/* Implements the directory check from rend_service_check_private_dir,
+ * without doing the single onion poison checks. */
static int
-rend_service_load_keys(rend_service_t *s)
+rend_service_check_private_dir_impl(const or_options_t *options,
+ const rend_service_t *s,
+ int create)
{
- char fname[512];
- char buf[128];
- cpd_check_t check_opts = CPD_CREATE;
-
+ cpd_check_t check_opts = CPD_NONE;
+ if (create) {
+ check_opts |= CPD_CREATE;
+ } else {
+ check_opts |= CPD_CHECK_MODE_ONLY;
+ check_opts |= CPD_CHECK;
+ }
if (s->dir_group_readable) {
check_opts |= CPD_GROUP_READ;
}
/* Check/create directory */
- if (check_private_dir(s->directory, check_opts, get_options()->User) < 0) {
+ if (check_private_dir(s->directory, check_opts, options->User) < 0) {
+ log_warn(LD_REND, "Checking service directory %s failed.", s->directory);
return -1;
}
-#ifndef _WIN32
- if (s->dir_group_readable) {
- /* Only new dirs created get new opts, also enforce group read. */
- if (chmod(s->directory, 0750)) {
- log_warn(LD_FS,"Unable to make %s group-readable.", s->directory);
- }
+
+ return 0;
+}
+
+/** Make sure that the directory for <b>s</b> is private, using the config in
+ * <b>options</b>.
+ * If <b>create</b> is true:
+ * - if the directory exists, change permissions if needed,
+ * - if the directory does not exist, create it with the correct permissions.
+ * If <b>create</b> is false:
+ * - if the directory exists, check permissions,
+ * - if the directory does not exist, check if we think we can create it.
+ * Return 0 on success, -1 on failure. */
+static int
+rend_service_check_private_dir(const or_options_t *options,
+ const rend_service_t *s,
+ int create)
+{
+ /* Passing a NULL service is a bug */
+ if (BUG(!s)) {
+ return -1;
}
-#endif
- /* Load key */
- if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
- strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname))
- >= sizeof(fname)) {
- log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".",
- s->directory);
+ /* Check/create directory */
+ if (rend_service_check_private_dir_impl(options, s, create) < 0) {
+ return -1;
+ }
+
+ /* Check if the hidden service key exists, and was created in a different
+ * single onion service mode, and refuse to launch if it has.
+ * This is safe to call even when create is false, as it ignores missing
+ * keys and directories: they are always valid.
+ */
+ if (rend_service_verify_single_onion_poison(s, options) < 0) {
+ /* We can't use s->service_id here, as the key may not have been loaded */
+ log_warn(LD_GENERAL, "We are configured with "
+ "HiddenServiceNonAnonymousMode %d, but the hidden "
+ "service key in directory %s was created in %s mode. "
+ "This is not allowed.",
+ rend_service_non_anonymous_mode_enabled(options) ? 1 : 0,
+ rend_service_escaped_dir(s),
+ rend_service_non_anonymous_mode_enabled(options) ?
+ "an anonymous" : "a non-anonymous"
+ );
return -1;
}
+
+ /* Poison new single onion directories immediately after they are created,
+ * so that we never accidentally launch non-anonymous hidden services
+ * thinking they are anonymous. Any keys created later will end up with the
+ * correct poisoning state.
+ */
+ if (create && rend_service_non_anonymous_mode_enabled(options)) {
+ static int logged_warning = 0;
+
+ if (rend_service_poison_new_single_onion_dir(s, options) < 0) {
+ log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous"
+ ".");
+ return -1;
+ }
+
+ if (!logged_warning) {
+ /* The keys for these services are linked to the server IP address */
+ log_notice(LD_REND, "The configured onion service directories have been "
+ "used in single onion mode. They can not be used for "
+ "anonymous hidden services.");
+ logged_warning = 1;
+ }
+ }
+
+ return 0;
+}
+
+/** Load and/or generate private keys for the hidden service <b>s</b>,
+ * possibly including keys for client authorization. Return 0 on success, -1
+ * on failure. */
+static int
+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))
+ goto err;
+
+ /* Load key */
+ fname = rend_service_path(s, private_key_fname);
s->private_key = init_key_from_file(fname, 1, LOG_ERR, 0);
+
if (!s->private_key)
- return -1;
+ goto err;
if (rend_service_derive_key_digests(s) < 0)
- return -1;
+ goto err;
+ tor_free(fname);
/* Create service file */
- if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
- strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
- >= sizeof(fname)) {
- log_warn(LD_CONFIG, "Directory name too long to store hostname file:"
- " \"%s\".", s->directory);
- return -1;
- }
+ fname = rend_service_path(s, hostname_fname);
tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id);
if (write_str_to_file(fname,buf,0)<0) {
log_warn(LD_CONFIG, "Could not write onion address to hostname file.");
- memwipe(buf, 0, sizeof(buf));
- return -1;
+ goto err;
}
#ifndef _WIN32
if (s->dir_group_readable) {
@@ -1133,15 +1458,21 @@ rend_service_load_keys(rend_service_t *s)
}
#endif
- memwipe(buf, 0, sizeof(buf));
-
/* If client authorization is configured, load or generate keys. */
if (s->auth_type != REND_NO_AUTH) {
- if (rend_service_load_auth_keys(s, fname) < 0)
- return -1;
+ if (rend_service_load_auth_keys(s, fname) < 0) {
+ goto err;
+ }
}
- return 0;
+ int r = 0;
+ goto done;
+ err:
+ r = -1;
+ done:
+ memwipe(buf, 0, sizeof(buf));
+ tor_free(fname);
+ return r;
}
/** Load and/or generate client authorization keys for the hidden service
@@ -1151,23 +1482,17 @@ static int
rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
{
int r = 0;
- char cfname[512];
+ char *cfname = NULL;
char *client_keys_str = NULL;
strmap_t *parsed_clients = strmap_new();
FILE *cfile, *hfile;
open_file_t *open_cfile = NULL, *open_hfile = NULL;
- char extended_desc_cookie[REND_DESC_COOKIE_LEN+1];
char desc_cook_out[3*REND_DESC_COOKIE_LEN_BASE64+1];
char service_id[16+1];
char buf[1500];
/* Load client keys and descriptor cookies, if available. */
- if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys",
- s->directory)<0) {
- log_warn(LD_CONFIG, "Directory name too long to store client keys "
- "file: \"%s\".", s->directory);
- goto err;
- }
+ cfname = rend_service_path(s, client_keys_fname);
client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL);
if (client_keys_str) {
if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) {
@@ -1208,10 +1533,12 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
memcpy(client->descriptor_cookie, parsed->descriptor_cookie,
REND_DESC_COOKIE_LEN);
} else {
- crypto_rand(client->descriptor_cookie, REND_DESC_COOKIE_LEN);
+ crypto_rand((char *) client->descriptor_cookie, REND_DESC_COOKIE_LEN);
}
+ /* For compatibility with older tor clients, this does not
+ * truncate the padding characters, unlike rend_auth_encode_cookie. */
if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
- client->descriptor_cookie,
+ (char *) client->descriptor_cookie,
REND_DESC_COOKIE_LEN, 0) < 0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
goto err;
@@ -1272,6 +1599,8 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
log_warn(LD_BUG, "Could not write client entry.");
goto err;
}
+ } else {
+ strlcpy(service_id, s->service_id, sizeof(service_id));
}
if (fputs(buf, cfile) < 0) {
@@ -1280,27 +1609,18 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
goto err;
}
- /* Add line to hostname file. */
- if (s->auth_type == REND_BASIC_AUTH) {
- /* Remove == signs (newline has been removed above). */
- desc_cook_out[strlen(desc_cook_out)-2] = '\0';
- tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
- s->service_id, desc_cook_out, client->client_name);
- } else {
- memcpy(extended_desc_cookie, client->descriptor_cookie,
- REND_DESC_COOKIE_LEN);
- extended_desc_cookie[REND_DESC_COOKIE_LEN] =
- ((int)s->auth_type - 1) << 4;
- if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
- extended_desc_cookie,
- REND_DESC_COOKIE_LEN+1, 0) < 0) {
- log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
- goto err;
- }
- desc_cook_out[strlen(desc_cook_out)-2] = '\0'; /* Remove A=. */
- tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
- service_id, desc_cook_out, client->client_name);
+ /* Add line to hostname file. This is not the same encoding as in
+ * client_keys. */
+ char *encoded_cookie = rend_auth_encode_cookie(client->descriptor_cookie,
+ s->auth_type);
+ if (!encoded_cookie) {
+ log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
+ goto err;
}
+ tor_snprintf(buf, sizeof(buf), "%s.onion %s # client: %s\n",
+ service_id, encoded_cookie, client->client_name);
+ memwipe(encoded_cookie, 0, strlen(encoded_cookie));
+ tor_free(encoded_cookie);
if (fputs(buf, hfile)<0) {
log_warn(LD_FS, "Could not append host entry to file: %s",
@@ -1326,13 +1646,15 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
}
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
- memwipe(cfname, 0, sizeof(cfname));
+ if (cfname) {
+ memwipe(cfname, 0, strlen(cfname));
+ tor_free(cfname);
+ }
/* Clear stack buffers that held key-derived material. */
memwipe(buf, 0, sizeof(buf));
memwipe(desc_cook_out, 0, sizeof(desc_cook_out));
memwipe(service_id, 0, sizeof(service_id));
- memwipe(extended_desc_cookie, 0, sizeof(extended_desc_cookie));
return r;
}
@@ -1429,6 +1751,31 @@ rend_check_authorization(rend_service_t *service,
return 1;
}
+/* Can this service make a direct connection to ei?
+ * It must be a single onion service, and the firewall rules must allow ei. */
+static int
+rend_service_use_direct_connection(const or_options_t* options,
+ const extend_info_t* ei)
+{
+ /* We'll connect directly all reachable addresses, whether preferred or not.
+ * The prefer_ipv6 argument to fascist_firewall_allows_address_addr is
+ * ignored, because pref_only is 0. */
+ return (rend_service_allow_non_anonymous_connection(options) &&
+ fascist_firewall_allows_address_addr(&ei->addr, ei->port,
+ FIREWALL_OR_CONNECTION, 0, 0));
+}
+
+/* Like rend_service_use_direct_connection, but to a node. */
+static int
+rend_service_use_direct_connection_node(const or_options_t* options,
+ const node_t* node)
+{
+ /* We'll connect directly all reachable addresses, whether preferred or not.
+ */
+ return (rend_service_allow_non_anonymous_connection(options) &&
+ fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0));
+}
+
/******
* Handle cells
******/
@@ -1479,9 +1826,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
goto err;
}
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(!(circuit->build_state->onehop_tunnel));
-#endif
+ assert_circ_anonymity_ok(circuit, options);
tor_assert(circuit->rend_data);
/* We'll use this in a bazillion log messages */
@@ -1686,6 +2031,11 @@ rend_service_receive_introduction(origin_circuit_t *circuit,
for (i=0;i<MAX_REND_FAILURES;i++) {
int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL;
if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME;
+ /* A Single Onion Service only uses a direct connection if its
+ * firewall rules permit direct connections to the address. */
+ if (rend_service_use_direct_connection(options, rp)) {
+ flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL;
+ }
launched = circuit_launch_by_extend_info(
CIRCUIT_PURPOSE_S_CONNECT_REND, rp, flags);
@@ -1798,7 +2148,10 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
goto err;
}
- rp = extend_info_from_node(node, 0);
+ /* Are we in single onion mode? */
+ const int allow_direct = rend_service_allow_non_anonymous_connection(
+ get_options());
+ rp = extend_info_from_node(node, allow_direct);
if (!rp) {
if (err_msg_out) {
tor_asprintf(&err_msg,
@@ -1823,6 +2176,10 @@ find_rp_for_intro(const rend_intro_cell_t *intro,
goto err;
}
+ /* rp is always set here: extend_info_dup guarantees a non-NULL result, and
+ * the other cases goto err. */
+ tor_assert(rp);
+
/* Make sure the RP we are being asked to connect to is _not_ a private
* address unless it's allowed. Let's avoid to build a circuit to our
* second middle node and fail right after when extending to the RP. */
@@ -2597,6 +2954,10 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
log_info(LD_REND,"Reattempting rendezvous circuit to '%s'",
safe_str(extend_info_describe(oldstate->chosen_exit)));
+ /* You'd think Single Onion Services would want to retry the rendezvous
+ * using a direct connection. But if it's blocked by a firewall, or the
+ * service is IPv6-only, or the rend point avoiding becoming a one-hop
+ * proxy, we need a 3-hop connection. */
newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND,
oldstate->chosen_exit,
CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL);
@@ -2625,26 +2986,72 @@ rend_service_launch_establish_intro(rend_service_t *service,
rend_intro_point_t *intro)
{
origin_circuit_t *launched;
+ int flags = CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL;
+ const or_options_t *options = get_options();
+ extend_info_t *launch_ei = intro->extend_info;
+ extend_info_t *direct_ei = NULL;
+
+ /* Are we in single onion mode? */
+ if (rend_service_allow_non_anonymous_connection(options)) {
+ /* Do we have a descriptor for the node?
+ * We've either just chosen it from the consensus, or we've just reviewed
+ * our intro points to see which ones are still valid, and deleted the ones
+ * that aren't in the consensus any more. */
+ const node_t *node = node_get_by_id(launch_ei->identity_digest);
+ if (BUG(!node)) {
+ /* The service has kept an intro point after it went missing from the
+ * consensus. If we did anything else here, it would be a consensus
+ * distinguisher. Which are less of an issue for single onion services,
+ * but still a bug. */
+ return -1;
+ }
+ /* Can we connect to the node directly? If so, replace launch_ei
+ * (a multi-hop extend_info) with one suitable for direct connection. */
+ if (rend_service_use_direct_connection_node(options, node)) {
+ direct_ei = extend_info_from_node(node, 1);
+ if (BUG(!direct_ei)) {
+ /* rend_service_use_direct_connection_node and extend_info_from_node
+ * disagree about which addresses on this node are permitted. This
+ * should never happen. Avoiding the connection is a safe response. */
+ return -1;
+ }
+ flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL;
+ launch_ei = direct_ei;
+ }
+ }
+ /* launch_ei is either intro->extend_info, or has been replaced with a valid
+ * extend_info for single onion service direct connection. */
+ tor_assert(launch_ei);
+ /* We must have the same intro when making a direct connection. */
+ tor_assert(tor_memeq(intro->extend_info->identity_digest,
+ launch_ei->identity_digest,
+ DIGEST_LEN));
log_info(LD_REND,
- "Launching circuit to introduction point %s for service %s",
+ "Launching circuit to introduction point %s%s%s for service %s",
safe_str_client(extend_info_describe(intro->extend_info)),
+ direct_ei ? " via direct address " : "",
+ direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : "",
service->service_id);
rep_hist_note_used_internal(time(NULL), 1, 0);
++service->n_intro_circuits_launched;
launched = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO,
- intro->extend_info,
- CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL);
+ launch_ei, flags);
if (!launched) {
log_info(LD_REND,
- "Can't launch circuit to establish introduction at %s.",
- safe_str_client(extend_info_describe(intro->extend_info)));
+ "Can't launch circuit to establish introduction at %s%s%s.",
+ safe_str_client(extend_info_describe(intro->extend_info)),
+ direct_ei ? " via direct address " : "",
+ direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : ""
+ );
+ extend_info_free(direct_ei);
return -1;
}
- /* We must have the same exit node even if cannibalized. */
+ /* We must have the same exit node even if cannibalized or direct connection.
+ */
tor_assert(tor_memeq(intro->extend_info->identity_digest,
launched->build_state->chosen_exit->identity_digest,
DIGEST_LEN));
@@ -2655,6 +3062,7 @@ rend_service_launch_establish_intro(rend_service_t *service,
launched->intro_key = crypto_pk_dup_key(intro->intro_key);
if (launched->base_.state == CIRCUIT_STATE_OPEN)
rend_service_intro_has_opened(launched);
+ extend_info_free(direct_ei);
return 0;
}
@@ -2708,12 +3116,9 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
char auth[DIGEST_LEN + 9];
char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
int reason = END_CIRC_REASON_TORPROTOCOL;
- crypto_pk_t *intro_key;
tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(!(circuit->build_state->onehop_tunnel));
-#endif
+ assert_circ_anonymity_ok(circuit, get_options());
tor_assert(circuit->cpath);
tor_assert(circuit->rend_data);
@@ -2780,9 +3185,10 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
log_info(LD_REND,
"Established circuit %u as introduction point for service %s",
(unsigned)circuit->base_.n_circ_id, serviceid);
+ circuit_log_path(LOG_INFO, LD_REND, circuit);
/* Use the intro key instead of the service key in ESTABLISH_INTRO. */
- intro_key = circuit->intro_key;
+ crypto_pk_t *intro_key = circuit->intro_key;
/* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
r = crypto_pk_asn1_encode(intro_key, buf+2,
RELAY_PAYLOAD_SIZE-2);
@@ -2909,9 +3315,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
tor_assert(circuit->cpath);
tor_assert(circuit->build_state);
-#ifndef NON_ANONYMOUS_MODE_ENABLED
- tor_assert(!(circuit->build_state->onehop_tunnel));
-#endif
+ assert_circ_anonymity_ok(circuit, get_options());
tor_assert(circuit->rend_data);
/* Declare the circuit dirty to avoid reuse, and for path-bias */
@@ -2931,6 +3335,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
"Done building circuit %u to rendezvous with "
"cookie %s for service %s",
(unsigned)circuit->base_.n_circ_id, hexcookie, serviceid);
+ circuit_log_path(LOG_INFO, LD_REND, circuit);
/* Clear the 'in-progress HS circ has timed out' flag for
* consistency with what happens on the client side; this line has
@@ -3501,6 +3906,9 @@ rend_consider_services_intro_points(void)
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(
+ get_options());
/* List of nodes we need to _exclude_ when choosing a new node to
* establish an intro point to. */
smartlist_t *exclude_nodes;
@@ -3596,8 +4004,24 @@ rend_consider_services_intro_points(void)
router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC;
if (get_options()->AllowInvalid_ & ALLOW_INVALID_INTRODUCTION)
flags |= CRN_ALLOW_INVALID;
+ router_crn_flags_t direct_flags = flags;
+ direct_flags |= CRN_PREF_ADDR;
+ direct_flags |= CRN_DIRECT_CONN;
+
node = router_choose_random_node(exclude_nodes,
- options->ExcludeNodes, flags);
+ options->ExcludeNodes,
+ allow_direct ? direct_flags : flags);
+ /* If we are in single onion mode, retry node selection for a 3-hop
+ * path */
+ if (allow_direct && !node) {
+ log_info(LD_REND,
+ "Unable to find an intro point that we can connect to "
+ "directly for %s, falling back to a 3-hop path.",
+ safe_str_client(service->service_id));
+ node = router_choose_random_node(exclude_nodes,
+ options->ExcludeNodes, flags);
+ }
+
if (!node) {
log_warn(LD_REND,
"We only have %d introduction points established for %s; "
@@ -3607,10 +4031,13 @@ rend_consider_services_intro_points(void)
n_intro_points_to_open);
break;
}
- /* Add the choosen node to the exclusion list in order to avoid to
- * pick it again in the next iteration. */
+ /* Add the choosen node to the exclusion list in order to avoid picking
+ * it again in the next iteration. */
smartlist_add(exclude_nodes, (void*)node);
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
+ /* extend_info is for clients, so we want the multi-hop primary ORPort,
+ * even if we are a single onion service and intend to connect to it
+ * directly ourselves. */
intro->extend_info = extend_info_from_node(node, 0);
intro->intro_key = crypto_pk_new();
const int fail = crypto_pk_generate_key(intro->intro_key);
@@ -3656,8 +4083,9 @@ rend_consider_services_upload(time_t now)
{
int i;
rend_service_t *service;
- int rendpostperiod = get_options()->RendPostPeriod;
- int rendinitialpostdelay = (get_options()->TestingTorNetwork ?
+ const or_options_t *options = get_options();
+ int rendpostperiod = options->RendPostPeriod;
+ int rendinitialpostdelay = (options->TestingTorNetwork ?
MIN_REND_INITIAL_POST_DELAY_TESTING :
MIN_REND_INITIAL_POST_DELAY);
@@ -3668,6 +4096,12 @@ rend_consider_services_upload(time_t now)
* the descriptor is stable before being published. See comment below. */
service->next_upload_time =
now + rendinitialpostdelay + crypto_rand_int(2*rendpostperiod);
+ /* Single Onion Services prioritise availability over hiding their
+ * startup time, as their IP address is publicly discoverable anyway.
+ */
+ if (rend_service_reveal_startup_time(options)) {
+ service->next_upload_time = now + rendinitialpostdelay;
+ }
}
/* Does every introduction points have been established? */
unsigned int intro_points_ready =
@@ -3908,3 +4342,51 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
return -2;
}
+/* Are HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode consistent?
+ */
+static int
+rend_service_non_anonymous_mode_consistent(const or_options_t *options)
+{
+ /* !! is used to make these options boolean */
+ return (!! options->HiddenServiceSingleHopMode ==
+ !! options->HiddenServiceNonAnonymousMode);
+}
+
+/* Do the options allow onion services to make direct (non-anonymous)
+ * connections to introduction or rendezvous points?
+ * Must only be called after options_validate_single_onion() has successfully
+ * checked onion service option consistency.
+ * Returns true if tor is in HiddenServiceSingleHopMode. */
+int
+rend_service_allow_non_anonymous_connection(const or_options_t *options)
+{
+ tor_assert(rend_service_non_anonymous_mode_consistent(options));
+ return options->HiddenServiceSingleHopMode ? 1 : 0;
+}
+
+/* Do the options allow us to reveal the exact startup time of the onion
+ * service?
+ * Single Onion Services prioritise availability over hiding their
+ * startup time, as their IP address is publicly discoverable anyway.
+ * Must only be called after options_validate_single_onion() has successfully
+ * checked onion service option consistency.
+ * Returns true if tor is in non-anonymous hidden service mode. */
+int
+rend_service_reveal_startup_time(const or_options_t *options)
+{
+ tor_assert(rend_service_non_anonymous_mode_consistent(options));
+ return rend_service_non_anonymous_mode_enabled(options);
+}
+
+/* Is non-anonymous mode enabled using the HiddenServiceNonAnonymousMode
+ * config option?
+ * Must only be called after options_validate_single_onion() has successfully
+ * checked onion service option consistency.
+ */
+int
+rend_service_non_anonymous_mode_enabled(const or_options_t *options)
+{
+ tor_assert(rend_service_non_anonymous_mode_consistent(options));
+ return options->HiddenServiceNonAnonymousMode ? 1 : 0;
+}
+
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 101b37e18d..3b185672f6 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -63,11 +63,77 @@ struct rend_intro_cell_s {
uint8_t dh[DH_KEY_LEN];
};
+/** Represents a single hidden service running at this OP. */
+typedef struct rend_service_t {
+ /* Fields specified in config file */
+ char *directory; /**< where in the filesystem it stores it. Will be NULL if
+ * this service is ephemeral. */
+ int dir_group_readable; /**< if 1, allow group read
+ permissions on directory */
+ smartlist_t *ports; /**< List of rend_service_port_config_t */
+ rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client
+ * authorization is performed. */
+ smartlist_t *clients; /**< List of rend_authorized_client_t's of
+ * clients that may access our service. Can be NULL
+ * if no client authorization is performed. */
+ /* Other fields */
+ crypto_pk_t *private_key; /**< Permanent hidden-service key. */
+ char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without
+ * '.onion' */
+ char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */
+ smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have,
+ * or are trying to establish. */
+ /** List of rend_intro_point_t that are expiring. They are removed once
+ * the new descriptor is successfully uploaded. A node in this list CAN
+ * NOT appear in the intro_nodes list. */
+ smartlist_t *expiring_nodes;
+ time_t intro_period_started; /**< Start of the current period to build
+ * introduction points. */
+ int n_intro_circuits_launched; /**< Count of intro circuits we have
+ * established in this period. */
+ unsigned int n_intro_points_wanted; /**< Number of intro points this
+ * service wants to have open. */
+ rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */
+ time_t desc_is_dirty; /**< Time at which changes to the hidden service
+ * descriptor content occurred, or 0 if it's
+ * up-to-date. */
+ time_t next_upload_time; /**< Scheduled next hidden service descriptor
+ * upload time. */
+ /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, 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 *accepted_intro_dh_parts;
+ /** If true, we don't close circuits for making requests to unsupported
+ * ports. */
+ int allow_unknown_ports;
+ /** The maximum number of simultanious streams-per-circuit that are allowed
+ * to be established, or 0 if no limit is set.
+ */
+ int max_streams_per_circuit;
+ /** If true, we close circuits that exceed the max_streams_per_circuit
+ * limit. */
+ int max_streams_close_circuit;
+} 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);
#endif
int num_rend_services(void);
int rend_config_services(const or_options_t *options, int validate_only);
-int rend_service_load_all_keys(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);
@@ -106,8 +172,11 @@ rend_service_port_config_t *rend_service_parse_port_config(const char *string,
char **err_msg_out);
void rend_service_port_config_free(rend_service_port_config_t *p);
+void rend_authorized_client_free(rend_authorized_client_t *client);
+
/** Return value from rend_service_add_ephemeral. */
typedef enum {
+ RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
RSAE_ADDREXISTS = -3, /**< Onion address collision */
RSAE_BADPRIVKEY = -2, /**< Invalid public key */
@@ -118,6 +187,8 @@ rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
smartlist_t *ports,
int max_streams_per_circuit,
int max_streams_close_circuit,
+ rend_auth_type_t auth_type,
+ smartlist_t *auth_clients,
char **service_id_out);
int rend_service_del_ephemeral(const char *service_id);
@@ -126,5 +197,9 @@ void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
const char *service_id, int seconds_valid);
void rend_service_desc_has_uploaded(const rend_data_t *rend_data);
+int rend_service_allow_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
diff --git a/src/or/rephist.c b/src/or/rephist.c
index 651fd43ade..f0bac57898 100644
--- a/src/or/rephist.c
+++ b/src/or/rephist.c
@@ -4,10 +4,74 @@
/**
* \file rephist.c
- * \brief Basic history and "reputation" functionality to remember
+ * \brief Basic history and performance-tracking functionality.
+ *
+ * Basic history and performance-tracking functionality to remember
* which servers have worked in the past, how much bandwidth we've
* been using, which ports we tend to want, and so on; further,
* exit port statistics, cell statistics, and connection statistics.
+ *
+ * The history and information tracked in this module could sensibly be
+ * divided into several categories:
+ *
+ * <ul><li>Statistics used by authorities to remember the uptime and
+ * stability information about various relays, including "uptime",
+ * "weighted fractional uptime" and "mean time between failures".
+ *
+ * <li>Bandwidth usage history, used by relays to self-report how much
+ * bandwidth they've used for different purposes over last day or so,
+ * in order to generate the {dirreq-,}{read,write}-history lines in
+ * that they publish.
+ *
+ * <li>Predicted ports, used by clients to remember how long it's been
+ * since they opened an exit connection to each given target
+ * port. Clients use this information in order to try to keep circuits
+ * open to exit nodes that can connect to the ports that they care
+ * about. (The predicted ports mechanism also handles predicted circuit
+ * usage that _isn't_ port-specific, such as resolves, internal circuits,
+ * and so on.)
+ *
+ * <li>Public key operation counters, for tracking how many times we've
+ * done each public key operation. (This is unmaintained and we should
+ * remove it.)
+ *
+ * <li>Exit statistics by port, used by exits to keep track of the
+ * number of streams and bytes they've served at each exit port, so they
+ * can generate their exit-kibibytes-{read,written} and
+ * exit-streams-opened statistics.
+ *
+ * <li>Circuit stats, used by relays instances to tract circuit
+ * queue fullness and delay over time, and generate cell-processed-cells,
+ * cell-queued-cells, cell-time-in-queue, and cell-circuits-per-decile
+ * statistics.
+ *
+ * <li>Descriptor serving statistics, used by directory caches to track
+ * how many descriptors they've served.
+ *
+ * <li>Connection statistics, used by relays to track one-way and
+ * bidirectional connections.
+ *
+ * <li>Onion handshake statistics, used by relays to count how many
+ * TAP and ntor handshakes they've handled.
+ *
+ * <li>Hidden service statistics, used by relays to count rendezvous
+ * traffic and HSDir-stored descriptors.
+ *
+ * <li>Link protocol statistics, used by relays to count how many times
+ * each link protocol has been used.
+ *
+ * </ul>
+ *
+ * The entry points for this module are scattered throughout the
+ * codebase. Sending data, receiving data, connecting to a relay,
+ * losing a connection to a relay, and so on can all trigger a change in
+ * our current stats. Relays also invoke this module in order to
+ * extract their statistics when building routerinfo and extrainfo
+ * objects in router.c.
+ *
+ * TODO: This module should be broken up.
+ *
+ * (The "rephist" name originally stood for "reputation and history". )
**/
#include "or.h"
@@ -604,7 +668,7 @@ rep_hist_get_weighted_time_known(const char *id, time_t when)
int
rep_hist_have_measured_enough_stability(void)
{
- /* XXXX023 This doesn't do so well when we change our opinion
+ /* XXXX++ This doesn't do so well when we change our opinion
* as to whether we're tracking router stability. */
return started_tracking_stability < time(NULL) - 4*60*60;
}
@@ -743,14 +807,15 @@ rep_history_clean(time_t before)
orhist_it = digestmap_iter_init(history_map);
while (!digestmap_iter_done(orhist_it)) {
- int remove;
+ int should_remove;
digestmap_iter_get(orhist_it, &d1, &or_history_p);
or_history = or_history_p;
- remove = authority ? (or_history->total_run_weights < STABILITY_EPSILON &&
+ should_remove = authority ?
+ (or_history->total_run_weights < STABILITY_EPSILON &&
!or_history->start_of_run)
: (or_history->changed < before);
- if (remove) {
+ if (should_remove) {
orhist_it = digestmap_iter_next_rmv(history_map, orhist_it);
free_or_history(or_history);
continue;
@@ -1074,7 +1139,8 @@ rep_hist_load_mtbf_data(time_t now)
if (mtbf_idx > i)
i = mtbf_idx;
}
- if (base16_decode(digest, DIGEST_LEN, hexbuf, HEX_DIGEST_LEN) < 0) {
+ if (base16_decode(digest, DIGEST_LEN,
+ hexbuf, HEX_DIGEST_LEN) != DIGEST_LEN) {
log_warn(LD_HIST, "Couldn't hex string %s", escaped(hexbuf));
continue;
}
@@ -2293,16 +2359,16 @@ void
rep_hist_add_buffer_stats(double mean_num_cells_in_queue,
double mean_time_cells_in_queue, uint32_t processed_cells)
{
- circ_buffer_stats_t *stat;
+ circ_buffer_stats_t *stats;
if (!start_of_buffer_stats_interval)
return; /* Not initialized. */
- stat = tor_malloc_zero(sizeof(circ_buffer_stats_t));
- stat->mean_num_cells_in_queue = mean_num_cells_in_queue;
- stat->mean_time_cells_in_queue = mean_time_cells_in_queue;
- stat->processed_cells = processed_cells;
+ stats = tor_malloc_zero(sizeof(circ_buffer_stats_t));
+ stats->mean_num_cells_in_queue = mean_num_cells_in_queue;
+ stats->mean_time_cells_in_queue = mean_time_cells_in_queue;
+ stats->processed_cells = processed_cells;
if (!circuits_for_buffer_stats)
circuits_for_buffer_stats = smartlist_new();
- smartlist_add(circuits_for_buffer_stats, stat);
+ smartlist_add(circuits_for_buffer_stats, stats);
}
/** Remember cell statistics for circuit <b>circ</b> at time
@@ -2372,7 +2438,7 @@ rep_hist_reset_buffer_stats(time_t now)
if (!circuits_for_buffer_stats)
circuits_for_buffer_stats = smartlist_new();
SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *,
- stat, tor_free(stat));
+ stats, tor_free(stats));
smartlist_clear(circuits_for_buffer_stats);
start_of_buffer_stats_interval = now;
}
@@ -2413,15 +2479,15 @@ rep_hist_format_buffer_stats(time_t now)
buffer_stats_compare_entries_);
i = 0;
SMARTLIST_FOREACH_BEGIN(circuits_for_buffer_stats,
- circ_buffer_stats_t *, stat)
+ circ_buffer_stats_t *, stats)
{
int share = i++ * SHARES / number_of_circuits;
- processed_cells[share] += stat->processed_cells;
- queued_cells[share] += stat->mean_num_cells_in_queue;
- time_in_queue[share] += stat->mean_time_cells_in_queue;
+ processed_cells[share] += stats->processed_cells;
+ queued_cells[share] += stats->mean_num_cells_in_queue;
+ time_in_queue[share] += stats->mean_time_cells_in_queue;
circs_in_share[share]++;
}
- SMARTLIST_FOREACH_END(stat);
+ SMARTLIST_FOREACH_END(stats);
}
/* Write deciles to strings. */
@@ -2648,7 +2714,9 @@ rep_hist_desc_stats_write(time_t now)
return start_of_served_descs_stats_interval + WRITE_STATS_INTERVAL;
}
-/* DOCDOC rep_hist_note_desc_served */
+/** Called to note that we've served a given descriptor (by
+ * digest). Incrememnts the count of descriptors served, and the number
+ * of times we've served this descriptor. */
void
rep_hist_note_desc_served(const char * desc)
{
@@ -2738,7 +2806,7 @@ bidi_map_ent_hash(const bidi_map_entry_t *entry)
}
HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
- bidi_map_ent_eq);
+ bidi_map_ent_eq)
HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash,
bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_)
@@ -2933,7 +3001,7 @@ static time_t start_of_hs_stats_interval;
* information needed. */
typedef struct hs_stats_t {
/** How many relay cells have we seen as rendezvous points? */
- int64_t rp_relay_cells_seen;
+ uint64_t rp_relay_cells_seen;
/** Set of unique public key digests we've seen this stat period
* (could also be implemented as sorted smartlist). */
@@ -2947,22 +3015,22 @@ static hs_stats_t *hs_stats = NULL;
static hs_stats_t *
hs_stats_new(void)
{
- hs_stats_t * hs_stats = tor_malloc_zero(sizeof(hs_stats_t));
- hs_stats->onions_seen_this_period = digestmap_new();
+ hs_stats_t *new_hs_stats = tor_malloc_zero(sizeof(hs_stats_t));
+ new_hs_stats->onions_seen_this_period = digestmap_new();
- return hs_stats;
+ return new_hs_stats;
}
/** Free an hs_stats_t structure. */
static void
-hs_stats_free(hs_stats_t *hs_stats)
+hs_stats_free(hs_stats_t *victim_hs_stats)
{
- if (!hs_stats) {
+ if (!victim_hs_stats) {
return;
}
- digestmap_free(hs_stats->onions_seen_this_period, NULL);
- tor_free(hs_stats);
+ digestmap_free(victim_hs_stats->onions_seen_this_period, NULL);
+ tor_free(victim_hs_stats);
}
/** Initialize hidden service statistics. */
@@ -3074,16 +3142,20 @@ rep_hist_format_hs_stats(time_t now)
int64_t obfuscated_cells_seen;
int64_t obfuscated_onions_seen;
- obfuscated_cells_seen = round_int64_to_next_multiple_of(
- hs_stats->rp_relay_cells_seen,
- REND_CELLS_BIN_SIZE);
- obfuscated_cells_seen = add_laplace_noise(obfuscated_cells_seen,
+ uint64_t rounded_cells_seen
+ = round_uint64_to_next_multiple_of(hs_stats->rp_relay_cells_seen,
+ REND_CELLS_BIN_SIZE);
+ rounded_cells_seen = MIN(rounded_cells_seen, INT64_MAX);
+ obfuscated_cells_seen = add_laplace_noise((int64_t)rounded_cells_seen,
crypto_rand_double(),
REND_CELLS_DELTA_F, REND_CELLS_EPSILON);
- obfuscated_onions_seen = round_int64_to_next_multiple_of(digestmap_size(
- hs_stats->onions_seen_this_period),
- ONIONS_SEEN_BIN_SIZE);
- obfuscated_onions_seen = add_laplace_noise(obfuscated_onions_seen,
+
+ uint64_t rounded_onions_seen =
+ round_uint64_to_next_multiple_of((size_t)digestmap_size(
+ hs_stats->onions_seen_this_period),
+ ONIONS_SEEN_BIN_SIZE);
+ rounded_onions_seen = MIN(rounded_onions_seen, INT64_MAX);
+ obfuscated_onions_seen = add_laplace_noise((int64_t)rounded_onions_seen,
crypto_rand_double(), ONIONS_SEEN_DELTA_F,
ONIONS_SEEN_EPSILON);
@@ -3217,7 +3289,7 @@ rep_hist_free_all(void)
rep_hist_desc_stats_term();
total_descriptor_downloads = 0;
- tor_assert(rephist_total_alloc == 0);
- tor_assert(rephist_total_num == 0);
+ tor_assert_nonfatal(rephist_total_alloc == 0);
+ tor_assert_nonfatal_once(rephist_total_num == 0);
}
diff --git a/src/or/rephist.h b/src/or/rephist.h
index 145da97d02..ff4810a56d 100644
--- a/src/or/rephist.h
+++ b/src/or/rephist.h
@@ -112,5 +112,12 @@ void rep_hist_note_negotiated_link_proto(unsigned link_proto,
int started_here);
void rep_hist_log_link_protocol_counts(void);
+extern uint64_t rephist_total_alloc;
+extern uint32_t rephist_total_num;
+#ifdef TOR_UNIT_TESTS
+extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1];
+extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1];
+#endif
+
#endif
diff --git a/src/or/replaycache.c b/src/or/replaycache.c
index 23a1737b18..8290fa6964 100644
--- a/src/or/replaycache.c
+++ b/src/or/replaycache.c
@@ -1,10 +1,22 @@
/* Copyright (c) 2012-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-/*
+/**
* \file replaycache.c
*
* \brief Self-scrubbing replay cache for rendservice.c
+ *
+ * To prevent replay attacks, hidden services need to recognize INTRODUCE2
+ * cells that they've already seen, and drop them. If they didn't, then
+ * sending the same INTRODUCE2 cell over and over would force the hidden
+ * service to make a huge number of circuits to the same rendezvous
+ * point, aiding traffic analysis.
+ *
+ * (It's not that simple, actually. We only check for replays in the
+ * RSA-encrypted portion of the handshake, since the rest of the handshake is
+ * malleable.)
+ *
+ * This module is used from rendservice.c.
*/
#define REPLAYCACHE_PRIVATE
diff --git a/src/or/router.c b/src/or/router.c
index b134f2e315..31f2ff00d2 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -23,6 +23,7 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "protover.h"
#include "relay.h"
#include "rephist.h"
#include "router.h"
@@ -36,12 +37,25 @@
/**
* \file router.c
- * \brief OR functionality, including key maintenance, generating
- * and uploading server descriptors, retrying OR connections.
+ * \brief Miscellaneous relay functionality, including RSA key maintenance,
+ * generating and uploading server descriptors, picking an address to
+ * advertise, and so on.
+ *
+ * This module handles the job of deciding whether we are a Tor relay, and if
+ * so what kind. (Mostly through functions like server_mode() that inspect an
+ * or_options_t, but in some cases based on our own capabilities, such as when
+ * we are deciding whether to be a directory cache in
+ * router_has_bandwidth_to_be_dirserver().)
+ *
+ * Also in this module are the functions to generate our own routerinfo_t and
+ * extrainfo_t, and to encode those to signed strings for upload to the
+ * directory authorities.
+ *
+ * This module also handles key maintenance for RSA and Curve25519-ntor keys,
+ * and for our TLS context. (These functions should eventually move to
+ * routerkeys.c along with the code that handles Ed25519 keys now.)
**/
-extern long stats_n_seconds_working;
-
/************************************************************/
/*****
@@ -454,7 +468,8 @@ init_key_from_file(const char *fname, int generate, int severity,
goto error;
}
} else {
- log_info(LD_GENERAL, "No key found in \"%s\"", fname);
+ tor_log(severity, LD_GENERAL, "No key found in \"%s\"", fname);
+ goto error;
}
return prkey;
case FN_FILE:
@@ -562,7 +577,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out,
fname = get_datadir_fname2("keys",
legacy ? "legacy_signing_key" : "authority_signing_key");
- signing_key = init_key_from_file(fname, 0, LOG_INFO, 0);
+ signing_key = init_key_from_file(fname, 0, LOG_ERR, 0);
if (!signing_key) {
log_warn(LD_DIR, "No version 3 directory key found in %s", fname);
goto done;
@@ -1054,7 +1069,8 @@ init_keys(void)
log_info(LD_DIR, "adding my own v3 cert");
if (trusted_dirs_load_certs_from_string(
cert->cache_info.signed_descriptor_body,
- TRUSTED_DIRS_CERTS_SRC_SELF, 0)<0) {
+ TRUSTED_DIRS_CERTS_SRC_SELF, 0,
+ NULL)<0) {
log_warn(LD_DIR, "Unable to parse my own v3 cert! Failing.");
return -1;
}
@@ -1285,15 +1301,17 @@ decide_to_advertise_begindir(const or_options_t *options,
}
/** Allocate and return a new extend_info_t that can be used to build
- * a circuit to or through the router <b>r</b>. Use the primary
- * address of the router unless <b>for_direct_connect</b> is true, in
- * which case the preferred address is used instead. */
+ * a circuit to or through the router <b>r</b>. Uses the primary
+ * address of the router, so should only be called on a server. */
static extend_info_t *
extend_info_from_router(const routerinfo_t *r)
{
tor_addr_port_t ap;
tor_assert(r);
+ /* Make sure we don't need to check address reachability */
+ tor_assert_nonfatal(router_skip_or_reachability(get_options(), 0));
+
router_get_prim_orport(r, &ap);
return extend_info_new(r->nickname, r->cache_info.identity_digest,
r->onion_pkey, r->onion_curve25519_pkey,
@@ -1537,7 +1555,7 @@ MOCK_IMPL(int,
server_mode,(const or_options_t *options))
{
if (options->ClientOnly) return 0;
- /* XXXX024 I believe we can kill off ORListenAddress here.*/
+ /* XXXX I believe we can kill off ORListenAddress here.*/
return (options->ORPort_set || options->ORListenAddress);
}
@@ -1943,23 +1961,111 @@ static int router_guess_address_from_dir_headers(uint32_t *guess);
/** Make a current best guess at our address, either because
* it's configured in torrc, or because we've learned it from
* dirserver headers. Place the answer in *<b>addr</b> and return
- * 0 on success, else return -1 if we have no guess. */
+ * 0 on success, else return -1 if we have no guess.
+ *
+ * If <b>cache_only</b> is true, just return any cached answers, and
+ * don't try to get any new answers.
+ */
MOCK_IMPL(int,
-router_pick_published_address,(const or_options_t *options, uint32_t *addr))
+router_pick_published_address,(const or_options_t *options, uint32_t *addr,
+ int cache_only))
{
+ /* First, check the cached output from resolve_my_address(). */
*addr = get_last_resolved_addr();
- if (!*addr &&
- resolve_my_address(LOG_INFO, options, addr, NULL, NULL) < 0) {
- log_info(LD_CONFIG, "Could not determine our address locally. "
- "Checking if directory headers provide any hints.");
- if (router_guess_address_from_dir_headers(addr) < 0) {
- log_info(LD_CONFIG, "No hints from directory headers either. "
- "Will try again later.");
- return -1;
+ if (*addr)
+ return 0;
+
+ /* Second, consider doing a resolve attempt right here. */
+ if (!cache_only) {
+ if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) {
+ log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
+ return 0;
}
}
- log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr));
- return 0;
+
+ /* Third, check the cached output from router_new_address_suggestion(). */
+ if (router_guess_address_from_dir_headers(addr) >= 0)
+ return 0;
+
+ /* We have no useful cached answers. Return failure. */
+ return -1;
+}
+
+/* Like router_check_descriptor_address_consistency, but specifically for the
+ * ORPort or DirPort.
+ * listener_type is either CONN_TYPE_OR_LISTENER or CONN_TYPE_DIR_LISTENER. */
+static void
+router_check_descriptor_address_port_consistency(uint32_t ipv4h_desc_addr,
+ int listener_type)
+{
+ tor_assert(listener_type == CONN_TYPE_OR_LISTENER ||
+ listener_type == CONN_TYPE_DIR_LISTENER);
+
+ /* The first advertised Port may be the magic constant CFG_AUTO_PORT.
+ */
+ int port_v4_cfg = get_first_advertised_port_by_type_af(listener_type,
+ AF_INET);
+ if (port_v4_cfg != 0 &&
+ !port_exists_by_type_addr32h_port(listener_type,
+ ipv4h_desc_addr, port_v4_cfg, 1)) {
+ const tor_addr_t *port_addr = get_first_advertised_addr_by_type_af(
+ listener_type,
+ AF_INET);
+ /* If we're building a descriptor with no advertised address,
+ * something is terribly wrong. */
+ tor_assert(port_addr);
+
+ tor_addr_t desc_addr;
+ char port_addr_str[TOR_ADDR_BUF_LEN];
+ char desc_addr_str[TOR_ADDR_BUF_LEN];
+
+ tor_addr_to_str(port_addr_str, port_addr, TOR_ADDR_BUF_LEN, 0);
+
+ tor_addr_from_ipv4h(&desc_addr, ipv4h_desc_addr);
+ tor_addr_to_str(desc_addr_str, &desc_addr, TOR_ADDR_BUF_LEN, 0);
+
+ const char *listener_str = (listener_type == CONN_TYPE_OR_LISTENER ?
+ "OR" : "Dir");
+ log_warn(LD_CONFIG, "The IPv4 %sPort address %s does not match the "
+ "descriptor address %s. If you have a static public IPv4 "
+ "address, use 'Address <IPv4>' and 'OutboundBindAddress "
+ "<IPv4>'. If you are behind a NAT, use two %sPort lines: "
+ "'%sPort <PublicPort> NoListen' and '%sPort <InternalPort> "
+ "NoAdvertise'.",
+ listener_str, port_addr_str, desc_addr_str, listener_str,
+ listener_str, listener_str);
+ }
+}
+
+/* Tor relays only have one IPv4 address in the descriptor, which is derived
+ * from the Address torrc option, or guessed using various methods in
+ * router_pick_published_address().
+ * Warn the operator if there is no ORPort on the descriptor address
+ * ipv4h_desc_addr.
+ * Warn the operator if there is no DirPort on the descriptor address.
+ * This catches a few common config errors:
+ * - operators who expect ORPorts and DirPorts to be advertised on the
+ * ports' listen addresses, rather than the torrc Address (or guessed
+ * addresses in the absence of an Address config). This includes
+ * operators who attempt to put their ORPort and DirPort on different
+ * addresses;
+ * - discrepancies between guessed addresses and configured listen
+ * addresses (when the Address option isn't set).
+ * If a listener is listening on all IPv4 addresses, it is assumed that it
+ * is listening on the configured Address, and no messages are logged.
+ * If an operators has specified NoAdvertise ORPorts in a NAT setting,
+ * no messages are logged, unless they have specified other advertised
+ * addresses.
+ * The message tells operators to configure an ORPort and DirPort that match
+ * the Address (using NoListen if needed).
+ */
+static void
+router_check_descriptor_address_consistency(uint32_t ipv4h_desc_addr)
+{
+ router_check_descriptor_address_port_consistency(ipv4h_desc_addr,
+ CONN_TYPE_OR_LISTENER);
+ router_check_descriptor_address_port_consistency(ipv4h_desc_addr,
+ CONN_TYPE_DIR_LISTENER);
}
/** Build a fresh routerinfo, signed server descriptor, and extra-info document
@@ -1979,11 +2085,15 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
int hibernating = we_are_hibernating();
const or_options_t *options = get_options();
- if (router_pick_published_address(options, &addr) < 0) {
+ if (router_pick_published_address(options, &addr, 0) < 0) {
log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
return -1;
}
+ /* Log a message if the address in the descriptor doesn't match the ORPort
+ * and DirPort addresses configured by the operator. */
+ router_check_descriptor_address_consistency(addr);
+
ri = tor_malloc_zero(sizeof(routerinfo_t));
ri->cache_info.routerlist_index = -1;
ri->nickname = tor_strdup(options->Nickname);
@@ -2009,8 +2119,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
tor_addr_family(&p->addr) == AF_INET6) {
/* Like IPv4, if the relay is configured using the default
* authorities, disallow internal IPs. Otherwise, allow them. */
- const int default_auth = (!options->DirAuthorities &&
- !options->AlternateDirAuthority);
+ const int default_auth = using_default_dir_authorities(options);
if (! tor_addr_is_internal(&p->addr, 0) || ! default_auth) {
ipv6_orport = p;
break;
@@ -2042,6 +2151,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
get_platform_str(platform, sizeof(platform));
ri->platform = tor_strdup(platform);
+ ri->protocol_list = tor_strdup(protover_get_supported_protocols());
+
/* compute ri->bandwidthrate as the min of various options */
ri->bandwidthrate = get_effective_bwrate(options);
@@ -2058,8 +2169,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
&ri->exit_policy);
}
ri->policy_is_reject_star =
- policy_is_reject_star(ri->exit_policy, AF_INET) &&
- policy_is_reject_star(ri->exit_policy, AF_INET6);
+ policy_is_reject_star(ri->exit_policy, AF_INET, 1) &&
+ policy_is_reject_star(ri->exit_policy, AF_INET6, 1);
if (options->IPv6Exit) {
char *p_tmp = policy_summarize(ri->exit_policy, AF_INET6);
@@ -2223,7 +2334,7 @@ router_rebuild_descriptor(int force)
if (desc_clean_since && !force)
return 0;
- if (router_pick_published_address(options, &addr) < 0 ||
+ if (router_pick_published_address(options, &addr, 0) < 0 ||
router_get_advertised_or_port(options) == 0) {
/* Stop trying to rebuild our descriptor every second. We'll
* learn that it's time to try again when ip_address_changed()
@@ -2534,6 +2645,7 @@ router_dump_router_to_string(routerinfo_t *router,
char *ed_cert_line = NULL;
char *rsa_tap_cc_line = NULL;
char *ntor_cc_line = NULL;
+ char *proto_line = NULL;
/* Make sure the identity key matches the one in the routerinfo. */
if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) {
@@ -2698,6 +2810,12 @@ router_dump_router_to_string(routerinfo_t *router,
}
}
+ if (router->protocol_list) {
+ tor_asprintf(&proto_line, "proto %s\n", router->protocol_list);
+ } else {
+ proto_line = tor_strdup("");
+ }
+
address = tor_dup_ip(router->addr);
chunks = smartlist_new();
@@ -2707,7 +2825,7 @@ router_dump_router_to_string(routerinfo_t *router,
"%s"
"%s"
"platform %s\n"
- "protocols Link 1 2 Circuit 1\n"
+ "%s"
"published %s\n"
"fingerprint %s\n"
"uptime %ld\n"
@@ -2724,6 +2842,7 @@ router_dump_router_to_string(routerinfo_t *router,
ed_cert_line ? ed_cert_line : "",
extra_or_address ? extra_or_address : "",
router->platform,
+ proto_line,
published,
fingerprint,
stats_n_seconds_working,
@@ -2763,6 +2882,10 @@ router_dump_router_to_string(routerinfo_t *router,
(const char *)router->onion_curve25519_pkey->public_key,
CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
+ } else {
+ /* Authorities will start rejecting relays without ntor keys in 0.2.9 */
+ log_err(LD_BUG, "A relay must have an ntor onion key");
+ goto err;
}
/* Write the exit policy to the end of 's'. */
@@ -2865,6 +2988,7 @@ router_dump_router_to_string(routerinfo_t *router,
tor_free(rsa_tap_cc_line);
tor_free(ntor_cc_line);
tor_free(extra_info_line);
+ tor_free(proto_line);
return output;
}
@@ -3087,17 +3211,17 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
}
if (emit_ed_sigs) {
- char digest[DIGEST256_LEN];
+ char sha256_digest[DIGEST256_LEN];
smartlist_add(chunks, tor_strdup("router-sig-ed25519 "));
- crypto_digest_smartlist_prefix(digest, DIGEST256_LEN,
+ crypto_digest_smartlist_prefix(sha256_digest, DIGEST256_LEN,
ED_DESC_SIGNATURE_PREFIX,
chunks, "", DIGEST_SHA256);
- ed25519_signature_t sig;
+ ed25519_signature_t ed_sig;
char buf[ED25519_SIG_BASE64_LEN+1];
- if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN,
+ if (ed25519_sign(&ed_sig, (const uint8_t*)sha256_digest, DIGEST256_LEN,
signing_keypair) < 0)
goto err;
- if (ed25519_signature_to_base64(buf, &sig) < 0)
+ if (ed25519_signature_to_base64(buf, &ed_sig) < 0)
goto err;
smartlist_add_asprintf(chunks, "%s\n", buf);
@@ -3171,7 +3295,7 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
done:
tor_free(s);
- SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(chunks, char *, chunk, tor_free(chunk));
smartlist_free(chunks);
tor_free(s_dup);
tor_free(ed_cert_line);
diff --git a/src/or/router.h b/src/or/router.h
index 73bfea1faa..c30a0301b7 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -91,7 +91,8 @@ const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
MOCK_DECL(int,router_pick_published_address,(const or_options_t *options,
- uint32_t *addr));
+ uint32_t *addr,
+ int cache_only));
int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index fba3491f2b..ca32228fc7 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -48,7 +48,7 @@ do_getpass(const char *prompt, char *buf, size_t buflen,
size_t p2len = strlen(prompt) + 1;
if (p2len < sizeof(msg))
p2len = sizeof(msg);
- prompt2 = tor_malloc(strlen(prompt)+1);
+ prompt2 = tor_malloc(p2len);
memset(prompt2, ' ', p2len);
memcpy(prompt2 + p2len - sizeof(msg), msg, sizeof(msg));
@@ -115,20 +115,20 @@ read_encrypted_secret_key(ed25519_secret_key_t *out,
while (1) {
ssize_t pwlen =
- do_getpass("Enter pasphrase for master key:", pwbuf, sizeof(pwbuf), 0,
+ do_getpass("Enter passphrase for master key:", pwbuf, sizeof(pwbuf), 0,
get_options());
if (pwlen < 0) {
saved_errno = EINVAL;
goto done;
}
- const int r = crypto_unpwbox(&secret, &secret_len,
- encrypted_key, encrypted_len,
- pwbuf, pwlen);
- if (r == UNPWBOX_CORRUPTED) {
+ const int r_unbox = crypto_unpwbox(&secret, &secret_len,
+ encrypted_key, encrypted_len,
+ pwbuf, pwlen);
+ if (r_unbox == UNPWBOX_CORRUPTED) {
log_err(LD_OR, "%s is corrupted.", fname);
saved_errno = EINVAL;
goto done;
- } else if (r == UNPWBOX_OKAY) {
+ } else if (r_unbox == UNPWBOX_OKAY) {
break;
}
@@ -931,15 +931,15 @@ load_ed_keys(const or_options_t *options, time_t now)
int
generate_ed_link_cert(const or_options_t *options, time_t now)
{
- const tor_x509_cert_t *link = NULL, *id = NULL;
+ const tor_x509_cert_t *link_ = NULL, *id = NULL;
tor_cert_t *link_cert = NULL;
- if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL) {
+ if (tor_tls_get_my_certs(1, &link_, &id) < 0 || link_ == NULL) {
log_warn(LD_OR, "Can't get my x509 link cert.");
return -1;
}
- const common_digests_t *digests = tor_x509_cert_get_cert_digests(link);
+ const common_digests_t *digests = tor_x509_cert_get_cert_digests(link_);
if (link_cert_cert &&
! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) &&
@@ -979,12 +979,12 @@ should_make_new_ed_keys(const or_options_t *options, const time_t now)
EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop))
return 1;
- const tor_x509_cert_t *link = NULL, *id = NULL;
+ const tor_x509_cert_t *link_ = NULL, *id = NULL;
- if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL)
+ if (tor_tls_get_my_certs(1, &link_, &id) < 0 || link_ == NULL)
return 1;
- const common_digests_t *digests = tor_x509_cert_get_cert_digests(link);
+ const common_digests_t *digests = tor_x509_cert_get_cert_digests(link_);
if (!fast_memeq(digests->d[DIGEST_SHA256],
link_cert_cert->signed_key.pubkey,
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 0c7c5e98f1..1ad03b6cda 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -9,6 +9,85 @@
* \brief Code to
* maintain and access the global list of routerinfos for known
* servers.
+ *
+ * A "routerinfo_t" object represents a single self-signed router
+ * descriptor, as generated by a Tor relay in order to tell the rest of
+ * the world about its keys, address, and capabilities. An
+ * "extrainfo_t" object represents an adjunct "extra-info" object,
+ * certified by a corresponding router descriptor, reporting more
+ * information about the relay that nearly all users will not need.
+ *
+ * Most users will not use router descriptors for most relays. Instead,
+ * they use the information in microdescriptors and in the consensus
+ * networkstatus.
+ *
+ * Right now, routerinfo_t objects are used in these ways:
+ * <ul>
+ * <li>By clients, in order to learn about bridge keys and capabilities.
+ * (Bridges aren't listed in the consensus networkstatus, so they
+ * can't have microdescriptors.)
+ * <li>By relays, since relays want more information about other relays
+ * than they can learn from microdescriptors. (TODO: Is this still true?)
+ * <li>By authorities, which receive them and use them to generate the
+ * consensus and the microdescriptors.
+ * <li>By all directory caches, which download them in case somebody
+ * else wants them.
+ * </ul>
+ *
+ * Routerinfos are mostly created by parsing them from a string, in
+ * routerparse.c. We store them to disk on receiving them, and
+ * periodically discard the ones we don't need. On restarting, we
+ * re-read them from disk. (This also applies to extrainfo documents, if
+ * we are configured to fetch them.)
+ *
+ * In order to keep our list of routerinfos up-to-date, we periodically
+ * check whether there are any listed in the latest consensus (or in the
+ * votes from other authorities, if we are an authority) that we don't
+ * have. (This also applies to extrainfo documents, if we are
+ * configured to fetch them.)
+ *
+ * Almost nothing in Tor should use a routerinfo_t to refer directly to
+ * a relay; instead, almost everything should use node_t (implemented in
+ * nodelist.c), which provides a common interface to routerinfo_t,
+ * routerstatus_t, and microdescriptor_t.
+ *
+ * <br>
+ *
+ * This module also has some of the functions used for choosing random
+ * nodes according to different rules and weights. Historically, they
+ * were all in this module. Now, they are spread across this module,
+ * nodelist.c, and networkstatus.c. (TODO: Fix that.)
+ *
+ * <br>
+ *
+ * (For historical reasons) this module also contains code for handling
+ * the list of fallback directories, the list of directory authorities,
+ * and the list of authority certificates.
+ *
+ * For the directory authorities, we have a list containing the public
+ * identity key, and contact points, for each authority. The
+ * authorities receive descriptors from relays, and publish consensuses,
+ * descriptors, and microdescriptors. This list is pre-configured.
+ *
+ * Fallback directories are well-known, stable, but untrusted directory
+ * caches that clients which have not yet bootstrapped can use to get
+ * their first networkstatus consensus, in order to find out where the
+ * Tor network really is. This list is pre-configured in
+ * fallback_dirs.inc. Every authority also serves as a fallback.
+ *
+ * Both fallback directories and directory authorities are are
+ * represented by a dir_server_t.
+ *
+ * Authority certificates are signed with authority identity keys; they
+ * are used to authenticate shorter-term authority signing keys. We
+ * fetch them when we find a consensus or a vote that has been signed
+ * with a signing key we don't recognize. We cache them on disk and
+ * load them on startup. Authority operators generate them with the
+ * "tor-gencert" utility.
+ *
+ * TODO: Authority certificates should be a separate module.
+ *
+ * TODO: dir_server_t stuff should be in a separate module.
**/
#define ROUTERLIST_PRIVATE
@@ -46,6 +125,9 @@
/****************************************************************************/
+/* Typed wrappers for different digestmap types; used to avoid type
+ * confusion. */
+
DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t)
DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t)
DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t)
@@ -67,7 +149,7 @@ typedef struct cert_list_t cert_list_t;
/* static function prototypes */
static int compute_weighted_bandwidths(const smartlist_t *sl,
bandwidth_weight_rule_t rule,
- u64_dbl_t **bandwidths_out);
+ double **bandwidths_out);
static const routerstatus_t *router_pick_trusteddirserver_impl(
const smartlist_t *sourcelist, dirinfo_type_t auth,
int flags, int *n_busy_out);
@@ -159,6 +241,9 @@ download_status_cert_init(download_status_t *dlstatus)
dlstatus->schedule = DL_SCHED_CONSENSUS;
dlstatus->want_authority = DL_WANT_ANY_DIRSERVER;
dlstatus->increment_on = DL_SCHED_INCREMENT_FAILURE;
+ dlstatus->backoff = DL_SCHED_RANDOM_EXPONENTIAL;
+ dlstatus->last_backoff_position = 0;
+ dlstatus->last_delay_used = 0;
/* Use the new schedule to set next_attempt_at */
download_status_reset(dlstatus);
@@ -250,6 +335,112 @@ get_cert_list(const char *id_digest)
return cl;
}
+/** Return a list of authority ID digests with potentially enumerable lists
+ * of download_status_t objects; used by controller GETINFO queries.
+ */
+
+MOCK_IMPL(smartlist_t *,
+list_authority_ids_with_downloads, (void))
+{
+ smartlist_t *ids = smartlist_new();
+ digestmap_iter_t *i;
+ const char *digest;
+ char *tmp;
+ void *cl;
+
+ if (trusted_dir_certs) {
+ for (i = digestmap_iter_init(trusted_dir_certs);
+ !(digestmap_iter_done(i));
+ i = digestmap_iter_next(trusted_dir_certs, i)) {
+ /*
+ * We always have at least dl_status_by_id to query, so no need to
+ * probe deeper than the existence of a cert_list_t.
+ */
+ digestmap_iter_get(i, &digest, &cl);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, digest, DIGEST_LEN);
+ smartlist_add(ids, tmp);
+ }
+ }
+ /* else definitely no downlaods going since nothing even has a cert list */
+
+ return ids;
+}
+
+/** Given an authority ID digest, return a pointer to the default download
+ * status, or NULL if there is no such entry in trusted_dir_certs */
+
+MOCK_IMPL(download_status_t *,
+id_only_download_status_for_authority_id, (const char *digest))
+{
+ download_status_t *dl = NULL;
+ cert_list_t *cl;
+
+ if (trusted_dir_certs) {
+ cl = digestmap_get(trusted_dir_certs, digest);
+ if (cl) {
+ dl = &(cl->dl_status_by_id);
+ }
+ }
+
+ return dl;
+}
+
+/** Given an authority ID digest, return a smartlist of signing key digests
+ * for which download_status_t is potentially queryable, or NULL if no such
+ * authority ID digest is known. */
+
+MOCK_IMPL(smartlist_t *,
+list_sk_digests_for_authority_id, (const char *digest))
+{
+ smartlist_t *sks = NULL;
+ cert_list_t *cl;
+ dsmap_iter_t *i;
+ const char *sk_digest;
+ char *tmp;
+ download_status_t *dl;
+
+ if (trusted_dir_certs) {
+ cl = digestmap_get(trusted_dir_certs, digest);
+ if (cl) {
+ sks = smartlist_new();
+ if (cl->dl_status_map) {
+ for (i = dsmap_iter_init(cl->dl_status_map);
+ !(dsmap_iter_done(i));
+ i = dsmap_iter_next(cl->dl_status_map, i)) {
+ /* Pull the digest out and add it to the list */
+ dsmap_iter_get(i, &sk_digest, &dl);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, sk_digest, DIGEST_LEN);
+ smartlist_add(sks, tmp);
+ }
+ }
+ }
+ }
+
+ return sks;
+}
+
+/** Given an authority ID digest and a signing key digest, return the
+ * download_status_t or NULL if none exists. */
+
+MOCK_IMPL(download_status_t *,
+ download_status_for_authority_id_and_sk,
+ (const char *id_digest, const char *sk_digest))
+{
+ download_status_t *dl = NULL;
+ cert_list_t *cl = NULL;
+
+ if (trusted_dir_certs) {
+ cl = digestmap_get(trusted_dir_certs, id_digest);
+ if (cl && cl->dl_status_map) {
+ dl = dsmap_get(cl->dl_status_map, sk_digest);
+ }
+ }
+
+ return dl;
+}
+
/** Release all space held by a cert_list_t */
static void
cert_list_free(cert_list_t *cl)
@@ -287,7 +478,7 @@ trusted_dirs_reload_certs(void)
return 0;
r = trusted_dirs_load_certs_from_string(
contents,
- TRUSTED_DIRS_CERTS_SRC_FROM_STORE, 1);
+ TRUSTED_DIRS_CERTS_SRC_FROM_STORE, 1, NULL);
tor_free(contents);
return r;
}
@@ -317,16 +508,20 @@ already_have_cert(authority_cert_t *cert)
* or TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_SK_DIGEST. If <b>flush</b> is true, we
* need to flush any changed certificates to disk now. Return 0 on success,
* -1 if any certs fail to parse.
+ *
+ * If source_dir is non-NULL, it's the identity digest for a directory that
+ * we've just successfully retrieved certificates from, so try it first to
+ * fetch any missing certificates.
*/
-
int
trusted_dirs_load_certs_from_string(const char *contents, int source,
- int flush)
+ int flush, const char *source_dir)
{
dir_server_t *ds;
const char *s, *eos;
int failure_code = 0;
int from_store = (source == TRUSTED_DIRS_CERTS_SRC_FROM_STORE);
+ int added_trusted_cert = 0;
for (s = contents; *s; s = eos) {
authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
@@ -386,6 +581,7 @@ trusted_dirs_load_certs_from_string(const char *contents, int source,
}
if (ds) {
+ added_trusted_cert = 1;
log_info(LD_DIR, "Adding %s certificate for directory authority %s with "
"signing key %s", from_store ? "cached" : "downloaded",
ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN));
@@ -430,8 +626,15 @@ trusted_dirs_load_certs_from_string(const char *contents, int source,
trusted_dirs_flush_certs_to_disk();
/* call this even if failure_code is <0, since some certs might have
- * succeeded. */
- networkstatus_note_certs_arrived();
+ * succeeded, but only pass source_dir if there were no failures,
+ * and at least one more authority certificate was added to the store.
+ * This avoids retrying a directory that's serving bad or entirely duplicate
+ * certificates. */
+ if (failure_code == 0 && added_trusted_cert) {
+ networkstatus_note_certs_arrived(source_dir);
+ } else {
+ networkstatus_note_certs_arrived(NULL);
+ }
return failure_code;
}
@@ -679,7 +882,9 @@ static const char *BAD_SIGNING_KEYS[] = {
NULL,
};
-/* DOCDOC */
+/** Return true iff <b>cert</b> authenticates some atuhority signing key
+ * which, because of the old openssl heartbleed vulnerability, should
+ * never be trusted. */
int
authority_cert_is_blacklisted(const authority_cert_t *cert)
{
@@ -713,14 +918,81 @@ authority_cert_dl_looks_uncertain(const char *id_digest)
return n_failures >= N_AUTH_CERT_DL_FAILURES_TO_BUG_USER;
}
+/* Fetch the authority certificates specified in resource.
+ * If we are a bridge client, and node is a configured bridge, fetch from node
+ * using dir_hint as the fingerprint. Otherwise, if rs is not NULL, fetch from
+ * rs. Otherwise, fetch from a random directory mirror. */
+static void
+authority_certs_fetch_resource_impl(const char *resource,
+ const char *dir_hint,
+ const node_t *node,
+ const routerstatus_t *rs)
+{
+ const or_options_t *options = get_options();
+ int get_via_tor = purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0);
+
+ /* Make sure bridge clients never connect to anything but a bridge */
+ if (options->UseBridges) {
+ if (node && !node_is_a_configured_bridge(node)) {
+ /* If we're using bridges, and node is not a bridge, use a 3-hop path. */
+ get_via_tor = 1;
+ } else if (!node) {
+ /* If we're using bridges, and there's no node, use a 3-hop path. */
+ get_via_tor = 1;
+ }
+ }
+
+ const dir_indirection_t indirection = get_via_tor ? DIRIND_ANONYMOUS
+ : DIRIND_ONEHOP;
+
+ /* If we've just downloaded a consensus from a bridge, re-use that
+ * bridge */
+ if (options->UseBridges && node && !get_via_tor) {
+ /* clients always make OR connections to bridges */
+ tor_addr_port_t or_ap;
+ /* we are willing to use a non-preferred address if we need to */
+ fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0,
+ &or_ap);
+ directory_initiate_command(&or_ap.addr, or_ap.port,
+ NULL, 0, /*no dirport*/
+ dir_hint,
+ DIR_PURPOSE_FETCH_CERTIFICATE,
+ 0,
+ indirection,
+ resource, NULL, 0, 0);
+ return;
+ }
+
+ if (rs) {
+ /* If we've just downloaded a consensus from a directory, re-use that
+ * directory */
+ directory_initiate_command_routerstatus(rs,
+ DIR_PURPOSE_FETCH_CERTIFICATE,
+ 0, indirection, resource, NULL,
+ 0, 0);
+ return;
+ }
+
+ /* Otherwise, we want certs from a random fallback or directory
+ * mirror, because they will almost always succeed. */
+ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
+ resource, PDS_RETRY_IF_NO_SERVERS,
+ DL_WANT_ANY_DIRSERVER);
+}
+
/** Try to download any v3 authority certificates that we may be missing. If
* <b>status</b> is provided, try to get all the ones that were used to sign
* <b>status</b>. Additionally, try to have a non-expired certificate for
* every V3 authority in trusted_dir_servers. Don't fetch certificates we
* already have.
+ *
+ * If dir_hint is non-NULL, it's the identity digest for a directory that
+ * we've just successfully retrieved a consensus or certificates from, so try
+ * it first to fetch any missing certificates.
**/
void
-authority_certs_fetch_missing(networkstatus_t *status, time_t now)
+authority_certs_fetch_missing(networkstatus_t *status, time_t now,
+ const char *dir_hint)
{
/*
* The pending_id digestmap tracks pending certificate downloads by
@@ -729,7 +1001,6 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
*/
digestmap_t *pending_id;
fp_pair_map_t *pending_cert;
- authority_cert_t *cert;
/*
* The missing_id_digests smartlist will hold a list of id digests
* we want to fetch the newest cert for; the missing_cert_digests
@@ -739,12 +1010,13 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
smartlist_t *missing_cert_digests, *missing_id_digests;
char *resource = NULL;
cert_list_t *cl;
- const int cache = directory_caches_unknown_auth_certs(get_options());
+ const or_options_t *options = get_options();
+ const int cache = directory_caches_unknown_auth_certs(options);
fp_pair_t *fp_tmp = NULL;
char id_digest_str[2*DIGEST_LEN+1];
char sk_digest_str[2*DIGEST_LEN+1];
- if (should_delay_dir_fetches(get_options(), NULL))
+ if (should_delay_dir_fetches(options, NULL))
return;
pending_cert = fp_pair_map_new();
@@ -785,7 +1057,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
} SMARTLIST_FOREACH_END(cert);
if (!found &&
download_status_is_ready(&(cl->dl_status_by_id), now,
- get_options()->TestingCertMaxDownloadTries) &&
+ options->TestingCertMaxDownloadTries) &&
!digestmap_get(pending_id, ds->v3_identity_digest)) {
log_info(LD_DIR,
"No current certificate known for authority %s "
@@ -838,8 +1110,9 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
}
SMARTLIST_FOREACH_BEGIN(voter->sigs, document_signature_t *, sig) {
- cert = authority_cert_get_by_digests(voter->identity_digest,
- sig->signing_key_digest);
+ authority_cert_t *cert =
+ authority_cert_get_by_digests(voter->identity_digest,
+ sig->signing_key_digest);
if (cert) {
if (now < cert->expires)
download_status_reset_by_sk_in_cl(cl, sig->signing_key_digest);
@@ -847,7 +1120,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
}
if (download_status_is_ready_by_sk_in_cl(
cl, sig->signing_key_digest,
- now, get_options()->TestingCertMaxDownloadTries) &&
+ now, options->TestingCertMaxDownloadTries) &&
!fp_pair_map_get_by_digests(pending_cert,
voter->identity_digest,
sig->signing_key_digest)) {
@@ -884,6 +1157,46 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
} SMARTLIST_FOREACH_END(voter);
}
+ /* Bridge clients look up the node for the dir_hint */
+ const node_t *node = NULL;
+ /* All clients, including bridge clients, look up the routerstatus for the
+ * dir_hint */
+ const routerstatus_t *rs = NULL;
+
+ /* If we still need certificates, try the directory that just successfully
+ * served us a consensus or certificates.
+ * As soon as the directory fails to provide additional certificates, we try
+ * another, randomly selected directory. This avoids continual retries.
+ * (We only ever have one outstanding request per certificate.)
+ */
+ if (dir_hint) {
+ if (options->UseBridges) {
+ /* Bridge clients try the nodelist. If the dir_hint is from an authority,
+ * or something else fetched over tor, we won't find the node here, but
+ * we will find the rs. */
+ node = node_get_by_id(dir_hint);
+ }
+
+ /* All clients try the consensus routerstatus, then the fallback
+ * routerstatus */
+ rs = router_get_consensus_status_by_id(dir_hint);
+ if (!rs) {
+ /* This will also find authorities */
+ const dir_server_t *ds = router_get_fallback_dirserver_by_digest(
+ dir_hint);
+ if (ds) {
+ rs = &ds->fake_status;
+ }
+ }
+
+ if (!node && !rs) {
+ log_warn(LD_BUG, "Directory %s delivered a consensus, but %s"
+ "no routerstatus could be found for it.",
+ options->UseBridges ? "no node and " : "",
+ hex_str(dir_hint, DIGEST_LEN));
+ }
+ }
+
/* Do downloads by identity digest */
if (smartlist_len(missing_id_digests) > 0) {
int need_plus = 0;
@@ -913,11 +1226,9 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
if (smartlist_len(fps) > 1) {
resource = smartlist_join_strings(fps, "", 0, NULL);
- /* We want certs from mirrors, because they will almost always succeed.
- */
- directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource, PDS_RETRY_IF_NO_SERVERS,
- DL_WANT_ANY_DIRSERVER);
+ /* node and rs are directories that just gave us a consensus or
+ * certificates */
+ authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
tor_free(resource);
}
/* else we didn't add any: they were all pending */
@@ -960,11 +1271,9 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now)
if (smartlist_len(fp_pairs) > 1) {
resource = smartlist_join_strings(fp_pairs, "", 0, NULL);
- /* We want certs from mirrors, because they will almost always succeed.
- */
- directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0,
- resource, PDS_RETRY_IF_NO_SERVERS,
- DL_WANT_ANY_DIRSERVER);
+ /* node and rs are directories that just gave us a consensus or
+ * certificates */
+ authority_certs_fetch_resource_impl(resource, dir_hint, node, rs);
tor_free(resource);
}
/* else they were all pending */
@@ -1420,8 +1729,8 @@ router_digest_is_fallback_dir(const char *digest)
* v3 identity key hashes to <b>digest</b>, or NULL if no such authority
* is known.
*/
-dir_server_t *
-trusteddirserver_get_by_v3_auth_digest(const char *digest)
+MOCK_IMPL(dir_server_t *,
+trusteddirserver_get_by_v3_auth_digest, (const char *digest))
{
if (!trusted_dir_servers)
return NULL;
@@ -1645,7 +1954,7 @@ router_picked_poor_directory_log(const routerstatus_t *rs)
/* When iterating through the routerlist, can OR address/port preference
* and reachability checks be skipped?
*/
-static int
+int
router_skip_or_reachability(const or_options_t *options, int try_ip_pref)
{
/* Servers always have and prefer IPv4.
@@ -1815,20 +2124,23 @@ dirserver_choose_by_weight(const smartlist_t *servers, double authority_weight)
{
int n = smartlist_len(servers);
int i;
- u64_dbl_t *weights;
+ double *weights_dbl;
+ uint64_t *weights_u64;
const dir_server_t *ds;
- weights = tor_calloc(n, sizeof(u64_dbl_t));
+ weights_dbl = tor_calloc(n, sizeof(double));
+ weights_u64 = tor_calloc(n, sizeof(uint64_t));
for (i = 0; i < n; ++i) {
ds = smartlist_get(servers, i);
- weights[i].dbl = ds->weight;
+ weights_dbl[i] = ds->weight;
if (ds->is_authority)
- weights[i].dbl *= authority_weight;
+ weights_dbl[i] *= authority_weight;
}
- scale_array_elements_to_u64(weights, n, NULL);
- i = choose_array_element_by_weight(weights, n);
- tor_free(weights);
+ scale_array_elements_to_u64(weights_u64, weights_dbl, n, NULL);
+ i = choose_array_element_by_weight(weights_u64, n);
+ tor_free(weights_dbl);
+ tor_free(weights_u64);
return (i < 0) ? NULL : smartlist_get(servers, i);
}
@@ -2032,10 +2344,17 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid,
continue;
if (node_is_unreliable(node, need_uptime, need_capacity, need_guard))
continue;
- /* Choose a node with an OR address that matches the firewall rules,
- * if we are making a direct connection */
+ /* Don't choose nodes if we are certain they can't do EXTEND2 cells */
+ if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1))
+ continue;
+ /* Don't choose nodes if we are certain they can't do ntor. */
+ if ((node->ri || node->md) && !node_has_curve25519_onion_key(node))
+ continue;
+ /* Choose a node with an OR address that matches the firewall rules */
if (direct_conn && check_reach &&
- !fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr))
+ !fascist_firewall_allows_node(node,
+ FIREWALL_OR_CONNECTION,
+ pref_addr))
continue;
smartlist_add(sl, (void *)node);
@@ -2090,59 +2409,43 @@ router_get_advertised_bandwidth_capped(const routerinfo_t *router)
* much of the range of uint64_t. If <b>total_out</b> is provided, set it to
* the sum of all elements in the array _before_ scaling. */
STATIC void
-scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
+scale_array_elements_to_u64(uint64_t *entries_out, const double *entries_in,
+ int n_entries,
uint64_t *total_out)
{
double total = 0.0;
double scale_factor = 0.0;
int i;
- /* big, but far away from overflowing an int64_t */
-#define SCALE_TO_U64_MAX ((int64_t) (INT64_MAX / 4))
for (i = 0; i < n_entries; ++i)
- total += entries[i].dbl;
+ total += entries_in[i];
- if (total > 0.0)
- scale_factor = SCALE_TO_U64_MAX / total;
+ if (total > 0.0) {
+ scale_factor = ((double)INT64_MAX) / total;
+ scale_factor /= 4.0; /* make sure we're very far away from overflowing */
+ }
for (i = 0; i < n_entries; ++i)
- entries[i].u64 = tor_llround(entries[i].dbl * scale_factor);
+ entries_out[i] = tor_llround(entries_in[i] * scale_factor);
if (total_out)
*total_out = (uint64_t) total;
-
-#undef SCALE_TO_U64_MAX
}
-/** Time-invariant 64-bit greater-than; works on two integers in the range
- * (0,INT64_MAX). */
-#if SIZEOF_VOID_P == 8
-#define gt_i64_timei(a,b) ((a) > (b))
-#else
-static inline int
-gt_i64_timei(uint64_t a, uint64_t b)
-{
- int64_t diff = (int64_t) (b - a);
- int res = diff >> 63;
- return res & 1;
-}
-#endif
-
/** Pick a random element of <b>n_entries</b>-element array <b>entries</b>,
* choosing each element with a probability proportional to its (uint64_t)
* value, and return the index of that element. If all elements are 0, choose
* an index at random. Return -1 on error.
*/
STATIC int
-choose_array_element_by_weight(const u64_dbl_t *entries, int n_entries)
+choose_array_element_by_weight(const uint64_t *entries, int n_entries)
{
- int i, i_chosen=-1, n_chosen=0;
- uint64_t total_so_far = 0;
+ int i;
uint64_t rand_val;
uint64_t total = 0;
for (i = 0; i < n_entries; ++i)
- total += entries[i].u64;
+ total += entries[i];
if (n_entries < 1)
return -1;
@@ -2154,22 +2457,8 @@ choose_array_element_by_weight(const u64_dbl_t *entries, int n_entries)
rand_val = crypto_rand_uint64(total);
- for (i = 0; i < n_entries; ++i) {
- total_so_far += entries[i].u64;
- if (gt_i64_timei(total_so_far, rand_val)) {
- i_chosen = i;
- n_chosen++;
- /* Set rand_val to INT64_MAX rather than stopping the loop. This way,
- * the time we spend in the loop does not leak which element we chose. */
- rand_val = INT64_MAX;
- }
- }
- tor_assert(total_so_far == total);
- tor_assert(n_chosen == 1);
- tor_assert(i_chosen >= 0);
- tor_assert(i_chosen < n_entries);
-
- return i_chosen;
+ return select_array_member_cumulative_timei(
+ entries, n_entries, total, rand_val);
}
/** When weighting bridges, enforce these values as lower and upper
@@ -2221,17 +2510,21 @@ static const node_t *
smartlist_choose_node_by_bandwidth_weights(const smartlist_t *sl,
bandwidth_weight_rule_t rule)
{
- u64_dbl_t *bandwidths=NULL;
+ double *bandwidths_dbl=NULL;
+ uint64_t *bandwidths_u64=NULL;
- if (compute_weighted_bandwidths(sl, rule, &bandwidths) < 0)
+ if (compute_weighted_bandwidths(sl, rule, &bandwidths_dbl) < 0)
return NULL;
- scale_array_elements_to_u64(bandwidths, smartlist_len(sl), NULL);
+ bandwidths_u64 = tor_calloc(smartlist_len(sl), sizeof(uint64_t));
+ scale_array_elements_to_u64(bandwidths_u64, bandwidths_dbl,
+ smartlist_len(sl), NULL);
{
- int idx = choose_array_element_by_weight(bandwidths,
+ int idx = choose_array_element_by_weight(bandwidths_u64,
smartlist_len(sl));
- tor_free(bandwidths);
+ tor_free(bandwidths_dbl);
+ tor_free(bandwidths_u64);
return idx < 0 ? NULL : smartlist_get(sl, idx);
}
}
@@ -2244,14 +2537,14 @@ smartlist_choose_node_by_bandwidth_weights(const smartlist_t *sl,
static int
compute_weighted_bandwidths(const smartlist_t *sl,
bandwidth_weight_rule_t rule,
- u64_dbl_t **bandwidths_out)
+ double **bandwidths_out)
{
int64_t weight_scale;
double Wg = -1, Wm = -1, We = -1, Wd = -1;
double Wgb = -1, Wmb = -1, Web = -1, Wdb = -1;
uint64_t weighted_bw = 0;
guardfraction_bandwidth_t guardfraction_bw;
- u64_dbl_t *bandwidths;
+ double *bandwidths;
/* Can't choose exit and guard at same time */
tor_assert(rule == NO_WEIGHTING ||
@@ -2333,7 +2626,7 @@ compute_weighted_bandwidths(const smartlist_t *sl,
Web /= weight_scale;
Wdb /= weight_scale;
- bandwidths = tor_calloc(smartlist_len(sl), sizeof(u64_dbl_t));
+ bandwidths = tor_calloc(smartlist_len(sl), sizeof(double));
// Cycle through smartlist and total the bandwidth.
static int warned_missing_bw = 0;
@@ -2420,7 +2713,7 @@ compute_weighted_bandwidths(const smartlist_t *sl,
final_weight = weight*this_bw;
}
- bandwidths[node_sl_idx].dbl = final_weight + 0.5;
+ bandwidths[node_sl_idx] = final_weight + 0.5;
} SMARTLIST_FOREACH_END(node);
log_debug(LD_CIRC, "Generated weighted bandwidths for rule %s based "
@@ -2441,7 +2734,7 @@ double
frac_nodes_with_descriptors(const smartlist_t *sl,
bandwidth_weight_rule_t rule)
{
- u64_dbl_t *bandwidths = NULL;
+ double *bandwidths = NULL;
double total, present;
if (smartlist_len(sl) == 0)
@@ -2458,7 +2751,7 @@ frac_nodes_with_descriptors(const smartlist_t *sl,
total = present = 0.0;
SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) {
- const double bw = bandwidths[node_sl_idx].dbl;
+ const double bw = bandwidths[node_sl_idx];
total += bw;
if (node_has_descriptor(node))
present += bw;
@@ -2637,7 +2930,8 @@ hex_digest_nickname_decode(const char *hexdigest,
return -1;
}
- if (base16_decode(digest_out, DIGEST_LEN, hexdigest, HEX_DIGEST_LEN)<0)
+ if (base16_decode(digest_out, DIGEST_LEN,
+ hexdigest, HEX_DIGEST_LEN) != DIGEST_LEN)
return -1;
return 0;
}
@@ -2722,7 +3016,7 @@ hexdigest_to_digest(const char *hexdigest, char *digest)
if (hexdigest[0]=='$')
++hexdigest;
if (strlen(hexdigest) < HEX_DIGEST_LEN ||
- base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN) < 0)
+ base16_decode(digest,DIGEST_LEN,hexdigest,HEX_DIGEST_LEN) != DIGEST_LEN)
return -1;
return 0;
}
@@ -2896,6 +3190,7 @@ routerinfo_free(routerinfo_t *router)
tor_free(router->cache_info.signed_descriptor_body);
tor_free(router->nickname);
tor_free(router->platform);
+ tor_free(router->protocol_list);
tor_free(router->contact_info);
if (router->onion_pkey)
crypto_pk_free(router->onion_pkey);
@@ -2943,6 +3238,17 @@ signed_descriptor_free(signed_descriptor_t *sd)
tor_free(sd);
}
+/** Reset the given signed descriptor <b>sd</b> by freeing the allocated
+ * memory inside the object and by zeroing its content. */
+static void
+signed_descriptor_reset(signed_descriptor_t *sd)
+{
+ tor_assert(sd);
+ tor_free(sd->signed_descriptor_body);
+ tor_cert_free(sd->signing_key_cert);
+ memset(sd, 0, sizeof(*sd));
+}
+
/** Copy src into dest, and steal all references inside src so that when
* we free src, we don't mess up dest. */
static void
@@ -2950,6 +3256,8 @@ signed_descriptor_move(signed_descriptor_t *dest,
signed_descriptor_t *src)
{
tor_assert(dest != src);
+ /* Cleanup destination object before overwriting it.*/
+ signed_descriptor_reset(dest);
memcpy(dest, src, sizeof(signed_descriptor_t));
src->signed_descriptor_body = NULL;
src->signing_key_cert = NULL;
@@ -3707,7 +4015,7 @@ router_add_extrainfo_to_routerlist(extrainfo_t *ei, const char **msg,
was_router_added_t inserted;
(void)from_fetch;
if (msg) *msg = NULL;
- /*XXXX023 Do something with msg */
+ /*XXXX Do something with msg */
inserted = extrainfo_insert(router_get_routerlist(), ei, !from_cache);
@@ -4267,6 +4575,10 @@ void
routerlist_retry_directory_downloads(time_t now)
{
(void)now;
+
+ log_debug(LD_GENERAL,
+ "In routerlist_retry_directory_downloads()");
+
router_reset_status_download_failures();
router_reset_descriptor_download_failures();
reschedule_directory_downloads();
@@ -4310,7 +4622,7 @@ dir_server_new(int is_authority,
return NULL;
if (!hostname)
- hostname_ = tor_dup_addr(addr);
+ hostname_ = tor_addr_to_str_dup(addr);
else
hostname_ = tor_strdup(hostname);
@@ -4726,6 +5038,11 @@ launch_descriptor_downloads(int purpose,
log_debug(LD_DIR,
"There are enough downloadable %ss to launch requests.",
descname);
+ } else if (! router_have_minimum_dir_info()) {
+ log_debug(LD_DIR,
+ "We are only missing %d %ss, but we'll fetch anyway, since "
+ "we don't yet have enough directory info.",
+ n_downloadable, descname);
} else {
/* should delay */
@@ -4920,7 +5237,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote,
/** How often should we launch a server/authority request to be sure of getting
* a guess for our IP? */
-/*XXXX024 this info should come from netinfo cells or something, or we should
+/*XXXX+ this info should come from netinfo cells or something, or we should
* do this only when we aren't seeing incoming data. see bug 652. */
#define DUMMY_DOWNLOAD_INTERVAL (20*60)
@@ -4931,7 +5248,7 @@ launch_dummy_descriptor_download_as_needed(time_t now,
const or_options_t *options)
{
static time_t last_dummy_download = 0;
- /* XXXX024 we could be smarter here; see notes on bug 652. */
+ /* XXXX+ we could be smarter here; see notes on bug 652. */
/* If we're a server that doesn't have a configured address, we rely on
* directory fetches to learn when our address changes. So if we haven't
* tried to get any routerdescs in a long time, try a dummy fetch now. */
@@ -5079,6 +5396,9 @@ update_extrainfo_downloads(time_t now)
void
router_reset_descriptor_download_failures(void)
{
+ log_debug(LD_GENERAL,
+ "In router_reset_descriptor_download_failures()");
+
networkstatus_reset_download_failures();
last_descriptor_download_attempted = 0;
if (!routerlist)
@@ -5133,7 +5453,7 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
(r1->contact_info && r2->contact_info &&
strcasecmp(r1->contact_info, r2->contact_info)) ||
r1->is_hibernating != r2->is_hibernating ||
- cmp_addr_policies(r1->exit_policy, r2->exit_policy) ||
+ ! addr_policies_eq(r1->exit_policy, r2->exit_policy) ||
(r1->supports_tunnelled_dir_requests !=
r2->supports_tunnelled_dir_requests))
return 0;
@@ -5290,6 +5610,47 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey,
return r;
}
+/* Does ri have a valid ntor onion key?
+ * Valid ntor onion keys exist and have at least one non-zero byte. */
+int
+routerinfo_has_curve25519_onion_key(const routerinfo_t *ri)
+{
+ if (!ri) {
+ return 0;
+ }
+
+ if (!ri->onion_curve25519_pkey) {
+ return 0;
+ }
+
+ if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key,
+ CURVE25519_PUBKEY_LEN)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Is rs running a tor version known to support EXTEND2 cells?
+ * If allow_unknown_versions is true, return true if we can't tell
+ * (from a versions line or a protocols line) whether it supports extend2
+ * cells.
+ * Otherwise, return false if the version is unknown. */
+int
+routerstatus_version_supports_extend2_cells(const routerstatus_t *rs,
+ int allow_unknown_versions)
+{
+ if (!rs) {
+ return allow_unknown_versions;
+ }
+
+ if (!rs->protocols_known) {
+ return allow_unknown_versions;
+ }
+
+ return rs->supports_extend2_cells;
+}
+
/** Assert that the internal representation of <b>rl</b> is
* self-consistent. */
void
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index cb5b42a3b8..606e9085ce 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -29,7 +29,7 @@ int trusted_dirs_reload_certs(void);
#define TRUSTED_DIRS_CERTS_SRC_FROM_VOTE 4
int trusted_dirs_load_certs_from_string(const char *contents, int source,
- int flush);
+ int flush, const char *source_dir);
void trusted_dirs_flush_certs_to_disk(void);
authority_cert_t *authority_cert_get_newest_by_id(const char *id_digest);
authority_cert_t *authority_cert_get_by_sk_digest(const char *sk_digest);
@@ -38,7 +38,8 @@ authority_cert_t *authority_cert_get_by_digests(const char *id_digest,
void authority_cert_get_all(smartlist_t *certs_out);
void authority_cert_dl_failed(const char *id_digest,
const char *signing_key_digest, int status);
-void authority_certs_fetch_missing(networkstatus_t *status, time_t now);
+void authority_certs_fetch_missing(networkstatus_t *status, time_t now,
+ const char *dir_hint);
int router_reload_router_list(void);
int authority_cert_dl_looks_uncertain(const char *id_digest);
const smartlist_t *router_get_trusted_dir_servers(void);
@@ -51,11 +52,13 @@ dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
dir_server_t *router_get_fallback_dirserver_by_digest(
const char *digest);
int router_digest_is_fallback_dir(const char *digest);
-dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
+MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest,
+ (const char *d));
const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
int flags);
const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type,
int flags);
+int router_skip_or_reachability(const or_options_t *options, int try_ip_pref);
int router_get_my_share_of_directory_requests(double *v3_share_out);
void router_reset_status_download_failures(void);
int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2);
@@ -103,6 +106,14 @@ void routerlist_remove(routerlist_t *rl, routerinfo_t *ri, int make_old,
void routerlist_free_all(void);
void routerlist_reset_warnings(void);
+MOCK_DECL(smartlist_t *, list_authority_ids_with_downloads, (void));
+MOCK_DECL(download_status_t *, id_only_download_status_for_authority_id,
+ (const char *digest));
+MOCK_DECL(smartlist_t *, list_sk_digests_for_authority_id,
+ (const char *digest));
+MOCK_DECL(download_status_t *, download_status_for_authority_id_and_sk,
+ (const char *id_digest, const char *sk_digest));
+
static int WRA_WAS_ADDED(was_router_added_t s);
static int WRA_WAS_OUTDATED(was_router_added_t s);
static int WRA_WAS_REJECTED(was_router_added_t s);
@@ -195,6 +206,9 @@ int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri,
extrainfo_t *ei,
signed_descriptor_t *sd,
const char **msg);
+int routerinfo_has_curve25519_onion_key(const routerinfo_t *ri);
+int routerstatus_version_supports_extend2_cells(const routerstatus_t *rs,
+ int allow_unknown_versions);
void routerlist_assert_ok(const routerlist_t *rl);
const char *esc_router_info(const routerinfo_t *router);
@@ -217,17 +231,11 @@ int hex_digest_nickname_matches(const char *hexdigest,
const char *nickname, int is_named);
#ifdef ROUTERLIST_PRIVATE
-/** Helper type for choosing routers by bandwidth: contains a union of
- * double and uint64_t. Before we call scale_array_elements_to_u64, it holds
- * a double; after, it holds a uint64_t. */
-typedef union u64_dbl_t {
- uint64_t u64;
- double dbl;
-} u64_dbl_t;
-
-STATIC int choose_array_element_by_weight(const u64_dbl_t *entries,
+STATIC int choose_array_element_by_weight(const uint64_t *entries,
int n_entries);
-STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
+STATIC void scale_array_elements_to_u64(uint64_t *entries_out,
+ const double *entries_in,
+ int n_entries,
uint64_t *total_out);
STATIC const routerstatus_t *router_pick_directory_server_impl(
dirinfo_type_t auth, int flags,
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index b6a90431a7..521e237be2 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -6,7 +6,51 @@
/**
* \file routerparse.c
- * \brief Code to parse and validate router descriptors and directories.
+ * \brief Code to parse and validate router descriptors, consenus directories,
+ * and similar objects.
+ *
+ * The objects parsed by this module use a common text-based metaformat,
+ * documented in dir-spec.txt in torspec.git. This module is itself divided
+ * into two major kinds of function: code to handle the metaformat, and code
+ * to convert from particular instances of the metaformat into the
+ * objects that Tor uses.
+ *
+ * The generic parsing code works by calling a table-based tokenizer on the
+ * input string. Each token corresponds to a single line with a token, plus
+ * optional arguments on that line, plus an optional base-64 encoded object
+ * after that line. Each token has a definition in a table of token_rule_t
+ * entries that describes how many arguments it can take, whether it takes an
+ * object, how many times it may appear, whether it must appear first, and so
+ * on.
+ *
+ * The tokenizer function tokenize_string() converts its string input into a
+ * smartlist full of instances of directory_token_t, according to a provided
+ * table of token_rule_t.
+ *
+ * The generic parts of this module additionally include functions for
+ * finding the start and end of signed information inside a signed object, and
+ * computing the digest that will be signed.
+ *
+ * There are also functions for saving objects to disk that have caused
+ * parsing to fail.
+ *
+ * The specific parts of this module describe conversions between
+ * particular lists of directory_token_t and particular objects. The
+ * kinds of objects that can be parsed here are:
+ * <ul>
+ * <li>router descriptors (managed from routerlist.c)
+ * <li>extra-info documents (managed from routerlist.c)
+ * <li>microdescriptors (managed from microdesc.c)
+ * <li>vote and consensus networkstatus documents, and the routerstatus_t
+ * objects that they comprise (managed from networkstatus.c)
+ * <li>detached-signature objects used by authorities for gathering
+ * signatures on the networkstatus consensus (managed from dirvote.c)
+ * <li>authority key certificates (managed from routerlist.c)
+ * <li>hidden service descriptors (managed from rendcommon.c and rendcache.c)
+ * </ul>
+ *
+ * For no terribly good reason, the functions to <i>generate</i> signatures on
+ * the above directory objects are also in this module.
**/
#define ROUTERPARSE_PRIVATE
@@ -17,6 +61,7 @@
#include "dirserv.h"
#include "dirvote.h"
#include "policies.h"
+#include "protover.h"
#include "rendcommon.h"
#include "router.h"
#include "routerlist.h"
@@ -28,6 +73,8 @@
#include "routerparse.h"
#include "entrynodes.h"
#include "torcert.h"
+#include "sandbox.h"
+#include "shared_random.h"
#undef log
#include <math.h>
@@ -56,6 +103,7 @@ typedef enum {
K_RUNNING_ROUTERS,
K_ROUTER_STATUS,
K_PLATFORM,
+ K_PROTO,
K_OPT,
K_BANDWIDTH,
K_CONTACT,
@@ -72,6 +120,10 @@ typedef enum {
K_DIR_OPTIONS,
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
+ K_RECOMMENDED_CLIENT_PROTOCOLS,
+ K_RECOMMENDED_RELAY_PROTOCOLS,
+ K_REQUIRED_CLIENT_PROTOCOLS,
+ K_REQUIRED_RELAY_PROTOCOLS,
K_OR_ADDRESS,
K_ID,
K_P,
@@ -145,6 +197,11 @@ typedef enum {
K_CONSENSUS_METHOD,
K_LEGACY_DIR_KEY,
K_DIRECTORY_FOOTER,
+ K_SIGNING_CERT_ED,
+ K_SR_FLAG,
+ K_COMMIT,
+ K_PREVIOUS_SRV,
+ K_CURRENT_SRV,
K_PACKAGE,
A_PURPOSE,
@@ -245,12 +302,14 @@ typedef struct token_rule_t {
int is_annotation;
} token_rule_t;
-/*
+/**
+ * @name macros for defining token rules
+ *
* Helper macros to define token tables. 's' is a string, 't' is a
* directory_keyword, 'a' is a trio of argument multiplicities, and 'o' is an
* object syntax.
- *
*/
+/**@{*/
/** Appears to indicate the end of a table. */
#define END_OF_TABLE { NULL, NIL_, 0,0,0, NO_OBJ, 0, INT_MAX, 0, 0 }
@@ -271,16 +330,17 @@ typedef struct token_rule_t {
/** An annotation that must appear no more than once */
#define A01(s,t,a,o) { s, t, a, o, 0, 1, 0, 1 }
-/* Argument multiplicity: any number of arguments. */
+/** Argument multiplicity: any number of arguments. */
#define ARGS 0,INT_MAX,0
-/* Argument multiplicity: no arguments. */
+/** Argument multiplicity: no arguments. */
#define NO_ARGS 0,0,0
-/* Argument multiplicity: concatenate all arguments. */
+/** Argument multiplicity: concatenate all arguments. */
#define CONCAT_ARGS 1,1,1
-/* Argument multiplicity: at least <b>n</b> arguments. */
+/** Argument multiplicity: at least <b>n</b> arguments. */
#define GE(n) n,INT_MAX,0
-/* Argument multiplicity: exactly <b>n</b> arguments. */
+/** Argument multiplicity: exactly <b>n</b> arguments. */
#define EQ(n) n,n,0
+/**@}*/
/** List of tokens recognized in router descriptors */
static token_rule_t routerdesc_token_table[] = {
@@ -299,6 +359,7 @@ static token_rule_t routerdesc_token_table[] = {
T01("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ ),
T01("platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ ),
+ T01("proto", K_PROTO, CONCAT_ARGS, NO_OBJ ),
T01("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ),
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
@@ -375,6 +436,7 @@ static token_rule_t rtrstatus_token_table[] = {
T01("w", K_W, ARGS, NO_OBJ ),
T0N("m", K_M, CONCAT_ARGS, NO_OBJ ),
T0N("id", K_ID, GE(2), NO_OBJ ),
+ T01("pr", K_PROTO, CONCAT_ARGS, NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
END_OF_TABLE
};
@@ -446,7 +508,20 @@ static token_rule_t networkstatus_token_table[] = {
T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
T01("params", K_PARAMS, ARGS, NO_OBJ ),
T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
+ T01("signing-ed25519", K_SIGNING_CERT_ED, NO_ARGS , NEED_OBJ ),
+ T01("shared-rand-participate",K_SR_FLAG, NO_ARGS, NO_OBJ ),
+ T0N("shared-rand-commit", K_COMMIT, GE(3), NO_OBJ ),
+ T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2), NO_OBJ ),
+ T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
+ T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
+ T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
+ T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
+ T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
CERTIFICATE_MEMBERS
@@ -485,6 +560,18 @@ static token_rule_t networkstatus_consensus_token_table[] = {
T01("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ),
T01("params", K_PARAMS, ARGS, NO_OBJ ),
+ T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2), NO_OBJ ),
+ T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
+
+ T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
+ T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
+ T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
+ T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
+ CONCAT_ARGS, NO_OBJ ),
+
END_OF_TABLE
};
@@ -585,32 +672,579 @@ static int check_signature_token(const char *digest,
#define DUMP_AREA(a,name) STMT_NIL
#endif
-/** Last time we dumped a descriptor to disk. */
-static time_t last_desc_dumped = 0;
+/* Dump mechanism for unparseable descriptors */
+
+/** List of dumped descriptors for FIFO cleanup purposes */
+STATIC smartlist_t *descs_dumped = NULL;
+/** Total size of dumped descriptors for FIFO cleanup */
+STATIC uint64_t len_descs_dumped = 0;
+/** Directory to stash dumps in */
+static int have_dump_desc_dir = 0;
+static int problem_with_dump_desc_dir = 0;
+
+#define DESC_DUMP_DATADIR_SUBDIR "unparseable-descs"
+#define DESC_DUMP_BASE_FILENAME "unparseable-desc"
+
+/** Find the dump directory and check if we'll be able to create it */
+static void
+dump_desc_init(void)
+{
+ char *dump_desc_dir;
+
+ dump_desc_dir = get_datadir_fname(DESC_DUMP_DATADIR_SUBDIR);
+
+ /*
+ * We just check for it, don't create it at this point; we'll
+ * create it when we need it if it isn't already there.
+ */
+ if (check_private_dir(dump_desc_dir, CPD_CHECK, get_options()->User) < 0) {
+ /* Error, log and flag it as having a problem */
+ log_notice(LD_DIR,
+ "Doesn't look like we'll be able to create descriptor dump "
+ "directory %s; dumps will be disabled.",
+ dump_desc_dir);
+ problem_with_dump_desc_dir = 1;
+ tor_free(dump_desc_dir);
+ return;
+ }
+
+ /* Check if it exists */
+ switch (file_status(dump_desc_dir)) {
+ case FN_DIR:
+ /* We already have a directory */
+ have_dump_desc_dir = 1;
+ break;
+ case FN_NOENT:
+ /* Nothing, we'll need to create it later */
+ have_dump_desc_dir = 0;
+ break;
+ case FN_ERROR:
+ /* Log and flag having a problem */
+ log_notice(LD_DIR,
+ "Couldn't check whether descriptor dump directory %s already"
+ " exists: %s",
+ dump_desc_dir, strerror(errno));
+ problem_with_dump_desc_dir = 1;
+ break;
+ case FN_FILE:
+ case FN_EMPTY:
+ default:
+ /* Something else was here! */
+ log_notice(LD_DIR,
+ "Descriptor dump directory %s already exists and isn't a "
+ "directory",
+ dump_desc_dir);
+ problem_with_dump_desc_dir = 1;
+ }
+
+ if (have_dump_desc_dir && !problem_with_dump_desc_dir) {
+ dump_desc_populate_fifo_from_directory(dump_desc_dir);
+ }
+
+ tor_free(dump_desc_dir);
+}
+
+/** Create the dump directory if needed and possible */
+static void
+dump_desc_create_dir(void)
+{
+ char *dump_desc_dir;
+
+ /* If the problem flag is set, skip it */
+ if (problem_with_dump_desc_dir) return;
+
+ /* Do we need it? */
+ if (!have_dump_desc_dir) {
+ dump_desc_dir = get_datadir_fname(DESC_DUMP_DATADIR_SUBDIR);
+
+ if (check_private_dir(dump_desc_dir, CPD_CREATE,
+ get_options()->User) < 0) {
+ log_notice(LD_DIR,
+ "Failed to create descriptor dump directory %s",
+ dump_desc_dir);
+ problem_with_dump_desc_dir = 1;
+ }
+
+ /* Okay, we created it */
+ have_dump_desc_dir = 1;
+
+ tor_free(dump_desc_dir);
+ }
+}
+
+/** Dump desc FIFO/cleanup; take ownership of the given filename, add it to
+ * the FIFO, and clean up the oldest entries to the extent they exceed the
+ * configured cap. If any old entries with a matching hash existed, they
+ * just got overwritten right before this was called and we should adjust
+ * the total size counter without deleting them.
+ */
+static void
+dump_desc_fifo_add_and_clean(char *filename, const uint8_t *digest_sha256,
+ size_t len)
+{
+ dumped_desc_t *ent = NULL, *tmp;
+ uint64_t max_len;
+
+ tor_assert(filename != NULL);
+ tor_assert(digest_sha256 != NULL);
+
+ if (descs_dumped == NULL) {
+ /* We better have no length, then */
+ tor_assert(len_descs_dumped == 0);
+ /* Make a smartlist */
+ descs_dumped = smartlist_new();
+ }
+
+ /* Make a new entry to put this one in */
+ ent = tor_malloc_zero(sizeof(*ent));
+ ent->filename = filename;
+ ent->len = len;
+ ent->when = time(NULL);
+ memcpy(ent->digest_sha256, digest_sha256, DIGEST256_LEN);
+
+ /* Do we need to do some cleanup? */
+ max_len = get_options()->MaxUnparseableDescSizeToLog;
+ /* Iterate over the list until we've freed enough space */
+ while (len > max_len - len_descs_dumped &&
+ smartlist_len(descs_dumped) > 0) {
+ /* Get the oldest thing on the list */
+ tmp = (dumped_desc_t *)(smartlist_get(descs_dumped, 0));
+
+ /*
+ * Check if it matches the filename we just added, so we don't delete
+ * something we just emitted if we get repeated identical descriptors.
+ */
+ if (strcmp(tmp->filename, filename) != 0) {
+ /* Delete it and adjust the length counter */
+ tor_unlink(tmp->filename);
+ tor_assert(len_descs_dumped >= tmp->len);
+ len_descs_dumped -= tmp->len;
+ log_info(LD_DIR,
+ "Deleting old unparseable descriptor dump %s due to "
+ "space limits",
+ tmp->filename);
+ } else {
+ /*
+ * Don't delete, but do adjust the counter since we will bump it
+ * later
+ */
+ tor_assert(len_descs_dumped >= tmp->len);
+ len_descs_dumped -= tmp->len;
+ log_info(LD_DIR,
+ "Replacing old descriptor dump %s with new identical one",
+ tmp->filename);
+ }
+
+ /* Free it and remove it from the list */
+ smartlist_del_keeporder(descs_dumped, 0);
+ tor_free(tmp->filename);
+ tor_free(tmp);
+ }
+
+ /* Append our entry to the end of the list and bump the counter */
+ smartlist_add(descs_dumped, ent);
+ len_descs_dumped += len;
+}
+
+/** Check if we already have a descriptor for this hash and move it to the
+ * head of the queue if so. Return 1 if one existed and 0 otherwise.
+ */
+static int
+dump_desc_fifo_bump_hash(const uint8_t *digest_sha256)
+{
+ dumped_desc_t *match = NULL;
+
+ tor_assert(digest_sha256);
+
+ if (descs_dumped) {
+ /* Find a match if one exists */
+ SMARTLIST_FOREACH_BEGIN(descs_dumped, dumped_desc_t *, ent) {
+ if (ent &&
+ tor_memeq(ent->digest_sha256, digest_sha256, DIGEST256_LEN)) {
+ /*
+ * Save a pointer to the match and remove it from its current
+ * position.
+ */
+ match = ent;
+ SMARTLIST_DEL_CURRENT_KEEPORDER(descs_dumped, ent);
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ent);
+
+ if (match) {
+ /* Update the timestamp */
+ match->when = time(NULL);
+ /* Add it back at the end of the list */
+ smartlist_add(descs_dumped, match);
+
+ /* Indicate we found one */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/** Clean up on exit; just memory, leave the dumps behind
+ */
+STATIC void
+dump_desc_fifo_cleanup(void)
+{
+ if (descs_dumped) {
+ /* Free each descriptor */
+ SMARTLIST_FOREACH_BEGIN(descs_dumped, dumped_desc_t *, ent) {
+ tor_assert(ent);
+ tor_free(ent->filename);
+ tor_free(ent);
+ } SMARTLIST_FOREACH_END(ent);
+ /* Free the list */
+ smartlist_free(descs_dumped);
+ descs_dumped = NULL;
+ len_descs_dumped = 0;
+ }
+}
+
+/** Handle one file for dump_desc_populate_fifo_from_directory(); make sure
+ * the filename is sensibly formed and matches the file content, and either
+ * return a dumped_desc_t for it or remove the file and return NULL.
+ */
+MOCK_IMPL(STATIC dumped_desc_t *,
+dump_desc_populate_one_file, (const char *dirname, const char *f))
+{
+ dumped_desc_t *ent = NULL;
+ char *path = NULL, *desc = NULL;
+ const char *digest_str;
+ char digest[DIGEST256_LEN], content_digest[DIGEST256_LEN];
+ /* Expected prefix before digest in filenames */
+ const char *f_pfx = DESC_DUMP_BASE_FILENAME ".";
+ /*
+ * Stat while reading; this is important in case the file
+ * contains a NUL character.
+ */
+ struct stat st;
+
+ /* Sanity-check args */
+ tor_assert(dirname != NULL);
+ tor_assert(f != NULL);
+
+ /* Form the full path */
+ tor_asprintf(&path, "%s" PATH_SEPARATOR "%s", dirname, f);
+
+ /* Check that f has the form DESC_DUMP_BASE_FILENAME.<digest256> */
+
+ if (!strcmpstart(f, f_pfx)) {
+ /* It matches the form, but is the digest parseable as such? */
+ digest_str = f + strlen(f_pfx);
+ if (base16_decode(digest, DIGEST256_LEN,
+ digest_str, strlen(digest_str)) != DIGEST256_LEN) {
+ /* We failed to decode it */
+ digest_str = NULL;
+ }
+ } else {
+ /* No match */
+ digest_str = NULL;
+ }
+
+ if (!digest_str) {
+ /* We couldn't get a sensible digest */
+ log_notice(LD_DIR,
+ "Removing unrecognized filename %s from unparseable "
+ "descriptors directory", f);
+ tor_unlink(path);
+ /* We're done */
+ goto done;
+ }
+
+ /*
+ * The filename has the form DESC_DUMP_BASE_FILENAME "." <digest256> and
+ * we've decoded the digest. Next, check that we can read it and the
+ * content matches this digest. We are relying on the fact that if the
+ * file contains a '\0', read_file_to_str() will allocate space for and
+ * read the entire file and return the correct size in st.
+ */
+ desc = read_file_to_str(path, RFTS_IGNORE_MISSING|RFTS_BIN, &st);
+ if (!desc) {
+ /* We couldn't read it */
+ log_notice(LD_DIR,
+ "Failed to read %s from unparseable descriptors directory; "
+ "attempting to remove it.", f);
+ tor_unlink(path);
+ /* We're done */
+ goto done;
+ }
+
+#if SIZE_MAX > UINT64_MAX
+ if (BUG((uint64_t)st.st_size > (uint64_t)SIZE_MAX)) {
+ /* LCOV_EXCL_START
+ * Should be impossible since RFTS above should have failed to read the
+ * huge file into RAM. */
+ goto done;
+ /* LCOV_EXCL_STOP */
+ }
+#endif
+ if (BUG(st.st_size < 0)) {
+ /* LCOV_EXCL_START
+ * Should be impossible, since the OS isn't supposed to be b0rken. */
+ goto done;
+ /* LCOV_EXCL_STOP */
+ }
+ /* (Now we can be sure that st.st_size is safe to cast to a size_t.) */
+
+ /*
+ * We got one; now compute its digest and check that it matches the
+ * filename.
+ */
+ if (crypto_digest256((char *)content_digest, desc, (size_t) st.st_size,
+ DIGEST_SHA256) != 0) {
+ /* Weird, but okay */
+ log_info(LD_DIR,
+ "Unable to hash content of %s from unparseable descriptors "
+ "directory", f);
+ tor_unlink(path);
+ /* We're done */
+ goto done;
+ }
+
+ /* Compare the digests */
+ if (tor_memneq(digest, content_digest, DIGEST256_LEN)) {
+ /* No match */
+ log_info(LD_DIR,
+ "Hash of %s from unparseable descriptors directory didn't "
+ "match its filename; removing it", f);
+ tor_unlink(path);
+ /* We're done */
+ goto done;
+ }
+
+ /* Okay, it's a match, we should prepare ent */
+ ent = tor_malloc_zero(sizeof(dumped_desc_t));
+ ent->filename = path;
+ memcpy(ent->digest_sha256, digest, DIGEST256_LEN);
+ ent->len = (size_t) st.st_size;
+ ent->when = st.st_mtime;
+ /* Null out path so we don't free it out from under ent */
+ path = NULL;
+
+ done:
+ /* Free allocations if we had them */
+ tor_free(desc);
+ tor_free(path);
+
+ return ent;
+}
+
+/** Sort helper for dump_desc_populate_fifo_from_directory(); compares
+ * the when field of dumped_desc_ts in a smartlist to put the FIFO in
+ * the correct order after reconstructing it from the directory.
+ */
+static int
+dump_desc_compare_fifo_entries(const void **a_v, const void **b_v)
+{
+ const dumped_desc_t **a = (const dumped_desc_t **)a_v;
+ const dumped_desc_t **b = (const dumped_desc_t **)b_v;
+
+ if ((a != NULL) && (*a != NULL)) {
+ if ((b != NULL) && (*b != NULL)) {
+ /* We have sensible dumped_desc_ts to compare */
+ if ((*a)->when < (*b)->when) {
+ return -1;
+ } else if ((*a)->when == (*b)->when) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ /*
+ * We shouldn't see this, but what the hell, NULLs precede everythin
+ * else
+ */
+ return 1;
+ }
+ } else {
+ return -1;
+ }
+}
+
+/** Scan the contents of the directory, and update FIFO/counters; this will
+ * consistency-check descriptor dump filenames against hashes of descriptor
+ * dump file content, and remove any inconsistent/unreadable dumps, and then
+ * reconstruct the dump FIFO as closely as possible for the last time the
+ * tor process shut down. If a previous dump was repeated more than once and
+ * moved ahead in the FIFO, the mtime will not have been updated and the
+ * reconstructed order will be wrong, but will always be a permutation of
+ * the original.
+ */
+STATIC void
+dump_desc_populate_fifo_from_directory(const char *dirname)
+{
+ smartlist_t *files = NULL;
+ dumped_desc_t *ent = NULL;
+
+ tor_assert(dirname != NULL);
+
+ /* Get a list of files */
+ files = tor_listdir(dirname);
+ if (!files) {
+ log_notice(LD_DIR,
+ "Unable to get contents of unparseable descriptor dump "
+ "directory %s",
+ dirname);
+ return;
+ }
+
+ /*
+ * Iterate through the list and decide which files should go in the
+ * FIFO and which should be purged.
+ */
+
+ SMARTLIST_FOREACH_BEGIN(files, char *, f) {
+ /* Try to get a FIFO entry */
+ ent = dump_desc_populate_one_file(dirname, f);
+ if (ent) {
+ /*
+ * We got one; add it to the FIFO. No need for duplicate checking
+ * here since we just verified the name and digest match.
+ */
+
+ /* Make sure we have a list to add it to */
+ if (!descs_dumped) {
+ descs_dumped = smartlist_new();
+ len_descs_dumped = 0;
+ }
+
+ /* Add it and adjust the counter */
+ smartlist_add(descs_dumped, ent);
+ len_descs_dumped += ent->len;
+ }
+ /*
+ * If we didn't, we will have unlinked the file if necessary and
+ * possible, and emitted a log message about it, so just go on to
+ * the next.
+ */
+ } SMARTLIST_FOREACH_END(f);
+
+ /* Did we get anything? */
+ if (descs_dumped != NULL) {
+ /* Sort the FIFO in order of increasing timestamp */
+ smartlist_sort(descs_dumped, dump_desc_compare_fifo_entries);
+
+ /* Log some stats */
+ log_info(LD_DIR,
+ "Reloaded unparseable descriptor dump FIFO with %d dump(s) "
+ "totaling " U64_FORMAT " bytes",
+ smartlist_len(descs_dumped), U64_PRINTF_ARG(len_descs_dumped));
+ }
+
+ /* Free the original list */
+ SMARTLIST_FOREACH(files, char *, f, tor_free(f));
+ smartlist_free(files);
+}
/** For debugging purposes, dump unparseable descriptor *<b>desc</b> of
* type *<b>type</b> to file $DATADIR/unparseable-desc. Do not write more
* than one descriptor to disk per minute. If there is already such a
* file in the data directory, overwrite it. */
-static void
+STATIC void
dump_desc(const char *desc, const char *type)
{
- time_t now = time(NULL);
tor_assert(desc);
tor_assert(type);
- if (!last_desc_dumped || last_desc_dumped + 60 < now) {
- char *debugfile = get_datadir_fname("unparseable-desc");
- size_t filelen = 50 + strlen(type) + strlen(desc);
- char *content = tor_malloc_zero(filelen);
- tor_snprintf(content, filelen, "Unable to parse descriptor of type "
- "%s:\n%s", type, desc);
- write_str_to_file(debugfile, content, 1);
- log_info(LD_DIR, "Unable to parse descriptor of type %s. See file "
- "unparseable-desc in data directory for details.", type);
- tor_free(content);
- tor_free(debugfile);
- last_desc_dumped = now;
+ size_t len;
+ /* The SHA256 of the string */
+ uint8_t digest_sha256[DIGEST256_LEN];
+ char digest_sha256_hex[HEX_DIGEST256_LEN+1];
+ /* Filename to log it to */
+ char *debugfile, *debugfile_base;
+
+ /* Get the hash for logging purposes anyway */
+ len = strlen(desc);
+ if (crypto_digest256((char *)digest_sha256, desc, len,
+ DIGEST_SHA256) != 0) {
+ log_info(LD_DIR,
+ "Unable to parse descriptor of type %s, and unable to even hash"
+ " it!", type);
+ goto err;
+ }
+
+ base16_encode(digest_sha256_hex, sizeof(digest_sha256_hex),
+ (const char *)digest_sha256, sizeof(digest_sha256));
+
+ /*
+ * We mention type and hash in the main log; don't clutter up the files
+ * with anything but the exact dump.
+ */
+ tor_asprintf(&debugfile_base,
+ DESC_DUMP_BASE_FILENAME ".%s", digest_sha256_hex);
+ debugfile = get_datadir_fname2(DESC_DUMP_DATADIR_SUBDIR, debugfile_base);
+
+ /*
+ * Check if the sandbox is active or will become active; see comment
+ * below at the log message for why.
+ */
+ if (!(sandbox_is_active() || get_options()->Sandbox)) {
+ if (len <= get_options()->MaxUnparseableDescSizeToLog) {
+ if (!dump_desc_fifo_bump_hash(digest_sha256)) {
+ /* Create the directory if needed */
+ dump_desc_create_dir();
+ /* Make sure we've got it */
+ if (have_dump_desc_dir && !problem_with_dump_desc_dir) {
+ /* Write it, and tell the main log about it */
+ write_str_to_file(debugfile, desc, 1);
+ log_info(LD_DIR,
+ "Unable to parse descriptor of type %s with hash %s and "
+ "length %lu. See file %s in data directory for details.",
+ type, digest_sha256_hex, (unsigned long)len,
+ debugfile_base);
+ dump_desc_fifo_add_and_clean(debugfile, digest_sha256, len);
+ /* Since we handed ownership over, don't free debugfile later */
+ debugfile = NULL;
+ } else {
+ /* Problem with the subdirectory */
+ log_info(LD_DIR,
+ "Unable to parse descriptor of type %s with hash %s and "
+ "length %lu. Descriptor not dumped because we had a "
+ "problem creating the " DESC_DUMP_DATADIR_SUBDIR
+ " subdirectory",
+ type, digest_sha256_hex, (unsigned long)len);
+ /* We do have to free debugfile in this case */
+ }
+ } else {
+ /* We already had one with this hash dumped */
+ log_info(LD_DIR,
+ "Unable to parse descriptor of type %s with hash %s and "
+ "length %lu. Descriptor not dumped because one with that "
+ "hash has already been dumped.",
+ type, digest_sha256_hex, (unsigned long)len);
+ /* We do have to free debugfile in this case */
+ }
+ } else {
+ /* Just log that it happened without dumping */
+ log_info(LD_DIR,
+ "Unable to parse descriptor of type %s with hash %s and "
+ "length %lu. Descriptor not dumped because it exceeds maximum"
+ " log size all by itself.",
+ type, digest_sha256_hex, (unsigned long)len);
+ /* We do have to free debugfile in this case */
+ }
+ } else {
+ /*
+ * Not logging because the sandbox is active and seccomp2 apparently
+ * doesn't have a sensible way to allow filenames according to a pattern
+ * match. (If we ever figure out how to say "allow writes to /regex/",
+ * remove this checK).
+ */
+ log_info(LD_DIR,
+ "Unable to parse descriptor of type %s with hash %s and "
+ "length %lu. Descriptor not dumped because the sandbox is "
+ "configured",
+ type, digest_sha256_hex, (unsigned long)len);
}
+
+ tor_free(debugfile_base);
+ tor_free(debugfile);
+
+ err:
+ return;
}
/** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
@@ -1217,11 +1851,11 @@ router_parse_entry_from_string(const char *s, const char *end,
if (cache_copy) {
size_t len = router->cache_info.signed_descriptor_len +
router->cache_info.annotations_len;
- char *cp =
+ char *signed_body =
router->cache_info.signed_descriptor_body = tor_malloc(len+1);
if (prepend_annotations) {
- memcpy(cp, prepend_annotations, prepend_len);
- cp += prepend_len;
+ memcpy(signed_body, prepend_annotations, prepend_len);
+ signed_body += prepend_len;
}
/* This assertion will always succeed.
* len == signed_desc_len + annotations_len
@@ -1229,9 +1863,9 @@ router_parse_entry_from_string(const char *s, const char *end,
* == end-start_of_annotations + prepend_len
* We already wrote prepend_len bytes into the buffer; now we're
* writing end-start_of_annotations -NM. */
- tor_assert(cp+(end-start_of_annotations) ==
+ tor_assert(signed_body+(end-start_of_annotations) ==
router->cache_info.signed_descriptor_body+len);
- memcpy(cp, start_of_annotations, end-start_of_annotations);
+ memcpy(signed_body, start_of_annotations, end-start_of_annotations);
router->cache_info.signed_descriptor_body[len] = '\0';
tor_assert(strlen(router->cache_info.signed_descriptor_body) == len);
}
@@ -1513,7 +2147,8 @@ router_parse_entry_from_string(const char *s, const char *end,
char d[DIGEST_LEN];
tor_assert(tok->n_args == 1);
tor_strstrip(tok->args[0], " ");
- if (base16_decode(d, DIGEST_LEN, tok->args[0], strlen(tok->args[0]))) {
+ if (base16_decode(d, DIGEST_LEN,
+ tok->args[0], strlen(tok->args[0])) != DIGEST_LEN) {
log_warn(LD_DIR, "Couldn't decode router fingerprint %s",
escaped(tok->args[0]));
goto err;
@@ -1529,6 +2164,10 @@ router_parse_entry_from_string(const char *s, const char *end,
router->platform = tor_strdup(tok->args[0]);
}
+ if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
+ router->protocol_list = tor_strdup(tok->args[0]);
+ }
+
if ((tok = find_opt_by_keyword(tokens, K_CONTACT))) {
router->contact_info = tor_strdup(tok->args[0]);
}
@@ -1567,7 +2206,7 @@ router_parse_entry_from_string(const char *s, const char *end,
}
}
- if (policy_is_reject_star(router->exit_policy, AF_INET) &&
+ if (policy_is_reject_star(router->exit_policy, AF_INET, 1) &&
(!router->ipv6_exit_policy ||
short_policy_is_reject_star(router->ipv6_exit_policy)))
router->policy_is_reject_star = 1;
@@ -1594,8 +2233,10 @@ router_parse_entry_from_string(const char *s, const char *end,
if ((tok = find_opt_by_keyword(tokens, K_EXTRA_INFO_DIGEST))) {
tor_assert(tok->n_args >= 1);
if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
- base16_decode(router->cache_info.extra_info_digest,
- DIGEST_LEN, tok->args[0], HEX_DIGEST_LEN);
+ if (base16_decode(router->cache_info.extra_info_digest, DIGEST_LEN,
+ tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN) {
+ log_warn(LD_DIR,"Invalid extra info digest");
+ }
} else {
log_warn(LD_DIR, "Invalid extra info digest %s", escaped(tok->args[0]));
}
@@ -1738,7 +2379,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
strlcpy(extrainfo->nickname, tok->args[0], sizeof(extrainfo->nickname));
if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
base16_decode(extrainfo->cache_info.identity_digest, DIGEST_LEN,
- tok->args[1], HEX_DIGEST_LEN)) {
+ tok->args[1], HEX_DIGEST_LEN) != DIGEST_LEN) {
log_warn(LD_DIR,"Invalid fingerprint %s on \"extra-info\"",
escaped(tok->args[1]));
goto err;
@@ -1960,7 +2601,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
tok = find_by_keyword(tokens, K_FINGERPRINT);
tor_assert(tok->n_args);
if (base16_decode(fp_declared, DIGEST_LEN, tok->args[0],
- strlen(tok->args[0]))) {
+ strlen(tok->args[0])) != DIGEST_LEN) {
log_warn(LD_DIR, "Couldn't decode key certificate fingerprint %s",
escaped(tok->args[0]));
goto err;
@@ -1981,7 +2622,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string)
struct in_addr in;
char *address = NULL;
tor_assert(tok->n_args);
- /* XXX024 use some tor_addr parse function below instead. -RD */
+ /* XXX++ use some tor_addr parse function below instead. -RD */
if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
&cert->dir_port) < 0 ||
tor_inet_aton(address, &in) == 0) {
@@ -2166,7 +2807,7 @@ routerstatus_parse_guardfraction(const char *guardfraction_str,
*
* Parse according to the syntax used by the consensus flavor <b>flav</b>.
**/
-static routerstatus_t *
+STATIC routerstatus_t *
routerstatus_parse_entry_from_string(memarea_t *area,
const char **s, smartlist_t *tokens,
networkstatus_t *vote,
@@ -2280,6 +2921,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
}
}
} else if (tok) {
+ /* This is a consensus, not a vote. */
int i;
for (i=0; i < tok->n_args; ++i) {
if (!strcmp(tok->args[i], "Exit"))
@@ -2310,14 +2952,28 @@ routerstatus_parse_entry_from_string(memarea_t *area,
rs->is_v2_dir = 1;
}
}
+ /* These are implied true by having been included in a consensus made
+ * with a given method */
+ rs->is_flagged_running = 1; /* Starting with consensus method 4. */
+ if (consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES)
+ rs->is_valid = 1;
+ }
+ int found_protocol_list = 0;
+ if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
+ found_protocol_list = 1;
+ rs->protocols_known = 1;
+ rs->supports_extend2_cells =
+ protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2);
}
if ((tok = find_opt_by_keyword(tokens, K_V))) {
tor_assert(tok->n_args == 1);
- rs->version_known = 1;
- if (strcmpstart(tok->args[0], "Tor ")) {
- } else {
- rs->version_supports_extend2_cells =
+ if (!strcmpstart(tok->args[0], "Tor ") && !found_protocol_list) {
+ /* We only do version checks like this in the case where
+ * the version is a "Tor" version, and where there is no
+ * list of protocol versions that we should be looking at instead. */
+ rs->supports_extend2_cells =
tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha");
+ rs->protocols_known = 1;
}
if (vote_rs) {
vote_rs->version = tor_strdup(tok->args[0]);
@@ -2400,6 +3056,10 @@ routerstatus_parse_entry_from_string(memarea_t *area,
}
}
}
+ if (t->tp == K_PROTO) {
+ tor_assert(t->n_args == 1);
+ vote_rs->protocols = tor_strdup(t->args[0]);
+ }
} SMARTLIST_FOREACH_END(t);
} else if (flav == FLAV_MICRODESC) {
tok = find_opt_by_keyword(tokens, K_M);
@@ -2840,6 +3500,134 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
return valid;
}
+/** Parse and extract all SR commits from <b>tokens</b> and place them in
+ * <b>ns</b>. */
+static void
+extract_shared_random_commits(networkstatus_t *ns, smartlist_t *tokens)
+{
+ smartlist_t *chunks = NULL;
+
+ tor_assert(ns);
+ tor_assert(tokens);
+ /* Commits are only present in a vote. */
+ tor_assert(ns->type == NS_TYPE_VOTE);
+
+ ns->sr_info.commits = smartlist_new();
+
+ smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT);
+ /* It's normal that a vote might contain no commits even if it participates
+ * in the SR protocol. Don't treat it as an error. */
+ if (commits == NULL) {
+ goto end;
+ }
+
+ /* Parse the commit. We do NO validation of number of arguments or ordering
+ * for forward compatibility, it's the parse commit job to inform us if it's
+ * supported or not. */
+ chunks = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) {
+ /* Extract all arguments and put them in the chunks list. */
+ for (int i = 0; i < tok->n_args; i++) {
+ smartlist_add(chunks, tok->args[i]);
+ }
+ sr_commit_t *commit = sr_parse_commit(chunks);
+ smartlist_clear(chunks);
+ if (commit == NULL) {
+ /* Get voter identity so we can warn that this dirauth vote contains
+ * commit we can't parse. */
+ networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
+ tor_assert(voter);
+ log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.",
+ escaped(tok->object_body),
+ hex_str(voter->identity_digest,
+ sizeof(voter->identity_digest)));
+ /* Commitment couldn't be parsed. Continue onto the next commit because
+ * this one could be unsupported for instance. */
+ continue;
+ }
+ /* Add newly created commit object to the vote. */
+ smartlist_add(ns->sr_info.commits, commit);
+ } SMARTLIST_FOREACH_END(tok);
+
+ end:
+ smartlist_free(chunks);
+ smartlist_free(commits);
+}
+
+/** Check if a shared random value of type <b>srv_type</b> is in
+ * <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
+ * -1 on failure, 0 on success. The resulting srv is allocated on the heap and
+ * it's the responsibility of the caller to free it. */
+static int
+extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
+ sr_srv_t **srv_out)
+{
+ int ret = -1;
+ directory_token_t *tok;
+ sr_srv_t *srv = NULL;
+ smartlist_t *chunks;
+
+ tor_assert(tokens);
+
+ chunks = smartlist_new();
+ tok = find_opt_by_keyword(tokens, srv_type);
+ if (!tok) {
+ /* That's fine, no SRV is allowed. */
+ ret = 0;
+ goto end;
+ }
+ for (int i = 0; i < tok->n_args; i++) {
+ smartlist_add(chunks, tok->args[i]);
+ }
+ srv = sr_parse_srv(chunks);
+ if (srv == NULL) {
+ log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
+ goto end;
+ }
+ /* All is good. */
+ *srv_out = srv;
+ ret = 0;
+ end:
+ smartlist_free(chunks);
+ return ret;
+}
+
+/** Extract any shared random values found in <b>tokens</b> and place them in
+ * the networkstatus <b>ns</b>. */
+static void
+extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
+{
+ const char *voter_identity;
+ networkstatus_voter_info_t *voter;
+
+ tor_assert(ns);
+ tor_assert(tokens);
+ /* Can be only one of them else code flow. */
+ tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
+
+ if (ns->type == NS_TYPE_VOTE) {
+ voter = smartlist_get(ns->voters, 0);
+ tor_assert(voter);
+ voter_identity = hex_str(voter->identity_digest,
+ sizeof(voter->identity_digest));
+ } else {
+ /* Consensus has multiple voters so no specific voter. */
+ 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). */
+ 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);
+ /* Maybe we have a chance with the current SRV so let's try it anyway. */
+ }
+ if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
+ log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
+ voter_identity);
+ }
+}
+
/** Parse a v3 networkstatus vote, opinion, or consensus (depending on
* ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
networkstatus_t *
@@ -2853,7 +3641,6 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
common_digests_t ns_digests;
const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
directory_token_t *tok;
- int ok;
struct in_addr in;
int i, inorder, n_signatures = 0;
memarea_t *area = NULL, *rs_area = NULL;
@@ -2943,15 +3730,25 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
} else {
tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHOD);
if (tok) {
+ int num_ok;
ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX,
- &ok, NULL);
- if (!ok)
+ &num_ok, NULL);
+ if (!num_ok)
goto err;
} else {
ns->consensus_method = 1;
}
}
+ if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_CLIENT_PROTOCOLS)))
+ ns->recommended_client_protocols = tor_strdup(tok->args[0]);
+ if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_RELAY_PROTOCOLS)))
+ ns->recommended_relay_protocols = tor_strdup(tok->args[0]);
+ if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_CLIENT_PROTOCOLS)))
+ ns->required_client_protocols = tor_strdup(tok->args[0]);
+ if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_RELAY_PROTOCOLS)))
+ ns->required_relay_protocols = tor_strdup(tok->args[0]);
+
tok = find_by_keyword(tokens, K_VALID_AFTER);
if (parse_iso_time(tok->args[0], &ns->valid_after))
goto err;
@@ -2966,14 +3763,17 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
tok = find_by_keyword(tokens, K_VOTING_DELAY);
tor_assert(tok->n_args >= 2);
- ns->vote_seconds =
- (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
- if (!ok)
- goto err;
- ns->dist_seconds =
- (int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
- if (!ok)
- goto err;
+ {
+ int ok;
+ ns->vote_seconds =
+ (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
+ if (!ok)
+ goto err;
+ ns->dist_seconds =
+ (int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
+ if (!ok)
+ goto err;
+ }
if (ns->valid_after +
(get_options()->TestingTorNetwork ?
MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL) > ns->fresh_until) {
@@ -3097,7 +3897,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
voter->nickname = tor_strdup(tok->args[0]);
if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
base16_decode(voter->identity_digest, sizeof(voter->identity_digest),
- tok->args[1], HEX_DIGEST_LEN) < 0) {
+ tok->args[1], HEX_DIGEST_LEN)
+ != sizeof(voter->identity_digest)) {
log_warn(LD_DIR, "Error decoding identity digest %s in "
"network-status document.", escaped(tok->args[1]));
goto err;
@@ -3123,6 +3924,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
goto err;
}
voter->addr = ntohl(in.s_addr);
+ int ok;
voter->dir_port = (uint16_t)
tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
if (!ok)
@@ -3146,7 +3948,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
base16_decode(voter->vote_digest, sizeof(voter->vote_digest),
- tok->args[0], HEX_DIGEST_LEN) < 0) {
+ tok->args[0], HEX_DIGEST_LEN)
+ != sizeof(voter->vote_digest)) {
log_warn(LD_DIR, "Error decoding vote digest %s in "
"network-status consensus.", escaped(tok->args[0]));
goto err;
@@ -3169,9 +3972,9 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
(tok = find_opt_by_keyword(tokens, K_LEGACY_DIR_KEY))) {
int bad = 1;
if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
- networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
- if (base16_decode(voter->legacy_id_digest, DIGEST_LEN,
- tok->args[0], HEX_DIGEST_LEN)<0)
+ networkstatus_voter_info_t *voter_0 = smartlist_get(ns->voters, 0);
+ if (base16_decode(voter_0->legacy_id_digest, DIGEST_LEN,
+ tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN)
bad = 1;
else
bad = 0;
@@ -3182,6 +3985,22 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
}
+ /* If this is a vote document, check if information about the shared
+ randomness protocol is included, and extract it. */
+ if (ns->type == NS_TYPE_VOTE) {
+ /* Does this authority participates in the SR protocol? */
+ tok = find_opt_by_keyword(tokens, K_SR_FLAG);
+ if (tok) {
+ ns->sr_info.participate = 1;
+ /* Get the SR commitments and reveals from the vote. */
+ extract_shared_random_commits(ns, tokens);
+ }
+ }
+ /* For both a vote and consensus, extract the shared random values. */
+ if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
+ extract_shared_random_srvs(ns, tokens);
+ }
+
/* Parse routerstatus lines. */
rs_tokens = smartlist_new();
rs_area = memarea_new();
@@ -3203,8 +4022,11 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens,
NULL, NULL,
ns->consensus_method,
- flav)))
+ flav))) {
+ /* Use exponential-backoff scheduling when downloading microdescs */
+ rs->dl_status.backoff = DL_SCHED_RANDOM_EXPONENTIAL;
smartlist_add(ns->routerstatus_list, rs);
+ }
}
}
for (i = 1; i < smartlist_len(ns->routerstatus_list); ++i) {
@@ -3330,7 +4152,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
base16_decode(declared_identity, sizeof(declared_identity),
- id_hexdigest, HEX_DIGEST_LEN) < 0) {
+ id_hexdigest, HEX_DIGEST_LEN)
+ != sizeof(declared_identity)) {
log_warn(LD_DIR, "Error decoding declared identity %s in "
"network-status document.", escaped(id_hexdigest));
goto err;
@@ -3345,7 +4168,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
sig->alg = alg;
if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
- sk_hexdigest, HEX_DIGEST_LEN) < 0) {
+ sk_hexdigest, HEX_DIGEST_LEN)
+ != sizeof(sig->signing_key_digest)) {
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
"network-status document.", escaped(sk_hexdigest));
tor_free(sig);
@@ -3508,7 +4332,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
digest_algorithm_t alg;
const char *flavor;
const char *hexdigest;
- size_t expected_length;
+ size_t expected_length, digest_length;
tok = _tok;
@@ -3531,8 +4355,8 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
continue;
}
- expected_length =
- (alg == DIGEST_SHA1) ? HEX_DIGEST_LEN : HEX_DIGEST256_LEN;
+ digest_length = crypto_digest_algorithm_get_length(alg);
+ expected_length = digest_length * 2; /* hex encoding */
if (strlen(hexdigest) != expected_length) {
log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
@@ -3541,13 +4365,13 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
}
digests = detached_get_digests(sigs, flavor);
tor_assert(digests);
- if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
+ if (!tor_mem_is_zero(digests->d[alg], digest_length)) {
log_warn(LD_DIR, "Multiple digests for %s with %s on detached "
"signatures document", flavor, algname);
continue;
}
- if (base16_decode(digests->d[alg], DIGEST256_LEN,
- hexdigest, strlen(hexdigest)) < 0) {
+ if (base16_decode(digests->d[alg], digest_length,
+ hexdigest, strlen(hexdigest)) != (int) digest_length) {
log_warn(LD_DIR, "Bad encoding on consensus-digest in detached "
"networkstatus signatures");
goto err;
@@ -3620,14 +4444,14 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
base16_decode(id_digest, sizeof(id_digest),
- id_hexdigest, HEX_DIGEST_LEN) < 0) {
+ id_hexdigest, HEX_DIGEST_LEN) != sizeof(id_digest)) {
log_warn(LD_DIR, "Error decoding declared identity %s in "
"network-status vote.", escaped(id_hexdigest));
goto err;
}
if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
base16_decode(sk_digest, sizeof(sk_digest),
- sk_hexdigest, HEX_DIGEST_LEN) < 0) {
+ sk_hexdigest, HEX_DIGEST_LEN) != sizeof(sk_digest)) {
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
"network-status vote.", escaped(sk_hexdigest));
goto err;
@@ -4688,40 +5512,78 @@ microdescs_parse_from_string(const char *s, const char *eos,
return result;
}
-/** Parse the Tor version of the platform string <b>platform</b>,
- * and compare it to the version in <b>cutoff</b>. Return 1 if
- * the router is at least as new as the cutoff, else return 0.
+/** Extract a Tor version from a <b>platform</b> line from a router
+ * descriptor, and place the result in <b>router_version</b>.
+ *
+ * Return 1 on success, -1 on parsing failure, and 0 if the
+ * platform line does not indicate some version of Tor.
+ *
+ * If <b>strict</b> is non-zero, finding any weird version components
+ * (like negative numbers) counts as a parsing failure.
*/
int
-tor_version_as_new_as(const char *platform, const char *cutoff)
+tor_version_parse_platform(const char *platform,
+ tor_version_t *router_version,
+ int strict)
{
- tor_version_t cutoff_version, router_version;
- char *s, *s2, *start;
char tmp[128];
+ char *s, *s2, *start;
- tor_assert(platform);
-
- if (tor_version_parse(cutoff, &cutoff_version)<0) {
- log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
+ if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
return 0;
- }
- if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; be safe and say yes */
- return 1;
start = (char *)eat_whitespace(platform+3);
- if (!*start) return 0;
+ if (!*start) return -1;
s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
s2 = (char*)eat_whitespace(s);
if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
s = (char*)find_whitespace(s2);
if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
- return 0;
+ return -1;
strlcpy(tmp, start, s-start+1);
- if (tor_version_parse(tmp, &router_version)<0) {
+ if (tor_version_parse(tmp, router_version)<0) {
log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
- return 1; /* be safe and say yes */
+ return -1;
+ }
+
+ if (strict) {
+ if (router_version->major < 0 ||
+ router_version->minor < 0 ||
+ router_version->micro < 0 ||
+ router_version->patchlevel < 0 ||
+ router_version->svn_revision < 0) {
+ return -1;
+ }
+ }
+
+ return 1;
+}
+
+/** Parse the Tor version of the platform string <b>platform</b>,
+ * and compare it to the version in <b>cutoff</b>. Return 1 if
+ * the router is at least as new as the cutoff, else return 0.
+ */
+int
+tor_version_as_new_as(const char *platform, const char *cutoff)
+{
+ tor_version_t cutoff_version, router_version;
+ int r;
+ tor_assert(platform);
+
+ if (tor_version_parse(cutoff, &cutoff_version)<0) {
+ log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
+ return 0;
+ }
+
+ r = tor_version_parse_platform(platform, &router_version, 0);
+ if (r == 0) {
+ /* nonstandard Tor; be safe and say yes */
+ return 1;
+ } else if (r < 0) {
+ /* unparseable version; be safe and say yes. */
+ return 1;
}
/* Here's why we don't need to do any special handling for svn revisions:
@@ -4743,6 +5605,7 @@ tor_version_parse(const char *s, tor_version_t *out)
{
char *eos=NULL;
const char *cp=NULL;
+ int ok = 1;
/* Format is:
* "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
*/
@@ -4758,7 +5621,11 @@ tor_version_parse(const char *s, tor_version_t *out)
#define NUMBER(m) \
do { \
- out->m = (int)strtol(cp, &eos, 10); \
+ if (!cp || *cp < '0' || *cp > '9') \
+ return -1; \
+ out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \
+ if (!ok) \
+ return -1; \
if (!eos || eos == cp) \
return -1; \
cp = eos; \
@@ -4829,7 +5696,7 @@ tor_version_parse(const char *s, tor_version_t *out)
memwipe(digest, 0, sizeof(digest));
if ( hexlen == 0 || (hexlen % 2) == 1)
return -1;
- if (base16_decode(digest, hexlen/2, cp, hexlen))
+ if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
return -1;
memcpy(out->git_tag, digest, hexlen/2);
out->git_tag_len = hexlen/2;
@@ -4985,7 +5852,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
eos = eos + 1;
/* Check length. */
if (eos-desc > REND_DESC_MAX_SIZE) {
- /* XXX023 If we are parsing this descriptor as a server, this
+ /* XXXX+ If we are parsing this descriptor as a server, this
* should be a protocol warning. */
log_warn(LD_REND, "Descriptor length is %d which exceeds "
"maximum rendezvous descriptor size of %d bytes.",
@@ -5385,6 +6252,7 @@ rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
directory_token_t *tok;
const char *current_entry = NULL;
memarea_t *area = NULL;
+ char *err_msg = NULL;
if (!ckstr || strlen(ckstr) == 0)
return -1;
tokens = smartlist_new();
@@ -5394,8 +6262,6 @@ rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
current_entry = eat_whitespace(ckstr);
while (!strcmpstart(current_entry, "client-name ")) {
rend_authorized_client_t *parsed_entry;
- size_t len;
- char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
/* Determine end of string. */
const char *eos = strstr(current_entry, "\nclient-name ");
if (!eos)
@@ -5424,12 +6290,10 @@ rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
tor_assert(tok == smartlist_get(tokens, 0));
tor_assert(tok->n_args == 1);
- len = strlen(tok->args[0]);
- if (len < 1 || len > 19 ||
- strspn(tok->args[0], REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
+ if (!rend_valid_client_name(tok->args[0])) {
log_warn(LD_CONFIG, "Illegal client name: %s. (Length must be "
- "between 1 and 19, and valid characters are "
- "[A-Za-z0-9+-_].)", tok->args[0]);
+ "between 1 and %d, and valid characters are "
+ "[A-Za-z0-9+-_].)", tok->args[0], REND_CLIENTNAME_MAX_LEN);
goto err;
}
/* Check if client name is duplicate. */
@@ -5451,23 +6315,13 @@ rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
/* Parse descriptor cookie. */
tok = find_by_keyword(tokens, C_DESCRIPTOR_COOKIE);
tor_assert(tok->n_args == 1);
- if (strlen(tok->args[0]) != REND_DESC_COOKIE_LEN_BASE64 + 2) {
- log_warn(LD_REND, "Descriptor cookie has illegal length: %s",
- escaped(tok->args[0]));
- goto err;
- }
- /* The size of descriptor_cookie_tmp needs to be REND_DESC_COOKIE_LEN+2,
- * because a base64 encoding of length 24 does not fit into 16 bytes in all
- * cases. */
- if (base64_decode(descriptor_cookie_tmp, sizeof(descriptor_cookie_tmp),
- tok->args[0], strlen(tok->args[0]))
- != REND_DESC_COOKIE_LEN) {
- log_warn(LD_REND, "Descriptor cookie contains illegal characters: "
- "%s", escaped(tok->args[0]));
+ if (rend_auth_decode_cookie(tok->args[0], parsed_entry->descriptor_cookie,
+ NULL, &err_msg) < 0) {
+ tor_assert(err_msg);
+ log_warn(LD_REND, "%s", err_msg);
+ tor_free(err_msg);
goto err;
}
- memcpy(parsed_entry->descriptor_cookie, descriptor_cookie_tmp,
- REND_DESC_COOKIE_LEN);
}
result = strmap_size(parsed_clients);
goto done;
@@ -5482,3 +6336,27 @@ rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
return result;
}
+/** Called on startup; right now we just handle scanning the unparseable
+ * descriptor dumps, but hang anything else we might need to do in the
+ * future here as well.
+ */
+void
+routerparse_init(void)
+{
+ /*
+ * Check both if the sandbox is active and whether it's configured; no
+ * point in loading all that if we won't be able to use it after the
+ * sandbox becomes active.
+ */
+ if (!(sandbox_is_active() || get_options()->Sandbox)) {
+ dump_desc_init();
+ }
+}
+
+/** Clean up all data structures used by routerparse.c at exit */
+void
+routerparse_free_all(void)
+{
+ dump_desc_fifo_cleanup();
+}
+
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index c46eb1c0ae..01a5de88e8 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -45,6 +45,9 @@ MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
(const char *s, int assume_action, int *malformed_list));
version_status_t tor_version_is_obsolete(const char *myversion,
const char *versionlist);
+int tor_version_parse_platform(const char *platform,
+ tor_version_t *version_out,
+ int strict);
int tor_version_as_new_as(const char *platform, const char *cutoff);
int tor_version_parse(const char *s, tor_version_t *out);
int tor_version_compare(tor_version_t *a, tor_version_t *b);
@@ -85,11 +88,41 @@ int rend_parse_introduction_points(rend_service_descriptor_t *parsed,
size_t intro_points_encoded_size);
int rend_parse_client_keys(strmap_t *parsed_clients, const char *str);
+void routerparse_init(void);
+void routerparse_free_all(void);
+
#ifdef ROUTERPARSE_PRIVATE
+/*
+ * One entry in the list of dumped descriptors; filename dumped to, length,
+ * SHA-256 and timestamp.
+ */
+
+typedef struct {
+ char *filename;
+ size_t len;
+ uint8_t digest_sha256[DIGEST256_LEN];
+ time_t when;
+} dumped_desc_t;
+
+EXTERN(uint64_t, len_descs_dumped)
+EXTERN(smartlist_t *, descs_dumped)
STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str,
networkstatus_t *vote,
vote_routerstatus_t *vote_rs,
routerstatus_t *rs);
+MOCK_DECL(STATIC dumped_desc_t *, dump_desc_populate_one_file,
+ (const char *dirname, const char *f));
+STATIC void dump_desc_populate_fifo_from_directory(const char *dirname);
+STATIC void dump_desc(const char *desc, const char *type);
+STATIC void dump_desc_fifo_cleanup(void);
+struct memarea_t;
+STATIC routerstatus_t *routerstatus_parse_entry_from_string(
+ struct memarea_t *area,
+ const char **s, smartlist_t *tokens,
+ networkstatus_t *vote,
+ vote_routerstatus_t *vote_rs,
+ int consensus_method,
+ consensus_flavor_t flav);
#endif
#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1"
diff --git a/src/or/routerset.c b/src/or/routerset.c
index f260914f4b..58b66ea777 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -9,6 +9,20 @@
*
* \brief Functions and structures to handle set-type selection of routers
* by name, ID, address, etc.
+ *
+ * This module implements the routerset_t data structure, whose purpose
+ * is to specify a set of relays based on a list of their identities or
+ * properties. Routersets can restrict relays by IP address mask,
+ * identity fingerprint, country codes, and nicknames (deprecated).
+ *
+ * Routersets are typically used for user-specified restrictions, and
+ * are created by invoking routerset_new and routerset_parse from
+ * config.c and confparse.c. To use a routerset, invoke one of
+ * routerset_contains_...() functions , or use
+ * routerstatus_get_all_nodes() / routerstatus_subtract_nodes() to
+ * manipulate a smartlist of node_t pointers.
+ *
+ * Country-code restrictions are implemented in geoip.c.
*/
#define ROUTERSET_PRIVATE
diff --git a/src/or/scheduler.c b/src/or/scheduler.c
index 8e4810b199..49ac1b939a 100644
--- a/src/or/scheduler.c
+++ b/src/or/scheduler.c
@@ -15,11 +15,7 @@
#define SCHEDULER_PRIVATE_
#include "scheduler.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
/*
* Scheduler high/low watermarks
@@ -500,13 +496,13 @@ scheduler_run, (void))
/* Readd any channels we need to */
if (to_readd) {
- SMARTLIST_FOREACH_BEGIN(to_readd, channel_t *, chan) {
- chan->scheduler_state = SCHED_CHAN_PENDING;
+ 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),
- chan);
- } SMARTLIST_FOREACH_END(chan);
+ readd_chan);
+ } SMARTLIST_FOREACH_END(readd_chan);
smartlist_free(to_readd);
}
diff --git a/src/or/scheduler.h b/src/or/scheduler.h
index 94a44a0aa3..3dcfd2faca 100644
--- a/src/or/scheduler.h
+++ b/src/or/scheduler.h
@@ -44,6 +44,13 @@ MOCK_DECL(STATIC 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);
+
+#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
#endif /* !defined(TOR_SCHEDULER_H) */
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
new file mode 100644
index 0000000000..5f6b03f1ba
--- /dev/null
+++ b/src/or/shared_random.c
@@ -0,0 +1,1363 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random.c
+ *
+ * \brief Functions and data structure needed to accomplish the shared
+ * random protocol as defined in proposal #250.
+ *
+ * \details
+ *
+ * This file implements the dirauth-only commit-and-reveal protocol specified
+ * by proposal #250. The protocol has two phases (sr_phase_t): the commitment
+ * phase and the reveal phase (see get_sr_protocol_phase()).
+ *
+ * During the protocol, directory authorities keep state in memory (using
+ * sr_state_t) and in disk (using sr_disk_state_t). The synchronization between
+ * these two data structures happens in disk_state_update() and
+ * disk_state_parse().
+ *
+ * Here is a rough protocol outline:
+ *
+ * 1) In the beginning of the commitment phase, dirauths generate a
+ * commitment/reveal value for the current protocol run (see
+ * new_protocol_run() and sr_generate_our_commit()).
+ *
+ * 2) During voting, dirauths publish their commits in their votes
+ * depending on the current phase. Dirauths also include the two
+ * latest shared random values (SRV) in their votes.
+ * (see sr_get_string_for_vote())
+ *
+ * 3) Upon receiving a commit from a vote, authorities parse it, verify
+ * it, and attempt to save any new commitment or reveal information in
+ * their state file (see extract_shared_random_commits() and
+ * sr_handle_received_commits()). They also parse SRVs from votes to
+ * decide which SRV should be included in the final consensus (see
+ * extract_shared_random_srvs()).
+ *
+ * 3) After voting is done, we count the SRVs we extracted from the votes,
+ * to find the one voted by the majority of dirauths which should be
+ * included in the final consensus (see get_majority_srv_from_votes()).
+ * If an appropriate SRV is found, it is embedded in the consensus (see
+ * sr_get_string_for_consensus()).
+ *
+ * 4) At the end of the reveal phase, dirauths compute a fresh SRV for the
+ * day using the active commits (see sr_compute_srv()). This new SRV
+ * is embedded in the votes as described above.
+ *
+ * Some more notes:
+ *
+ * - To support rebooting authorities and to avoid double voting, each dirauth
+ * saves the current state of the protocol on disk so that it can resume
+ * normally in case of reboot. The disk state (sr_disk_state_t) is managed by
+ * shared_random_state.c:state_query() and we go to extra lengths to ensure
+ * that the state is flushed on disk everytime we receive any useful
+ * information like commits or SRVs.
+ *
+ * - When we receive a commit from a vote, we examine it to see if it's useful
+ * to us and whether it's appropriate to receive it according to the current
+ * phase of the protocol (see should_keep_commit()). If the commit is useful
+ * to us, we save it in our disk state using save_commit_to_state(). When we
+ * receive the reveal information corresponding to a commitment, we verify
+ * that they indeed match using verify_commit_and_reveal().
+ *
+ * - We treat consensuses as the ground truth, so everytime we generate a new
+ * consensus we update our SR state accordingly even if our local view was
+ * different (see sr_act_post_consensus()).
+ *
+ * - After a consensus has been composed, the SR protocol state gets prepared
+ * for the next voting session using sr_state_update(). That function takes
+ * care of housekeeping and also rotates the SRVs and commits in case a new
+ * protocol run is coming up. We also call sr_state_update() on bootup (in
+ * sr_state_init()), to prepare the state for the very first voting session.
+ *
+ * Terminology:
+ *
+ * - "Commitment" is the commitment value of the commit-and-reveal protocol.
+ *
+ * - "Reveal" is the reveal value of the commit-and-reveal protocol.
+ *
+ * - "Commit" is a struct (sr_commit_t) that contains a commitment value and
+ * optionally also a corresponding reveal value.
+ *
+ * - "SRV" is the Shared Random Value that gets generated as the result of the
+ * commit-and-reveal protocol.
+ **/
+
+#define SHARED_RANDOM_PRIVATE
+
+#include "or.h"
+#include "shared_random.h"
+#include "config.h"
+#include "confparse.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "routerkeys.h"
+#include "router.h"
+#include "routerlist.h"
+#include "shared_random_state.h"
+#include "util.h"
+
+/* String prefix of shared random values in votes/consensuses. */
+static const char previous_srv_str[] = "shared-rand-previous-value";
+static const char current_srv_str[] = "shared-rand-current-value";
+static const char commit_ns_str[] = "shared-rand-commit";
+static const char sr_flag_ns_str[] = "shared-rand-participate";
+
+/* The value of the consensus param AuthDirNumSRVAgreements found in the
+ * vote. This is set once the consensus creation subsystem requests the
+ * SRV(s) that should be put in the consensus. We use this value to decide
+ * if we keep or not an SRV. */
+static int32_t num_srv_agreements_from_vote;
+
+/* Return a heap allocated copy of the SRV <b>orig</b>. */
+STATIC sr_srv_t *
+srv_dup(const sr_srv_t *orig)
+{
+ sr_srv_t *duplicate = NULL;
+
+ if (!orig) {
+ return NULL;
+ }
+
+ duplicate = tor_malloc_zero(sizeof(sr_srv_t));
+ duplicate->num_reveals = orig->num_reveals;
+ memcpy(duplicate->value, orig->value, sizeof(duplicate->value));
+ return duplicate;
+}
+
+/* Allocate a new commit object and initializing it with <b>rsa_identity</b>
+ * that MUST be provided. The digest algorithm is set to the default one
+ * that is supported. The rest is uninitialized. This never returns NULL. */
+static sr_commit_t *
+commit_new(const char *rsa_identity)
+{
+ sr_commit_t *commit;
+
+ tor_assert(rsa_identity);
+
+ commit = tor_malloc_zero(sizeof(*commit));
+ commit->alg = SR_DIGEST_ALG;
+ memcpy(commit->rsa_identity, rsa_identity, sizeof(commit->rsa_identity));
+ base16_encode(commit->rsa_identity_hex, sizeof(commit->rsa_identity_hex),
+ commit->rsa_identity, sizeof(commit->rsa_identity));
+ return commit;
+}
+
+/* Issue a log message describing <b>commit</b>. */
+static void
+commit_log(const sr_commit_t *commit)
+{
+ tor_assert(commit);
+
+ log_debug(LD_DIR, "SR: Commit from %s", sr_commit_get_rsa_fpr(commit));
+ log_debug(LD_DIR, "SR: Commit: [TS: %" PRIu64 "] [Encoded: %s]",
+ commit->commit_ts, commit->encoded_commit);
+ log_debug(LD_DIR, "SR: Reveal: [TS: %" PRIu64 "] [Encoded: %s]",
+ commit->reveal_ts, safe_str(commit->encoded_reveal));
+}
+
+/* Make sure that the commitment and reveal information in <b>commit</b>
+ * match. If they match return 0, return -1 otherwise. This function MUST be
+ * used everytime we receive a new reveal value. Furthermore, the commit
+ * object MUST have a reveal value and the hash of the reveal value. */
+STATIC int
+verify_commit_and_reveal(const sr_commit_t *commit)
+{
+ tor_assert(commit);
+
+ log_debug(LD_DIR, "SR: Validating commit from authority %s",
+ sr_commit_get_rsa_fpr(commit));
+
+ /* Check that the timestamps match. */
+ if (commit->commit_ts != commit->reveal_ts) {
+ log_warn(LD_BUG, "SR: Commit timestamp %" PRIu64 " doesn't match reveal "
+ "timestamp %" PRIu64, commit->commit_ts,
+ commit->reveal_ts);
+ goto invalid;
+ }
+
+ /* Verify that the hashed_reveal received in the COMMIT message, matches
+ * the reveal we just received. */
+ {
+ /* We first hash the reveal we just received. */
+ char received_hashed_reveal[sizeof(commit->hashed_reveal)];
+
+ /* Only sha3-256 is supported. */
+ if (commit->alg != SR_DIGEST_ALG) {
+ goto invalid;
+ }
+
+ /* Use the invariant length since the encoded reveal variable has an
+ * extra byte for the NUL terminated byte. */
+ if (crypto_digest256(received_hashed_reveal, commit->encoded_reveal,
+ SR_REVEAL_BASE64_LEN, commit->alg)) {
+ /* Unable to digest the reveal blob, this is unlikely. */
+ goto invalid;
+ }
+
+ /* Now compare that with the hashed_reveal we received in COMMIT. */
+ if (fast_memneq(received_hashed_reveal, commit->hashed_reveal,
+ sizeof(received_hashed_reveal))) {
+ log_warn(LD_BUG, "SR: Received reveal value from authority %s "
+ "doesn't match the commit value.",
+ sr_commit_get_rsa_fpr(commit));
+ goto invalid;
+ }
+ }
+
+ return 0;
+ invalid:
+ return -1;
+}
+
+/* Return true iff the commit contains an encoded reveal value. */
+STATIC int
+commit_has_reveal_value(const sr_commit_t *commit)
+{
+ return !tor_mem_is_zero(commit->encoded_reveal,
+ sizeof(commit->encoded_reveal));
+}
+
+/* Parse the encoded commit. The format is:
+ * base64-encode( TIMESTAMP || H(REVEAL) )
+ *
+ * If successfully decoded and parsed, commit is updated and 0 is returned.
+ * On error, return -1. */
+STATIC int
+commit_decode(const char *encoded, sr_commit_t *commit)
+{
+ int decoded_len = 0;
+ size_t offset = 0;
+ /* XXX: Needs two extra bytes for the base64 decode calculation matches
+ * the binary length once decoded. #17868. */
+ char b64_decoded[SR_COMMIT_LEN + 2];
+
+ tor_assert(encoded);
+ tor_assert(commit);
+
+ if (strlen(encoded) > SR_COMMIT_BASE64_LEN) {
+ /* This means that if we base64 decode successfully the reveiced commit,
+ * we'll end up with a bigger decoded commit thus unusable. */
+ goto error;
+ }
+
+ /* Decode our encoded commit. Let's be careful here since _encoded_ is
+ * coming from the network in a dirauth vote so we expect nothing more
+ * than the base64 encoded length of a commit. */
+ decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded),
+ encoded, strlen(encoded));
+ if (decoded_len < 0) {
+ log_warn(LD_BUG, "SR: Commit from authority %s can't be decoded.",
+ sr_commit_get_rsa_fpr(commit));
+ goto error;
+ }
+
+ if (decoded_len != SR_COMMIT_LEN) {
+ log_warn(LD_BUG, "SR: Commit from authority %s decoded length doesn't "
+ "match the expected length (%d vs %u).",
+ sr_commit_get_rsa_fpr(commit), decoded_len,
+ (unsigned)SR_COMMIT_LEN);
+ goto error;
+ }
+
+ /* First is the timestamp (8 bytes). */
+ commit->commit_ts = tor_ntohll(get_uint64(b64_decoded));
+ offset += sizeof(uint64_t);
+ /* Next is hashed reveal. */
+ memcpy(commit->hashed_reveal, b64_decoded + offset,
+ sizeof(commit->hashed_reveal));
+ /* Copy the base64 blob to the commit. Useful for voting. */
+ strlcpy(commit->encoded_commit, encoded, sizeof(commit->encoded_commit));
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/* Parse the b64 blob at <b>encoded</b> containing reveal information and
+ * store the information in-place in <b>commit</b>. Return 0 on success else
+ * a negative value. */
+STATIC int
+reveal_decode(const char *encoded, sr_commit_t *commit)
+{
+ int decoded_len = 0;
+ /* XXX: Needs two extra bytes for the base64 decode calculation matches
+ * the binary length once decoded. #17868. */
+ char b64_decoded[SR_REVEAL_LEN + 2];
+
+ tor_assert(encoded);
+ tor_assert(commit);
+
+ if (strlen(encoded) > SR_REVEAL_BASE64_LEN) {
+ /* This means that if we base64 decode successfully the received reveal
+ * value, we'll end up with a bigger decoded value thus unusable. */
+ goto error;
+ }
+
+ /* Decode our encoded reveal. Let's be careful here since _encoded_ is
+ * coming from the network in a dirauth vote so we expect nothing more
+ * than the base64 encoded length of our reveal. */
+ decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded),
+ encoded, strlen(encoded));
+ if (decoded_len < 0) {
+ log_warn(LD_BUG, "SR: Reveal from authority %s can't be decoded.",
+ sr_commit_get_rsa_fpr(commit));
+ goto error;
+ }
+
+ if (decoded_len != SR_REVEAL_LEN) {
+ log_warn(LD_BUG, "SR: Reveal from authority %s decoded length is "
+ "doesn't match the expected length (%d vs %u)",
+ sr_commit_get_rsa_fpr(commit), decoded_len,
+ (unsigned)SR_REVEAL_LEN);
+ goto error;
+ }
+
+ commit->reveal_ts = tor_ntohll(get_uint64(b64_decoded));
+ /* Copy the last part, the random value. */
+ memcpy(commit->random_number, b64_decoded + 8,
+ sizeof(commit->random_number));
+ /* Also copy the whole message to use during verification */
+ strlcpy(commit->encoded_reveal, encoded, sizeof(commit->encoded_reveal));
+
+ return 0;
+
+ error:
+ return -1;
+}
+
+/* Encode a reveal element using a given commit object to dst which is a
+ * buffer large enough to put the base64-encoded reveal construction. The
+ * format is as follow:
+ * REVEAL = base64-encode( TIMESTAMP || H(RN) )
+ * Return base64 encoded length on success else a negative value.
+ */
+STATIC int
+reveal_encode(const sr_commit_t *commit, char *dst, size_t len)
+{
+ int ret;
+ size_t offset = 0;
+ char buf[SR_REVEAL_LEN] = {0};
+
+ tor_assert(commit);
+ tor_assert(dst);
+
+ set_uint64(buf, tor_htonll(commit->reveal_ts));
+ offset += sizeof(uint64_t);
+ memcpy(buf + offset, commit->random_number,
+ sizeof(commit->random_number));
+
+ /* Let's clean the buffer and then b64 encode it. */
+ memset(dst, 0, len);
+ ret = base64_encode(dst, len, buf, sizeof(buf), 0);
+ /* Wipe this buffer because it contains our random value. */
+ memwipe(buf, 0, sizeof(buf));
+ return ret;
+}
+
+/* Encode the given commit object to dst which is a buffer large enough to
+ * put the base64-encoded commit. The format is as follow:
+ * COMMIT = base64-encode( TIMESTAMP || H(H(RN)) )
+ * Return base64 encoded length on success else a negative value.
+ */
+STATIC int
+commit_encode(const sr_commit_t *commit, char *dst, size_t len)
+{
+ size_t offset = 0;
+ char buf[SR_COMMIT_LEN] = {0};
+
+ tor_assert(commit);
+ tor_assert(dst);
+
+ /* First is the timestamp (8 bytes). */
+ set_uint64(buf, tor_htonll(commit->commit_ts));
+ offset += sizeof(uint64_t);
+ /* and then the hashed reveal. */
+ memcpy(buf + offset, commit->hashed_reveal,
+ sizeof(commit->hashed_reveal));
+
+ /* Clean the buffer and then b64 encode it. */
+ memset(dst, 0, len);
+ return base64_encode(dst, len, buf, sizeof(buf), 0);
+}
+
+/* Cleanup both our global state and disk state. */
+static void
+sr_cleanup(void)
+{
+ sr_state_free();
+}
+
+/* Using <b>commit</b>, return a newly allocated string containing the commit
+ * information that should be used during SRV calculation. It's the caller
+ * responsibility to free the memory. Return NULL if this is not a commit to be
+ * used for SRV calculation. */
+static char *
+get_srv_element_from_commit(const sr_commit_t *commit)
+{
+ char *element;
+ tor_assert(commit);
+
+ if (!commit_has_reveal_value(commit)) {
+ return NULL;
+ }
+
+ tor_asprintf(&element, "%s%s", sr_commit_get_rsa_fpr(commit),
+ commit->encoded_reveal);
+ return element;
+}
+
+/* Return a srv object that is built with the construction:
+ * SRV = SHA3-256("shared-random" | INT_8(reveal_num) |
+ * INT_4(version) | HASHED_REVEALS | previous_SRV)
+ * This function cannot fail. */
+static sr_srv_t *
+generate_srv(const char *hashed_reveals, uint64_t reveal_num,
+ const sr_srv_t *previous_srv)
+{
+ char msg[DIGEST256_LEN + SR_SRV_MSG_LEN] = {0};
+ size_t offset = 0;
+ sr_srv_t *srv;
+
+ tor_assert(hashed_reveals);
+
+ /* Add the invariant token. */
+ memcpy(msg, SR_SRV_TOKEN, SR_SRV_TOKEN_LEN);
+ offset += SR_SRV_TOKEN_LEN;
+ set_uint64(msg + offset, tor_htonll(reveal_num));
+ offset += sizeof(uint64_t);
+ set_uint32(msg + offset, htonl(SR_PROTO_VERSION));
+ offset += sizeof(uint32_t);
+ memcpy(msg + offset, hashed_reveals, DIGEST256_LEN);
+ offset += DIGEST256_LEN;
+ if (previous_srv != NULL) {
+ memcpy(msg + offset, previous_srv->value, sizeof(previous_srv->value));
+ }
+
+ /* Ok we have our message and key for the HMAC computation, allocate our
+ * srv object and do the last step. */
+ srv = tor_malloc_zero(sizeof(*srv));
+ crypto_digest256((char *) srv->value, msg, sizeof(msg), SR_DIGEST_ALG);
+ srv->num_reveals = reveal_num;
+
+ {
+ /* Debugging. */
+ char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+ sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
+ log_info(LD_DIR, "SR: Generated SRV: %s", srv_hash_encoded);
+ }
+ return srv;
+}
+
+/* Compare reveal values and return the result. This should exclusively be
+ * used by smartlist_sort(). */
+static int
+compare_reveal_(const void **_a, const void **_b)
+{
+ const sr_commit_t *a = *_a, *b = *_b;
+ return fast_memcmp(a->hashed_reveal, b->hashed_reveal,
+ sizeof(a->hashed_reveal));
+}
+
+/* Given <b>commit</b> give the line that we should place in our votes.
+ * It's the responsibility of the caller to free the string. */
+static char *
+get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
+{
+ char *vote_line = NULL;
+
+ switch (phase) {
+ case SR_PHASE_COMMIT:
+ tor_asprintf(&vote_line, "%s %u %s %s %s\n",
+ commit_ns_str,
+ SR_PROTO_VERSION,
+ crypto_digest_algorithm_get_name(commit->alg),
+ sr_commit_get_rsa_fpr(commit),
+ commit->encoded_commit);
+ break;
+ case SR_PHASE_REVEAL:
+ {
+ /* Send a reveal value for this commit if we have one. */
+ const char *reveal_str = commit->encoded_reveal;
+ if (tor_mem_is_zero(commit->encoded_reveal,
+ sizeof(commit->encoded_reveal))) {
+ reveal_str = "";
+ }
+ tor_asprintf(&vote_line, "%s %u %s %s %s %s\n",
+ commit_ns_str,
+ SR_PROTO_VERSION,
+ crypto_digest_algorithm_get_name(commit->alg),
+ sr_commit_get_rsa_fpr(commit),
+ commit->encoded_commit, reveal_str);
+ break;
+ }
+ default:
+ tor_assert(0);
+ }
+
+ log_debug(LD_DIR, "SR: Commit vote line: %s", vote_line);
+ return vote_line;
+}
+
+/* Return a heap allocated string that contains the given <b>srv</b> string
+ * representation formatted for a networkstatus document using the
+ * <b>key</b> as the start of the line. This doesn't return NULL. */
+static char *
+srv_to_ns_string(const sr_srv_t *srv, const char *key)
+{
+ char *srv_str;
+ char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+ tor_assert(srv);
+ tor_assert(key);
+
+ sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
+ tor_asprintf(&srv_str, "%s %" PRIu64 " %s\n", key,
+ srv->num_reveals, srv_hash_encoded);
+ log_debug(LD_DIR, "SR: Consensus SRV line: %s", srv_str);
+ return srv_str;
+}
+
+/* Given the previous SRV and the current SRV, return a heap allocated
+ * string with their data that could be put in a vote or a consensus. Caller
+ * must free the returned string. Return NULL if no SRVs were provided. */
+static char *
+get_ns_str_from_sr_values(const sr_srv_t *prev_srv, const sr_srv_t *cur_srv)
+{
+ smartlist_t *chunks = NULL;
+ char *srv_str;
+
+ if (!prev_srv && !cur_srv) {
+ return NULL;
+ }
+
+ chunks = smartlist_new();
+
+ if (prev_srv) {
+ char *srv_line = srv_to_ns_string(prev_srv, previous_srv_str);
+ smartlist_add(chunks, srv_line);
+ }
+
+ if (cur_srv) {
+ char *srv_line = srv_to_ns_string(cur_srv, current_srv_str);
+ smartlist_add(chunks, srv_line);
+ }
+
+ /* Join the line(s) here in one string to return. */
+ srv_str = smartlist_join_strings(chunks, "", 0, NULL);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+
+ return srv_str;
+}
+
+/* Return 1 iff the two commits have the same commitment values. This
+ * function does not care about reveal values. */
+STATIC int
+commitments_are_the_same(const sr_commit_t *commit_one,
+ const sr_commit_t *commit_two)
+{
+ tor_assert(commit_one);
+ tor_assert(commit_two);
+
+ if (strcmp(commit_one->encoded_commit, commit_two->encoded_commit)) {
+ return 0;
+ }
+ return 1;
+}
+
+/* We just received a commit from the vote of authority with
+ * <b>identity_digest</b>. Return 1 if this commit is authorititative that
+ * is, it belongs to the authority that voted it. Else return 0 if not. */
+STATIC int
+commit_is_authoritative(const sr_commit_t *commit,
+ const char *voter_key)
+{
+ tor_assert(commit);
+ tor_assert(voter_key);
+
+ return fast_memeq(commit->rsa_identity, voter_key,
+ sizeof(commit->rsa_identity));
+}
+
+/* Decide if the newly received <b>commit</b> should be kept depending on
+ * the current phase and state of the protocol. The <b>voter_key</b> is the
+ * RSA identity key fingerprint of the authority's vote from which the
+ * commit comes from. The <b>phase</b> is the phase we should be validating
+ * the commit for. Return 1 if the commit should be added to our state or 0
+ * if not. */
+STATIC int
+should_keep_commit(const sr_commit_t *commit, const char *voter_key,
+ sr_phase_t phase)
+{
+ const sr_commit_t *saved_commit;
+
+ tor_assert(commit);
+ tor_assert(voter_key);
+
+ log_debug(LD_DIR, "SR: Inspecting commit from %s (voter: %s)?",
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
+
+ /* For a commit to be considered, it needs to be authoritative (it should
+ * be the voter's own commit). */
+ if (!commit_is_authoritative(commit, voter_key)) {
+ log_debug(LD_DIR, "SR: Ignoring non-authoritative commit.");
+ goto ignore;
+ }
+
+ /* Let's make sure, for extra safety, that this fingerprint is known to
+ * us. Even though this comes from a vote, doesn't hurt to be
+ * extracareful. */
+ if (trusteddirserver_get_by_v3_auth_digest(commit->rsa_identity) == NULL) {
+ log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
+ "authority. Discarding commit.",
+ escaped(commit->rsa_identity));
+ goto ignore;
+ }
+
+ /* Check if the authority that voted for <b>commit</b> has already posted
+ * a commit before. */
+ saved_commit = sr_state_get_commit(commit->rsa_identity);
+
+ switch (phase) {
+ case SR_PHASE_COMMIT:
+ /* Already having a commit for an authority so ignore this one. */
+ if (saved_commit) {
+ /* Receiving known commits should happen naturally since commit phase
+ lasts multiple rounds. However if the commitment value changes
+ during commit phase, it might be a bug so log more loudly. */
+ if (!commitments_are_the_same(commit, saved_commit)) {
+ log_info(LD_DIR,
+ "SR: Received altered commit from %s in commit phase.",
+ sr_commit_get_rsa_fpr(commit));
+ } else {
+ log_debug(LD_DIR, "SR: Ignoring known commit during commit phase.");
+ }
+ goto ignore;
+ }
+
+ /* A commit with a reveal value during commitment phase is very wrong. */
+ if (commit_has_reveal_value(commit)) {
+ log_warn(LD_DIR, "SR: Commit from authority %s has a reveal value "
+ "during COMMIT phase. (voter: %s)",
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
+ goto ignore;
+ }
+ break;
+ case SR_PHASE_REVEAL:
+ /* We are now in reveal phase. We keep a commit if and only if:
+ *
+ * - We have already seen a commit by this auth, AND
+ * - the saved commit has the same commitment value as this one, AND
+ * - the saved commit has no reveal information, AND
+ * - this commit does have reveal information, AND
+ * - the reveal & commit information are matching.
+ *
+ * If all the above are true, then we are interested in this new commit
+ * for its reveal information. */
+
+ if (!saved_commit) {
+ log_debug(LD_DIR, "SR: Ignoring commit first seen in reveal phase.");
+ goto ignore;
+ }
+
+ if (!commitments_are_the_same(commit, saved_commit)) {
+ log_warn(LD_DIR, "SR: Commit from authority %s is different from "
+ "previous commit in our state (voter: %s)",
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
+ goto ignore;
+ }
+
+ if (commit_has_reveal_value(saved_commit)) {
+ log_debug(LD_DIR, "SR: Ignoring commit with known reveal info.");
+ goto ignore;
+ }
+
+ if (!commit_has_reveal_value(commit)) {
+ log_debug(LD_DIR, "SR: Ignoring commit without reveal value.");
+ goto ignore;
+ }
+
+ if (verify_commit_and_reveal(commit) < 0) {
+ log_warn(LD_BUG, "SR: Commit from authority %s has an invalid "
+ "reveal value. (voter: %s)",
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
+ goto ignore;
+ }
+ break;
+ default:
+ tor_assert(0);
+ }
+
+ return 1;
+
+ ignore:
+ return 0;
+}
+
+/* We are in reveal phase and we found a valid and verified <b>commit</b> in
+ * a vote that contains reveal values that we could use. Update the commit
+ * we have in our state. Never call this with an unverified commit. */
+STATIC void
+save_commit_during_reveal_phase(const sr_commit_t *commit)
+{
+ sr_commit_t *saved_commit;
+
+ tor_assert(commit);
+
+ /* Get the commit from our state. */
+ saved_commit = sr_state_get_commit(commit->rsa_identity);
+ tor_assert(saved_commit);
+ /* Safety net. They can not be different commitments at this point. */
+ int same_commits = commitments_are_the_same(commit, saved_commit);
+ tor_assert(same_commits);
+
+ /* Copy reveal information to our saved commit. */
+ sr_state_copy_reveal_info(saved_commit, commit);
+}
+
+/* Save <b>commit</b> to our persistent state. Depending on the current
+ * phase, different actions are taken. Steals reference of <b>commit</b>.
+ * The commit object MUST be valid and verified before adding it to the
+ * state. */
+STATIC void
+save_commit_to_state(sr_commit_t *commit)
+{
+ sr_phase_t phase = sr_state_get_phase();
+
+ ASSERT_COMMIT_VALID(commit);
+
+ switch (phase) {
+ case SR_PHASE_COMMIT:
+ /* During commit phase, just save any new authoritative commit */
+ sr_state_add_commit(commit);
+ break;
+ case SR_PHASE_REVEAL:
+ save_commit_during_reveal_phase(commit);
+ sr_commit_free(commit);
+ break;
+ default:
+ tor_assert(0);
+ }
+}
+
+/* Return 1 if we should we keep an SRV voted by <b>n_agreements</b> auths.
+ * Return 0 if we should ignore it. */
+static int
+should_keep_srv(int n_agreements)
+{
+ /* Check if the most popular SRV has reached majority. */
+ int n_voters = get_n_authorities(V3_DIRINFO);
+ int votes_required_for_majority = (n_voters / 2) + 1;
+
+ /* We need at the very least majority to keep a value. */
+ if (n_agreements < votes_required_for_majority) {
+ log_notice(LD_DIR, "SR: SRV didn't reach majority [%d/%d]!",
+ n_agreements, votes_required_for_majority);
+ return 0;
+ }
+
+ /* When we just computed a new SRV, we need to have super majority in order
+ * to keep it. */
+ if (sr_state_srv_is_fresh()) {
+ /* Check if we have super majority for this new SRV value. */
+ if (n_agreements < num_srv_agreements_from_vote) {
+ log_notice(LD_DIR, "SR: New SRV didn't reach agreement [%d/%d]!",
+ n_agreements, num_srv_agreements_from_vote);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Helper: compare two DIGEST256_LEN digests. */
+static int
+compare_srvs_(const void **_a, const void **_b)
+{
+ const sr_srv_t *a = *_a, *b = *_b;
+ return tor_memcmp(a->value, b->value, sizeof(a->value));
+}
+
+/* Return the most frequent member of the sorted list of DIGEST256_LEN
+ * digests in <b>sl</b> with the count of that most frequent element. */
+static sr_srv_t *
+smartlist_get_most_frequent_srv(const smartlist_t *sl, int *count_out)
+{
+ return smartlist_get_most_frequent_(sl, compare_srvs_, count_out);
+}
+
+/** Compare two SRVs. Used in smartlist sorting. */
+static int
+compare_srv_(const void **_a, const void **_b)
+{
+ const sr_srv_t *a = *_a, *b = *_b;
+ return fast_memcmp(a->value, b->value,
+ sizeof(a->value));
+}
+
+/* Using a list of <b>votes</b>, return the SRV object from them that has
+ * been voted by the majority of dirauths. If <b>current</b> is set, we look
+ * for the current SRV value else the previous one. The returned pointer is
+ * an object located inside a vote. NULL is returned if no appropriate value
+ * could be found. */
+STATIC sr_srv_t *
+get_majority_srv_from_votes(const smartlist_t *votes, int current)
+{
+ int count = 0;
+ sr_srv_t *most_frequent_srv = NULL;
+ sr_srv_t *the_srv = NULL;
+ smartlist_t *srv_list;
+
+ tor_assert(votes);
+
+ srv_list = smartlist_new();
+
+ /* Walk over votes and register any SRVs found. */
+ SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
+ sr_srv_t *srv_tmp = NULL;
+
+ if (!v->sr_info.participate) {
+ /* Ignore vote that do not participate. */
+ continue;
+ }
+ /* Do we want previous or current SRV? */
+ srv_tmp = current ? v->sr_info.current_srv : v->sr_info.previous_srv;
+ if (!srv_tmp) {
+ continue;
+ }
+
+ smartlist_add(srv_list, srv_tmp);
+ } SMARTLIST_FOREACH_END(v);
+
+ smartlist_sort(srv_list, compare_srv_);
+ most_frequent_srv = smartlist_get_most_frequent_srv(srv_list, &count);
+ if (!most_frequent_srv) {
+ goto end;
+ }
+
+ /* Was this SRV voted by enough auths for us to keep it? */
+ if (!should_keep_srv(count)) {
+ goto end;
+ }
+
+ /* We found an SRV that we can use! Habemus SRV! */
+ the_srv = most_frequent_srv;
+
+ {
+ /* Debugging */
+ char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+ sr_srv_encode(encoded, sizeof(encoded), the_srv);
+ log_debug(LD_DIR, "SR: Chosen SRV by majority: %s (%d votes)", encoded,
+ count);
+ }
+
+ end:
+ /* We do not free any sr_srv_t values, we don't have the ownership. */
+ smartlist_free(srv_list);
+ return the_srv;
+}
+
+/* Encode the given shared random value and put it in dst. Destination
+ * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
+void
+sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
+{
+ int ret;
+ /* Extra byte for the NULL terminated char. */
+ char buf[SR_SRV_VALUE_BASE64_LEN + 1];
+
+ tor_assert(dst);
+ tor_assert(srv);
+ tor_assert(dst_len >= sizeof(buf));
+
+ ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
+ sizeof(srv->value), 0);
+ /* Always expect the full length without the NULL byte. */
+ tor_assert(ret == (sizeof(buf) - 1));
+ tor_assert(ret <= (int) dst_len);
+ strlcpy(dst, buf, dst_len);
+}
+
+/* Free a commit object. */
+void
+sr_commit_free(sr_commit_t *commit)
+{
+ if (commit == NULL) {
+ return;
+ }
+ /* Make sure we do not leave OUR random number in memory. */
+ memwipe(commit->random_number, 0, sizeof(commit->random_number));
+ tor_free(commit);
+}
+
+/* Generate the commitment/reveal value for the protocol run starting at
+ * <b>timestamp</b>. <b>my_rsa_cert</b> is our authority RSA certificate. */
+sr_commit_t *
+sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert)
+{
+ sr_commit_t *commit = NULL;
+ char digest[DIGEST_LEN];
+
+ tor_assert(my_rsa_cert);
+
+ /* Get our RSA identity fingerprint */
+ if (crypto_pk_get_digest(my_rsa_cert->identity_key, digest) < 0) {
+ goto error;
+ }
+
+ /* New commit with our identity key. */
+ commit = commit_new(digest);
+
+ /* Generate the reveal random value */
+ crypto_strongest_rand(commit->random_number,
+ sizeof(commit->random_number));
+ commit->commit_ts = commit->reveal_ts = timestamp;
+
+ /* Now get the base64 blob that corresponds to our reveal */
+ if (reveal_encode(commit, commit->encoded_reveal,
+ sizeof(commit->encoded_reveal)) < 0) {
+ log_err(LD_DIR, "SR: Unable to encode our reveal value!");
+ goto error;
+ }
+
+ /* Now let's create the commitment */
+ tor_assert(commit->alg == SR_DIGEST_ALG);
+ /* The invariant length is used here since the encoded reveal variable
+ * has an extra byte added for the NULL terminated byte. */
+ if (crypto_digest256(commit->hashed_reveal, commit->encoded_reveal,
+ SR_REVEAL_BASE64_LEN, commit->alg)) {
+ goto error;
+ }
+
+ /* Now get the base64 blob that corresponds to our commit. */
+ if (commit_encode(commit, commit->encoded_commit,
+ sizeof(commit->encoded_commit)) < 0) {
+ log_err(LD_DIR, "SR: Unable to encode our commit value!");
+ goto error;
+ }
+
+ log_debug(LD_DIR, "SR: Generated our commitment:");
+ commit_log(commit);
+ /* Our commit better be valid :). */
+ commit->valid = 1;
+ return commit;
+
+ error:
+ sr_commit_free(commit);
+ return NULL;
+}
+
+/* Compute the shared random value based on the active commits in our state. */
+void
+sr_compute_srv(void)
+{
+ uint64_t reveal_num = 0;
+ char *reveals = NULL;
+ smartlist_t *chunks, *commits;
+ digestmap_t *state_commits;
+
+ /* Computing a shared random value in the commit phase is very wrong. This
+ * should only happen at the very end of the reveal phase when a new
+ * protocol run is about to start. */
+ tor_assert(sr_state_get_phase() == SR_PHASE_REVEAL);
+ state_commits = sr_state_get_commits();
+
+ commits = smartlist_new();
+ chunks = smartlist_new();
+
+ /* We must make a list of commit ordered by authority fingerprint in
+ * ascending order as specified by proposal 250. */
+ DIGESTMAP_FOREACH(state_commits, key, sr_commit_t *, c) {
+ /* Extra safety net, make sure we have valid commit before using it. */
+ ASSERT_COMMIT_VALID(c);
+ /* Let's not use a commit from an authority that we don't know. It's
+ * possible that an authority could be removed during a protocol run so
+ * that commit value should never be used in the SRV computation. */
+ if (trusteddirserver_get_by_v3_auth_digest(c->rsa_identity) == NULL) {
+ log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
+ "authority. Discarding commit for the SRV computation.",
+ sr_commit_get_rsa_fpr(c));
+ continue;
+ }
+ /* We consider this commit valid. */
+ smartlist_add(commits, c);
+ } DIGESTMAP_FOREACH_END;
+ smartlist_sort(commits, compare_reveal_);
+
+ /* Now for each commit for that sorted list in ascending order, we'll
+ * build the element for each authority that needs to go into the srv
+ * computation. */
+ SMARTLIST_FOREACH_BEGIN(commits, const sr_commit_t *, c) {
+ char *element = get_srv_element_from_commit(c);
+ if (element) {
+ smartlist_add(chunks, element);
+ reveal_num++;
+ }
+ } SMARTLIST_FOREACH_END(c);
+ smartlist_free(commits);
+
+ {
+ /* Join all reveal values into one giant string that we'll hash so we
+ * can generated our shared random value. */
+ sr_srv_t *current_srv;
+ char hashed_reveals[DIGEST256_LEN];
+ reveals = smartlist_join_strings(chunks, "", 0, NULL);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ if (crypto_digest256(hashed_reveals, reveals, strlen(reveals),
+ SR_DIGEST_ALG)) {
+ goto end;
+ }
+ current_srv = generate_srv(hashed_reveals, reveal_num,
+ sr_state_get_previous_srv());
+ sr_state_set_current_srv(current_srv);
+ /* We have a fresh SRV, flag our state. */
+ sr_state_set_fresh_srv();
+ }
+
+ end:
+ tor_free(reveals);
+}
+
+/* Parse a list of arguments from a SRV value either from a vote, consensus
+ * or from our disk state and return a newly allocated srv object. NULL is
+ * returned on error.
+ *
+ * The arguments' order:
+ * num_reveals, value
+ */
+sr_srv_t *
+sr_parse_srv(const smartlist_t *args)
+{
+ char *value;
+ int ok, ret;
+ uint64_t num_reveals;
+ sr_srv_t *srv = NULL;
+
+ tor_assert(args);
+
+ if (smartlist_len(args) < 2) {
+ goto end;
+ }
+
+ /* First argument is the number of reveal values */
+ num_reveals = tor_parse_uint64(smartlist_get(args, 0),
+ 10, 0, UINT64_MAX, &ok, NULL);
+ if (!ok) {
+ goto end;
+ }
+ /* Second and last argument is the shared random value it self. */
+ value = smartlist_get(args, 1);
+ if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
+ goto end;
+ }
+
+ srv = tor_malloc_zero(sizeof(*srv));
+ srv->num_reveals = num_reveals;
+ /* We substract one byte from the srclen because the function ignores the
+ * '=' character in the given buffer. This is broken but it's a documented
+ * behavior of the implementation. */
+ ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
+ SR_SRV_VALUE_BASE64_LEN - 1);
+ if (ret != sizeof(srv->value)) {
+ tor_free(srv);
+ srv = NULL;
+ goto end;
+ }
+ end:
+ return srv;
+}
+
+/* Parse a commit from a vote or from our disk state and return a newly
+ * allocated commit object. NULL is returned on error.
+ *
+ * The commit's data is in <b>args</b> and the order matters very much:
+ * version, algname, RSA fingerprint, commit value[, reveal value]
+ */
+sr_commit_t *
+sr_parse_commit(const smartlist_t *args)
+{
+ uint32_t version;
+ char *value, digest[DIGEST_LEN];
+ digest_algorithm_t alg;
+ const char *rsa_identity_fpr;
+ sr_commit_t *commit = NULL;
+
+ if (smartlist_len(args) < 4) {
+ goto error;
+ }
+
+ /* First is the version number of the SR protocol which indicates at which
+ * version that commit was created. */
+ value = smartlist_get(args, 0);
+ version = (uint32_t) tor_parse_ulong(value, 10, 1, UINT32_MAX, NULL, NULL);
+ if (version > SR_PROTO_VERSION) {
+ log_info(LD_DIR, "SR: Commit version %" PRIu32 " (%s) is not supported.",
+ version, escaped(value));
+ goto error;
+ }
+
+ /* Second is the algorithm. */
+ value = smartlist_get(args, 1);
+ alg = crypto_digest_algorithm_parse_name(value);
+ if (alg != SR_DIGEST_ALG) {
+ log_warn(LD_BUG, "SR: Commit algorithm %s is not recognized.",
+ escaped(value));
+ goto error;
+ }
+
+ /* Third argument is the RSA fingerprint of the auth and turn it into a
+ * digest value. */
+ rsa_identity_fpr = smartlist_get(args, 2);
+ if (base16_decode(digest, DIGEST_LEN, rsa_identity_fpr,
+ HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "SR: RSA fingerprint %s not decodable",
+ escaped(rsa_identity_fpr));
+ goto error;
+ }
+
+ /* Allocate commit since we have a valid identity now. */
+ commit = commit_new(digest);
+
+ /* Fourth argument is the commitment value base64-encoded. */
+ value = smartlist_get(args, 3);
+ if (commit_decode(value, commit) < 0) {
+ goto error;
+ }
+
+ /* (Optional) Fifth argument is the revealed value. */
+ if (smartlist_len(args) > 4) {
+ value = smartlist_get(args, 4);
+ if (reveal_decode(value, commit) < 0) {
+ goto error;
+ }
+ }
+
+ return commit;
+
+ error:
+ sr_commit_free(commit);
+ return NULL;
+}
+
+/* Called when we are done parsing a vote by <b>voter_key</b> that might
+ * contain some useful <b>commits</b>. Find if any of them should be kept
+ * and update our state accordingly. Once done, the list of commitments will
+ * be empty. */
+void
+sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
+{
+ char rsa_identity[DIGEST_LEN];
+
+ tor_assert(voter_key);
+
+ /* It's possible that the vote has _NO_ commits. */
+ if (commits == NULL) {
+ return;
+ }
+
+ /* Get the RSA identity fingerprint of this voter */
+ if (crypto_pk_get_digest(voter_key, rsa_identity) < 0) {
+ return;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(commits, sr_commit_t *, commit) {
+ /* We won't need the commit in this list anymore, kept or not. */
+ SMARTLIST_DEL_CURRENT(commits, commit);
+ /* Check if this commit is valid and should be stored in our state. */
+ if (!should_keep_commit(commit, rsa_identity,
+ sr_state_get_phase())) {
+ sr_commit_free(commit);
+ continue;
+ }
+ /* Ok, we have a valid commit now that we are about to put in our state.
+ * so flag it valid from now on. */
+ commit->valid = 1;
+ /* Everything lines up: save this commit to state then! */
+ save_commit_to_state(commit);
+ } SMARTLIST_FOREACH_END(commit);
+}
+
+/* Return a heap-allocated string containing commits that should be put in
+ * the votes. It's the responsibility of the caller to free the string.
+ * This always return a valid string, either empty or with line(s). */
+char *
+sr_get_string_for_vote(void)
+{
+ char *vote_str = NULL;
+ digestmap_t *state_commits;
+ smartlist_t *chunks = smartlist_new();
+ const or_options_t *options = get_options();
+
+ /* Are we participating in the protocol? */
+ if (!options->AuthDirSharedRandomness) {
+ goto end;
+ }
+
+ log_debug(LD_DIR, "SR: Preparing our vote info:");
+
+ /* First line, put in the vote the participation flag. */
+ {
+ char *sr_flag_line;
+ tor_asprintf(&sr_flag_line, "%s\n", sr_flag_ns_str);
+ smartlist_add(chunks, sr_flag_line);
+ }
+
+ /* In our vote we include every commitment in our permanent state. */
+ state_commits = sr_state_get_commits();
+ smartlist_t *state_commit_vote_lines = smartlist_new();
+ DIGESTMAP_FOREACH(state_commits, key, const sr_commit_t *, commit) {
+ char *line = get_vote_line_from_commit(commit, sr_state_get_phase());
+ smartlist_add(state_commit_vote_lines, line);
+ } DIGESTMAP_FOREACH_END;
+
+ /* Sort the commit strings by version (string, not numeric), algorithm,
+ * and fingerprint. This makes sure the commit lines in votes are in a
+ * recognisable, stable order. */
+ smartlist_sort_strings(state_commit_vote_lines);
+
+ /* Now add the sorted list of commits to the vote */
+ smartlist_add_all(chunks, state_commit_vote_lines);
+ smartlist_free(state_commit_vote_lines);
+
+ /* Add the SRV value(s) if any. */
+ {
+ char *srv_lines = get_ns_str_from_sr_values(sr_state_get_previous_srv(),
+ sr_state_get_current_srv());
+ if (srv_lines) {
+ smartlist_add(chunks, srv_lines);
+ }
+ }
+
+ end:
+ vote_str = smartlist_join_strings(chunks, "", 0, NULL);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ return vote_str;
+}
+
+/* Return a heap-allocated string that should be put in the consensus and
+ * contains the shared randomness values. It's the responsibility of the
+ * caller to free the string. NULL is returned if no SRV(s) available.
+ *
+ * This is called when a consensus (any flavor) is bring created thus it
+ * should NEVER change the state nor the state should be changed in between
+ * consensus creation.
+ *
+ * <b>num_srv_agreements</b> is taken from the votes thus the voted value
+ * that should be used.
+ * */
+char *
+sr_get_string_for_consensus(const smartlist_t *votes,
+ int32_t num_srv_agreements)
+{
+ char *srv_str;
+ const or_options_t *options = get_options();
+
+ tor_assert(votes);
+
+ /* Not participating, avoid returning anything. */
+ if (!options->AuthDirSharedRandomness) {
+ log_info(LD_DIR, "SR: Support disabled (AuthDirSharedRandomness %d)",
+ options->AuthDirSharedRandomness);
+ goto end;
+ }
+
+ /* Set the global value of AuthDirNumSRVAgreements found in the votes. */
+ num_srv_agreements_from_vote = num_srv_agreements;
+
+ /* Check the votes and figure out if SRVs should be included in the final
+ * consensus. */
+ sr_srv_t *prev_srv = get_majority_srv_from_votes(votes, 0);
+ sr_srv_t *cur_srv = get_majority_srv_from_votes(votes, 1);
+ srv_str = get_ns_str_from_sr_values(prev_srv, cur_srv);
+ if (!srv_str) {
+ goto end;
+ }
+
+ return srv_str;
+ end:
+ return NULL;
+}
+
+/* We just computed a new <b>consensus</b>. Update our state with the SRVs
+ * from the consensus (might be NULL as well). Register the SRVs in our SR
+ * state and prepare for the upcoming protocol round. */
+void
+sr_act_post_consensus(const networkstatus_t *consensus)
+{
+ const or_options_t *options = get_options();
+
+ /* Don't act if our state hasn't been initialized. We can be called during
+ * boot time when loading consensus from disk which is prior to the
+ * initialization of the SR subsystem. We also should not be doing
+ * anything if we are _not_ a directory authority and if we are a bridge
+ * authority. */
+ if (!sr_state_is_initialized() || !authdir_mode_v3(options) ||
+ authdir_mode_bridge(options)) {
+ return;
+ }
+
+ /* Set the majority voted SRVs in our state even if both are NULL. It
+ * doesn't matter this is what the majority has decided. Obviously, we can
+ * only do that if we have a consensus. */
+ if (consensus) {
+ /* Start by freeing the current SRVs since the SRVs we believed during
+ * voting do not really matter. Now that all the votes are in, we use the
+ * majority's opinion on which are the active SRVs. */
+ sr_state_clean_srvs();
+ /* Reset the fresh flag of the SRV so we know that from now on we don't
+ * have a new SRV to vote for. We just used the one from the consensus
+ * decided by the majority. */
+ sr_state_unset_fresh_srv();
+ /* Set the SR values from the given consensus. */
+ sr_state_set_previous_srv(srv_dup(consensus->sr_info.previous_srv));
+ sr_state_set_current_srv(srv_dup(consensus->sr_info.current_srv));
+ }
+
+ /* 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);
+ }
+}
+
+/* Initialize shared random subsystem. This MUST be called early in the boot
+ * process of tor. Return 0 on success else -1 on error. */
+int
+sr_init(int save_to_disk)
+{
+ return sr_state_init(save_to_disk, 1);
+}
+
+/* Save our state to disk and cleanup everything. */
+void
+sr_save_and_cleanup(void)
+{
+ sr_state_save();
+ sr_cleanup();
+}
+
+#ifdef TOR_UNIT_TESTS
+
+/* Set the global value of number of SRV agreements so the test can play
+ * along by calling specific functions that don't parse the votes prior for
+ * the AuthDirNumSRVAgreements value. */
+void
+set_num_srv_agreements(int32_t value)
+{
+ num_srv_agreements_from_vote = value;
+}
+
+#endif /* TOR_UNIT_TESTS */
+
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
new file mode 100644
index 0000000000..9885934cc7
--- /dev/null
+++ b/src/or/shared_random.h
@@ -0,0 +1,168 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_SHARED_RANDOM_H
+#define TOR_SHARED_RANDOM_H
+
+/*
+ * This file contains ABI/API of the shared random protocol defined in
+ * proposal #250. Every public functions and data structure are namespaced
+ * with "sr_" which stands for shared random.
+ */
+
+#include "or.h"
+
+/* Protocol version */
+#define SR_PROTO_VERSION 1
+/* Default digest algorithm. */
+#define SR_DIGEST_ALG DIGEST_SHA3_256
+/* Invariant token in the SRV calculation. */
+#define SR_SRV_TOKEN "shared-random"
+/* Don't count the NUL terminated byte even though the TOKEN has it. */
+#define SR_SRV_TOKEN_LEN (sizeof(SR_SRV_TOKEN) - 1)
+
+/* Length of the random number (in bytes). */
+#define SR_RANDOM_NUMBER_LEN 32
+/* Size of a decoded commit value in a vote or state. It's a hash and a
+ * timestamp. It adds up to 40 bytes. */
+#define SR_COMMIT_LEN (sizeof(uint64_t) + DIGEST256_LEN)
+/* Size of a decoded reveal value from a vote or state. It's a 64 bit
+ * timestamp and the hashed random number. This adds up to 40 bytes. */
+#define SR_REVEAL_LEN (sizeof(uint64_t) + DIGEST256_LEN)
+/* Size of SRV message length. The construction is has follow:
+ * "shared-random" | INT_8(reveal_num) | INT_4(version) | PREV_SRV */
+#define SR_SRV_MSG_LEN \
+ (SR_SRV_TOKEN_LEN + sizeof(uint64_t) + sizeof(uint32_t) + DIGEST256_LEN)
+
+/* Length of base64 encoded commit NOT including the NUL terminated byte.
+ * Formula is taken from base64_encode_size. This adds up to 56 bytes. */
+#define SR_COMMIT_BASE64_LEN \
+ (((SR_COMMIT_LEN - 1) / 3) * 4 + 4)
+/* Length of base64 encoded reveal NOT including the NUL terminated byte.
+ * Formula is taken from base64_encode_size. This adds up to 56 bytes. */
+#define SR_REVEAL_BASE64_LEN \
+ (((SR_REVEAL_LEN - 1) / 3) * 4 + 4)
+/* Length of base64 encoded shared random value. It's 32 bytes long so 44
+ * bytes from the base64_encode_size formula. That includes the '='
+ * character at the end. */
+#define SR_SRV_VALUE_BASE64_LEN \
+ (((DIGEST256_LEN - 1) / 3) * 4 + 4)
+
+/* Assert if commit valid flag is not set. */
+#define ASSERT_COMMIT_VALID(c) tor_assert((c)->valid)
+
+/* Protocol phase. */
+typedef enum {
+ /* Commitment phase */
+ SR_PHASE_COMMIT = 1,
+ /* Reveal phase */
+ SR_PHASE_REVEAL = 2,
+} sr_phase_t;
+
+/* A shared random value (SRV). */
+typedef struct sr_srv_t {
+ /* The number of reveal values used to derive this SRV. */
+ uint64_t num_reveals;
+ /* The actual value. This is the stored result of SHA3-256. */
+ uint8_t value[DIGEST256_LEN];
+} sr_srv_t;
+
+/* A commit (either ours or from another authority). */
+typedef struct sr_commit_t {
+ /* Hashing algorithm used. */
+ digest_algorithm_t alg;
+ /* Indicate if this commit has been verified thus valid. */
+ unsigned int valid:1;
+
+ /* Commit owner info */
+
+ /* The RSA identity key of the authority and its base16 representation,
+ * which includes the NUL terminated byte. */
+ char rsa_identity[DIGEST_LEN];
+ char rsa_identity_hex[HEX_DIGEST_LEN + 1];
+
+ /* Commitment information */
+
+ /* Timestamp of reveal. Correspond to TIMESTAMP. */
+ uint64_t reveal_ts;
+ /* H(REVEAL) as found in COMMIT message. */
+ char hashed_reveal[DIGEST256_LEN];
+ /* Base64 encoded COMMIT. We use this to put it in our vote. */
+ char encoded_commit[SR_COMMIT_BASE64_LEN + 1];
+
+ /* Reveal information */
+
+ /* H(RN) which is what we used as the random value for this commit. We
+ * don't use the raw bytes since those are sent on the network thus
+ * avoiding possible information leaks of our PRNG. */
+ uint8_t random_number[SR_RANDOM_NUMBER_LEN];
+ /* Timestamp of commit. Correspond to TIMESTAMP. */
+ uint64_t commit_ts;
+ /* This is the whole reveal message. We use it during verification */
+ char encoded_reveal[SR_REVEAL_BASE64_LEN + 1];
+} sr_commit_t;
+
+/* API */
+
+/* Public methods: */
+
+int sr_init(int save_to_disk);
+void sr_save_and_cleanup(void);
+void sr_act_post_consensus(const networkstatus_t *consensus);
+void sr_handle_received_commits(smartlist_t *commits,
+ crypto_pk_t *voter_key);
+sr_commit_t *sr_parse_commit(const smartlist_t *args);
+sr_srv_t *sr_parse_srv(const smartlist_t *args);
+char *sr_get_string_for_vote(void);
+char *sr_get_string_for_consensus(const smartlist_t *votes,
+ int32_t num_srv_agreements);
+void sr_commit_free(sr_commit_t *commit);
+void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
+
+/* Private methods (only used by shared_random_state.c): */
+static inline
+const char *sr_commit_get_rsa_fpr(const sr_commit_t *commit)
+{
+ return commit->rsa_identity_hex;
+}
+
+void sr_compute_srv(void);
+sr_commit_t *sr_generate_our_commit(time_t timestamp,
+ const authority_cert_t *my_rsa_cert);
+#ifdef SHARED_RANDOM_PRIVATE
+
+/* Encode */
+STATIC int reveal_encode(const sr_commit_t *commit, char *dst, size_t len);
+STATIC int commit_encode(const sr_commit_t *commit, char *dst, size_t len);
+/* Decode. */
+STATIC int commit_decode(const char *encoded, sr_commit_t *commit);
+STATIC int reveal_decode(const char *encoded, sr_commit_t *commit);
+
+STATIC int commit_has_reveal_value(const sr_commit_t *commit);
+
+STATIC int verify_commit_and_reveal(const sr_commit_t *commit);
+
+STATIC sr_srv_t *get_majority_srv_from_votes(const smartlist_t *votes,
+ int current);
+
+STATIC void save_commit_to_state(sr_commit_t *commit);
+STATIC sr_srv_t *srv_dup(const sr_srv_t *orig);
+STATIC int commitments_are_the_same(const sr_commit_t *commit_one,
+ const sr_commit_t *commit_two);
+STATIC int commit_is_authoritative(const sr_commit_t *commit,
+ const char *voter_key);
+STATIC int should_keep_commit(const sr_commit_t *commit,
+ const char *voter_key,
+ sr_phase_t phase);
+STATIC void save_commit_during_reveal_phase(const sr_commit_t *commit);
+
+#endif /* SHARED_RANDOM_PRIVATE */
+
+#ifdef TOR_UNIT_TESTS
+
+void set_num_srv_agreements(int32_t value);
+
+#endif /* TOR_UNIT_TESTS */
+
+#endif /* TOR_SHARED_RANDOM_H */
+
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
new file mode 100644
index 0000000000..87db9031ee
--- /dev/null
+++ b/src/or/shared_random_state.c
@@ -0,0 +1,1359 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_state.c
+ *
+ * \brief Functions and data structures for the state of the random protocol
+ * as defined in proposal #250.
+ **/
+
+#define SHARED_RANDOM_STATE_PRIVATE
+
+#include "or.h"
+#include "shared_random.h"
+#include "config.h"
+#include "confparse.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "router.h"
+#include "shared_random_state.h"
+
+/* Default filename of the shared random state on disk. */
+static const char default_fname[] = "sr-state";
+
+/* String representation of a protocol phase. */
+static const char *phase_str[] = { "unknown", "commit", "reveal" };
+
+/* Our shared random protocol state. There is only one possible state per
+ * protocol run so this is the global state which is reset at every run once
+ * the shared random value has been computed. */
+static sr_state_t *sr_state = NULL;
+
+/* Representation of our persistent state on disk. The sr_state above
+ * contains the data parsed from this state. When we save to disk, we
+ * translate the sr_state to this sr_disk_state. */
+static sr_disk_state_t *sr_disk_state = NULL;
+
+/* Disk state file keys. */
+static const char dstate_commit_key[] = "Commit";
+static const char dstate_prev_srv_key[] = "SharedRandPreviousValue";
+static const char dstate_cur_srv_key[] = "SharedRandCurrentValue";
+
+/* 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 }
+/* As VAR, but the option name and member name are the same. */
+#define V(member, conftype, initvalue) \
+ VAR(#member, conftype, member, initvalue)
+/* Our persistent state magic number. */
+#define SR_DISK_STATE_MAGIC 0x98AB1254
+/* Each protocol phase has 12 rounds */
+#define SHARED_RANDOM_N_ROUNDS 12
+/* Number of phase we have in a protocol. */
+#define SHARED_RANDOM_N_PHASES 2
+
+static int
+disk_state_validate_cb(void *old_state, void *state, void *default_state,
+ int from_setconf, char **msg);
+
+/* Array of variables that are saved to disk as a persistent state. */
+static config_var_t state_vars[] = {
+ V(Version, UINT, "0"),
+ V(TorVersion, STRING, NULL),
+ V(ValidAfter, ISOTIME, NULL),
+ V(ValidUntil, ISOTIME, NULL),
+
+ V(Commit, LINELIST, NULL),
+
+ V(SharedRandValues, LINELIST_V, NULL),
+ VAR("SharedRandPreviousValue",LINELIST_S, SharedRandValues, NULL),
+ VAR("SharedRandCurrentValue", LINELIST_S, SharedRandValues, NULL),
+ { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+};
+
+/* "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
+};
+
+/* 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_),
+ NULL,
+ NULL,
+ state_vars,
+ disk_state_validate_cb,
+ &state_extra_var,
+};
+
+/* Return a string representation of a protocol phase. */
+STATIC const char *
+get_phase_str(sr_phase_t phase)
+{
+ const char *the_string = NULL;
+
+ switch (phase) {
+ case SR_PHASE_COMMIT:
+ case SR_PHASE_REVEAL:
+ the_string = phase_str[phase];
+ break;
+ default:
+ /* Unknown phase shouldn't be possible. */
+ tor_assert(0);
+ }
+
+ return the_string;
+}
+
+/* Return the voting interval of the tor vote subsystem. */
+static int
+get_voting_interval(void)
+{
+ int interval;
+ networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
+
+ if (consensus) {
+ interval = (int)(consensus->fresh_until - consensus->valid_after);
+ } else {
+ /* Same for both a testing and real network. We voluntarily ignore the
+ * InitialVotingInterval since it complexifies things and it doesn't
+ * affect the SR protocol. */
+ interval = get_options()->V3AuthVotingInterval;
+ }
+ tor_assert(interval > 0);
+ return interval;
+}
+
+/* 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)
+{
+ 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;
+ /* 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);
+
+ voting_schedule_free(new_voting_schedule);
+
+ return curr_start;
+}
+
+/* Return the time we should expire the state file created at <b>now</b>.
+ * We expire the state file in the beginning of the next protocol run. */
+STATIC time_t
+get_state_valid_until_time(time_t now)
+{
+ int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ int current_round, voting_interval, rounds_left;
+ time_t valid_until, beginning_of_current_round;
+
+ voting_interval = get_voting_interval();
+ /* Find the time the current round started. */
+ beginning_of_current_round = get_start_time_of_current_round(now);
+
+ /* Find how many rounds are left till the end of the protocol run */
+ current_round = (now / voting_interval) % total_rounds;
+ rounds_left = total_rounds - current_round;
+
+ /* To find the valid-until time now, take the start time of the current
+ * round and add to it the time it takes for the leftover rounds to
+ * complete. */
+ valid_until = beginning_of_current_round + (rounds_left * voting_interval);
+
+ { /* Logging */
+ char tbuf[ISO_TIME_LEN + 1];
+ format_iso_time(tbuf, valid_until);
+ log_debug(LD_DIR, "SR: Valid until time for state set to %s.", tbuf);
+ }
+
+ return valid_until;
+}
+
+/* Given the consensus 'valid-after' time, return the protocol phase we should
+ * be in. */
+STATIC sr_phase_t
+get_sr_protocol_phase(time_t valid_after)
+{
+ /* Shared random protocol has two phases, commit and reveal. */
+ int total_periods = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ int current_slot;
+
+ /* Split time into slots of size 'voting_interval'. See which slot we are
+ * currently into, and find which phase it corresponds to. */
+ current_slot = (valid_after / get_voting_interval()) % total_periods;
+
+ if (current_slot < SHARED_RANDOM_N_ROUNDS) {
+ return SR_PHASE_COMMIT;
+ } else {
+ return SR_PHASE_REVEAL;
+ }
+}
+
+/* Add the given <b>commit</b> to <b>state</b>. It MUST be a valid commit
+ * and there shouldn't be a commit from the same authority in the state
+ * already else verification hasn't been done prior. This takes ownership of
+ * the commit once in our state. */
+static void
+commit_add_to_state(sr_commit_t *commit, sr_state_t *state)
+{
+ sr_commit_t *saved_commit;
+
+ tor_assert(commit);
+ tor_assert(state);
+
+ saved_commit = digestmap_set(state->commits, commit->rsa_identity,
+ commit);
+ if (saved_commit != NULL) {
+ /* This means we already have that commit in our state so adding twice
+ * the same commit is either a code flow error, a corrupted disk state
+ * or some new unknown issue. */
+ log_warn(LD_DIR, "SR: Commit from %s exists in our state while "
+ "adding it: '%s'", sr_commit_get_rsa_fpr(commit),
+ commit->encoded_commit);
+ sr_commit_free(saved_commit);
+ }
+}
+
+/* Helper: deallocate a commit object. (Used with digestmap_free(), which
+ * requires a function pointer whose argument is void *). */
+static void
+commit_free_(void *p)
+{
+ sr_commit_free(p);
+}
+
+/* Free a state that was allocated with state_new(). */
+static void
+state_free(sr_state_t *state)
+{
+ if (state == NULL) {
+ return;
+ }
+ tor_free(state->fname);
+ digestmap_free(state->commits, commit_free_);
+ tor_free(state->current_srv);
+ tor_free(state->previous_srv);
+ tor_free(state);
+}
+
+/* Allocate an sr_state_t object and returns it. If no <b>fname</b>, the
+ * default file name is used. This function does NOT initialize the state
+ * timestamp, phase or shared random value. NULL is never returned. */
+static sr_state_t *
+state_new(const char *fname, time_t now)
+{
+ sr_state_t *new_state = tor_malloc_zero(sizeof(*new_state));
+ /* If file name is not provided, use default. */
+ if (fname == NULL) {
+ fname = default_fname;
+ }
+ new_state->fname = tor_strdup(fname);
+ new_state->version = SR_PROTO_VERSION;
+ new_state->commits = digestmap_new();
+ new_state->phase = get_sr_protocol_phase(now);
+ new_state->valid_until = get_state_valid_until_time(now);
+ return new_state;
+}
+
+/* Set our global state pointer with the one given. */
+static void
+state_set(sr_state_t *state)
+{
+ tor_assert(state);
+ if (sr_state != NULL) {
+ state_free(sr_state);
+ }
+ sr_state = state;
+}
+
+/* Free an allocated disk state. */
+static void
+disk_state_free(sr_disk_state_t *state)
+{
+ if (state == NULL) {
+ return;
+ }
+ config_free(&state_format, state);
+}
+
+/* Allocate a new disk state, initialize it and return it. */
+static sr_disk_state_t *
+disk_state_new(time_t now)
+{
+ sr_disk_state_t *new_state = tor_malloc_zero(sizeof(*new_state));
+
+ new_state->magic_ = SR_DISK_STATE_MAGIC;
+ new_state->Version = SR_PROTO_VERSION;
+ new_state->TorVersion = tor_strdup(get_version());
+ new_state->ValidUntil = get_state_valid_until_time(now);
+ new_state->ValidAfter = now;
+
+ /* Init config format. */
+ config_init(&state_format, new_state);
+ return new_state;
+}
+
+/* Set our global disk state with the given state. */
+static void
+disk_state_set(sr_disk_state_t *state)
+{
+ tor_assert(state);
+ if (sr_disk_state != NULL) {
+ disk_state_free(sr_disk_state);
+ }
+ sr_disk_state = state;
+}
+
+/* Return -1 if the disk state is invalid (something in there that we can't or
+ * shouldn't use). Return 0 if everything checks out. */
+static int
+disk_state_validate(const sr_disk_state_t *state)
+{
+ time_t now;
+
+ tor_assert(state);
+
+ /* Do we support the protocol version in the state or is it 0 meaning
+ * Version wasn't found in the state file or bad anyway ? */
+ if (state->Version == 0 || state->Version > SR_PROTO_VERSION) {
+ goto invalid;
+ }
+
+ /* If the valid until time is before now, we shouldn't use that state. */
+ now = time(NULL);
+ if (state->ValidUntil < now) {
+ log_info(LD_DIR, "SR: Disk state has expired. Ignoring it.");
+ goto invalid;
+ }
+
+ /* Make sure we don't have a valid after time that is earlier than a valid
+ * until time which would make things not work well. */
+ if (state->ValidAfter >= state->ValidUntil) {
+ log_info(LD_DIR, "SR: Disk state valid after/until times are invalid.");
+ goto invalid;
+ }
+
+ return 0;
+
+ invalid:
+ return -1;
+}
+
+/* Validate the disk state (NOP for now). */
+static int
+disk_state_validate_cb(void *old_state, void *state, void *default_state,
+ int from_setconf, char **msg)
+{
+ /* We don't use these; only options do. */
+ (void) from_setconf;
+ (void) default_state;
+ (void) old_state;
+
+ /* This is called by config_dump which is just before we are about to
+ * write it to disk. At that point, our global memory state has been
+ * copied to the disk state so it's fair to assume it's trustable. */
+ (void) state;
+ (void) msg;
+ return 0;
+}
+
+/* Parse the Commit line(s) in the disk state and translate them to the
+ * the memory state. Return 0 on success else -1 on error. */
+static int
+disk_state_parse_commits(sr_state_t *state,
+ const sr_disk_state_t *disk_state)
+{
+ config_line_t *line;
+ smartlist_t *args = NULL;
+
+ tor_assert(state);
+ tor_assert(disk_state);
+
+ for (line = disk_state->Commit; line; line = line->next) {
+ sr_commit_t *commit = NULL;
+
+ /* Extra safety. */
+ if (strcasecmp(line->key, dstate_commit_key) ||
+ line->value == NULL) {
+ /* Ignore any lines that are not commits. */
+ tor_fragile_assert();
+ continue;
+ }
+ args = smartlist_new();
+ smartlist_split_string(args, line->value, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(args) < 3) {
+ log_warn(LD_BUG, "SR: Too few arguments in Commit Line: %s",
+ escaped(line->value));
+ goto error;
+ }
+ commit = sr_parse_commit(args);
+ if (commit == NULL) {
+ /* Ignore badly formed commit. It could also be a authority
+ * fingerprint that we don't know about so it shouldn't be used. */
+ continue;
+ }
+ /* We consider parseable commit from our disk state to be valid because
+ * they need to be in the first place to get in there. */
+ commit->valid = 1;
+ /* Add commit to our state pointer. */
+ commit_add_to_state(commit, state);
+
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ }
+
+ return 0;
+
+ error:
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ return -1;
+}
+
+/* Parse a share random value line from the disk state and save it to dst
+ * which is an allocated srv object. Return 0 on success else -1. */
+static int
+disk_state_parse_srv(const char *value, sr_srv_t *dst)
+{
+ int ret = -1;
+ smartlist_t *args;
+ sr_srv_t *srv;
+
+ tor_assert(value);
+ tor_assert(dst);
+
+ args = smartlist_new();
+ smartlist_split_string(args, value, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(args) < 2) {
+ log_warn(LD_BUG, "SR: Too few arguments in shared random value. "
+ "Line: %s", escaped(value));
+ goto error;
+ }
+ srv = sr_parse_srv(args);
+ if (srv == NULL) {
+ goto error;
+ }
+ dst->num_reveals = srv->num_reveals;
+ memcpy(dst->value, srv->value, sizeof(dst->value));
+ tor_free(srv);
+ ret = 0;
+
+ error:
+ SMARTLIST_FOREACH(args, char *, s, tor_free(s));
+ smartlist_free(args);
+ return ret;
+}
+
+/* Parse both SharedRandCurrentValue and SharedRandPreviousValue line from
+ * the state. Return 0 on success else -1. */
+static int
+disk_state_parse_sr_values(sr_state_t *state,
+ const sr_disk_state_t *disk_state)
+{
+ /* Only one value per type (current or previous) is allowed so we keep
+ * track of it with these flag. */
+ unsigned int seen_previous = 0, seen_current = 0;
+ config_line_t *line;
+ sr_srv_t *srv = NULL;
+
+ tor_assert(state);
+ tor_assert(disk_state);
+
+ for (line = disk_state->SharedRandValues; line; line = line->next) {
+ if (line->value == NULL) {
+ continue;
+ }
+ srv = tor_malloc_zero(sizeof(*srv));
+ if (disk_state_parse_srv(line->value, srv) < 0) {
+ log_warn(LD_BUG, "SR: Broken current SRV line in state %s",
+ escaped(line->value));
+ goto bad;
+ }
+ if (!strcasecmp(line->key, dstate_prev_srv_key)) {
+ if (seen_previous) {
+ log_warn(LD_DIR, "SR: Second previous SRV value seen. Bad state");
+ goto bad;
+ }
+ state->previous_srv = srv;
+ seen_previous = 1;
+ } else if (!strcasecmp(line->key, dstate_cur_srv_key)) {
+ if (seen_current) {
+ log_warn(LD_DIR, "SR: Second current SRV value seen. Bad state");
+ goto bad;
+ }
+ state->current_srv = srv;
+ seen_current = 1;
+ } else {
+ /* Unknown key. Ignoring. */
+ tor_free(srv);
+ }
+ }
+
+ return 0;
+ bad:
+ tor_free(srv);
+ return -1;
+}
+
+/* Parse the given disk state and set a newly allocated state. On success,
+ * return that state else NULL. */
+static sr_state_t *
+disk_state_parse(const sr_disk_state_t *new_disk_state)
+{
+ sr_state_t *new_state = state_new(default_fname, time(NULL));
+
+ tor_assert(new_disk_state);
+
+ new_state->version = new_disk_state->Version;
+ new_state->valid_until = new_disk_state->ValidUntil;
+ new_state->valid_after = new_disk_state->ValidAfter;
+
+ /* Set our current phase according to the valid-after time in our disk
+ * state. The disk state we are parsing contains everything for the phase
+ * starting at valid_after so make sure our phase reflects that. */
+ new_state->phase = get_sr_protocol_phase(new_state->valid_after);
+
+ /* Parse the shared random values. */
+ if (disk_state_parse_sr_values(new_state, new_disk_state) < 0) {
+ goto error;
+ }
+ /* Parse the commits. */
+ if (disk_state_parse_commits(new_state, new_disk_state) < 0) {
+ goto error;
+ }
+ /* Great! This new state contains everything we had on disk. */
+ return new_state;
+
+ error:
+ state_free(new_state);
+ return NULL;
+}
+
+/* From a valid commit object and an allocated config line, set the line's
+ * value to the state string representation of a commit. */
+static void
+disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line)
+{
+ char *reveal_str = NULL;
+
+ tor_assert(commit);
+ tor_assert(line);
+
+ if (!tor_mem_is_zero(commit->encoded_reveal,
+ sizeof(commit->encoded_reveal))) {
+ /* Add extra whitespace so we can format the line correctly. */
+ tor_asprintf(&reveal_str, " %s", commit->encoded_reveal);
+ }
+ tor_asprintf(&line->value, "%u %s %s %s%s",
+ SR_PROTO_VERSION,
+ crypto_digest_algorithm_get_name(commit->alg),
+ sr_commit_get_rsa_fpr(commit),
+ commit->encoded_commit,
+ reveal_str != NULL ? reveal_str : "");
+ if (reveal_str != NULL) {
+ memwipe(reveal_str, 0, strlen(reveal_str));
+ tor_free(reveal_str);
+ }
+}
+
+/* From a valid srv object and an allocated config line, set the line's
+ * value to the state string representation of a shared random value. */
+static void
+disk_state_put_srv_line(const sr_srv_t *srv, config_line_t *line)
+{
+ char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+
+ tor_assert(line);
+
+ /* No SRV value thus don't add the line. This is possible since we might
+ * not have a current or previous SRV value in our state. */
+ if (srv == NULL) {
+ return;
+ }
+ sr_srv_encode(encoded, sizeof(encoded), srv);
+ tor_asprintf(&line->value, "%" PRIu64 " %s", srv->num_reveals, encoded);
+}
+
+/* Reset disk state that is free allocated memory and zeroed the object. */
+static void
+disk_state_reset(void)
+{
+ /* Free allocated memory */
+ config_free_lines(sr_disk_state->Commit);
+ config_free_lines(sr_disk_state->SharedRandValues);
+ config_free_lines(sr_disk_state->ExtraLines);
+ tor_free(sr_disk_state->TorVersion);
+
+ /* Clean up the struct */
+ memset(sr_disk_state, 0, sizeof(*sr_disk_state));
+
+ /* Reset it with useful data */
+ sr_disk_state->magic_ = SR_DISK_STATE_MAGIC;
+ sr_disk_state->TorVersion = tor_strdup(get_version());
+}
+
+/* Update our disk state based on our global SR state. */
+static void
+disk_state_update(void)
+{
+ config_line_t **next, *line;
+
+ tor_assert(sr_disk_state);
+ tor_assert(sr_state);
+
+ /* Reset current disk state. */
+ disk_state_reset();
+
+ /* First, update elements that we don't need to do a construction. */
+ sr_disk_state->Version = sr_state->version;
+ sr_disk_state->ValidUntil = sr_state->valid_until;
+ sr_disk_state->ValidAfter = sr_state->valid_after;
+
+ /* Shared random values. */
+ next = &sr_disk_state->SharedRandValues;
+ if (sr_state->previous_srv != NULL) {
+ *next = line = tor_malloc_zero(sizeof(config_line_t));
+ line->key = tor_strdup(dstate_prev_srv_key);
+ disk_state_put_srv_line(sr_state->previous_srv, line);
+ /* Go to the next shared random value. */
+ next = &(line->next);
+ }
+ if (sr_state->current_srv != NULL) {
+ *next = line = tor_malloc_zero(sizeof(*line));
+ line->key = tor_strdup(dstate_cur_srv_key);
+ disk_state_put_srv_line(sr_state->current_srv, line);
+ }
+
+ /* Parse the commits and construct config line(s). */
+ next = &sr_disk_state->Commit;
+ DIGESTMAP_FOREACH(sr_state->commits, key, sr_commit_t *, commit) {
+ *next = line = tor_malloc_zero(sizeof(*line));
+ line->key = tor_strdup(dstate_commit_key);
+ disk_state_put_commit_line(commit, line);
+ next = &(line->next);
+ } DIGESTMAP_FOREACH_END;
+}
+
+/* Load state from disk and put it into our disk state. If the state passes
+ * validation, our global state will be updated with it. Return 0 on
+ * success. On error, -EINVAL is returned if the state on disk did contained
+ * something malformed or is unreadable. -ENOENT is returned indicating that
+ * the state file is either empty of non existing. */
+static int
+disk_state_load_from_disk(void)
+{
+ int ret;
+ char *fname;
+
+ fname = get_datadir_fname(default_fname);
+ ret = disk_state_load_from_disk_impl(fname);
+ tor_free(fname);
+
+ return ret;
+}
+
+/* Helper for disk_state_load_from_disk(). */
+STATIC int
+disk_state_load_from_disk_impl(const char *fname)
+{
+ int ret;
+ char *content = NULL;
+ sr_state_t *parsed_state = NULL;
+ sr_disk_state_t *disk_state = NULL;
+
+ /* Read content of file so we can parse it. */
+ if ((content = read_file_to_str(fname, 0, NULL)) == NULL) {
+ log_warn(LD_FS, "SR: Unable to read SR state file %s",
+ escaped(fname));
+ ret = -errno;
+ goto error;
+ }
+
+ {
+ config_line_t *lines = NULL;
+ char *errmsg = NULL;
+
+ /* Every error in this code path will return EINVAL. */
+ ret = -EINVAL;
+ if (config_get_lines(content, &lines, 0) < 0) {
+ config_free_lines(lines);
+ goto error;
+ }
+
+ disk_state = disk_state_new(time(NULL));
+ config_assign(&state_format, disk_state, lines, 0, &errmsg);
+ config_free_lines(lines);
+ if (errmsg) {
+ log_warn(LD_DIR, "SR: Reading state error: %s", errmsg);
+ tor_free(errmsg);
+ goto error;
+ }
+ }
+
+ /* So far so good, we've loaded our state file into our disk state. Let's
+ * validate it and then parse it. */
+ if (disk_state_validate(disk_state) < 0) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ parsed_state = disk_state_parse(disk_state);
+ if (parsed_state == NULL) {
+ ret = -EINVAL;
+ goto error;
+ }
+ state_set(parsed_state);
+ disk_state_set(disk_state);
+ tor_free(content);
+ log_info(LD_DIR, "SR: State loaded successfully from file %s", fname);
+ return 0;
+
+ error:
+ disk_state_free(disk_state);
+ tor_free(content);
+ return ret;
+}
+
+/* Save the disk state to disk but before that update it from the current
+ * state so we always have the latest. Return 0 on success else -1. */
+static int
+disk_state_save_to_disk(void)
+{
+ int ret;
+ char *state, *content = NULL, *fname = NULL;
+ char tbuf[ISO_TIME_LEN + 1];
+ time_t now = time(NULL);
+
+ /* If we didn't have the opportunity to setup an internal disk state,
+ * don't bother saving something to disk. */
+ if (sr_disk_state == NULL) {
+ ret = 0;
+ goto done;
+ }
+
+ /* Make sure that our disk state is up to date with our memory state
+ * before saving it to disk. */
+ disk_state_update();
+ state = config_dump(&state_format, NULL, sr_disk_state, 0, 0);
+ format_local_iso_time(tbuf, now);
+ tor_asprintf(&content,
+ "# Tor shared random state file last generated on %s "
+ "local time\n"
+ "# Other times below are in UTC\n"
+ "# Please *do not* edit this file.\n\n%s",
+ tbuf, state);
+ tor_free(state);
+ fname = get_datadir_fname(default_fname);
+ if (write_str_to_file(fname, content, 0) < 0) {
+ log_warn(LD_FS, "SR: Unable to write SR state to file %s", fname);
+ ret = -1;
+ goto done;
+ }
+ ret = 0;
+ log_debug(LD_DIR, "SR: Saved state to file %s", fname);
+
+ done:
+ tor_free(fname);
+ tor_free(content);
+ return ret;
+}
+
+/* Reset our state to prepare for a new protocol run. Once this returns, all
+ * commits in the state will be removed and freed. */
+STATIC void
+reset_state_for_new_protocol_run(time_t valid_after)
+{
+ tor_assert(sr_state);
+
+ /* Keep counters in track */
+ sr_state->n_reveal_rounds = 0;
+ sr_state->n_commit_rounds = 0;
+ sr_state->n_protocol_runs++;
+
+ /* Reset valid-until */
+ sr_state->valid_until = get_state_valid_until_time(valid_after);
+ sr_state->valid_after = valid_after;
+
+ /* We are in a new protocol run so cleanup commits. */
+ sr_state_delete_commits();
+}
+
+/* This is the first round of the new protocol run starting at
+ * <b>valid_after</b>. Do the necessary housekeeping. */
+STATIC void
+new_protocol_run(time_t valid_after)
+{
+ sr_commit_t *our_commitment = NULL;
+
+ /* Only compute the srv at the end of the reveal phase. */
+ if (sr_state->phase == SR_PHASE_REVEAL) {
+ /* We are about to compute a new shared random value that will be set in
+ * our state as the current value so rotate values. */
+ state_rotate_srv();
+ /* Compute the shared randomness value of the day. */
+ sr_compute_srv();
+ }
+
+ /* Prepare for the new protocol run by reseting the state */
+ reset_state_for_new_protocol_run(valid_after);
+
+ /* Do some logging */
+ log_info(LD_DIR, "SR: Protocol run #%" PRIu64 " starting!",
+ sr_state->n_protocol_runs);
+
+ /* Generate fresh commitments for this protocol run */
+ our_commitment = sr_generate_our_commit(valid_after,
+ get_my_v3_authority_cert());
+ if (our_commitment) {
+ /* Add our commitment to our state. In case we are unable to create one
+ * (highly unlikely), we won't vote for this protocol run since our
+ * commitment won't be in our state. */
+ sr_state_add_commit(our_commitment);
+ }
+}
+
+/* Return 1 iff the <b>next_phase</b> is a phase transition from the current
+ * phase that is it's different. */
+STATIC int
+is_phase_transition(sr_phase_t next_phase)
+{
+ return sr_state->phase != next_phase;
+}
+
+/* Helper function: return a commit using the RSA fingerprint of the
+ * authority or NULL if no such commit is known. */
+static sr_commit_t *
+state_query_get_commit(const char *rsa_fpr)
+{
+ tor_assert(rsa_fpr);
+ return digestmap_get(sr_state->commits, rsa_fpr);
+}
+
+/* Helper function: This handles the GET state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void *
+state_query_get_(sr_state_object_t obj_type, const void *data)
+{
+ void *obj = NULL;
+
+ switch (obj_type) {
+ case SR_STATE_OBJ_COMMIT:
+ {
+ obj = state_query_get_commit(data);
+ break;
+ }
+ case SR_STATE_OBJ_COMMITS:
+ obj = sr_state->commits;
+ break;
+ case SR_STATE_OBJ_CURSRV:
+ obj = sr_state->current_srv;
+ break;
+ case SR_STATE_OBJ_PREVSRV:
+ obj = sr_state->previous_srv;
+ break;
+ case SR_STATE_OBJ_PHASE:
+ obj = &sr_state->phase;
+ break;
+ case SR_STATE_OBJ_VALID_AFTER:
+ default:
+ tor_assert(0);
+ }
+ return obj;
+}
+
+/* Helper function: This handles the PUT state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void
+state_query_put_(sr_state_object_t obj_type, void *data)
+{
+ switch (obj_type) {
+ case SR_STATE_OBJ_COMMIT:
+ {
+ sr_commit_t *commit = data;
+ tor_assert(commit);
+ commit_add_to_state(commit, sr_state);
+ break;
+ }
+ case SR_STATE_OBJ_CURSRV:
+ sr_state->current_srv = (sr_srv_t *) data;
+ break;
+ case SR_STATE_OBJ_PREVSRV:
+ sr_state->previous_srv = (sr_srv_t *) data;
+ break;
+ case SR_STATE_OBJ_VALID_AFTER:
+ sr_state->valid_after = *((time_t *) data);
+ break;
+ /* It's not allowed to change the phase nor the full commitments map from
+ * the state. The phase is decided during a strict process post voting and
+ * the commits should be put individually. */
+ case SR_STATE_OBJ_PHASE:
+ case SR_STATE_OBJ_COMMITS:
+ default:
+ tor_assert(0);
+ }
+}
+
+/* Helper function: This handles the DEL_ALL state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void
+state_query_del_all_(sr_state_object_t obj_type)
+{
+ switch (obj_type) {
+ case SR_STATE_OBJ_COMMIT:
+ {
+ /* We are in a new protocol run so cleanup commitments. */
+ DIGESTMAP_FOREACH_MODIFY(sr_state->commits, key, sr_commit_t *, c) {
+ sr_commit_free(c);
+ MAP_DEL_CURRENT(key);
+ } DIGESTMAP_FOREACH_END;
+ break;
+ }
+ /* The following object are _NOT_ suppose to be removed. */
+ case SR_STATE_OBJ_CURSRV:
+ case SR_STATE_OBJ_PREVSRV:
+ case SR_STATE_OBJ_PHASE:
+ case SR_STATE_OBJ_COMMITS:
+ case SR_STATE_OBJ_VALID_AFTER:
+ default:
+ tor_assert(0);
+ }
+}
+
+/* Helper function: This handles the DEL state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void
+state_query_del_(sr_state_object_t obj_type, void *data)
+{
+ (void) data;
+
+ switch (obj_type) {
+ case SR_STATE_OBJ_PREVSRV:
+ tor_free(sr_state->previous_srv);
+ break;
+ case SR_STATE_OBJ_CURSRV:
+ tor_free(sr_state->current_srv);
+ break;
+ case SR_STATE_OBJ_COMMIT:
+ case SR_STATE_OBJ_COMMITS:
+ case SR_STATE_OBJ_PHASE:
+ case SR_STATE_OBJ_VALID_AFTER:
+ default:
+ tor_assert(0);
+ }
+}
+
+/* Query state using an <b>action</b> for an object type <b>obj_type</b>.
+ * The <b>data</b> pointer needs to point to an object that the action needs
+ * to use and if anything is required to be returned, it is stored in
+ * <b>out</b>.
+ *
+ * This mechanism exists so we have one single point where we synchronized
+ * our memory state with our disk state for every actions that changes it.
+ * We then trigger a write on disk immediately.
+ *
+ * This should be the only entry point to our memory state. It's used by all
+ * our state accessors and should be in the future. */
+static void
+state_query(sr_state_action_t action, sr_state_object_t obj_type,
+ void *data, void **out)
+{
+ switch (action) {
+ case SR_STATE_ACTION_GET:
+ *out = state_query_get_(obj_type, data);
+ break;
+ case SR_STATE_ACTION_PUT:
+ state_query_put_(obj_type, data);
+ break;
+ case SR_STATE_ACTION_DEL:
+ state_query_del_(obj_type, data);
+ break;
+ case SR_STATE_ACTION_DEL_ALL:
+ state_query_del_all_(obj_type);
+ break;
+ case SR_STATE_ACTION_SAVE:
+ /* Only trigger a disk state save. */
+ break;
+ default:
+ tor_assert(0);
+ }
+
+ /* If the action actually changes the state, immediately save it to disk.
+ * The following will sync the state -> disk state and then save it. */
+ if (action != SR_STATE_ACTION_GET) {
+ disk_state_save_to_disk();
+ }
+}
+
+/* Delete the current SRV value from the state freeing it and the value is set
+ * to NULL meaning empty. */
+static void
+state_del_current_srv(void)
+{
+ state_query(SR_STATE_ACTION_DEL, SR_STATE_OBJ_CURSRV, NULL, NULL);
+}
+
+/* Delete the previous SRV value from the state freeing it and the value is
+ * set to NULL meaning empty. */
+static void
+state_del_previous_srv(void)
+{
+ state_query(SR_STATE_ACTION_DEL, SR_STATE_OBJ_PREVSRV, NULL, NULL);
+}
+
+/* Rotate SRV value by freeing the previous value, assigning the current
+ * value to the previous one and nullifying the current one. */
+STATIC void
+state_rotate_srv(void)
+{
+ /* First delete previous SRV from the state. Object will be freed. */
+ state_del_previous_srv();
+ /* Set previous SRV with the current one. */
+ sr_state_set_previous_srv(sr_state_get_current_srv());
+ /* Nullify the current srv. */
+ sr_state_set_current_srv(NULL);
+}
+
+/* Set valid after time in the our state. */
+void
+sr_state_set_valid_after(time_t valid_after)
+{
+ state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_VALID_AFTER,
+ (void *) &valid_after, NULL);
+}
+
+/* Return the phase we are currently in according to our state. */
+sr_phase_t
+sr_state_get_phase(void)
+{
+ void *ptr;
+ state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_PHASE, NULL, &ptr);
+ return *(sr_phase_t *) ptr;
+}
+
+/* Return the previous SRV value from our state. Value CAN be NULL. */
+const sr_srv_t *
+sr_state_get_previous_srv(void)
+{
+ const sr_srv_t *srv;
+ state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_PREVSRV, NULL,
+ (void *) &srv);
+ return srv;
+}
+
+/* Set the current SRV value from our state. Value CAN be NULL. The srv
+ * object ownership is transfered to the state object. */
+void
+sr_state_set_previous_srv(const sr_srv_t *srv)
+{
+ state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_PREVSRV, (void *) srv,
+ NULL);
+}
+
+/* Return the current SRV value from our state. Value CAN be NULL. */
+const sr_srv_t *
+sr_state_get_current_srv(void)
+{
+ const sr_srv_t *srv;
+ state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_CURSRV, NULL,
+ (void *) &srv);
+ return srv;
+}
+
+/* Set the current SRV value from our state. Value CAN be NULL. The srv
+ * object ownership is transfered to the state object. */
+void
+sr_state_set_current_srv(const sr_srv_t *srv)
+{
+ state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_CURSRV, (void *) srv,
+ NULL);
+}
+
+/* Clean all the SRVs in our state. */
+void
+sr_state_clean_srvs(void)
+{
+ /* Remove SRVs from state. They will be set to NULL as "empty". */
+ state_del_previous_srv();
+ state_del_current_srv();
+}
+
+/* Return a pointer to the commits map from our state. CANNOT be NULL. */
+digestmap_t *
+sr_state_get_commits(void)
+{
+ digestmap_t *commits;
+ state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_COMMITS,
+ NULL, (void *) &commits);
+ tor_assert(commits);
+ return commits;
+}
+
+/* Update the current SR state as needed for the upcoming voting round at
+ * <b>valid_after</b>. */
+void
+sr_state_update(time_t valid_after)
+{
+ sr_phase_t next_phase;
+
+ tor_assert(sr_state);
+
+ /* Don't call this function twice in the same voting period. */
+ if (valid_after <= sr_state->valid_after) {
+ log_info(LD_DIR, "SR: Asked to update state twice. Ignoring.");
+ return;
+ }
+
+ /* Get phase of upcoming round. */
+ next_phase = get_sr_protocol_phase(valid_after);
+
+ /* If we are transitioning to a new protocol phase, prepare the stage. */
+ if (is_phase_transition(next_phase)) {
+ if (next_phase == SR_PHASE_COMMIT) {
+ /* Going into commit phase means we are starting a new protocol run. */
+ new_protocol_run(valid_after);
+ }
+ /* Set the new phase for this round */
+ sr_state->phase = next_phase;
+ } else if (sr_state->phase == SR_PHASE_COMMIT &&
+ digestmap_size(sr_state->commits) == 0) {
+ /* We are _NOT_ in a transition phase so if we are in the commit phase
+ * and have no commit, generate one. Chances are that we are booting up
+ * so let's have a commit in our state for the next voting period. */
+ sr_commit_t *our_commit =
+ sr_generate_our_commit(valid_after, get_my_v3_authority_cert());
+ if (our_commit) {
+ /* Add our commitment to our state. In case we are unable to create one
+ * (highly unlikely), we won't vote for this protocol run since our
+ * commitment won't be in our state. */
+ sr_state_add_commit(our_commit);
+ }
+ }
+
+ sr_state_set_valid_after(valid_after);
+
+ /* Count the current round */
+ if (sr_state->phase == SR_PHASE_COMMIT) {
+ /* invariant check: we've not entered reveal phase yet */
+ tor_assert(sr_state->n_reveal_rounds == 0);
+ sr_state->n_commit_rounds++;
+ } else {
+ sr_state->n_reveal_rounds++;
+ }
+
+ { /* Debugging. */
+ char tbuf[ISO_TIME_LEN + 1];
+ format_iso_time(tbuf, valid_after);
+ log_info(LD_DIR, "SR: State prepared for upcoming voting period (%s). "
+ "Upcoming phase is %s (counters: %d commit & %d reveal rounds).",
+ tbuf, get_phase_str(sr_state->phase),
+ sr_state->n_commit_rounds, sr_state->n_reveal_rounds);
+ }
+}
+
+/* Return commit object from the given authority digest <b>rsa_identity</b>.
+ * Return NULL if not found. */
+sr_commit_t *
+sr_state_get_commit(const char *rsa_identity)
+{
+ sr_commit_t *commit;
+
+ tor_assert(rsa_identity);
+
+ state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_COMMIT,
+ (void *) rsa_identity, (void *) &commit);
+ return commit;
+}
+
+/* Add <b>commit</b> to the permanent state. The commit object ownership is
+ * transfered to the state so the caller MUST not free it. */
+void
+sr_state_add_commit(sr_commit_t *commit)
+{
+ tor_assert(commit);
+
+ /* Put the commit to the global state. */
+ state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_COMMIT,
+ (void *) commit, NULL);
+
+ log_debug(LD_DIR, "SR: Commit from %s has been added to our state.",
+ sr_commit_get_rsa_fpr(commit));
+}
+
+/* Remove all commits from our state. */
+void
+sr_state_delete_commits(void)
+{
+ state_query(SR_STATE_ACTION_DEL_ALL, SR_STATE_OBJ_COMMIT, NULL, NULL);
+}
+
+/* Copy the reveal information from <b>commit</b> into <b>saved_commit</b>.
+ * This <b>saved_commit</b> MUST come from our current SR state. Once modified,
+ * the disk state is updated. */
+void
+sr_state_copy_reveal_info(sr_commit_t *saved_commit, const sr_commit_t *commit)
+{
+ tor_assert(saved_commit);
+ tor_assert(commit);
+
+ saved_commit->reveal_ts = commit->reveal_ts;
+ memcpy(saved_commit->random_number, commit->random_number,
+ sizeof(saved_commit->random_number));
+
+ strlcpy(saved_commit->encoded_reveal, commit->encoded_reveal,
+ sizeof(saved_commit->encoded_reveal));
+ state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL);
+ log_debug(LD_DIR, "SR: Reveal value learned %s (for commit %s) from %s",
+ saved_commit->encoded_reveal, saved_commit->encoded_commit,
+ sr_commit_get_rsa_fpr(saved_commit));
+}
+
+/* Set the fresh SRV flag from our state. This doesn't need to trigger a
+ * disk state synchronization so we directly change the state. */
+void
+sr_state_set_fresh_srv(void)
+{
+ sr_state->is_srv_fresh = 1;
+}
+
+/* Unset the fresh SRV flag from our state. This doesn't need to trigger a
+ * disk state synchronization so we directly change the state. */
+void
+sr_state_unset_fresh_srv(void)
+{
+ sr_state->is_srv_fresh = 0;
+}
+
+/* Return the value of the fresh SRV flag. */
+unsigned int
+sr_state_srv_is_fresh(void)
+{
+ return sr_state->is_srv_fresh;
+}
+
+/* Cleanup and free our disk and memory state. */
+void
+sr_state_free(void)
+{
+ state_free(sr_state);
+ disk_state_free(sr_disk_state);
+ /* Nullify our global state. */
+ sr_state = NULL;
+ sr_disk_state = NULL;
+}
+
+/* Save our current state in memory to disk. */
+void
+sr_state_save(void)
+{
+ /* Query a SAVE action on our current state so it's synced and saved. */
+ state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL);
+}
+
+/* Return 1 iff the state has been initialized that is it exists in memory.
+ * Return 0 otherwise. */
+int
+sr_state_is_initialized(void)
+{
+ return sr_state == NULL ? 0 : 1;
+}
+
+/* Initialize the disk and memory state.
+ *
+ * If save_to_disk is set to 1, the state is immediately saved to disk after
+ * creation else it's not thus only kept in memory.
+ * If read_from_disk is set to 1, we try to load the state from the disk and
+ * if not found, a new state is created.
+ *
+ * Return 0 on success else a negative value on error. */
+int
+sr_state_init(int save_to_disk, int read_from_disk)
+{
+ int ret = -ENOENT;
+ time_t now = time(NULL);
+
+ /* We shouldn't have those assigned. */
+ tor_assert(sr_disk_state == NULL);
+ tor_assert(sr_state == NULL);
+
+ /* First, try to load the state from disk. */
+ if (read_from_disk) {
+ ret = disk_state_load_from_disk();
+ }
+
+ if (ret < 0) {
+ switch (-ret) {
+ case EINVAL:
+ /* We have a state on disk but it contains something we couldn't parse
+ * or an invalid entry in the state file. Let's remove it since it's
+ * obviously unusable and replace it by an new fresh state below. */
+ case ENOENT:
+ {
+ /* No state on disk so allocate our states for the first time. */
+ sr_state_t *new_state = state_new(default_fname, now);
+ sr_disk_state_t *new_disk_state = disk_state_new(now);
+ state_set(new_state);
+ /* It's important to set our disk state pointer since the save call
+ * below uses it to synchronized it with our memory state. */
+ disk_state_set(new_disk_state);
+ /* No entry, let's save our new state to disk. */
+ if (save_to_disk && disk_state_save_to_disk() < 0) {
+ goto error;
+ }
+ break;
+ }
+ default:
+ /* Big problem. Not possible. */
+ tor_assert(0);
+ }
+ }
+ /* 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);
+ sr_state_update(valid_after);
+ }
+ return 0;
+
+ error:
+ return -1;
+}
+
+#ifdef TOR_UNIT_TESTS
+
+/* Set the current phase of the protocol. Used only by unit tests. */
+void
+set_sr_phase(sr_phase_t phase)
+{
+ tor_assert(sr_state);
+ sr_state->phase = phase;
+}
+
+/* Get the SR state. Used only by unit tests */
+sr_state_t *
+get_sr_state(void)
+{
+ return sr_state;
+}
+
+#endif /* TOR_UNIT_TESTS */
+
diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h
new file mode 100644
index 0000000000..43a7f1d284
--- /dev/null
+++ b/src/or/shared_random_state.h
@@ -0,0 +1,147 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_SHARED_RANDOM_STATE_H
+#define TOR_SHARED_RANDOM_STATE_H
+
+#include "shared_random.h"
+
+/* Action that can be performed on the state for any objects. */
+typedef enum {
+ SR_STATE_ACTION_GET = 1,
+ SR_STATE_ACTION_PUT = 2,
+ SR_STATE_ACTION_DEL = 3,
+ SR_STATE_ACTION_DEL_ALL = 4,
+ SR_STATE_ACTION_SAVE = 5,
+} sr_state_action_t;
+
+/* Object in the state that can be queried through the state API. */
+typedef enum {
+ /* Will return a single commit using an authority identity key. */
+ SR_STATE_OBJ_COMMIT,
+ /* Returns the entire list of commits from the state. */
+ SR_STATE_OBJ_COMMITS,
+ /* Return the current SRV object pointer. */
+ SR_STATE_OBJ_CURSRV,
+ /* Return the previous SRV object pointer. */
+ SR_STATE_OBJ_PREVSRV,
+ /* Return the phase. */
+ SR_STATE_OBJ_PHASE,
+ /* Get or Put the valid after time. */
+ SR_STATE_OBJ_VALID_AFTER,
+} sr_state_object_t;
+
+/* State of the protocol. It's also saved on disk in fname. This data
+ * structure MUST be synchronized at all time with the one on disk. */
+typedef struct sr_state_t {
+ /* Filename of the state file on disk. */
+ char *fname;
+ /* Version of the protocol. */
+ uint32_t version;
+ /* The valid-after of the voting period we have prepared the state for. */
+ time_t valid_after;
+ /* Until when is this state valid? */
+ time_t valid_until;
+ /* Protocol phase. */
+ sr_phase_t phase;
+
+ /* Number of runs completed. */
+ uint64_t n_protocol_runs;
+ /* The number of commitment rounds we've performed in this protocol run. */
+ unsigned int n_commit_rounds;
+ /* The number of reveal rounds we've performed in this protocol run. */
+ unsigned int n_reveal_rounds;
+
+ /* A map of all the received commitments for this protocol run. This is
+ * indexed by authority RSA identity digest. */
+ digestmap_t *commits;
+
+ /* Current and previous shared random value. */
+ sr_srv_t *previous_srv;
+ sr_srv_t *current_srv;
+
+ /* Indicate if the state contains an SRV that was _just_ generated. This is
+ * used during voting so that we know whether to use the super majority rule
+ * or not when deciding on keeping it for the consensus. It is _always_ set
+ * to 0 post consensus.
+ *
+ * EDGE CASE: if an authority computes a new SRV then immediately reboots
+ * and, once back up, votes for the current round, it won't know if the
+ * SRV is fresh or not ultimately making it _NOT_ use the super majority
+ * when deciding to put or not the SRV in the consensus. This is for now
+ * an acceptable very rare edge case. */
+ unsigned int is_srv_fresh:1;
+} sr_state_t;
+
+/* Persistent state of the protocol, as saved to disk. */
+typedef struct sr_disk_state_t {
+ uint32_t magic_;
+ /* Version of the protocol. */
+ uint32_t Version;
+ /* Version of our running tor. */
+ char *TorVersion;
+ /* Creation time of this state */
+ time_t ValidAfter;
+ /* State valid until? */
+ time_t ValidUntil;
+ /* All commits seen that are valid. */
+ config_line_t *Commit;
+ /* Previous and current shared random value. */
+ config_line_t *SharedRandValues;
+ /* Extra Lines for configuration we might not know. */
+ config_line_t *ExtraLines;
+} sr_disk_state_t;
+
+/* API */
+
+/* Public methods: */
+
+void sr_state_update(time_t valid_after);
+
+/* Private methods (only used by shared-random.c): */
+
+void sr_state_set_valid_after(time_t valid_after);
+sr_phase_t sr_state_get_phase(void);
+const sr_srv_t *sr_state_get_previous_srv(void);
+const sr_srv_t *sr_state_get_current_srv(void);
+void sr_state_set_previous_srv(const sr_srv_t *srv);
+void sr_state_set_current_srv(const sr_srv_t *srv);
+void sr_state_clean_srvs(void);
+digestmap_t *sr_state_get_commits(void);
+sr_commit_t *sr_state_get_commit(const char *rsa_fpr);
+void sr_state_add_commit(sr_commit_t *commit);
+void sr_state_delete_commits(void);
+void sr_state_copy_reveal_info(sr_commit_t *saved_commit,
+ const sr_commit_t *commit);
+unsigned int sr_state_srv_is_fresh(void);
+void sr_state_set_fresh_srv(void);
+void sr_state_unset_fresh_srv(void);
+int sr_state_init(int save_to_disk, int read_from_disk);
+int sr_state_is_initialized(void);
+void sr_state_save(void);
+void sr_state_free(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_state_valid_until_time(time_t now);
+STATIC const char *get_phase_str(sr_phase_t phase);
+STATIC void reset_state_for_new_protocol_run(time_t valid_after);
+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 */
+
+#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 /* TOR_SHARED_RANDOM_STATE_H */
+
diff --git a/src/or/statefile.c b/src/or/statefile.c
index 9594d9cec3..8fa4324b25 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -9,6 +9,23 @@
*
* \brief Handles parsing and encoding the persistent 'state' file that carries
* miscellaneous persistent state between Tor invocations.
+ *
+ * This 'state' file is a typed key-value store that allows multiple
+ * entries for the same key. It follows the same metaformat as described
+ * in confparse.c, and uses the same code to read and write itself.
+ *
+ * The state file is most suitable for small values that don't change too
+ * frequently. For values that become very large, we typically use a separate
+ * file -- for example, see how we handle microdescriptors, by storing them in
+ * a separate file with a journal.
+ *
+ * The current state is accessed via get_or_state(), which returns a singleton
+ * or_state_t object. Functions that change it should call
+ * or_state_mark_dirty() to ensure that it will get written to disk.
+ *
+ * The or_state_save() function additionally calls various functioens
+ * throughout Tor that might want to flush more state to the the disk,
+ * including some in rephist.c, entrynodes.c, circuitstats.c, hibernate.c.
*/
#define STATEFILE_PRIVATE
@@ -121,6 +138,7 @@ static const config_format_t state_format = {
OR_STATE_MAGIC,
STRUCT_OFFSET(or_state_t, magic_),
state_abbrevs_,
+ NULL,
state_vars_,
or_state_validate_cb,
&state_extra_var,
@@ -349,7 +367,7 @@ or_state_load(void)
if (config_get_lines(contents, &lines, 0)<0)
goto done;
assign_retval = config_assign(&state_format, new_state,
- lines, 0, 0, &errmsg);
+ lines, 0, &errmsg);
config_free_lines(lines);
if (assign_retval<0)
badstate = 1;
diff --git a/src/or/status.c b/src/or/status.c
index 749cee4edf..fce6a10157 100644
--- a/src/or/status.c
+++ b/src/or/status.c
@@ -3,7 +3,13 @@
/**
* \file status.c
- * \brief Keep status information and log the heartbeat messages.
+ * \brief Collect status information and log heartbeat messages.
+ *
+ * This module is responsible for implementing the heartbeat log messages,
+ * which periodically inform users and operators about basic facts to
+ * do with their Tor instance. The log_heartbeat() function, invoked from
+ * main.c, is the principle entry point. It collects data from elsewhere
+ * in Tor, and logs it in a human-readable format.
**/
#define STATUS_PRIVATE
diff --git a/src/or/tor_main.c b/src/or/tor_main.c
index ac32eef559..d67eda2ac9 100644
--- a/src/or/tor_main.c
+++ b/src/or/tor_main.c
@@ -3,6 +3,8 @@
* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+extern const char tor_git_revision[];
+
/** String describing which Tor Git repository version the source was
* built from. This string is generated by a bit of shell kludging in
* src/or/include.am, and is usually right.
@@ -15,8 +17,10 @@ const char tor_git_revision[] =
/**
* \file tor_main.c
- * \brief Stub module containing a main() function. Allows unit
- * test binary to link against main.c.
+ * \brief Stub module containing a main() function.
+ *
+ * We keep the main function in a separate module so that the unit
+ * tests, which have their own main()s, can link against main.c.
**/
int tor_main(int argc, char *argv[]);
diff --git a/src/or/transports.c b/src/or/transports.c
index 1b8b1e678c..7a52b737e4 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -1270,7 +1270,7 @@ get_transport_options_for_server_proxy(const managed_proxy_t *mp)
/** Return the string that tor should place in TOR_PT_SERVER_BINDADDR
* while configuring the server managed proxy in <b>mp</b>. The
- * string is stored in the heap, and it's the the responsibility of
+ * string is stored in the heap, and it's the responsibility of
* the caller to deallocate it after its use. */
static char *
get_bindaddr_for_server_proxy(const managed_proxy_t *mp)
@@ -1363,7 +1363,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
}
}
- /* XXX024 Remove the '=' here once versions of obfsproxy which
+ /* XXXX Remove the '=' here once versions of obfsproxy which
* assert that this env var exists are sufficiently dead.
*
* (If we remove this line entirely, some joker will stick this
@@ -1425,7 +1425,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
*
* Requires that proxy_argv have at least one element. */
STATIC managed_proxy_t *
-managed_proxy_create(const smartlist_t *transport_list,
+managed_proxy_create(const smartlist_t *with_transport_list,
char **proxy_argv, int is_server)
{
managed_proxy_t *mp = tor_malloc_zero(sizeof(managed_proxy_t));
@@ -1436,7 +1436,7 @@ managed_proxy_create(const smartlist_t *transport_list,
mp->proxy_uri = get_pt_proxy_uri();
mp->transports_to_launch = smartlist_new();
- SMARTLIST_FOREACH(transport_list, const char *, transport,
+ SMARTLIST_FOREACH(with_transport_list, const char *, transport,
add_transport_to_proxy(transport, mp));
/* register the managed proxy */
@@ -1460,7 +1460,7 @@ managed_proxy_create(const smartlist_t *transport_list,
* elements, containing at least one element.
**/
MOCK_IMPL(void,
-pt_kickstart_proxy, (const smartlist_t *transport_list,
+pt_kickstart_proxy, (const smartlist_t *with_transport_list,
char **proxy_argv, int is_server))
{
managed_proxy_t *mp=NULL;
@@ -1473,7 +1473,7 @@ pt_kickstart_proxy, (const smartlist_t *transport_list,
mp = get_managed_proxy_by_argv_and_type(proxy_argv, is_server);
if (!mp) { /* we haven't seen this proxy before */
- managed_proxy_create(transport_list, proxy_argv, is_server);
+ managed_proxy_create(with_transport_list, proxy_argv, is_server);
} else { /* known proxy. add its transport to its transport list */
if (mp->was_around_before_config_read) {
@@ -1490,14 +1490,14 @@ pt_kickstart_proxy, (const smartlist_t *transport_list,
/* For each new transport, check if the managed proxy used to
support it before the SIGHUP. If that was the case, make sure
it doesn't get removed because we might reuse it. */
- SMARTLIST_FOREACH_BEGIN(transport_list, const char *, transport) {
+ SMARTLIST_FOREACH_BEGIN(with_transport_list, const char *, transport) {
old_transport = transport_get_by_name(transport);
if (old_transport)
old_transport->marked_for_removal = 0;
} SMARTLIST_FOREACH_END(transport);
}
- SMARTLIST_FOREACH(transport_list, const char *, transport,
+ SMARTLIST_FOREACH(with_transport_list, const char *, transport,
add_transport_to_proxy(transport, mp));
free_execve_args(proxy_argv);
}
@@ -1611,7 +1611,7 @@ pt_get_extra_info_descriptor_string(void)
uint32_t external_ip_address = 0;
if (tor_addr_is_null(&t->addr) &&
router_pick_published_address(get_options(),
- &external_ip_address) >= 0) {
+ &external_ip_address, 0) >= 0) {
tor_addr_t addr;
tor_addr_from_ipv4h(&addr, external_ip_address);
addrport = fmt_addrport(&addr, t->port);
diff --git a/src/test/bench.c b/src/test/bench.c
index 5aefda5ff2..30984fda70 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -3,6 +3,7 @@
* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+extern const char tor_git_revision[];
/* Ordinarily defined in tor_main.c; this bit is just here to provide one
* since we're not linking to tor_main.c */
const char tor_git_revision[] = "";
@@ -89,7 +90,9 @@ bench_aes(void)
uint64_t start, end;
const int bytes_per_iter = (1<<24);
reset_perftime();
- c = crypto_cipher_new(NULL);
+ char key[CIPHER_KEY_LEN];
+ crypto_rand(key, sizeof(key));
+ c = crypto_cipher_new(key);
for (len = 1; len <= 8192; len *= 2) {
int iters = bytes_per_iter / len;
@@ -327,8 +330,9 @@ bench_cell_aes(void)
char *b = tor_malloc(len+max_misalign);
crypto_cipher_t *c;
int i, misalign;
-
- c = crypto_cipher_new(NULL);
+ char key[CIPHER_KEY_LEN];
+ crypto_rand(key, sizeof(key));
+ c = crypto_cipher_new(key);
reset_perftime();
for (misalign = 0; misalign <= max_misalign; ++misalign) {
@@ -500,8 +504,11 @@ bench_cell_ops(void)
or_circ->base_.purpose = CIRCUIT_PURPOSE_OR;
/* Initialize crypto */
- or_circ->p_crypto = crypto_cipher_new(NULL);
- or_circ->n_crypto = crypto_cipher_new(NULL);
+ char key1[CIPHER_KEY_LEN], key2[CIPHER_KEY_LEN];
+ crypto_rand(key1, sizeof(key1));
+ crypto_rand(key2, sizeof(key2));
+ or_circ->p_crypto = crypto_cipher_new(key1);
+ or_circ->n_crypto = crypto_cipher_new(key2);
or_circ->p_digest = crypto_digest_new();
or_circ->n_digest = crypto_digest_new();
@@ -556,7 +563,7 @@ bench_dh(void)
dh_b, dh_pubkey_a, sizeof(dh_pubkey_a),
secret_b, sizeof(secret_b));
tor_assert(slen_a == slen_b);
- tor_assert(!memcmp(secret_a, secret_b, slen_a));
+ tor_assert(fast_memeq(secret_a, secret_b, slen_a));
crypto_dh_free(dh_a);
crypto_dh_free(dh_b);
}
@@ -594,7 +601,7 @@ bench_ecdh_impl(int nid, const char *name)
NULL);
tor_assert(slen_a == slen_b);
- tor_assert(!memcmp(secret_a, secret_b, slen_a));
+ tor_assert(fast_memeq(secret_a, secret_b, slen_a));
EC_KEY_free(dh_a);
EC_KEY_free(dh_b);
}
@@ -661,7 +668,6 @@ main(int argc, const char **argv)
{
int i;
int list=0, n_enabled=0;
- benchmark_t *b;
char *errmsg;
or_options_t *options;
@@ -671,10 +677,10 @@ main(int argc, const char **argv)
if (!strcmp(argv[i], "--list")) {
list = 1;
} else {
- benchmark_t *b = find_benchmark(argv[i]);
+ benchmark_t *benchmark = find_benchmark(argv[i]);
++n_enabled;
- if (b) {
- b->enabled = 1;
+ if (benchmark) {
+ benchmark->enabled = 1;
} else {
printf("No such benchmark as %s\n", argv[i]);
}
@@ -699,7 +705,7 @@ main(int argc, const char **argv)
return 1;
}
- for (b = benchmarks; b->name; ++b) {
+ for (benchmark_t *b = benchmarks; b->name; ++b) {
if (b->enabled || n_enabled == 0) {
printf("===== %s =====\n", b->name);
if (!list)
diff --git a/src/test/example_extrainfo.inc b/src/test/example_extrainfo.inc
index e096afd6c4..0bf2341ef5 100644
--- a/src/test/example_extrainfo.inc
+++ b/src/test/example_extrainfo.inc
@@ -133,7 +133,7 @@ static const char EX_EI_BAD_NICKNAME_KEY[] =
"/UBWNSyXCFDMqnddb/LZ8+VgttmxfYkpeRzSSmDijN3RbOvYJhhBAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n";
-const char EX_EI_BAD_TOKENS[] =
+static const char EX_EI_BAD_TOKENS[] =
"extra-info bob 6F314FB01A31162BD5E473D4977AC570DC5B86BB\n"
"published 2014-10-05 20:07:00\n"
"published 2014-10-05 20:07:00\n"
@@ -145,8 +145,9 @@ const char EX_EI_BAD_TOKENS[] =
"-----END SIGNATURE-----\n"
;
-const char EX_EI_BAD_TOKENS_FP[] = "6F314FB01A31162BD5E473D4977AC570DC5B86BB";
-const char EX_EI_BAD_TOKENS_KEY[] =
+static const char EX_EI_BAD_TOKENS_FP[] =
+ "6F314FB01A31162BD5E473D4977AC570DC5B86BB";
+static const char EX_EI_BAD_TOKENS_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAL7Z8tz45Tb4tnEFS2sAyjubBV/giSfZdmXRkDV8Jo4xqWqhWFJn7+zN\n"
"AXBWBThGeVH2WXrpz5seNJXgZJPxMTMsrnSCGcRXZw0Npti2MkLuQ6+prZa+OPwE\n"
@@ -210,7 +211,8 @@ static const char EX_EI_GOOD_ED_EI[] =
"\n"
"\n"
;
-const char EX_EI_GOOD_ED_EI_FP[] = "A692FE045C32B5E3A54B52882EF678A9DAC46A73";
+static const char EX_EI_GOOD_ED_EI_FP[] =
+ "A692FE045C32B5E3A54B52882EF678A9DAC46A73";
static const char EX_EI_GOOD_ED_EI_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAM3jdYwjwGxDWYj/vyFkQT7RgeCNIn89Ei6D2+L/fdtFnqrMXOreFFHL\n"
@@ -237,7 +239,8 @@ static const char EX_EI_ED_MISSING_SIG[] =
"\n"
"\n"
;
-const char EX_EI_ED_MISSING_SIG_FP[] = "2A7521497B91A8437021515308A47491164EDBA1";
+static const char EX_EI_ED_MISSING_SIG_FP[] =
+ "2A7521497B91A8437021515308A47491164EDBA1";
static const char EX_EI_ED_MISSING_SIG_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAOOB8ccxbtk2dB5FuKFhGndDcO6STNjB6KiG0b9X2QwKrOZMfmXSigto\n"
@@ -260,7 +263,8 @@ static const char EX_EI_ED_MISSING_CERT[] =
"\n"
"\n"
;
-const char EX_EI_ED_MISSING_CERT_FP[] = "E88E43E86015345A323D93D825C33E4AD1028F65";
+static const char EX_EI_ED_MISSING_CERT_FP[] =
+ "E88E43E86015345A323D93D825C33E4AD1028F65";
static const char EX_EI_ED_MISSING_CERT_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBALjA/geb0TR9rp/UPvLhABQpB0XUDYuZAnLkrv+i7AAV7FemTDveEGnc\n"
@@ -284,7 +288,8 @@ static const char EX_EI_ED_BAD_CERT1[] =
"-----END SIGNATURE-----\n"
"\n"
;
-const char EX_EI_ED_BAD_CERT1_FP[] = "F78D8A655607D32281D02144817A4F1D26AE520F";
+static const char EX_EI_ED_BAD_CERT1_FP[] =
+ "F78D8A655607D32281D02144817A4F1D26AE520F";
static const char EX_EI_ED_BAD_CERT1_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAMlR46JhxsCmWYtmIB/JjTV2TUYIhJLmHy+X7FfkK3ZVQvvl9/3GSXFL\n"
@@ -309,7 +314,8 @@ static const char EX_EI_ED_BAD_CERT2[] =
"cVrtU6RVmzldSbyir8V/Z4S/Cm67gYAgjM5gfoFUqDs=\n"
"-----END SIGNATURE-----\n"
;
-const char EX_EI_ED_BAD_CERT2_FP[] = "7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD";
+static const char EX_EI_ED_BAD_CERT2_FP[] =
+ "7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD";
static const char EX_EI_ED_BAD_CERT2_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBALAM1F/0XJEsbxIQqb3+ObX/yGVnq9of8Q9sLsmxffD6hwVpCqnV3lTg\n"
@@ -335,7 +341,8 @@ static const char EX_EI_ED_BAD_SIG1[] =
"-----END SIGNATURE-----\n"
"\n"
;
-const char EX_EI_ED_BAD_SIG1_FP[] = "5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A";
+static const char EX_EI_ED_BAD_SIG1_FP[] =
+ "5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A";
static const char EX_EI_ED_BAD_SIG1_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAMvb6SuoIkPfBkJgQuo5aQDepAs1kEETZ9VXotMlhB0JJikrqBrAAz+7\n"
@@ -361,7 +368,8 @@ static const char EX_EI_ED_BAD_SIG2[] =
"-----END SIGNATURE-----\n"
"\n"
;
-const char EX_EI_ED_BAD_SIG2_FP[] = "7F1D4DD477E340C6D6B389FAC26EDC746113082F";
+static const char EX_EI_ED_BAD_SIG2_FP[] =
+ "7F1D4DD477E340C6D6B389FAC26EDC746113082F";
static const char EX_EI_ED_BAD_SIG2_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBALzOyfCEUZnvCyhlyMctPkdXg/XRE3Cr6QgyzdKf5kQbUiu2n0FgSHOX\n"
@@ -388,7 +396,8 @@ static const char EX_EI_ED_MISPLACED_CERT[] =
"-----END SIGNATURE-----\n"
"\n"
;
-const char EX_EI_ED_MISPLACED_CERT_FP[] = "3B788BD0CE348BC5CED48313307C78175EB6D0F3";
+static const char EX_EI_ED_MISPLACED_CERT_FP[] =
+ "3B788BD0CE348BC5CED48313307C78175EB6D0F3";
static const char EX_EI_ED_MISPLACED_CERT_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBALTwNqhTprg1oC6bEbDqwIYBoER6prqUXQFbwbFDn+ekXhZj8vltgGwp\n"
@@ -414,7 +423,8 @@ static const char EX_EI_ED_MISPLACED_SIG[] =
"-----END SIGNATURE-----\n"
"\n"
;
-const char EX_EI_ED_MISPLACED_SIG_FP[] = "384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284";
+static const char EX_EI_ED_MISPLACED_SIG_FP[] =
+ "384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284";
static const char EX_EI_ED_MISPLACED_SIG_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBAK0HgOCG/6433VCrwz/vhk3cKmyOfenCp0GZ4DIUwPWt4DeyP4nTbN6T\n"
diff --git a/src/test/include.am b/src/test/include.am
index 7d80fdf152..8ecfaf10c6 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -9,6 +9,12 @@ TESTS_ENVIRONMENT = \
export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)";
TESTSCRIPTS = src/test/test_zero_length_keys.sh \
+ src/test/test_workqueue_cancel.sh \
+ src/test/test_workqueue_efd.sh \
+ src/test/test_workqueue_efd2.sh \
+ src/test/test_workqueue_pipe.sh \
+ src/test/test_workqueue_pipe2.sh \
+ src/test/test_workqueue_socketpair.sh \
src/test/test_switch_id.sh
if USEPYTHON
@@ -16,13 +22,16 @@ TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh
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_workqueue \
+ src/test/test_keygen.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 bridges+hs
+TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-min single-onion
# only run if we can ping6 ::1 (localhost)
-TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min
+TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-ipv6 \
+ single-onion-ipv6
# only run if we can find a stable (or simply another) version of tor
TEST_CHUTNEY_FLAVORS_MIXED = mixed
@@ -40,7 +49,8 @@ noinst_PROGRAMS+= \
src/test/test-memwipe \
src/test/test-child \
src/test/test_workqueue \
- src/test/test-switch-id
+ src/test/test-switch-id \
+ src/test/test-timers
endif
src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
@@ -86,6 +96,7 @@ src_test_test_SOURCES = \
src/test/test_guardfraction.c \
src/test/test_extorport.c \
src/test/test_hs.c \
+ src/test/test_handles.c \
src/test/test_introduce.c \
src/test/test_keypin.c \
src/test/test_link_handshake.c \
@@ -93,10 +104,13 @@ src_test_test_SOURCES = \
src/test/test_microdesc.c \
src/test/test_nodelist.c \
src/test/test_oom.c \
+ src/test/test_oos.c \
src/test/test_options.c \
src/test/test_policy.c \
src/test/test_procmon.c \
+ src/test/test_protover.c \
src/test/test_pt.c \
+ src/test/test_pubsub.c \
src/test/test_relay.c \
src/test/test_relaycell.c \
src/test/test_rendcache.c \
@@ -105,6 +119,7 @@ src_test_test_SOURCES = \
src/test/test_routerlist.c \
src/test/test_routerset.c \
src/test/test_scheduler.c \
+ src/test/test_shared_random.c \
src/test/test_socks.c \
src/test/test_status.c \
src/test/test_threads.c \
@@ -127,6 +142,8 @@ src_test_test_slow_SOURCES = \
src_test_test_memwipe_SOURCES = \
src/test/test-memwipe.c
+src_test_test_timers_SOURCES = \
+ src/test/test-timers.c
src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
@@ -147,6 +164,7 @@ src_test_test_switch_id_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
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 \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@@ -156,6 +174,7 @@ src_test_test_LDADD = src/or/libtor-testing.a \
$(LIBKECCAK_TINY) \
$(LIBDONNA) \
src/common/libor-testing.a \
+ src/common/libor-ctime-testing.a \
src/common/libor-event-testing.a \
src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@@ -168,13 +187,17 @@ src_test_test_slow_LDADD = $(src_test_test_LDADD)
src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
src_test_test_memwipe_CPPFLAGS = $(src_test_test_CPPFLAGS)
-src_test_test_memwipe_CFLAGS = $(src_test_test_CFLAGS)
+# Don't use bugtrap cflags here: memwipe tests require memory violations.
+src_test_test_memwipe_CFLAGS = $(TEST_CFLAGS)
src_test_test_memwipe_LDADD = $(src_test_test_LDADD)
-src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS)
+# The LDFLAGS need to include the bugtrap cflags, or else we won't link
+# successfully with the libraries built with them.
+src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS) @CFLAGS_BUGTRAP@
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_bench_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 \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@@ -185,11 +208,23 @@ src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_test_workqueue_LDADD = src/or/libtor-testing.a \
src/common/libor-testing.a \
+ src/common/libor-ctime-testing.a \
src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
src/common/libor-event-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+src_test_test_timers_CPPFLAGS = $(src_test_test_CPPFLAGS)
+src_test_test_timers_CFLAGS = $(src_test_test_CFLAGS)
+src_test_test_timers_LDADD = \
+ src/common/libor-testing.a \
+ src/common/libor-ctime-testing.a \
+ src/common/libor-event-testing.a \
+ src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \
+ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
+ @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS)
+
noinst_HEADERS+= \
src/test/fakechans.h \
src/test/log_test_helpers.h \
@@ -208,6 +243,7 @@ noinst_PROGRAMS+= src/test/test-ntor-cl
src_test_test_ntor_cl_SOURCES = src/test/test_ntor_cl.c
src_test_test_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
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) \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
@@ -217,6 +253,7 @@ src_test_test_ntor_cl_AM_CPPFLAGS = \
noinst_PROGRAMS += src/test/test-bt-cl
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 \
@TOR_LIB_MATH@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@
src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
@@ -228,7 +265,14 @@ EXTRA_DIST += \
src/test/slownacl_curve25519.py \
src/test/zero_length_keys.sh \
src/test/test_keygen.sh \
- src/test/test_zero_length_keys.sh \
- src/test/test_ntor.sh src/test/test_bt.sh \
+ src/test/test_zero_length_keys.sh \
+ src/test/test_ntor.sh src/test/test_bt.sh \
src/test/test-network.sh \
- src/test/test_switch_id.sh
+ src/test/test_switch_id.sh \
+ src/test/test_workqueue_cancel.sh \
+ src/test/test_workqueue_efd.sh \
+ src/test/test_workqueue_efd2.sh \
+ src/test/test_workqueue_pipe.sh \
+ src/test/test_workqueue_pipe2.sh \
+ src/test/test_workqueue_socketpair.sh
+
diff --git a/src/test/log_test_helpers.c b/src/test/log_test_helpers.c
index 3bb36ac36c..c788a33c17 100644
--- a/src/test/log_test_helpers.c
+++ b/src/test/log_test_helpers.c
@@ -4,26 +4,92 @@
#include "torlog.h"
#include "log_test_helpers.h"
+/**
+ * \file log_test_helpers.c
+ * \brief Code to check for expected log messages during testing.
+ */
+
+static void mock_saving_logv(int severity, log_domain_mask_t domain,
+ const char *funcname, const char *suffix,
+ const char *format, va_list ap)
+ CHECK_PRINTF(5, 0);
+
+/**
+ * Smartlist of all the logs we've received since we last set up
+ * log capture.
+ */
static smartlist_t *saved_logs = NULL;
-int
+/** Boolean: should we also send messages to the test-runner? */
+static int echo_to_real_logs = 1;
+
+/** Record logs at this level or more severe */
+static int record_logs_at_level = LOG_ERR;
+
+static int saved_log_level = 0;
+
+/**
+ * As setup_capture_of_logs, but do not relay log messages into the main
+ * logging system.
+ *
+ * Avoid using this function; use setup_capture_of_logs() instead if you
+ * can. If you must use this function, then make sure you detect any
+ * unexpected log messages, and treat them as test failures. */
+void
+setup_full_capture_of_logs(int new_level)
+{
+ setup_capture_of_logs(new_level);
+ echo_to_real_logs = 0;
+}
+
+/**
+ * Temporarily capture all the messages logged at severity <b>new_level</b> or
+ * higher.
+ *
+ * This function does not prevent messages from being sent to the main
+ * logging system.
+ */
+void
setup_capture_of_logs(int new_level)
{
- int previous_log = log_global_min_severity_;
- log_global_min_severity_ = new_level;
+ if (saved_log_level == 0) {
+ saved_log_level = log_global_min_severity_;
+ } else {
+ tor_assert(0);
+ }
+
+ /* Only change the log_global_min_severity_ if we're making things _more_
+ * verbose. Otherwise we could prevent real log messages that the test-
+ * runner wanted.
+ */
+ if (log_global_min_severity_ < new_level)
+ log_global_min_severity_ = new_level;
+
+ record_logs_at_level = new_level;
mock_clean_saved_logs();
+ saved_logs = smartlist_new();
MOCK(logv, mock_saving_logv);
- return previous_log;
+ echo_to_real_logs = 1;
}
+/**
+ * Undo setup_capture_of_logs().
+ *
+ * This function is safe to call more than once.
+ */
void
-teardown_capture_of_logs(int prev)
+teardown_capture_of_logs(void)
{
UNMOCK(logv);
- log_global_min_severity_ = prev;
+ if (saved_log_level)
+ log_global_min_severity_ = saved_log_level;
+ saved_log_level = 0;
mock_clean_saved_logs();
}
+/**
+ * Clear all messages in mock_saved_logs()
+ */
void
mock_clean_saved_logs(void)
{
@@ -35,6 +101,11 @@ mock_clean_saved_logs(void)
saved_logs = NULL;
}
+/**
+ * Return a list of all the messages captured since the last
+ * setup_[full_]capture_of_logs() call. Each log call is recorded as a
+ * mock_saved_log_entry_t.
+ */
const smartlist_t *
mock_saved_logs(void)
{
@@ -42,23 +113,52 @@ mock_saved_logs(void)
}
int
+mock_saved_log_n_entries(void)
+{
+ return saved_logs ? smartlist_len(saved_logs) : 0;
+}
+
+/**
+ * Return true iff there is a message recorded by log capture
+ * that is exactly equal to <b>msg</b>
+ */
+int
mock_saved_log_has_message(const char *msg)
{
- int has_msg = 0;
if (saved_logs) {
SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
{
if (msg && m->generated_msg &&
!strcmp(msg, m->generated_msg)) {
- has_msg = 1;
+ return 1;
+ }
+ });
+ }
+
+ return 0;
+}
+
+/**
+ * Return true iff there is a message recorded by log capture
+ * that contains <b>msg</b> as a substring.
+ */
+int
+mock_saved_log_has_message_containing(const char *msg)
+{
+ if (saved_logs) {
+ SMARTLIST_FOREACH(saved_logs, mock_saved_log_entry_t *, m,
+ {
+ if (msg && m->generated_msg &&
+ strstr(m->generated_msg, msg)) {
+ return 1;
}
});
}
- return has_msg;
+ return 0;
}
-/* Do the saved logs have any messages with severity? */
+/** Return true iff the saved logs have any messages with <b>severity</b> */
int
mock_saved_log_has_severity(int severity)
{
@@ -75,7 +175,7 @@ mock_saved_log_has_severity(int severity)
return has_sev;
}
-/* Do the saved logs have any messages? */
+/** Return true iff the the saved logs have at lease one message */
int
mock_saved_log_has_entry(void)
{
@@ -85,12 +185,14 @@ mock_saved_log_has_entry(void)
return 0;
}
-void
+/* Replacement for logv: record the log message, and (maybe) send it
+ * into the logging system again.
+ */
+static void
mock_saving_logv(int severity, log_domain_mask_t domain,
const char *funcname, const char *suffix,
const char *format, va_list ap)
{
- (void)domain;
char *buf = tor_malloc_zero(10240);
int n;
n = tor_vsnprintf(buf,10240,format,ap);
@@ -98,6 +200,18 @@ mock_saving_logv(int severity, log_domain_mask_t domain,
buf[n]='\n';
buf[n+1]='\0';
+ if (echo_to_real_logs) {
+ tor_log(severity, domain|LD_NO_MOCK, "%s", buf);
+ }
+
+ if (severity > record_logs_at_level) {
+ tor_free(buf);
+ return;
+ }
+
+ if (!saved_logs)
+ saved_logs = smartlist_new();
+
mock_saved_log_entry_t *e = tor_malloc_zero(sizeof(mock_saved_log_entry_t));
e->severity = severity;
e->funcname = funcname;
@@ -106,8 +220,22 @@ mock_saving_logv(int severity, log_domain_mask_t domain,
e->generated_msg = tor_strdup(buf);
tor_free(buf);
- if (!saved_logs)
- saved_logs = smartlist_new();
smartlist_add(saved_logs, e);
}
+void
+mock_dump_saved_logs(void)
+{
+ if (saved_logs == NULL) {
+ puts(" Captured logs: NULL");
+ return;
+ }
+
+ puts(" Captured logs:");
+ SMARTLIST_FOREACH_BEGIN(saved_logs, const mock_saved_log_entry_t *, m) {
+ printf("% 5d. %s: %s\n", m_sl_idx + 1,
+ log_level_to_string(m->severity),
+ escaped(m->generated_msg));
+ } SMARTLIST_FOREACH_END(m);
+}
+
diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h
index 1966f170fb..922c68b42f 100644
--- a/src/test/log_test_helpers.h
+++ b/src/test/log_test_helpers.h
@@ -6,50 +6,99 @@
#ifndef TOR_LOG_TEST_HELPERS_H
#define TOR_LOG_TEST_HELPERS_H
+/** An element of mock_saved_logs(); records the log element that we
+ * received. */
typedef struct mock_saved_log_entry_t {
int severity;
const char *funcname;
const char *suffix;
const char *format;
char *generated_msg;
- struct mock_saved_log_entry_t *next;
} mock_saved_log_entry_t;
-void mock_saving_logv(int severity, log_domain_mask_t domain,
- const char *funcname, const char *suffix,
- const char *format, va_list ap)
- CHECK_PRINTF(5, 0);
void mock_clean_saved_logs(void);
const smartlist_t *mock_saved_logs(void);
-int setup_capture_of_logs(int new_level);
-void teardown_capture_of_logs(int prev);
+void setup_capture_of_logs(int new_level);
+void setup_full_capture_of_logs(int new_level);
+void teardown_capture_of_logs(void);
int mock_saved_log_has_message(const char *msg);
+int mock_saved_log_has_message_containing(const char *msg);
int mock_saved_log_has_severity(int severity);
int mock_saved_log_has_entry(void);
+int mock_saved_log_n_entries(void);
+void mock_dump_saved_logs(void);
-#define expect_log_msg(str) \
- tt_assert_msg(mock_saved_log_has_message(str), \
+#define assert_log_predicate(predicate, failure_msg) \
+ do { \
+ if (!(predicate)) { \
+ tt_fail_msg((failure_msg)); \
+ mock_dump_saved_logs(); \
+ TT_EXIT_TEST_FUNCTION; \
+ } \
+ } while (0)
+
+#define expect_log_msg(str) \
+ assert_log_predicate(mock_saved_log_has_message(str), \
+ "expected log to contain " # str);
+
+#define expect_log_msg_containing(str) \
+ assert_log_predicate(mock_saved_log_has_message_containing(str), \
"expected log to contain " # str);
+#define expect_log_msg_containing_either(str1, str2) \
+ assert_log_predicate(mock_saved_log_has_message_containing(str1) || \
+ mock_saved_log_has_message_containing(str2), \
+ "expected log to contain " # str1 " or " # str2);
+
+#define expect_log_msg_containing_either3(str1, str2, str3) \
+ assert_log_predicate(mock_saved_log_has_message_containing(str1) || \
+ mock_saved_log_has_message_containing(str2) || \
+ mock_saved_log_has_message_containing(str3), \
+ "expected log to contain " # str1 " or " # str2 \
+ " or " # str3);
+
+#define expect_log_msg_containing_either4(str1, str2, str3, str4) \
+ assert_log_predicate(mock_saved_log_has_message_containing(str1) || \
+ mock_saved_log_has_message_containing(str2) || \
+ mock_saved_log_has_message_containing(str3) || \
+ mock_saved_log_has_message_containing(str4), \
+ "expected log to contain " # str1 " or " # str2 \
+ " or " # str3 " or " # str4);
+
+#define expect_single_log_msg(str) \
+ do { \
+ \
+ assert_log_predicate(mock_saved_log_has_message_containing(str) && \
+ mock_saved_log_n_entries() == 1, \
+ "expected log to contain exactly 1 message: " # str); \
+ } while (0);
+
+#define expect_single_log_msg_containing(str) \
+ do { \
+ assert_log_predicate(mock_saved_log_has_message_containing(str)&& \
+ mock_saved_log_n_entries() == 1 , \
+ "expected log to contain 1 message, containing" # str); \
+ } while (0);
+
#define expect_no_log_msg(str) \
- tt_assert_msg(!mock_saved_log_has_message(str), \
+ assert_log_predicate(!mock_saved_log_has_message(str), \
"expected log to not contain " # str);
#define expect_log_severity(severity) \
- tt_assert_msg(mock_saved_log_has_severity(severity), \
+ assert_log_predicate(mock_saved_log_has_severity(severity), \
"expected log to contain severity " # severity);
#define expect_no_log_severity(severity) \
- tt_assert_msg(!mock_saved_log_has_severity(severity), \
+ assert_log_predicate(!mock_saved_log_has_severity(severity), \
"expected log to not contain severity " # severity);
#define expect_log_entry() \
- tt_assert_msg(mock_saved_log_has_entry(), \
+ assert_log_predicate(mock_saved_log_has_entry(), \
"expected log to contain entries");
#define expect_no_log_entry() \
- tt_assert_msg(!mock_saved_log_has_entry(), \
+ assert_log_predicate(!mock_saved_log_has_entry(), \
"expected log to not contain entries");
#endif
diff --git a/src/test/sr_commit_calc_ref.py b/src/test/sr_commit_calc_ref.py
new file mode 100644
index 0000000000..45e629cfb0
--- /dev/null
+++ b/src/test/sr_commit_calc_ref.py
@@ -0,0 +1,51 @@
+# This is a reference implementation of the COMMIT/REVEAL calculation for
+# prop250. We use it to generate a test vector for the test_encoding()
+# unittest.
+#
+# Here is the computation formula:
+#
+# H = SHA3-256
+# TIMESTAMP = 8 bytes network-endian value
+# RAND = H(32 bytes of random)
+#
+# REVEAL = base64-encode( TIMESTAMP || RAND )
+# COMMIT = base64-encode( TIMESTAMP || H(REVEAL) )
+#
+
+import sys
+import hashlib
+import struct
+import base64
+
+# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires
+# the pysha3 package (pip install pysha3).
+if sys.version_info < (3, 6):
+ import sha3
+
+# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0
+# used the old Keccak implementation. During the finalization of SHA3, NIST
+# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function
+# stayed the same. pysha3 1.0 provides the previous Keccak hash, too.
+TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51"
+if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest():
+ print("pysha3 version is < 1.0. Please install from:")
+ print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3")
+ sys.exit(1)
+
+# TIMESTAMP
+ts = 1454333590
+# RAND
+data = 'A' * 32 # Yes very very random, NIST grade :).
+rand = hashlib.sha3_256(data)
+
+reveal = struct.pack('!Q', ts) + rand.digest()
+b64_reveal = base64.b64encode(reveal)
+print("REVEAL: %s" % (b64_reveal))
+
+# Yes we do hash the _encoded_ reveal here that is H(REVEAL)
+hashed_reveal = hashlib.sha3_256(b64_reveal)
+commit = struct.pack('!Q', ts) + hashed_reveal.digest()
+print("COMMIT: %s" % (base64.b64encode(commit)))
+
+# REVEAL: AAAAAFavXpZJxbwTupvaJCTeIUCQmOPxAMblc7ChL5H2nZKuGchdaA==
+# COMMIT: AAAAAFavXpbkBMzMQG7aNoaGLFNpm2Wkk1ozXhuWWqL//GynltxVAg==
diff --git a/src/test/sr_srv_calc_ref.py b/src/test/sr_srv_calc_ref.py
new file mode 100644
index 0000000000..492ca62b15
--- /dev/null
+++ b/src/test/sr_srv_calc_ref.py
@@ -0,0 +1,71 @@
+# This is a reference implementation of the SRV calculation for prop250. We
+# use it to generate a test vector for the test_sr_compute_srv() unittest.
+# (./test shared-random/sr_compute_srv)
+#
+# Here is the SRV computation formula:
+#
+# HASHED_REVEALS = H(ID_a | R_a | ID_b | R_b | ..)
+#
+# SRV = SHA3-256("shared-random" | INT_8(reveal_num) | INT_4(version) |
+# HASHED_REVEALS | previous_SRV)
+#
+
+import sys
+import hashlib
+import struct
+
+# 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)
+
+# In this example, we use three reveal values.
+reveal_num = 3
+version = 1
+
+# We set directly the ascii value because memset(buf, 'A', 20) makes it to 20
+# times "41" in the final string.
+
+# Identity and reveal value of dirauth a
+ID_a = 20 * "41" # RSA identity of 40 base16 bytes.
+R_a = 56 * 'A' # 56 base64 characters
+
+# Identity and reveal value of dirauth b
+ID_b = 20 * "42" # RSA identity of 40 base16 bytes.
+R_b = 56 * 'B' # 56 base64 characters
+
+# Identity and reveal value of dirauth c
+ID_c = 20 * "43" # RSA identity of 40 base16 bytes.
+R_c = 56 * 'C' # 56 base64 characters
+
+# Concatenate them all together and hash them to form HASHED_REVEALS.
+REVEALS = (ID_a + R_a + ID_b + R_b + ID_c + R_c).encode()
+hashed_reveals_object = hashlib.sha3_256(REVEALS)
+hashed_reveals = hashed_reveals_object.digest()
+
+previous_SRV = (32 * 'Z').encode()
+
+# Now form the message.
+#srv_msg = struct.pack('13sQL256ss', "shared-random", reveal_num, version,
+# hashed_reveals, previous_SRV)
+invariant_token = b"shared-random"
+srv_msg = invariant_token + \
+ struct.pack('!QL', reveal_num, version) + \
+ hashed_reveals + \
+ previous_SRV
+
+# Now calculate the HMAC
+srv = hashlib.sha3_256(srv_msg)
+print("%s" % srv.hexdigest().upper())
+
+# 2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D
diff --git a/src/test/test-child.c b/src/test/test-child.c
index e2552a499d..fdf3ccec0a 100644
--- a/src/test/test-child.c
+++ b/src/test/test-child.c
@@ -1,8 +1,8 @@
/* Copyright (c) 2011-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#include <stdio.h>
#include "orconfig.h"
+#include <stdio.h>
#ifdef _WIN32
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c
index 2cb4215b61..fd6457416a 100644
--- a/src/test/test-memwipe.c
+++ b/src/test/test-memwipe.c
@@ -1,3 +1,4 @@
+#include "orconfig.h"
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
@@ -5,9 +6,7 @@
#include "crypto.h"
#include "compat.h"
-
-#undef MIN
-#define MIN(a,b) ( ((a)<(b)) ? (a) : (b) )
+#include "util.h"
static unsigned fill_a_buffer_memset(void) __attribute__((noinline));
static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline));
@@ -17,6 +16,7 @@ static unsigned fill_heap_buffer_memwipe(void) __attribute__((noinline));
static unsigned fill_heap_buffer_nothing(void) __attribute__((noinline));
static unsigned check_a_buffer(void) __attribute__((noinline));
+extern const char *s; /* Make the linkage global */
const char *s = NULL;
#define BUF_LEN 2048
@@ -36,6 +36,12 @@ const char *s = NULL;
sum += (unsigned char)buf[i]; \
}
+#ifdef __OpenBSD__
+/* Disable some of OpenBSD's malloc protections for this test. This helps
+ * us do bad things, such as access freed buffers, without crashing. */
+const char *malloc_options="sufjj";
+#endif
+
static unsigned
fill_a_buffer_memset(void)
{
@@ -100,29 +106,29 @@ static char *heap_buf = NULL;
static unsigned
fill_heap_buffer_memset(void)
{
- char *buf = heap_buf = malloc(BUF_LEN);
+ char *buf = heap_buf = raw_malloc(BUF_LEN);
FILL_BUFFER_IMPL()
memset(buf, 0, BUF_LEN);
- free(buf);
+ raw_free(buf);
return sum;
}
static unsigned
fill_heap_buffer_memwipe(void)
{
- char *buf = heap_buf = malloc(BUF_LEN);
+ char *buf = heap_buf = raw_malloc(BUF_LEN);
FILL_BUFFER_IMPL()
memwipe(buf, 0, BUF_LEN);
- free(buf);
+ raw_free(buf);
return sum;
}
static unsigned
fill_heap_buffer_nothing(void)
{
- char *buf = heap_buf = malloc(BUF_LEN);
+ char *buf = heap_buf = raw_malloc(BUF_LEN);
FILL_BUFFER_IMPL()
- free(buf);
+ raw_free(buf);
return sum;
}
diff --git a/src/test/test-network.sh b/src/test/test-network.sh
index 05080e0c52..4d9776822b 100755
--- a/src/test/test-network.sh
+++ b/src/test/test-network.sh
@@ -1,7 +1,43 @@
#! /bin/sh
+# Please do not modify this script, it has been moved to chutney/tools
+
ECHO_N="/bin/echo -n"
-use_coverage_binary=false
+
+# Output is prefixed with the name of the script
+myname=$(basename $0)
+
+# We need to find CHUTNEY_PATH, so that we can call the version of this script
+# in chutney/tools. And we want to pass any arguments to that script as well.
+# So we source this script, which processes its arguments to find CHUTNEY_PATH.
+
+# Avoid recursively sourcing this script, and don't call the chutney version
+# while recursing, either
+if [ "$TEST_NETWORK_RECURSING" != true ]; then
+ # Process the arguments into environmental variables with this script
+ # to make sure $CHUTNEY_PATH is set
+ # When we switch to using test-network.sh in chutney/tools, --dry-run
+ # can be removed, because this script will find chutney, then pass all
+ # arguments to chutney's test-network.sh
+ echo "$myname: Parsing command-line arguments to find \$CHUTNEY_PATH"
+ export TEST_NETWORK_RECURSING=true
+ . "$0" --dry-run "$@"
+
+ # Call the chutney version of this script, if it exists, and we can find it
+ if [ -d "$CHUTNEY_PATH" -a -x "$CHUTNEY_PATH/tools/test-network.sh" ]; then
+ unset NETWORK_DRY_RUN
+ echo "$myname: Calling newer chutney script \
+$CHUTNEY_PATH/tools/test-network.sh"
+ "$CHUTNEY_PATH/tools/test-network.sh" "$@"
+ exit $?
+ else
+ echo "$myname: This script has moved to chutney/tools."
+ echo "$myname: Please update your chutney using 'git pull'."
+ # When we switch to using test-network.sh in chutney/tools, we should
+ # exit with a very loud failure here
+ echo "$myname: Falling back to the old tor version of the script."
+ fi
+fi
until [ -z "$1" ]
do
@@ -14,6 +50,9 @@ do
export TOR_DIR="$2"
shift
;;
+ # When we switch to using test-network.sh in chutney/tools, only the
+ # --chutney-path and --tor-path arguments need to be processed by this
+ # script, everything else can be handled by chutney's test-network.sh
--flavor|--flavour|--network-flavor|--network-flavour)
export NETWORK_FLAVOUR="$2"
shift
@@ -30,8 +69,8 @@ do
;;
# Make this many connections per client (1)
# Note: If you create 7 or more connections to a hidden service from
- # a single client, you'll likely get a verification failure due to
- # https://trac.torproject.org/projects/tor/ticket/15937
+ # a single Tor 0.2.7 client, you'll likely get a verification failure due
+ # to #15937. This is fixed in 0.2.8.
--connections|--connection|--connection-count|--count)
export CHUTNEY_CONNECTIONS="$2"
shift
@@ -44,44 +83,101 @@ do
shift
;;
--coverage)
- use_coverage_binary=true
+ export USE_COVERAGE_BINARY=true
+ ;;
+ --dry-run)
+ # process arguments, but don't call any other scripts
+ export NETWORK_DRY_RUN=true
;;
*)
- echo "Sorry, I don't know what to do with '$1'."
- exit 2
+ echo "$myname: Sorry, I don't know what to do with '$1'."
+ echo "$myname: Maybe chutney's test-network.sh understands '$1'."
+ echo "$myname: Please update your chutney using 'git pull', and set \
+\$CHUTNEY_PATH"
+ # continue processing arguments during a dry run
+ if [ "$NETWORK_DRY_RUN" != true ]; then
+ exit 2
+ fi
;;
esac
shift
done
-TOR_DIR="${TOR_DIR:-$PWD}"
-NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-"bridges+hs"}
-CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR
-myname=$(basename $0)
-
-[ -n "$CHUTNEY_PATH" ] || {
- echo "$myname: \$CHUTNEY_PATH not set, trying $TOR_DIR/../chutney"
- CHUTNEY_PATH="$TOR_DIR/../chutney"
-}
+# optional: $TOR_DIR is the tor build directory
+# it's used to find the location of tor binaries
+# if it's not set:
+# - set it ro $BUILDDIR, or
+# - if $PWD looks like a tor build directory, set it to $PWD, or
+# - unset $TOR_DIR, and let chutney fall back to finding tor binaries in $PATH
+if [ ! -d "$TOR_DIR" ]; then
+ if [ -d "$BUILDDIR/src/or" -a -d "$BUILDDIR/src/tools" ]; then
+ # Choose the build directory
+ # But only if it looks like one
+ echo "$myname: \$TOR_DIR not set, trying \$BUILDDIR"
+ export TOR_DIR="$BUILDDIR"
+ elif [ -d "$PWD/src/or" -a -d "$PWD/src/tools" ]; then
+ # Guess the tor directory is the current directory
+ # But only if it looks like one
+ echo "$myname: \$TOR_DIR not set, trying \$PWD"
+ export TOR_DIR="$PWD"
+ else
+ echo "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries"
+ unset TOR_DIR
+ fi
+fi
-[ -d "$CHUTNEY_PATH" ] && [ -x "$CHUTNEY_PATH/chutney" ] || {
- echo "$myname: missing 'chutney' in CHUTNEY_PATH ($CHUTNEY_PATH)"
- echo "$myname: Get chutney: git clone https://git.torproject.org/\
+# mandatory: $CHUTNEY_PATH is the path to the chutney launch script
+# if it's not set:
+# - if $PWD looks like a chutney directory, set it to $PWD, or
+# - set it based on $TOR_DIR, expecting chutney to be next to tor, or
+# - fail and tell the user how to clone the chutney repository
+if [ ! -d "$CHUTNEY_PATH" -o ! -x "$CHUTNEY_PATH/chutney" ]; then
+ if [ -x "$PWD/chutney" ]; then
+ echo "$myname: \$CHUTNEY_PATH not valid, trying \$PWD"
+ export CHUTNEY_PATH="$PWD"
+ elif [ -d "$TOR_DIR" -a -d "$TOR_DIR/../chutney" -a \
+ -x "$TOR_DIR/../chutney/chutney" ]; then
+ echo "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney"
+ export CHUTNEY_PATH="$TOR_DIR/../chutney"
+ else
+ # TODO: work out how to package and install chutney,
+ # so users can find it in $PATH
+ echo "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)"
+ echo "$myname: Get chutney: git clone https://git.torproject.org/\
chutney.git"
- echo "$myname: Set \$CHUTNEY_PATH to a non-standard location: export CHUTNEY_PATH=\`pwd\`/chutney"
- exit 1
-}
+ echo "$myname: Set \$CHUTNEY_PATH to a non-standard location: export \
+CHUTNEY_PATH=\`pwd\`/chutney"
+ unset CHUTNEY_PATH
+ exit 1
+ fi
+fi
+
+# When we switch to using test-network.sh in chutney/tools, this comment and
+# everything below it can be removed
-cd "$CHUTNEY_PATH"
# For picking up the right tor binaries.
-tor_name=tor
-tor_gencert_name=tor-gencert
-if test "$use_coverage_binary" = true; then
- tor_name=tor-cov
+# If these varibles aren't set, chutney looks for tor binaries in $PATH
+if [ -d "$TOR_DIR" ]; then
+ tor_name=tor
+ tor_gencert_name=tor-gencert
+ if [ "$USE_COVERAGE_BINARY" = true ]; then
+ tor_name=tor-cov
+ fi
+ export CHUTNEY_TOR="${TOR_DIR}/src/or/${tor_name}"
+ export CHUTNEY_TOR_GENCERT="${TOR_DIR}/src/tools/${tor_gencert_name}"
fi
-export CHUTNEY_TOR="${TOR_DIR}/src/or/${tor_name}"
-export CHUTNEY_TOR_GENCERT="${TOR_DIR}/src/tools/${tor_gencert_name}"
+# Set the variables for the chutney network flavour
+export NETWORK_FLAVOUR=${NETWORK_FLAVOUR:-"bridges+hs"}
+export CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR
+
+# And finish up if we're doing a dry run
+if [ "$NETWORK_DRY_RUN" = true ]; then
+ # we can't exit here, it breaks argument processing
+ return
+fi
+
+cd "$CHUTNEY_PATH"
./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2
# Sleep some, waiting for the network to bootstrap.
diff --git a/src/test/test-timers.c b/src/test/test-timers.c
new file mode 100644
index 0000000000..b5fcade7f8
--- /dev/null
+++ b/src/test/test-timers.c
@@ -0,0 +1,144 @@
+/* Copyright 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <event2/event.h>
+
+#include "compat.h"
+#include "compat_libevent.h"
+#include "crypto.h"
+#include "timers.h"
+#include "util.h"
+
+#define N_TIMERS 1000
+#define MAX_DURATION 30
+#define N_DISABLE 5
+
+static struct timeval fire_at[N_TIMERS] = { {0,0} };
+static int is_disabled[N_TIMERS] = {0};
+static int fired[N_TIMERS] = {0};
+static struct timeval difference[N_TIMERS] = { {0,0} };
+static tor_timer_t *timers[N_TIMERS] = {NULL};
+
+static int n_active_timers = 0;
+static int n_fired = 0;
+
+static monotime_t started_at;
+static int64_t delay_usec[N_TIMERS];
+static int64_t diffs_mono_usec[N_TIMERS];
+
+static void
+timer_cb(tor_timer_t *t, void *arg, const monotime_t *now_mono)
+{
+ struct timeval now;
+
+ tor_gettimeofday(&now);
+ tor_timer_t **t_ptr = arg;
+ tor_assert(*t_ptr == t);
+ int idx = (int) (t_ptr - timers);
+ ++fired[idx];
+ timersub(&now, &fire_at[idx], &difference[idx]);
+ diffs_mono_usec[idx] =
+ monotime_diff_usec(&started_at, now_mono) -
+ delay_usec[idx];
+ ++n_fired;
+
+ // printf("%d / %d\n",n_fired, N_TIMERS);
+ if (n_fired == n_active_timers) {
+ event_base_loopbreak(tor_libevent_get_base());
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ (void)argc;
+ (void)argv;
+ tor_libevent_cfg cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ tor_libevent_initialize(&cfg);
+ timers_initialize();
+
+ int i;
+ int ret;
+ struct timeval now;
+ tor_gettimeofday(&now);
+ monotime_get(&started_at);
+ for (i = 0; i < N_TIMERS; ++i) {
+ struct timeval delay;
+ delay.tv_sec = crypto_rand_int_range(0,MAX_DURATION);
+ delay.tv_usec = crypto_rand_int_range(0,1000000);
+ delay_usec[i] = delay.tv_sec * 1000000 + delay.tv_usec;
+ timeradd(&now, &delay, &fire_at[i]);
+ timers[i] = timer_new(timer_cb, &timers[i]);
+ timer_schedule(timers[i], &delay);
+ ++n_active_timers;
+ }
+
+ /* Disable some; we'll make sure they don't trigger. */
+ for (i = 0; i < N_DISABLE; ++i) {
+ int idx = crypto_rand_int_range(0, N_TIMERS);
+ if (is_disabled[idx])
+ continue;
+ is_disabled[idx] = 1;
+ timer_disable(timers[idx]);
+ --n_active_timers;
+ }
+
+ event_base_loop(tor_libevent_get_base(), 0);
+
+ int64_t total_difference = 0;
+ uint64_t total_square_difference = 0;
+ tor_assert(n_fired == n_active_timers);
+ for (i = 0; i < N_TIMERS; ++i) {
+ if (is_disabled[i]) {
+ tor_assert(fired[i] == 0);
+ continue;
+ }
+ tor_assert(fired[i] == 1);
+ //int64_t diff = difference[i].tv_usec + difference[i].tv_sec * 1000000;
+ int64_t diff = diffs_mono_usec[i];
+ total_difference += diff;
+ total_square_difference += diff*diff;
+ }
+ const int64_t mean_diff = total_difference / n_active_timers;
+ printf("mean difference: "I64_FORMAT" usec\n",
+ I64_PRINTF_ARG(mean_diff));
+
+ const double mean_sq = ((double)total_square_difference)/ n_active_timers;
+ const double sq_mean = mean_diff * mean_diff;
+ const double stddev = sqrt(mean_sq - sq_mean);
+ printf("standard deviation: %lf usec\n", stddev);
+
+#define MAX_DIFF_USEC (500*1000)
+#define MAX_STDDEV_USEC (500*1000)
+#define ODD_DIFF_USEC (2000)
+#define ODD_STDDEV_USEC (2000)
+
+ if (mean_diff < 0 || mean_diff > MAX_DIFF_USEC || stddev > MAX_STDDEV_USEC) {
+ printf("Either your system is under ridiculous load, or the "
+ "timer backend is broken.\n");
+ ret = 1;
+ } else if (mean_diff > ODD_DIFF_USEC || stddev > ODD_STDDEV_USEC) {
+ printf("Either your system is a bit slow or the "
+ "timer backend is odd.\n");
+ ret = 0;
+ } else {
+ printf("Looks good enough.\n");
+ ret = 0;
+ }
+
+ timer_free(NULL);
+
+ for (i = 0; i < N_TIMERS; ++i) {
+ timer_free(timers[i]);
+ }
+ timers_shutdown();
+ return ret;
+}
+
diff --git a/src/test/test.c b/src/test/test.c
index ed167a3e67..9a41b976b8 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -55,6 +55,7 @@ double fabs(double x);
#include "memarea.h"
#include "onion.h"
#include "onion_ntor.h"
+#include "onion_fast.h"
#include "onion_tap.h"
#include "policies.h"
#include "rephist.h"
@@ -178,20 +179,26 @@ test_bad_onion_handshake(void *arg)
s_buf, s_keys, 40));
/* Client: Case 1: The server sent back junk. */
+ const char *msg = NULL;
s_buf[64] ^= 33;
tt_int_op(-1, OP_EQ,
- onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, NULL));
+ onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, &msg));
s_buf[64] ^= 33;
+ tt_str_op(msg, OP_EQ, "Digest DOES NOT MATCH on onion handshake. "
+ "Bug or attack.");
/* Let the client finish; make sure it can. */
+ msg = NULL;
tt_int_op(0, OP_EQ,
- onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, NULL));
+ onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, &msg));
tt_mem_op(s_keys,OP_EQ, c_keys, 40);
+ tt_ptr_op(msg, OP_EQ, NULL);
/* Client: Case 2: The server sent back a degenerate DH. */
memset(s_buf, 0, sizeof(s_buf));
tt_int_op(-1, OP_EQ,
- onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, NULL));
+ onion_skin_TAP_client_handshake(c_dh, s_buf, c_keys, 40, &msg));
+ tt_str_op(msg, OP_EQ, "DH computation failed.");
done:
crypto_dh_free(c_dh);
@@ -246,11 +253,56 @@ test_ntor_handshake(void *arg)
memset(s_buf, 0, 40);
tt_mem_op(c_keys,OP_NE, s_buf, 40);
+ /* Now try with a bogus server response. Zero input should trigger
+ * All The Problems. */
+ memset(c_keys, 0, 400);
+ memset(s_buf, 0, NTOR_REPLY_LEN);
+ const char *msg = NULL;
+ tt_int_op(-1, OP_EQ, onion_skin_ntor_client_handshake(c_state, s_buf,
+ c_keys, 400, &msg));
+ tt_str_op(msg, OP_EQ, "Zero output from curve25519 handshake");
+
done:
ntor_handshake_state_free(c_state);
dimap_free(s_keymap, NULL);
}
+static void
+test_fast_handshake(void *arg)
+{
+ /* tests for the obsolete "CREATE_FAST" handshake. */
+ (void) arg;
+ fast_handshake_state_t *state = NULL;
+ uint8_t client_handshake[CREATE_FAST_LEN];
+ uint8_t server_handshake[CREATED_FAST_LEN];
+ uint8_t s_keys[100], c_keys[100];
+
+ /* First, test an entire handshake. */
+ memset(client_handshake, 0, sizeof(client_handshake));
+ tt_int_op(0, OP_EQ, fast_onionskin_create(&state, client_handshake));
+ tt_assert(! tor_mem_is_zero((char*)client_handshake,
+ sizeof(client_handshake)));
+
+ tt_int_op(0, OP_EQ,
+ fast_server_handshake(client_handshake, server_handshake,
+ s_keys, 100));
+ const char *msg = NULL;
+ tt_int_op(0, OP_EQ,
+ fast_client_handshake(state, server_handshake, c_keys, 100, &msg));
+ tt_ptr_op(msg, OP_EQ, NULL);
+ tt_mem_op(s_keys, OP_EQ, c_keys, 100);
+
+ /* Now test a failing handshake. */
+ server_handshake[0] ^= 3;
+ tt_int_op(-1, OP_EQ,
+ fast_client_handshake(state, server_handshake, c_keys, 100, &msg));
+ tt_str_op(msg, OP_EQ, "Digest DOES NOT MATCH on fast handshake. "
+ "Bug or attack.");
+
+ done:
+ fast_handshake_state_free(state);
+}
+
/** Run unit tests for the onion queues. */
static void
test_onion_queues(void *arg)
@@ -1115,6 +1167,7 @@ static struct testcase_t test_array[] = {
{ "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL },
ENT(onion_queues),
{ "ntor_handshake", test_ntor_handshake, 0, NULL, NULL },
+ { "fast_handshake", test_fast_handshake, 0, NULL, NULL },
FORK(circuit_timeout),
FORK(rend_fns),
ENT(geoip),
@@ -1124,60 +1177,6 @@ static struct testcase_t test_array[] = {
END_OF_TESTCASES
};
-extern struct testcase_t accounting_tests[];
-extern struct testcase_t addr_tests[];
-extern struct testcase_t address_tests[];
-extern struct testcase_t buffer_tests[];
-extern struct testcase_t cell_format_tests[];
-extern struct testcase_t cell_queue_tests[];
-extern struct testcase_t channel_tests[];
-extern struct testcase_t channeltls_tests[];
-extern struct testcase_t checkdir_tests[];
-extern struct testcase_t circuitlist_tests[];
-extern struct testcase_t circuitmux_tests[];
-extern struct testcase_t compat_libevent_tests[];
-extern struct testcase_t config_tests[];
-extern struct testcase_t connection_tests[];
-extern struct testcase_t container_tests[];
-extern struct testcase_t controller_tests[];
-extern struct testcase_t controller_event_tests[];
-extern struct testcase_t crypto_tests[];
-extern struct testcase_t dir_tests[];
-extern struct testcase_t dir_handle_get_tests[];
-extern struct testcase_t entryconn_tests[];
-extern struct testcase_t entrynodes_tests[];
-extern struct testcase_t guardfraction_tests[];
-extern struct testcase_t extorport_tests[];
-extern struct testcase_t hs_tests[];
-extern struct testcase_t introduce_tests[];
-extern struct testcase_t keypin_tests[];
-extern struct testcase_t link_handshake_tests[];
-extern struct testcase_t logging_tests[];
-extern struct testcase_t microdesc_tests[];
-extern struct testcase_t nodelist_tests[];
-extern struct testcase_t oom_tests[];
-extern struct testcase_t options_tests[];
-extern struct testcase_t policy_tests[];
-extern struct testcase_t procmon_tests[];
-extern struct testcase_t pt_tests[];
-extern struct testcase_t relay_tests[];
-extern struct testcase_t relaycell_tests[];
-extern struct testcase_t rend_cache_tests[];
-extern struct testcase_t replaycache_tests[];
-extern struct testcase_t router_tests[];
-extern struct testcase_t routerkeys_tests[];
-extern struct testcase_t routerlist_tests[];
-extern struct testcase_t routerset_tests[];
-extern struct testcase_t scheduler_tests[];
-extern struct testcase_t socks_tests[];
-extern struct testcase_t status_tests[];
-extern struct testcase_t thread_tests[];
-extern struct testcase_t tortls_tests[];
-extern struct testcase_t util_tests[];
-extern struct testcase_t util_format_tests[];
-extern struct testcase_t util_process_tests[];
-extern struct testcase_t dns_tests[];
-
struct testgroup_t testgroups[] = {
{ "", test_array },
{ "accounting/", accounting_tests },
@@ -1211,9 +1210,11 @@ struct testgroup_t testgroups[] = {
{ "link-handshake/", link_handshake_tests },
{ "nodelist/", nodelist_tests },
{ "oom/", oom_tests },
+ { "oos/", oos_tests },
{ "options/", options_tests },
{ "policy/" , policy_tests },
{ "procmon/", procmon_tests },
+ { "protover/", protover_tests },
{ "pt/", pt_tests },
{ "relay/" , relay_tests },
{ "relaycell/", relaycell_tests },
@@ -1224,13 +1225,16 @@ struct testgroup_t testgroups[] = {
{ "routerset/" , routerset_tests },
{ "scheduler/", scheduler_tests },
{ "socks/", socks_tests },
+ { "shared-random/", sr_tests },
{ "status/" , status_tests },
{ "tortls/", tortls_tests },
{ "util/", util_tests },
{ "util/format/", util_format_tests },
{ "util/logging/", logging_tests },
{ "util/process/", util_process_tests },
+ { "util/pubsub/", pubsub_tests },
{ "util/thread/", thread_tests },
+ { "util/handle/", handle_tests },
{ "dns/", dns_tests },
END_OF_GROUPS
};
diff --git a/src/test/test.h b/src/test/test.h
index e618ce1224..25336ac83e 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -73,7 +73,8 @@
{print_ = (I64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION)
const char *get_fname(const char *name);
-crypto_pk_t *pk_generate(int idx);
+const char *get_fname_rnd(const char *name);
+struct crypto_pk_t *pk_generate(int idx);
#define US2_CONCAT_2__(a, b) a ## __ ## b
#define US_CONCAT_2__(a, b) a ## _ ## b
@@ -163,11 +164,90 @@ crypto_pk_t *pk_generate(int idx);
#define CALLED(mock_name) US_CONCAT_2_(NS(mock_name), called)
#define NS_DECL(retval, mock_fn, args) \
+ extern int CALLED(mock_fn); \
static retval NS(mock_fn) args; int CALLED(mock_fn) = 0
#define NS_MOCK(name) MOCK(name, NS(name))
#define NS_UNMOCK(name) UNMOCK(name)
extern const struct testcase_setup_t passthrough_setup;
+extern const struct testcase_setup_t ed25519_test_setup;
+
+extern struct testcase_t accounting_tests[];
+extern struct testcase_t addr_tests[];
+extern struct testcase_t address_tests[];
+extern struct testcase_t buffer_tests[];
+extern struct testcase_t cell_format_tests[];
+extern struct testcase_t cell_queue_tests[];
+extern struct testcase_t channel_tests[];
+extern struct testcase_t channeltls_tests[];
+extern struct testcase_t checkdir_tests[];
+extern struct testcase_t circuitlist_tests[];
+extern struct testcase_t circuitmux_tests[];
+extern struct testcase_t compat_libevent_tests[];
+extern struct testcase_t config_tests[];
+extern struct testcase_t connection_tests[];
+extern struct testcase_t container_tests[];
+extern struct testcase_t controller_tests[];
+extern struct testcase_t controller_event_tests[];
+extern struct testcase_t crypto_tests[];
+extern struct testcase_t dir_tests[];
+extern struct testcase_t dir_handle_get_tests[];
+extern struct testcase_t entryconn_tests[];
+extern struct testcase_t entrynodes_tests[];
+extern struct testcase_t guardfraction_tests[];
+extern struct testcase_t extorport_tests[];
+extern struct testcase_t hs_tests[];
+extern struct testcase_t introduce_tests[];
+extern struct testcase_t keypin_tests[];
+extern struct testcase_t link_handshake_tests[];
+extern struct testcase_t logging_tests[];
+extern struct testcase_t microdesc_tests[];
+extern struct testcase_t nodelist_tests[];
+extern struct testcase_t oom_tests[];
+extern struct testcase_t oos_tests[];
+extern struct testcase_t options_tests[];
+extern struct testcase_t policy_tests[];
+extern struct testcase_t procmon_tests[];
+extern struct testcase_t protover_tests[];
+extern struct testcase_t pubsub_tests[];
+extern struct testcase_t pt_tests[];
+extern struct testcase_t relay_tests[];
+extern struct testcase_t relaycell_tests[];
+extern struct testcase_t rend_cache_tests[];
+extern struct testcase_t replaycache_tests[];
+extern struct testcase_t router_tests[];
+extern struct testcase_t routerkeys_tests[];
+extern struct testcase_t routerlist_tests[];
+extern struct testcase_t routerset_tests[];
+extern struct testcase_t scheduler_tests[];
+extern struct testcase_t socks_tests[];
+extern struct testcase_t status_tests[];
+extern struct testcase_t thread_tests[];
+extern struct testcase_t tortls_tests[];
+extern struct testcase_t util_tests[];
+extern struct testcase_t util_format_tests[];
+extern struct testcase_t util_process_tests[];
+extern struct testcase_t dns_tests[];
+extern struct testcase_t handle_tests[];
+extern struct testcase_t sr_tests[];
+
+extern struct testcase_t slow_crypto_tests[];
+extern struct testcase_t slow_util_tests[];
+
+extern struct testgroup_t testgroups[];
+
+extern const char AUTHORITY_CERT_1[];
+extern const char AUTHORITY_SIGNKEY_1[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
+extern const char AUTHORITY_SIGNKEY_A_DIGEST256[];
+extern const char AUTHORITY_CERT_2[];
+extern const char AUTHORITY_SIGNKEY_2[];
+extern const char AUTHORITY_SIGNKEY_B_DIGEST[];
+extern const char AUTHORITY_SIGNKEY_B_DIGEST256[];
+extern const char AUTHORITY_CERT_3[];
+extern const char AUTHORITY_SIGNKEY_3[];
+extern const char AUTHORITY_SIGNKEY_C_DIGEST[];
+extern const char AUTHORITY_SIGNKEY_C_DIGEST256[];
#endif
diff --git a/src/test/test_addr.c b/src/test/test_addr.c
index 56e79d707a..be440a0925 100644
--- a/src/test/test_addr.c
+++ b/src/test/test_addr.c
@@ -81,14 +81,14 @@ test_addr_basic(void *arg)
#define test_op_ip6_(a,op,b,e1,e2) \
STMT_BEGIN \
tt_assert_test_fmt_type(a,b,e1" "#op" "e2,struct in6_addr*, \
- (memcmp(val1_->s6_addr, val2_->s6_addr, 16) op 0), \
+ (fast_memcmp(val1_->s6_addr, val2_->s6_addr, 16) op 0), \
char *, "%s", \
- { int i; char *cp; \
+ { char *cp; \
cp = print_ = tor_malloc(64); \
- for (i=0;i<16;++i) { \
- tor_snprintf(cp, 3,"%02x", (unsigned)value_->s6_addr[i]);\
+ for (int ii_=0;ii_<16;++ii_) { \
+ tor_snprintf(cp, 3,"%02x", (unsigned)value_->s6_addr[ii_]); \
cp += 2; \
- if (i != 15) *cp++ = ':'; \
+ if (ii_ != 15) *cp++ = ':'; \
} \
}, \
{ tor_free(print_); }, \
@@ -1046,17 +1046,17 @@ test_addr_make_null(void *data)
(void) data;
/* Ensure that before tor_addr_make_null, addr != 0's */
memset(addr, 1, sizeof(*addr));
- tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_NE, 0);
+ tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_NE, 0);
/* Test with AF == AF_INET */
zeros->family = AF_INET;
tor_addr_make_null(addr, AF_INET);
- tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0);
+ tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0);
tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), OP_EQ, "0.0.0.0");
/* Test with AF == AF_INET6 */
memset(addr, 1, sizeof(*addr));
zeros->family = AF_INET6;
tor_addr_make_null(addr, AF_INET6);
- tt_int_op(memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0);
+ tt_int_op(fast_memcmp(addr, zeros, sizeof(*addr)), OP_EQ, 0);
tt_str_op(tor_addr_to_str(buf, addr, sizeof(buf), 0), OP_EQ, "::");
done:
tor_free(addr);
diff --git a/src/test/test_address.c b/src/test/test_address.c
index 3e5af56c52..0d142ad483 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -26,6 +26,7 @@
#include "or.h"
#include "address.h"
#include "test.h"
+#include "log_test_helpers.h"
/** Return 1 iff <b>sockaddr1</b> and <b>sockaddr2</b> represent
* the same IP address and port combination. Otherwise, return 0.
@@ -556,18 +557,25 @@ fake_open_socket(int domain, int type, int protocol)
return FAKE_SOCKET_FD;
}
+static int
+fake_close_socket(tor_socket_t s)
+{
+ (void)s;
+ return 0;
+}
+
static int last_connected_socket_fd = 0;
static int connect_retval = 0;
static tor_socket_t
-pretend_to_connect(tor_socket_t socket, const struct sockaddr *address,
+pretend_to_connect(tor_socket_t sock, const struct sockaddr *address,
socklen_t address_len)
{
(void)address;
(void)address_len;
- last_connected_socket_fd = socket;
+ last_connected_socket_fd = sock;
return connect_retval;
}
@@ -575,11 +583,11 @@ pretend_to_connect(tor_socket_t socket, const struct sockaddr *address,
static struct sockaddr *mock_addr = NULL;
static int
-fake_getsockname(tor_socket_t socket, struct sockaddr *address,
+fake_getsockname(tor_socket_t sock, struct sockaddr *address,
socklen_t *address_len)
{
socklen_t bytes_to_copy = 0;
- (void) socket;
+ (void) sock;
if (!mock_addr)
return -1;
@@ -616,6 +624,7 @@ test_address_udp_socket_trick_whitebox(void *arg)
MOCK(tor_open_socket,fake_open_socket);
MOCK(tor_connect_socket,pretend_to_connect);
MOCK(tor_getsockname,fake_getsockname);
+ MOCK(tor_close_socket,fake_close_socket);
mock_addr = tor_malloc_zero(sizeof(struct sockaddr_storage));
sockaddr_in_from_string("23.32.246.118",(struct sockaddr_in *)mock_addr);
@@ -646,11 +655,12 @@ test_address_udp_socket_trick_whitebox(void *arg)
tt_assert(sockaddr_in6_are_equal(mock_addr6,ipv6_to_check));
+ done:
UNMOCK(tor_open_socket);
UNMOCK(tor_connect_socket);
UNMOCK(tor_getsockname);
+ UNMOCK(tor_close_socket);
- done:
tor_free(ipv6_to_check);
tor_free(mock_addr);
tor_free(addr_from_hack);
@@ -793,7 +803,20 @@ test_address_get_if_addrs6_list_internal(void *arg)
(void)arg;
+ /* We might drop a log_err */
+ setup_full_capture_of_logs(LOG_ERR);
results = get_interface_address6_list(LOG_ERR, AF_INET6, 1);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1);
+ if (smartlist_len(mock_saved_logs()) == 1) {
+ expect_log_msg_containing_either4("connect() failed",
+ "unable to create socket",
+ "Address that we determined via UDP "
+ "socket magic is unsuitable for public "
+ "comms.",
+ "getsockname() to determine interface "
+ "failed");
+ }
+ teardown_capture_of_logs();
tt_assert(results != NULL);
/* Work even on systems without IPv6 interfaces */
@@ -812,6 +835,7 @@ test_address_get_if_addrs6_list_internal(void *arg)
done:
free_interface_address6_list(results);
+ teardown_capture_of_logs();
return;
}
@@ -822,7 +846,20 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
(void)arg;
+ /* We might drop a log_err */
+ setup_full_capture_of_logs(LOG_ERR);
results = get_interface_address6_list(LOG_ERR, AF_INET6, 0);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1);
+ if (smartlist_len(mock_saved_logs()) == 1) {
+ expect_log_msg_containing_either4("connect() failed",
+ "unable to create socket",
+ "Address that we determined via UDP "
+ "socket magic is unsuitable for public "
+ "comms.",
+ "getsockname() to determine interface "
+ "failed");
+ }
+ teardown_capture_of_logs();
tt_assert(results != NULL);
/* Work even on systems without IPv6 interfaces */
@@ -840,6 +877,7 @@ test_address_get_if_addrs6_list_no_internal(void *arg)
}
done:
+ teardown_capture_of_logs();
free_interface_address6_list(results);
return;
}
@@ -1110,7 +1148,7 @@ struct testcase_t address_tests[] = {
ADDRESS_TEST(get_if_addrs_list_internal, 0),
ADDRESS_TEST(get_if_addrs_list_no_internal, 0),
ADDRESS_TEST(get_if_addrs6_list_internal, 0),
- ADDRESS_TEST(get_if_addrs6_list_no_internal, 0),
+ ADDRESS_TEST(get_if_addrs6_list_no_internal, TT_FORK),
ADDRESS_TEST(get_if_addrs_internal_fail, 0),
ADDRESS_TEST(get_if_addrs_no_internal_fail, 0),
ADDRESS_TEST(get_if_addrs, 0),
diff --git a/src/test/test_bt.sh b/src/test/test_bt.sh
index 033acac955..312905a4e2 100755
--- a/src/test/test_bt.sh
+++ b/src/test/test_bt.sh
@@ -3,8 +3,11 @@
exitcode=0
+export ASAN_OPTIONS="handle_segv=0:allow_user_segv_handler=1"
"${builddir:-.}/src/test/test-bt-cl" backtraces || exit $?
-"${builddir:-.}/src/test/test-bt-cl" assert | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?"
-"${builddir:-.}/src/test/test-bt-cl" crash | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?"
+"${builddir:-.}/src/test/test-bt-cl" assert 2>&1 | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?"
+"${builddir:-.}/src/test/test-bt-cl" crash 2>&1 | "${PYTHON:-python}" "${abs_top_srcdir:-.}/src/test/bt_test.py" || exitcode="$?"
+
+"${builddir:-.}/src/test/test-bt-cl" none || exitcode="$?"
exit ${exitcode}
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
index 2f5e50fbf5..95b4f48f11 100644
--- a/src/test/test_bt_cl.c
+++ b/src/test/test_bt_cl.c
@@ -28,6 +28,9 @@ int a_tangled_web(int x) NOINLINE;
int we_weave(int x) NOINLINE;
static void abort_handler(int s) NORETURN;
+#ifdef HAVE_CFLAG_WNULL_DEREFERENCE
+DISABLE_GCC_WARNING(null-dereference)
+#endif
int
crash(int x)
{
@@ -47,6 +50,9 @@ crash(int x)
crashtype *= x;
return crashtype;
}
+#ifdef HAVE_CFLAG_WNULL_DEREFERENCE
+ENABLE_GCC_WARNING(null-dereference)
+#endif
int
oh_what(int x)
diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c
index e5e56edf75..3408da3aa9 100644
--- a/src/test/test_buffers.c
+++ b/src/test/test_buffers.c
@@ -178,10 +178,10 @@ test_buffers_basic(void *arg)
/* Try adding a string too long for any freelist. */
{
- char *cp = tor_malloc_zero(65536);
+ char *mem = tor_malloc_zero(65536);
buf = buf_new();
- write_to_buf(cp, 65536, buf);
- tor_free(cp);
+ write_to_buf(mem, 65536, buf);
+ tor_free(mem);
tt_int_op(buf_datalen(buf), OP_EQ, 65536);
buf_free(buf);
@@ -303,42 +303,42 @@ test_buffer_pullup(void *arg)
static void
test_buffer_copy(void *arg)
{
- generic_buffer_t *buf=NULL, *buf2=NULL;
+ buf_t *buf=NULL, *buf2=NULL;
const char *s;
size_t len;
char b[256];
int i;
(void)arg;
- buf = generic_buffer_new();
+ buf = buf_new();
tt_assert(buf);
/* Copy an empty buffer. */
- tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf));
+ tt_int_op(0, OP_EQ, buf_set_to_copy(&buf2, buf));
tt_assert(buf2);
- tt_int_op(0, OP_EQ, generic_buffer_len(buf2));
+ tt_int_op(0, OP_EQ, buf_datalen(buf2));
/* Now try with a short buffer. */
s = "And now comes an act of enormous enormance!";
len = strlen(s);
- generic_buffer_add(buf, s, len);
- tt_int_op(len, OP_EQ, generic_buffer_len(buf));
+ write_to_buf(s, len, buf);
+ tt_int_op(len, OP_EQ, buf_datalen(buf));
/* Add junk to buf2 so we can test replacing.*/
- generic_buffer_add(buf2, "BLARG", 5);
- tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf));
- tt_int_op(len, OP_EQ, generic_buffer_len(buf2));
- generic_buffer_get(buf2, b, len);
+ write_to_buf("BLARG", 5, buf2);
+ 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);
tt_mem_op(b, OP_EQ, s, len);
/* Now free buf2 and retry so we can test allocating */
- generic_buffer_free(buf2);
+ buf_free(buf2);
buf2 = NULL;
- tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf));
- tt_int_op(len, OP_EQ, generic_buffer_len(buf2));
- generic_buffer_get(buf2, b, len);
+ 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);
tt_mem_op(b, OP_EQ, s, len);
/* Clear buf for next test */
- generic_buffer_get(buf, b, len);
- tt_int_op(generic_buffer_len(buf),OP_EQ,0);
+ fetch_from_buf(b, len, buf);
+ tt_int_op(buf_datalen(buf),OP_EQ,0);
/* Okay, now let's try a bigger buffer. */
s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
@@ -347,95 +347,94 @@ test_buffer_copy(void *arg)
len = strlen(s);
for (i = 0; i < 256; ++i) {
b[0]=i;
- generic_buffer_add(buf, b, 1);
- generic_buffer_add(buf, s, len);
+ write_to_buf(b, 1, buf);
+ write_to_buf(s, len, buf);
}
- tt_int_op(0, OP_EQ, generic_buffer_set_to_copy(&buf2, buf));
- tt_int_op(generic_buffer_len(buf2), OP_EQ, generic_buffer_len(buf));
+ 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) {
- generic_buffer_get(buf2, b, len+1);
+ fetch_from_buf(b, len+1, buf2);
tt_int_op((unsigned char)b[0],OP_EQ,i);
tt_mem_op(b+1, OP_EQ, s, len);
}
done:
if (buf)
- generic_buffer_free(buf);
+ buf_free(buf);
if (buf2)
- generic_buffer_free(buf2);
+ buf_free(buf2);
}
static void
test_buffer_ext_or_cmd(void *arg)
{
ext_or_cmd_t *cmd = NULL;
- generic_buffer_t *buf = generic_buffer_new();
+ buf_t *buf = buf_new();
char *tmp = NULL;
(void) arg;
/* Empty -- should give "not there. */
- tt_int_op(0, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ 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. */
- generic_buffer_add(buf, "\x00\x20\x00", 3);
- tt_int_op(0, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ 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, generic_buffer_len(buf));
+ tt_int_op(3, OP_EQ, buf_datalen(buf));
/* 0020 0000: That's a nil command. It should work. */
- generic_buffer_add(buf, "\x00", 1);
- tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ 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, generic_buffer_len(buf));
+ 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. */
- generic_buffer_add(buf, "\x10\x21\x00\x06""abcde", 9);
- tt_int_op(0, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ 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);
- generic_buffer_add(buf, "f", 1);
- tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &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, generic_buffer_len(buf));
+ 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. */
- generic_buffer_add(buf, "\xff\xff\x00\x0a"
- "loremipsum\x10\x00\xff\xff", 18);
- tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ 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, generic_buffer_len(buf));
+ 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, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ tt_int_op(0, OP_EQ, fetch_ext_or_command_from_buf(buf, &cmd));
tmp = tor_malloc_zero(65535);
- generic_buffer_add(buf, tmp, 65535);
- tt_int_op(1, OP_EQ, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+ 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, generic_buffer_len(buf));
+ tt_int_op(0, OP_EQ, buf_datalen(buf));
ext_or_cmd_free(cmd);
cmd = NULL;
done:
ext_or_cmd_free(cmd);
- generic_buffer_free(buf);
+ buf_free(buf);
tor_free(tmp);
}
@@ -511,26 +510,26 @@ static void
test_buffer_time_tracking(void *arg)
{
buf_t *buf=NULL, *buf2=NULL;
- struct timeval tv0;
const time_t START = 1389288246;
- const uint32_t START_MSEC = (uint32_t) ((uint64_t)START * 1000);
+ const uint64_t START_NSEC = ((uint64_t)START) * 1000000000;
int i;
char tmp[4096];
(void)arg;
crypto_rand(tmp, sizeof(tmp));
- tv0.tv_sec = START;
- tv0.tv_usec = 0;
+ monotime_enable_test_mocking();
buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
tt_assert(buf);
+ monotime_coarse_set_mock_time_nsec(START_NSEC);
+ const uint32_t START_MSEC = (uint32_t)monotime_coarse_absolute_msec();
+
/* Empty buffer means the timestamp is 0. */
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));
- tor_gettimeofday_cache_set(&tv0);
write_to_buf("ABCDEFG", 7, buf);
tt_int_op(1000, OP_EQ, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000));
@@ -540,8 +539,7 @@ test_buffer_time_tracking(void *arg)
buf_get_oldest_chunk_timestamp(buf2, START_MSEC+1234));
/* Now add more bytes; enough to overflow the first chunk. */
- tv0.tv_usec += 123 * 1000;
- tor_gettimeofday_cache_set(&tv0);
+ monotime_coarse_set_mock_time_nsec(START_NSEC + 123 * (uint64_t)1000000);
for (i = 0; i < 600; ++i)
write_to_buf("ABCDEFG", 7, buf);
tt_int_op(4207, OP_EQ, buf_datalen(buf));
@@ -562,9 +560,7 @@ test_buffer_time_tracking(void *arg)
/* This time we'll be grabbing a chunk from the freelist, and making sure
its time gets updated */
- tv0.tv_sec += 5;
- tv0.tv_usec = 617*1000;
- tor_gettimeofday_cache_set(&tv0);
+ monotime_coarse_set_mock_time_nsec(START_NSEC + 5617 * (uint64_t)1000000);
for (i = 0; i < 600; ++i)
write_to_buf("ABCDEFG", 7, buf);
tt_int_op(4307, OP_EQ, buf_datalen(buf));
@@ -578,6 +574,7 @@ test_buffer_time_tracking(void *arg)
done:
buf_free(buf);
buf_free(buf2);
+ monotime_disable_test_mocking();
}
static void
@@ -695,9 +692,9 @@ test_buffers_zlib_fin_at_chunk_end(void *arg)
tor_free(msg);
}
-const uint8_t *tls_read_ptr;
-int n_remaining;
-int next_reply_val[16];
+static const uint8_t *tls_read_ptr;
+static int n_remaining;
+static int next_reply_val[16];
static int
mock_tls_read(tor_tls_t *tls, char *cp, size_t len)
@@ -747,6 +744,27 @@ test_buffers_tls_read_mocked(void *arg)
buf_free(buf);
}
+static void
+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);
+ /* 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);
+ done:
+ ;
+}
+
struct testcase_t buffer_tests[] = {
{ "basic", test_buffers_basic, TT_FORK, NULL, NULL },
{ "copy", test_buffer_copy, TT_FORK, NULL, NULL },
@@ -761,6 +779,7 @@ struct testcase_t buffer_tests[] = {
NULL, NULL},
{ "tls_read_mocked", test_buffers_tls_read_mocked, 0,
NULL, NULL },
+ { "chunk_size", test_buffers_chunk_size, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c
index 499a637959..f429f4291d 100644
--- a/src/test/test_cell_formats.c
+++ b/src/test/test_cell_formats.c
@@ -345,9 +345,9 @@ test_cfmt_connected_cells(void *arg)
memset(&cell, 0, sizeof(cell));
tor_addr_parse(&addr, "30.40.50.60");
rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE,
- &addr, 128);
+ &addr, 1024);
tt_int_op(rh.length, OP_EQ, 8);
- test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000080");
+ test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000e10");
/* Try parsing it. */
tor_addr_make_unspec(&addr);
@@ -355,7 +355,7 @@ test_cfmt_connected_cells(void *arg)
tt_int_op(r, OP_EQ, 0);
tt_int_op(tor_addr_family(&addr), OP_EQ, AF_INET);
tt_str_op(fmt_addr(&addr), OP_EQ, "30.40.50.60");
- tt_int_op(ttl, OP_EQ, 128);
+ tt_int_op(ttl, OP_EQ, 3600); /* not 1024, since we clipped to 3600 */
/* Try an IPv6 address */
memset(&rh, 0, sizeof(rh));
@@ -882,8 +882,8 @@ test_cfmt_resolved_cells(void *arg)
memset(&rh, 0, sizeof(rh)); \
} while (0)
#define CLEAR_ADDRS() do { \
- SMARTLIST_FOREACH(addrs, address_ttl_t *, a, \
- address_ttl_free(a); ); \
+ SMARTLIST_FOREACH(addrs, address_ttl_t *, aa_, \
+ address_ttl_free(aa_); ); \
smartlist_clear(addrs); \
} while (0)
#define SET_CELL(s) do { \
diff --git a/src/test/test_channel.c b/src/test/test_channel.c
index 846e419fea..a9e0634d9e 100644
--- a/src/test/test_channel.c
+++ b/src/test/test_channel.c
@@ -20,9 +20,6 @@
#include "test.h"
#include "fakechans.h"
-/* This comes from channel.c */
-extern uint64_t estimated_total_queue_size;
-
static int test_chan_accept_cells = 0;
static int test_chan_fixed_cells_recved = 0;
static cell_t * test_chan_last_seen_fixed_cell_ptr = NULL;
@@ -33,7 +30,7 @@ static int test_destroy_not_pending_calls = 0;
static int test_doesnt_want_writes_count = 0;
static int test_dumpstats_calls = 0;
static int test_has_waiting_cells_count = 0;
-static double test_overhead_estimate = 1.0f;
+static double test_overhead_estimate = 1.0;
static int test_releases_count = 0;
static circuitmux_t *test_target_cmux = NULL;
static unsigned int test_cmux_cells = 0;
@@ -792,7 +789,7 @@ test_channel_incoming(void *arg)
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
/* Use default overhead factor */
- test_overhead_estimate = 1.0f;
+ test_overhead_estimate = 1.0;
ch = new_fake_channel();
tt_assert(ch);
@@ -881,7 +878,7 @@ test_channel_lifecycle(void *arg)
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
/* Use default overhead factor */
- test_overhead_estimate = 1.0f;
+ test_overhead_estimate = 1.0;
ch1 = new_fake_channel();
tt_assert(ch1);
@@ -989,7 +986,7 @@ test_channel_lifecycle_2(void *arg)
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
/* Use default overhead factor */
- test_overhead_estimate = 1.0f;
+ test_overhead_estimate = 1.0;
ch = new_fake_channel();
tt_assert(ch);
@@ -1136,7 +1133,7 @@ test_channel_multi(void *arg)
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
/* Use default overhead factor */
- test_overhead_estimate = 1.0f;
+ test_overhead_estimate = 1.0;
ch1 = new_fake_channel();
tt_assert(ch1);
@@ -1444,7 +1441,7 @@ test_channel_queue_incoming(void *arg)
/* Accept cells to lower layer */
test_chan_accept_cells = 1;
/* Use default overhead factor */
- test_overhead_estimate = 1.0f;
+ test_overhead_estimate = 1.0;
ch = new_fake_channel();
tt_assert(ch);
@@ -1584,16 +1581,16 @@ test_channel_queue_size(void *arg)
/* One cell, times an overhead factor of 1.0 */
tt_u64_op(ch->bytes_queued_for_xmit, ==, 512);
/* Try a different overhead factor */
- test_overhead_estimate = 0.5f;
+ 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);
/* Now try a larger one */
- test_overhead_estimate = 2.0f;
+ test_overhead_estimate = 2.0;
channel_update_xmit_queue_size(ch);
tt_u64_op(ch->bytes_queued_for_xmit, ==, 1024);
/* Go back to 1.0 */
- test_overhead_estimate = 1.0f;
+ test_overhead_estimate = 1.0;
channel_update_xmit_queue_size(ch);
tt_u64_op(ch->bytes_queued_for_xmit, ==, 512);
/* Check the global estimate too */
diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c
index 04ae9a6da7..08442e01b6 100644
--- a/src/test/test_channeltls.c
+++ b/src/test/test_channeltls.c
@@ -1,6 +1,8 @@
/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#include "orconfig.h"
+
#include <math.h>
#define TOR_CHANNEL_INTERNAL_
@@ -124,8 +126,7 @@ test_channeltls_num_bytes_queued(void *arg)
* Next, we have to test ch->num_bytes_queued, which is
* channel_tls_num_bytes_queued_method. We can't mock
* connection_get_outbuf_len() directly because it's static inline
- * in connection.h, but we can mock buf_datalen(). Note that
- * if bufferevents ever work, this will break with them enabled.
+ * in connection.h, but we can mock buf_datalen().
*/
tt_assert(ch->num_bytes_queued != NULL);
@@ -185,7 +186,7 @@ test_channeltls_overhead_estimate(void *arg)
const char test_digest[DIGEST_LEN] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14 };
- float r;
+ double r;
channel_tls_t *tlschan = NULL;
(void)arg;
@@ -206,31 +207,31 @@ test_channeltls_overhead_estimate(void *arg)
ch = channel_tls_connect(&test_addr, 567, test_digest);
tt_assert(ch != NULL);
- /* First case: silly low ratios should get clamped to 1.0f */
+ /* First case: silly low ratios should get clamped to 1.0 */
tlschan = BASE_CHAN_TO_TLS(ch);
tt_assert(tlschan != NULL);
tlschan->conn->bytes_xmitted = 128;
tlschan->conn->bytes_xmitted_by_tls = 64;
r = ch->get_overhead_estimate(ch);
- tt_assert(fabsf(r - 1.0f) < 1E-12);
+ tt_assert(fabs(r - 1.0) < 1E-12);
tlschan->conn->bytes_xmitted_by_tls = 127;
r = ch->get_overhead_estimate(ch);
- tt_assert(fabsf(r - 1.0f) < 1E-12);
+ tt_assert(fabs(r - 1.0) < 1E-12);
/* Now middle of the range */
tlschan->conn->bytes_xmitted_by_tls = 192;
r = ch->get_overhead_estimate(ch);
- tt_assert(fabsf(r - 1.5f) < 1E-12);
+ tt_assert(fabs(r - 1.5) < 1E-12);
- /* Now above the 2.0f clamp */
+ /* Now above the 2.0 clamp */
tlschan->conn->bytes_xmitted_by_tls = 257;
r = ch->get_overhead_estimate(ch);
- tt_assert(fabsf(r - 2.0f) < 1E-12);
+ tt_assert(fabs(r - 2.0) < 1E-12);
tlschan->conn->bytes_xmitted_by_tls = 512;
r = ch->get_overhead_estimate(ch);
- tt_assert(fabsf(r - 2.0f) < 1E-12);
+ tt_assert(fabs(r - 2.0) < 1E-12);
done:
if (ch) {
diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c
index 1e640b5709..e996c42115 100644
--- a/src/test/test_circuitlist.c
+++ b/src/test/test_circuitlist.c
@@ -9,6 +9,7 @@
#include "circuitbuild.h"
#include "circuitlist.h"
#include "test.h"
+#include "log_test_helpers.h"
static channel_t *
new_fake_channel(void)
@@ -270,6 +271,13 @@ test_rend_token_maps(void *arg)
}
static void
+mock_channel_dump_statistics(channel_t *chan, int severity)
+{
+ (void)chan;
+ (void)severity;
+}
+
+static void
test_pick_circid(void *arg)
{
bitarray_t *ba = NULL;
@@ -278,12 +286,22 @@ test_pick_circid(void *arg)
int i;
(void) arg;
+ MOCK(channel_dump_statistics, mock_channel_dump_statistics);
+
chan1 = tor_malloc_zero(sizeof(channel_t));
chan2 = tor_malloc_zero(sizeof(channel_t));
chan2->wide_circ_ids = 1;
+ chan1->cmux = circuitmux_alloc();
+ chan2->cmux = circuitmux_alloc();
+
+ /* CIRC_ID_TYPE_NEITHER is supposed to create a warning. */
chan1->circ_id_type = CIRC_ID_TYPE_NEITHER;
+ setup_full_capture_of_logs(LOG_WARN);
tt_int_op(0, OP_EQ, get_unique_circ_id_by_chan(chan1));
+ expect_single_log_msg_containing("Trying to pick a circuit ID for a "
+ "connection from a client with no identity.");
+ teardown_capture_of_logs();
/* Basic tests, with no collisions */
chan1->circ_id_type = CIRC_ID_TYPE_LOWER;
@@ -337,10 +355,14 @@ test_pick_circid(void *arg)
}
done:
+ circuitmux_free(chan1->cmux);
+ circuitmux_free(chan2->cmux);
tor_free(chan1);
tor_free(chan2);
bitarray_free(ba);
circuit_free_all();
+ teardown_capture_of_logs();
+ UNMOCK(channel_dump_statistics);
}
struct testcase_t circuitlist_tests[] = {
diff --git a/src/test/test_compat_libevent.c b/src/test/test_compat_libevent.c
index 266ebbcf3b..0443cc0b1c 100644
--- a/src/test/test_compat_libevent.c
+++ b/src/test/test_compat_libevent.c
@@ -9,15 +9,8 @@
#include "compat_libevent.h"
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
#include <event2/thread.h>
-#ifdef USE_BUFFEREVENTS
-#include <event2/bufferevent.h>
-#endif
-#else
-#include <event.h>
-#endif
#include "log_test_helpers.h"
@@ -27,31 +20,36 @@ static void
test_compat_libevent_logging_callback(void *ignored)
{
(void)ignored;
- int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ setup_full_capture_of_logs(LOG_DEBUG);
libevent_logging_callback(_EVENT_LOG_DEBUG, "hello world");
expect_log_msg("Message from libevent: hello world\n");
expect_log_severity(LOG_DEBUG);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
mock_clean_saved_logs();
libevent_logging_callback(_EVENT_LOG_MSG, "hello world another time");
expect_log_msg("Message from libevent: hello world another time\n");
expect_log_severity(LOG_INFO);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
mock_clean_saved_logs();
libevent_logging_callback(_EVENT_LOG_WARN, "hello world a third time");
expect_log_msg("Warning from libevent: hello world a third time\n");
expect_log_severity(LOG_WARN);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
mock_clean_saved_logs();
libevent_logging_callback(_EVENT_LOG_ERR, "hello world a fourth time");
expect_log_msg("Error from libevent: hello world a fourth time\n");
expect_log_severity(LOG_ERR);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
mock_clean_saved_logs();
libevent_logging_callback(42, "hello world a fifth time");
expect_log_msg("Message [42] from libevent: hello world a fifth time\n");
expect_log_severity(LOG_WARN);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
mock_clean_saved_logs();
libevent_logging_callback(_EVENT_LOG_DEBUG,
@@ -82,130 +80,43 @@ test_compat_libevent_logging_callback(void *ignored)
"012345678901234567890123456789"
"012345678901234567890123456789\n");
expect_log_severity(LOG_DEBUG);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
mock_clean_saved_logs();
libevent_logging_callback(42, "xxx\n");
expect_log_msg("Message [42] from libevent: xxx\n");
expect_log_severity(LOG_WARN);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
suppress_libevent_log_msg("something");
mock_clean_saved_logs();
libevent_logging_callback(_EVENT_LOG_MSG, "hello there");
expect_log_msg("Message from libevent: hello there\n");
expect_log_severity(LOG_INFO);
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
mock_clean_saved_logs();
libevent_logging_callback(_EVENT_LOG_MSG, "hello there something else");
expect_no_log_msg("hello there something else");
+ if (mock_saved_logs())
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 0);
// No way of verifying the result of this, it seems =/
configure_libevent_logging();
done:
suppress_libevent_log_msg(NULL);
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
}
static void
-test_compat_libevent_le_versions_compatibility(void *ignored)
-{
- (void)ignored;
- int res;
-
- res = le_versions_compatibility(LE_OTHER);
- tt_int_op(res, OP_EQ, 0);
-
- res = le_versions_compatibility(V_OLD(0,9,'c'));
- tt_int_op(res, OP_EQ, 1);
-
- res = le_versions_compatibility(V(1,3,98));
- tt_int_op(res, OP_EQ, 2);
-
- res = le_versions_compatibility(V(1,4,98));
- tt_int_op(res, OP_EQ, 3);
-
- res = le_versions_compatibility(V(1,5,0));
- tt_int_op(res, OP_EQ, 4);
-
- res = le_versions_compatibility(V(2,0,0));
- tt_int_op(res, OP_EQ, 4);
-
- res = le_versions_compatibility(V(2,0,2));
- tt_int_op(res, OP_EQ, 5);
-
- done:
- (void)0;
-}
-
-static void
-test_compat_libevent_tor_decode_libevent_version(void *ignored)
-{
- (void)ignored;
- le_version_t res;
-
- res = tor_decode_libevent_version("SOMETHING WRONG");
- tt_int_op(res, OP_EQ, LE_OTHER);
-
- res = tor_decode_libevent_version("1.4.11");
- tt_int_op(res, OP_EQ, V(1,4,11));
-
- res = tor_decode_libevent_version("1.4.12b-stable");
- tt_int_op(res, OP_EQ, V(1,4,12));
-
- res = tor_decode_libevent_version("1.4.17b_stable");
- tt_int_op(res, OP_EQ, V(1,4,17));
-
- res = tor_decode_libevent_version("1.4.12!stable");
- tt_int_op(res, OP_EQ, LE_OTHER);
-
- res = tor_decode_libevent_version("1.4.12b!stable");
- tt_int_op(res, OP_EQ, LE_OTHER);
-
- res = tor_decode_libevent_version("1.4.13-");
- tt_int_op(res, OP_EQ, V(1,4,13));
-
- res = tor_decode_libevent_version("1.4.14_");
- tt_int_op(res, OP_EQ, V(1,4,14));
-
- res = tor_decode_libevent_version("1.4.15c-");
- tt_int_op(res, OP_EQ, V(1,4,15));
-
- res = tor_decode_libevent_version("1.4.16c_");
- tt_int_op(res, OP_EQ, V(1,4,16));
-
- res = tor_decode_libevent_version("1.4.17-s");
- tt_int_op(res, OP_EQ, V(1,4,17));
-
- res = tor_decode_libevent_version("1.5");
- tt_int_op(res, OP_EQ, V(1,5,0));
-
- res = tor_decode_libevent_version("1.2");
- tt_int_op(res, OP_EQ, V(1,2,0));
-
- res = tor_decode_libevent_version("1.2-");
- tt_int_op(res, OP_EQ, LE_OTHER);
-
- res = tor_decode_libevent_version("1.6e");
- tt_int_op(res, OP_EQ, V_OLD(1,6,'e'));
-
- done:
- (void)0;
-}
-
-#if defined(LIBEVENT_VERSION)
-#define HEADER_VERSION LIBEVENT_VERSION
-#elif defined(_EVENT_VERSION)
-#define HEADER_VERSION _EVENT_VERSION
-#endif
-
-static void
test_compat_libevent_header_version(void *ignored)
{
(void)ignored;
const char *res;
res = tor_libevent_get_header_version_str();
- tt_str_op(res, OP_EQ, HEADER_VERSION);
+ tt_str_op(res, OP_EQ, LIBEVENT_VERSION);
done:
(void)0;
@@ -214,10 +125,6 @@ test_compat_libevent_header_version(void *ignored)
struct testcase_t compat_libevent_tests[] = {
{ "logging_callback", test_compat_libevent_logging_callback,
TT_FORK, NULL, NULL },
- { "le_versions_compatibility",
- test_compat_libevent_le_versions_compatibility, 0, NULL, NULL },
- { "tor_decode_libevent_version",
- test_compat_libevent_tor_decode_libevent_version, 0, NULL, NULL },
{ "header_version", test_compat_libevent_header_version, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_config.c b/src/test/test_config.c
index b48d5e8d29..89f9b3e2aa 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -3480,10 +3480,12 @@ test_config_default_dir_servers(void *arg)
static int mock_router_pick_published_address_result = 0;
static int
-mock_router_pick_published_address(const or_options_t *options, uint32_t *addr)
+mock_router_pick_published_address(const or_options_t *options,
+ uint32_t *addr, int cache_only)
{
(void)options;
(void)addr;
+ (void)cache_only;
return mock_router_pick_published_address_result;
}
@@ -3710,6 +3712,144 @@ test_config_default_fallback_dirs(void *arg)
clear_dir_servers();
}
+static void
+test_config_port_cfg_line_extract_addrport(void *arg)
+{
+ (void)arg;
+ int unixy = 0;
+ const char *rest = NULL;
+ char *a = NULL;
+
+ 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(rest, OP_EQ, "foo ");
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\\" ol foo ",
+ &a, &unixy, &rest), OP_EQ, -1);
+ tor_free(a);
+
+ tt_int_op(port_cfg_line_extract_addrport("unix:\"lol\\0\" ol foo ",
+ &a, &unixy, &rest), OP_EQ, -1);
+ tor_free(a);
+
+ done:
+ tor_free(a);
+}
+
static config_line_t *
mock_config_line(const char *key, const char *val)
{
@@ -3832,6 +3972,8 @@ test_config_parse_port_config__listenaddress(void *data)
tt_int_op(ret, OP_EQ, 0);
// Test warning nonlocal other
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
ret = parse_port_config(slout, config_port2, config_listen_address, "DNS",
0, NULL, 0, CL_PORT_WARN_NONLOCAL);
tt_int_op(ret, OP_EQ, 0);
@@ -3952,7 +4094,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(ret, OP_EQ, -1);
// Test error when encounters a unix domain specification but the listener
- // doesnt support domain sockets
+ // doesn't support domain sockets
config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar");
ret = parse_port_config(NULL, config_port_valid, NULL, "DNS",
CONN_TYPE_AP_DNS_LISTENER, NULL, 0, 0);
@@ -3961,7 +4103,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
// Test valid unix domain
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_clear(slout);
- ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
CONN_TYPE_AP_LISTENER, NULL, 0, 0);
#ifdef _WIN32
tt_int_op(ret, OP_EQ, -1);
@@ -3972,26 +4114,153 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->port, OP_EQ, 0);
tt_int_op(port_cfg->is_unix_addr, OP_EQ, 1);
tt_str_op(port_cfg->unix_addr, OP_EQ, "/tmp/foo/bar");
+ /* Test entry port defaults as initialised in parse_port_config */
+ 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.prefer_ipv6_virtaddr, OP_EQ, 1);
#endif
- // Test failure if we have no ipv4 and no ipv6 (for unix domain sockets,
- // this makes no sense - it should be fixed)
+ // 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;
+ config_port_invalid = mock_config_line("SOCKSPort",
+ "unix:/tmp/foo/bar NoIPv4Traffic "
+ "NoOnionTraffic");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure if we have no DNS and we're a DNSPort
config_free_lines(config_port_invalid); config_port_invalid = NULL;
config_port_invalid = mock_config_line("DNSPort",
- "unix:/tmp/foo/bar NoIPv4Traffic");
+ "127.0.0.1:80 NoDNSRequest");
ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS",
+ CONN_TYPE_AP_DNS_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // If we're a DNSPort, DNS only is ok
+ // Use a port because DNSPort doesn't support sockets
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("DNSPort", "127.0.0.1:80 "
+ "NoIPv4Traffic NoOnionTraffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ CONN_TYPE_AP_DNS_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ 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.dns_request, OP_EQ, 1);
+ 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, 0);
+
+ // Test failure if we have DNS but no ipv4 and no ipv6
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ config_port_invalid = mock_config_line("SOCKSPort",
+ "unix:/tmp/foo/bar NoIPv4Traffic");
+ ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with no DNS, no ipv4, no ipv6 (only onion, using separate
+ // options)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar "
+ "NoDNSRequest NoIPv4Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
CONN_TYPE_AP_LISTENER, NULL, 0,
CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
tt_int_op(ret, OP_EQ, -1);
+#else
+ 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.dns_request, OP_EQ, 0);
+ 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
- // Test success with no ipv4 but take ipv6 (for unix domain sockets, this
- // makes no sense - it should be fixed)
+ // Test success with quoted unix: address.
config_free_lines(config_port_valid); config_port_valid = NULL;
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_clear(slout);
- config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar "
+ config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar\" "
+ "NoDNSRequest NoIPv4Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ 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.dns_request, OP_EQ, 0);
+ 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
+
+ // Test failure with broken quoted unix: address.
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar "
+ "NoDNSRequest NoIPv4Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test failure with empty quoted unix: address.
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:\"\" "
+ "NoDNSRequest NoIPv4Traffic");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+ tt_int_op(ret, OP_EQ, -1);
+
+ // Test success with OnionTrafficOnly (no DNS, no ipv4, no ipv6)
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar "
+ "OnionTrafficOnly");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
+ CONN_TYPE_AP_LISTENER, NULL, 0,
+ CL_PORT_TAKES_HOSTNAMES);
+#ifdef _WIN32
+ tt_int_op(ret, OP_EQ, -1);
+#else
+ 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.dns_request, OP_EQ, 0);
+ 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
+
+ // Test success with no ipv4 but take ipv6
+ config_free_lines(config_port_valid); config_port_valid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar "
"NoIPv4Traffic IPv6Traffic");
- ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
CONN_TYPE_AP_LISTENER, NULL, 0,
CL_PORT_TAKES_HOSTNAMES);
#ifdef _WIN32
@@ -4004,14 +4273,13 @@ test_config_parse_port_config__ports__ports_given(void *data)
tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1);
#endif
- // Test success with both ipv4 and ipv6 (for unix domain sockets,
- // this makes no sense - it should be fixed)
+ // Test success with both ipv4 and ipv6
config_free_lines(config_port_valid); config_port_valid = NULL;
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_clear(slout);
- config_port_valid = mock_config_line("DNSPort", "unix:/tmp/foo/bar "
+ config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar "
"IPv4Traffic IPv6Traffic");
- ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
CONN_TYPE_AP_LISTENER, NULL, 0,
CL_PORT_TAKES_HOSTNAMES);
#ifdef _WIN32
@@ -4153,8 +4421,9 @@ test_config_parse_port_config__ports__ports_given(void *data)
config_free_lines(config_port_valid); config_port_valid = NULL;
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_clear(slout);
- config_port_valid = mock_config_line("DNSPort", "42 IPv6Traffic PreferIPv6");
- ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ config_port_valid = mock_config_line("SOCKSPort",
+ "42 IPv6Traffic PreferIPv6");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
CONN_TYPE_AP_LISTENER, "127.0.0.42", 0,
CL_PORT_TAKES_HOSTNAMES);
tt_int_op(ret, OP_EQ, 0);
@@ -4292,7 +4561,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
// Test success with warn non-local control
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_clear(slout);
- ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ ret = parse_port_config(slout, config_port_valid, NULL, "Control",
CONN_TYPE_CONTROL_LISTENER, "127.0.0.42", 0,
CL_PORT_WARN_NONLOCAL);
tt_int_op(ret, OP_EQ, 0);
@@ -4300,7 +4569,7 @@ test_config_parse_port_config__ports__ports_given(void *data)
// Test success with warn non-local listener
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_clear(slout);
- ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ ret = parse_port_config(slout, config_port_valid, NULL, "ExtOR",
CONN_TYPE_EXT_OR_LISTENER, "127.0.0.42", 0,
CL_PORT_WARN_NONLOCAL);
tt_int_op(ret, OP_EQ, 0);
@@ -4472,8 +4741,8 @@ test_config_parse_port_config__ports__ports_given(void *data)
config_free_lines(config_port_valid); config_port_valid = NULL;
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
smartlist_clear(slout);
- config_port_valid = mock_config_line("DNSPort", "unix:/tmp/somewhere");
- ret = parse_port_config(slout, config_port_valid, NULL, "DNS",
+ config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/somewhere");
+ ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS",
CONN_TYPE_AP_LISTENER, "127.0.0.46", 0,
CL_PORT_DFLT_GROUP_WRITABLE);
#ifdef _WIN32
@@ -4604,6 +4873,15 @@ test_config_parse_port_config__ports__server_options(void *data)
0, CL_PORT_SERVER_OPTIONS);
tt_int_op(ret, OP_EQ, -1);
+ // Check for failure with empty unix: address.
+ config_free_lines(config_port_invalid); config_port_invalid = NULL;
+ SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
+ smartlist_clear(slout);
+ config_port_invalid = mock_config_line("ORPort", "unix:\"\"");
+ ret = parse_port_config(slout, config_port_invalid, NULL, "ORPort", 0, NULL,
+ 0, CL_PORT_SERVER_OPTIONS);
+ tt_int_op(ret, OP_EQ, -1);
+
done:
if (slout)
SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf));
@@ -4633,6 +4911,7 @@ struct testcase_t config_tests[] = {
CONFIG_TEST(write_to_data_subdir, TT_FORK),
CONFIG_TEST(fix_my_family, 0),
CONFIG_TEST(directory_fetch, 0),
+ CONFIG_TEST(port_cfg_line_extract_addrport, 0),
CONFIG_TEST(parse_port_config__listenaddress, 0),
CONFIG_TEST(parse_port_config__ports__no_ports_given, 0),
CONFIG_TEST(parse_port_config__ports__server_options, 0),
diff --git a/src/test/test_connection.c b/src/test/test_connection.c
index bf95b0b59f..d394fc9852 100644
--- a/src/test/test_connection.c
+++ b/src/test/test_connection.c
@@ -95,6 +95,13 @@ mock_connection_connect_sockaddr(connection_t *conn,
return 1;
}
+static int
+fake_close_socket(evutil_socket_t sock)
+{
+ (void)sock;
+ return 0;
+}
+
static void
test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
{
@@ -124,6 +131,7 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
MOCK(connection_connect_sockaddr,
mock_connection_connect_sockaddr);
+ MOCK(tor_close_socket, fake_close_socket);
init_connection_lists();
@@ -148,12 +156,13 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t 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;
}
@@ -192,6 +201,15 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
if (!conn->linked_conn->marked_for_close) {
connection_close_immediate(conn->linked_conn);
+ if (CONN_IS_EDGE(conn->linked_conn)) {
+ /* Suppress warnings about all the stuff we didn't do */
+ TO_EDGE_CONN(conn->linked_conn)->edge_has_sent_end = 1;
+ TO_EDGE_CONN(conn->linked_conn)->end_reason =
+ END_STREAM_REASON_INTERNAL;
+ if (conn->linked_conn->type == CONN_TYPE_AP) {
+ TO_ENTRY_CONN(conn->linked_conn)->socks_request->has_finished = 1;
+ }
+ }
connection_mark_for_close(conn->linked_conn);
}
@@ -212,6 +230,14 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
if (!conn->marked_for_close) {
connection_close_immediate(conn);
+ if (CONN_IS_EDGE(conn)) {
+ /* Suppress warnings about all the stuff we didn't do */
+ TO_EDGE_CONN(conn)->edge_has_sent_end = 1;
+ TO_EDGE_CONN(conn)->end_reason = END_STREAM_REASON_INTERNAL;
+ if (conn->type == CONN_TYPE_AP) {
+ TO_ENTRY_CONN(conn)->socks_request->has_finished = 1;
+ }
+ }
connection_mark_for_close(conn);
}
@@ -340,10 +366,7 @@ test_conn_get_rsrc_teardown(const struct testcase_t *tc, void *arg)
static void *
test_conn_download_status_setup(const struct testcase_t *tc)
{
- (void)tc;
-
- /* Don't return NULL, that causes the test to fail */
- return (void*)"ok";
+ return (void*)tc;
}
static int
@@ -655,7 +678,8 @@ test_conn_download_status(void *arg)
dir_connection_t *conn4 = NULL;
connection_t *ap_conn = NULL;
- consensus_flavor_t usable_flavor = (consensus_flavor_t)arg;
+ const struct testcase_t *tc = arg;
+ consensus_flavor_t usable_flavor = (consensus_flavor_t)tc->setup_data;
/* The "other flavor" trick only works if there are two flavors */
tor_assert(N_CONSENSUS_FLAVORS == 2);
diff --git a/src/test/test_containers.c b/src/test/test_containers.c
index fd896760c0..d8b82e0661 100644
--- a/src/test/test_containers.c
+++ b/src/test/test_containers.c
@@ -132,7 +132,7 @@ test_container_smartlist_strings(void *arg)
tt_str_op("def",OP_EQ, smartlist_get(sl, 5));
tt_str_op(" ",OP_EQ, smartlist_get(sl, 6));
tt_str_op("ghijk",OP_EQ, smartlist_get(sl, 7));
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_clear(sl);
smartlist_split_string(sl, "a,bbd,cdef", ",", SPLIT_SKIP_SPACE, 0);
@@ -149,7 +149,7 @@ test_container_smartlist_strings(void *arg)
tt_str_op("bnud",OP_EQ, smartlist_get(sl,6));
tt_str_op("",OP_EQ, smartlist_get(sl,7));
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_clear(sl);
smartlist_split_string(sl, " ab\tc \td ef ", NULL,
@@ -165,7 +165,7 @@ test_container_smartlist_strings(void *arg)
tt_str_op("ghi",OP_EQ, smartlist_get(sl,4));
tt_str_op("j",OP_EQ, smartlist_get(sl,5));
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_clear(sl);
cp_alloc = smartlist_join_strings(sl, "XY", 0, NULL);
@@ -186,7 +186,7 @@ test_container_smartlist_strings(void *arg)
tt_int_op(5,OP_EQ, smartlist_len(sl));
tt_str_op("z",OP_EQ, smartlist_get(sl, 3));
tt_str_op("zhasd <> <> bnud<>",OP_EQ, smartlist_get(sl, 4));
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_clear(sl);
smartlist_split_string(sl, "abcd\n", "\n",
@@ -198,7 +198,7 @@ test_container_smartlist_strings(void *arg)
tt_int_op(2,OP_EQ, smartlist_len(sl));
tt_str_op("efgh",OP_EQ, smartlist_get(sl, 1));
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_clear(sl);
/* Test swapping, shuffling, and sorting. */
@@ -286,7 +286,7 @@ test_container_smartlist_strings(void *arg)
tt_str_op(cp_alloc,OP_EQ, "and");
tor_free(cp_alloc);
tt_int_op(smartlist_len(sl),OP_EQ, 6);
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_clear(sl);
cp_alloc = smartlist_pop_last(sl);
tt_ptr_op(cp_alloc,OP_EQ, NULL);
@@ -326,7 +326,7 @@ test_container_smartlist_strings(void *arg)
tt_assert(!allsame);
tt_assert(allin);
}
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_clear(sl);
/* Test string_remove and remove and join_strings2 */
@@ -348,7 +348,7 @@ test_container_smartlist_strings(void *arg)
done:
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
tor_free(cp_alloc);
}
@@ -437,7 +437,7 @@ test_container_smartlist_digests(void *arg)
tt_mem_op(smartlist_get(sl, 1),OP_EQ, "AAAAAAAAAAAAAAAAAAAA", DIGEST_LEN);
done:
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
}
@@ -490,7 +490,7 @@ test_container_smartlist_join(void *arg)
smartlist_free(sl3);
SMARTLIST_FOREACH(sl2, char *, cp, tor_free(cp));
smartlist_free(sl2);
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
tor_free(joined);
}
@@ -528,7 +528,7 @@ test_container_smartlist_pos(void *arg)
tt_int_op(smartlist_pos(sl, smartlist_get(sl,6)), ==, 6);
done:
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
}
@@ -1140,7 +1140,7 @@ test_container_smartlist_most_frequent(void *arg)
tt_str_op(cp, ==, "def"); /* No tie */
done:
- SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(sl, char *, str, tor_free(str));
smartlist_free(sl);
}
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
index 7f9db4312f..f19c846144 100644
--- a/src/test/test_controller.c
+++ b/src/test/test_controller.c
@@ -4,7 +4,10 @@
#define CONTROL_PRIVATE
#include "or.h"
#include "control.h"
+#include "entrynodes.h"
+#include "networkstatus.h"
#include "rendservice.h"
+#include "routerlist.h"
#include "test.h"
static void
@@ -134,6 +137,8 @@ test_rend_service_parse_port_config(void *arg)
cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg);
tt_assert(cfg);
tt_assert(!err_msg);
+ rend_service_port_config_free(cfg);
+ cfg = NULL;
/* XXX: Someone should add tests for AF_UNIX targets if supported. */
@@ -148,16 +153,1191 @@ test_rend_service_parse_port_config(void *arg)
cfg = rend_service_parse_port_config("90001", sep, &err_msg);
tt_assert(!cfg);
tt_assert(err_msg);
+ tor_free(err_msg);
+
+ /* unix port */
+ cfg = NULL;
+
+ /* quoted unix port */
+ tor_free(err_msg);
+ cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
+ " ", &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+ rend_service_port_config_free(cfg);
+ cfg = NULL;
+
+ /* quoted unix port */
+ tor_free(err_msg);
+ cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
+ " ", &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+ 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_str_op(err_msg, OP_EQ, "Couldn't process address <unix:\"/tmp/foo bar> "
+ "from hidden service configuration");
+ tor_free(err_msg);
+
+ /* bogus IP address */
+ cfg = rend_service_parse_port_config("100 1.2.3.4.5:9000",
+ " ", &err_msg);
+ tt_assert(!cfg);
+ tt_str_op(err_msg, OP_EQ, "Unparseable address in hidden service port "
+ "configuration.");
+ tor_free(err_msg);
+
+ /* bogus port port */
+ cfg = rend_service_parse_port_config("100 99999",
+ " ", &err_msg);
+ tt_assert(!cfg);
+ tt_str_op(err_msg, OP_EQ, "Unparseable or out-of-range port \"99999\" "
+ "in hidden service port configuration.");
+ tor_free(err_msg);
done:
rend_service_port_config_free(cfg);
tor_free(err_msg);
}
+static void
+test_add_onion_helper_clientauth(void *arg)
+{
+ rend_authorized_client_t *client = NULL;
+ char *err_msg = NULL;
+ int created = 0;
+
+ (void)arg;
+
+ /* Test "ClientName" only. */
+ client = add_onion_helper_clientauth("alice", &created, &err_msg);
+ tt_assert(client);
+ tt_assert(created);
+ tt_assert(!err_msg);
+ rend_authorized_client_free(client);
+
+ /* Test "ClientName:Blob" */
+ client = add_onion_helper_clientauth("alice:475hGBHPlq7Mc0cRZitK/B",
+ &created, &err_msg);
+ tt_assert(client);
+ tt_assert(!created);
+ tt_assert(!err_msg);
+ rend_authorized_client_free(client);
+
+ /* Test invalid client names */
+ client = add_onion_helper_clientauth("no*asterisks*allowed", &created,
+ &err_msg);
+ tt_assert(!client);
+ 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_assert(err_msg);
+ tor_free(err_msg);
+
+ /* Test invalid syntax */
+ client = add_onion_helper_clientauth(":475hGBHPlq7Mc0cRZitK/B", &created,
+ &err_msg);
+ tt_assert(!client);
+ tt_assert(err_msg);
+ tor_free(err_msg);
+
+ done:
+ rend_authorized_client_free(client);
+ tor_free(err_msg);
+}
+
+/* Mocks and data/variables used for GETINFO download status tests */
+
+static const download_status_t dl_status_default =
+ { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
+static download_status_t ns_dl_status[N_CONSENSUS_FLAVORS];
+static download_status_t ns_dl_status_bootstrap[N_CONSENSUS_FLAVORS];
+static download_status_t ns_dl_status_running[N_CONSENSUS_FLAVORS];
+
+/*
+ * These should explore all the possible cases of download_status_to_string()
+ * in control.c
+ */
+static const download_status_t dls_sample_1 =
+ { 1467163900, 0, 0, DL_SCHED_GENERIC, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE, DL_SCHED_DETERMINISTIC, 0, 0 };
+static const char * dls_sample_1_str =
+ "next-attempt-at 2016-06-29 01:31:40\n"
+ "n-download-failures 0\n"
+ "n-download-attempts 0\n"
+ "schedule DL_SCHED_GENERIC\n"
+ "want-authority DL_WANT_ANY_DIRSERVER\n"
+ "increment-on DL_SCHED_INCREMENT_FAILURE\n"
+ "backoff DL_SCHED_DETERMINISTIC\n";
+static const download_status_t dls_sample_2 =
+ { 1467164400, 1, 2, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_FAILURE, DL_SCHED_DETERMINISTIC, 0, 0 };
+static const char * dls_sample_2_str =
+ "next-attempt-at 2016-06-29 01:40:00\n"
+ "n-download-failures 1\n"
+ "n-download-attempts 2\n"
+ "schedule DL_SCHED_CONSENSUS\n"
+ "want-authority DL_WANT_AUTHORITY\n"
+ "increment-on DL_SCHED_INCREMENT_FAILURE\n"
+ "backoff DL_SCHED_DETERMINISTIC\n";
+static const download_status_t dls_sample_3 =
+ { 1467154400, 12, 25, DL_SCHED_BRIDGE, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_DETERMINISTIC, 0, 0 };
+static const char * dls_sample_3_str =
+ "next-attempt-at 2016-06-28 22:53:20\n"
+ "n-download-failures 12\n"
+ "n-download-attempts 25\n"
+ "schedule DL_SCHED_BRIDGE\n"
+ "want-authority DL_WANT_ANY_DIRSERVER\n"
+ "increment-on DL_SCHED_INCREMENT_ATTEMPT\n"
+ "backoff DL_SCHED_DETERMINISTIC\n";
+static const download_status_t dls_sample_4 =
+ { 1467166600, 3, 0, DL_SCHED_GENERIC, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 0, 0 };
+static const char * dls_sample_4_str =
+ "next-attempt-at 2016-06-29 02:16:40\n"
+ "n-download-failures 3\n"
+ "n-download-attempts 0\n"
+ "schedule DL_SCHED_GENERIC\n"
+ "want-authority DL_WANT_ANY_DIRSERVER\n"
+ "increment-on DL_SCHED_INCREMENT_FAILURE\n"
+ "backoff DL_SCHED_RANDOM_EXPONENTIAL\n"
+ "last-backoff-position 0\n"
+ "last-delay-used 0\n";
+static const download_status_t dls_sample_5 =
+ { 1467164600, 3, 7, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER,
+ DL_SCHED_INCREMENT_FAILURE, DL_SCHED_RANDOM_EXPONENTIAL, 1, 2112, };
+static const char * dls_sample_5_str =
+ "next-attempt-at 2016-06-29 01:43:20\n"
+ "n-download-failures 3\n"
+ "n-download-attempts 7\n"
+ "schedule DL_SCHED_CONSENSUS\n"
+ "want-authority DL_WANT_ANY_DIRSERVER\n"
+ "increment-on DL_SCHED_INCREMENT_FAILURE\n"
+ "backoff DL_SCHED_RANDOM_EXPONENTIAL\n"
+ "last-backoff-position 1\n"
+ "last-delay-used 2112\n";
+static const download_status_t dls_sample_6 =
+ { 1467164200, 4, 9, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY,
+ DL_SCHED_INCREMENT_ATTEMPT, DL_SCHED_RANDOM_EXPONENTIAL, 3, 432 };
+static const char * dls_sample_6_str =
+ "next-attempt-at 2016-06-29 01:36:40\n"
+ "n-download-failures 4\n"
+ "n-download-attempts 9\n"
+ "schedule DL_SCHED_CONSENSUS\n"
+ "want-authority DL_WANT_AUTHORITY\n"
+ "increment-on DL_SCHED_INCREMENT_ATTEMPT\n"
+ "backoff DL_SCHED_RANDOM_EXPONENTIAL\n"
+ "last-backoff-position 3\n"
+ "last-delay-used 432\n";
+
+/* Simulated auth certs */
+static const char *auth_id_digest_1_str =
+ "63CDD326DFEF0CA020BDD3FEB45A3286FE13A061";
+static download_status_t auth_def_cert_download_status_1;
+static const char *auth_id_digest_2_str =
+ "2C209FCDD8D48DC049777B8DC2C0F94A0408BE99";
+static download_status_t auth_def_cert_download_status_2;
+/* Expected form of digest list returned for GETINFO downloads/cert/fps */
+static const char *auth_id_digest_expected_list =
+ "63CDD326DFEF0CA020BDD3FEB45A3286FE13A061\n"
+ "2C209FCDD8D48DC049777B8DC2C0F94A0408BE99\n";
+
+/* Signing keys for simulated auth 1 */
+static const char *auth_1_sk_1_str =
+ "AA69566029B1F023BA09451B8F1B10952384EB58";
+static download_status_t auth_1_sk_1_dls;
+static const char *auth_1_sk_2_str =
+ "710865C7F06B73C5292695A8C34F1C94F769FF72";
+static download_status_t auth_1_sk_2_dls;
+/*
+ * Expected form of sk digest list for
+ * GETINFO downloads/cert/<auth_id_digest_1_str>/sks
+ */
+static const char *auth_1_sk_digest_expected_list =
+ "AA69566029B1F023BA09451B8F1B10952384EB58\n"
+ "710865C7F06B73C5292695A8C34F1C94F769FF72\n";
+
+/* Signing keys for simulated auth 2 */
+static const char *auth_2_sk_1_str =
+ "4299047E00D070AD6703FE00BE7AA756DB061E62";
+static download_status_t auth_2_sk_1_dls;
+static const char *auth_2_sk_2_str =
+ "9451B8F1B10952384EB58B5F230C0BB701626C9B";
+static download_status_t auth_2_sk_2_dls;
+/*
+ * Expected form of sk digest list for
+ * GETINFO downloads/cert/<auth_id_digest_2_str>/sks
+ */
+static const char *auth_2_sk_digest_expected_list =
+ "4299047E00D070AD6703FE00BE7AA756DB061E62\n"
+ "9451B8F1B10952384EB58B5F230C0BB701626C9B\n";
+
+/* Simulated router descriptor digests or bridge identity digests */
+static const char *descbr_digest_1_str =
+ "616408544C7345822696074A1A3DFA16AB381CBD";
+static download_status_t descbr_digest_1_dl;
+static const char *descbr_digest_2_str =
+ "06E8067246967265DBCB6641631B530EFEC12DC3";
+static download_status_t descbr_digest_2_dl;
+/* Expected form of digest list returned for GETINFO downloads/desc/descs */
+static const char *descbr_expected_list =
+ "616408544C7345822696074A1A3DFA16AB381CBD\n"
+ "06E8067246967265DBCB6641631B530EFEC12DC3\n";
+/*
+ * Flag to make all descbr queries fail, to simulate not being
+ * configured such that such queries make sense.
+ */
+static int disable_descbr = 0;
+
+static void
+reset_mocked_dl_statuses(void)
+{
+ int i;
+
+ for (i = 0; i < N_CONSENSUS_FLAVORS; ++i) {
+ memcpy(&(ns_dl_status[i]), &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&(ns_dl_status_bootstrap[i]), &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&(ns_dl_status_running[i]), &dl_status_default,
+ sizeof(download_status_t));
+ }
+
+ memcpy(&auth_def_cert_download_status_1, &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&auth_def_cert_download_status_2, &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&auth_1_sk_1_dls, &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&auth_1_sk_2_dls, &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&auth_2_sk_1_dls, &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&auth_2_sk_2_dls, &dl_status_default,
+ sizeof(download_status_t));
+
+ memcpy(&descbr_digest_1_dl, &dl_status_default,
+ sizeof(download_status_t));
+ memcpy(&descbr_digest_2_dl, &dl_status_default,
+ sizeof(download_status_t));
+}
+
+static download_status_t *
+ns_dl_status_mock(consensus_flavor_t flavor)
+{
+ return &(ns_dl_status[flavor]);
+}
+
+static download_status_t *
+ns_dl_status_bootstrap_mock(consensus_flavor_t flavor)
+{
+ return &(ns_dl_status_bootstrap[flavor]);
+}
+
+static download_status_t *
+ns_dl_status_running_mock(consensus_flavor_t flavor)
+{
+ return &(ns_dl_status_running[flavor]);
+}
+
+static void
+setup_ns_mocks(void)
+{
+ MOCK(networkstatus_get_dl_status_by_flavor, ns_dl_status_mock);
+ MOCK(networkstatus_get_dl_status_by_flavor_bootstrap,
+ ns_dl_status_bootstrap_mock);
+ MOCK(networkstatus_get_dl_status_by_flavor_running,
+ ns_dl_status_running_mock);
+ reset_mocked_dl_statuses();
+}
+
+static void
+clear_ns_mocks(void)
+{
+ UNMOCK(networkstatus_get_dl_status_by_flavor);
+ UNMOCK(networkstatus_get_dl_status_by_flavor_bootstrap);
+ UNMOCK(networkstatus_get_dl_status_by_flavor_running);
+}
+
+static smartlist_t *
+cert_dl_status_auth_ids_mock(void)
+{
+ char digest[DIGEST_LEN], *tmp;
+ int len;
+ smartlist_t *list = NULL;
+
+ /* Just pretend we have only the two hard-coded digests listed above */
+ list = smartlist_new();
+ len = base16_decode(digest, DIGEST_LEN,
+ auth_id_digest_1_str, strlen(auth_id_digest_1_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, digest, DIGEST_LEN);
+ smartlist_add(list, tmp);
+ len = base16_decode(digest, DIGEST_LEN,
+ auth_id_digest_2_str, strlen(auth_id_digest_2_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, digest, DIGEST_LEN);
+ smartlist_add(list, tmp);
+
+ done:
+ return list;
+}
+
+static download_status_t *
+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);
+ base16_encode(digest_str, HEX_DIGEST_LEN + 1,
+ digest, DIGEST_LEN);
+ digest_str[HEX_DIGEST_LEN] = '\0';
+
+ if (strcmp(digest_str, auth_id_digest_1_str) == 0) {
+ dl = &auth_def_cert_download_status_1;
+ } else if (strcmp(digest_str, auth_id_digest_2_str) == 0) {
+ dl = &auth_def_cert_download_status_2;
+ }
+
+ done:
+ return dl;
+}
+
+static smartlist_t *
+cert_dl_status_sks_for_auth_id_mock(const char *digest)
+{
+ smartlist_t *list = NULL;
+ char sk[DIGEST_LEN];
+ char digest_str[HEX_DIGEST_LEN+1];
+ char *tmp;
+ int len;
+
+ tt_assert(digest != NULL);
+ base16_encode(digest_str, HEX_DIGEST_LEN + 1,
+ digest, DIGEST_LEN);
+ digest_str[HEX_DIGEST_LEN] = '\0';
+
+ /*
+ * Build a list of two hard-coded digests, depending on what we
+ * were just passed.
+ */
+ if (strcmp(digest_str, auth_id_digest_1_str) == 0) {
+ list = smartlist_new();
+ len = base16_decode(sk, DIGEST_LEN,
+ auth_1_sk_1_str, strlen(auth_1_sk_1_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, sk, DIGEST_LEN);
+ smartlist_add(list, tmp);
+ len = base16_decode(sk, DIGEST_LEN,
+ auth_1_sk_2_str, strlen(auth_1_sk_2_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, sk, DIGEST_LEN);
+ smartlist_add(list, tmp);
+ } else if (strcmp(digest_str, auth_id_digest_2_str) == 0) {
+ list = smartlist_new();
+ len = base16_decode(sk, DIGEST_LEN,
+ auth_2_sk_1_str, strlen(auth_2_sk_1_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, sk, DIGEST_LEN);
+ smartlist_add(list, tmp);
+ len = base16_decode(sk, DIGEST_LEN,
+ auth_2_sk_2_str, strlen(auth_2_sk_2_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, sk, DIGEST_LEN);
+ smartlist_add(list, tmp);
+ }
+
+ done:
+ return list;
+}
+
+static download_status_t *
+cert_dl_status_fp_sk_mock(const char *fp_digest, const char *sk_digest)
+{
+ download_status_t *dl = NULL;
+ char fp_digest_str[HEX_DIGEST_LEN+1], sk_digest_str[HEX_DIGEST_LEN+1];
+
+ /*
+ * Unpack the digests so we can compare them and figure out which
+ * dl status we want.
+ */
+
+ tt_assert(fp_digest != 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);
+ base16_encode(sk_digest_str, HEX_DIGEST_LEN + 1,
+ sk_digest, DIGEST_LEN);
+ sk_digest_str[HEX_DIGEST_LEN] = '\0';
+
+ if (strcmp(fp_digest_str, auth_id_digest_1_str) == 0) {
+ if (strcmp(sk_digest_str, auth_1_sk_1_str) == 0) {
+ dl = &auth_1_sk_1_dls;
+ } else if (strcmp(sk_digest_str, auth_1_sk_2_str) == 0) {
+ dl = &auth_1_sk_2_dls;
+ }
+ } else if (strcmp(fp_digest_str, auth_id_digest_2_str) == 0) {
+ if (strcmp(sk_digest_str, auth_2_sk_1_str) == 0) {
+ dl = &auth_2_sk_1_dls;
+ } else if (strcmp(sk_digest_str, auth_2_sk_2_str) == 0) {
+ dl = &auth_2_sk_2_dls;
+ }
+ }
+
+ done:
+ return dl;
+}
+
+static void
+setup_cert_mocks(void)
+{
+ MOCK(list_authority_ids_with_downloads, cert_dl_status_auth_ids_mock);
+ MOCK(id_only_download_status_for_authority_id,
+ cert_dl_status_def_for_auth_mock);
+ MOCK(list_sk_digests_for_authority_id,
+ cert_dl_status_sks_for_auth_id_mock);
+ MOCK(download_status_for_authority_id_and_sk,
+ cert_dl_status_fp_sk_mock);
+ reset_mocked_dl_statuses();
+}
+
+static void
+clear_cert_mocks(void)
+{
+ UNMOCK(list_authority_ids_with_downloads);
+ UNMOCK(id_only_download_status_for_authority_id);
+ UNMOCK(list_sk_digests_for_authority_id);
+ UNMOCK(download_status_for_authority_id_and_sk);
+}
+
+static smartlist_t *
+descbr_get_digests_mock(void)
+{
+ char digest[DIGEST_LEN], *tmp;
+ int len;
+ smartlist_t *list = NULL;
+
+ if (!disable_descbr) {
+ /* Just pretend we have only the two hard-coded digests listed above */
+ list = smartlist_new();
+ len = base16_decode(digest, DIGEST_LEN,
+ descbr_digest_1_str, strlen(descbr_digest_1_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, digest, DIGEST_LEN);
+ smartlist_add(list, tmp);
+ len = base16_decode(digest, DIGEST_LEN,
+ descbr_digest_2_str, strlen(descbr_digest_2_str));
+ tt_int_op(len, OP_EQ, DIGEST_LEN);
+ tmp = tor_malloc(DIGEST_LEN);
+ memcpy(tmp, digest, DIGEST_LEN);
+ smartlist_add(list, tmp);
+ }
+
+ done:
+ return list;
+}
+
+static download_status_t *
+descbr_get_dl_by_digest_mock(const char *digest)
+{
+ download_status_t *dl = NULL;
+ char digest_str[HEX_DIGEST_LEN+1];
+
+ if (!disable_descbr) {
+ tt_assert(digest != NULL);
+ base16_encode(digest_str, HEX_DIGEST_LEN + 1,
+ digest, DIGEST_LEN);
+ digest_str[HEX_DIGEST_LEN] = '\0';
+
+ if (strcmp(digest_str, descbr_digest_1_str) == 0) {
+ dl = &descbr_digest_1_dl;
+ } else if (strcmp(digest_str, descbr_digest_2_str) == 0) {
+ dl = &descbr_digest_2_dl;
+ }
+ }
+
+ done:
+ return dl;
+}
+
+static void
+setup_desc_mocks(void)
+{
+ MOCK(router_get_descriptor_digests,
+ descbr_get_digests_mock);
+ MOCK(router_get_dl_status_by_descriptor_digest,
+ descbr_get_dl_by_digest_mock);
+ reset_mocked_dl_statuses();
+}
+
+static void
+clear_desc_mocks(void)
+{
+ UNMOCK(router_get_descriptor_digests);
+ UNMOCK(router_get_dl_status_by_descriptor_digest);
+}
+
+static void
+setup_bridge_mocks(void)
+{
+ disable_descbr = 0;
+
+ MOCK(list_bridge_identities,
+ descbr_get_digests_mock);
+ MOCK(get_bridge_dl_status_by_id,
+ descbr_get_dl_by_digest_mock);
+ reset_mocked_dl_statuses();
+}
+
+static void
+clear_bridge_mocks(void)
+{
+ UNMOCK(list_bridge_identities);
+ UNMOCK(get_bridge_dl_status_by_id);
+
+ disable_descbr = 0;
+}
+
+static void
+test_download_status_consensus(void *arg)
+{
+ /* We just need one of these to pass, it doesn't matter what's in it */
+ control_connection_t dummy;
+ /* Get results out */
+ char *answer = NULL;
+ const char *errmsg = NULL;
+
+ (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_str_op(errmsg, OP_EQ, "Unknown download status query");
+
+ setup_ns_mocks();
+
+ /*
+ * Check returning serialized dlstatuses, and implicitly also test
+ * download_status_to_string().
+ */
+
+ /* Case 1 default/FLAV_NS*/
+ memcpy(&(ns_dl_status[FLAV_NS]), &dls_sample_1,
+ sizeof(download_status_t));
+ getinfo_helper_downloads(&dummy, "downloads/networkstatus/ns",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_1_str);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 2 default/FLAV_MICRODESC */
+ memcpy(&(ns_dl_status[FLAV_MICRODESC]), &dls_sample_2,
+ sizeof(download_status_t));
+ getinfo_helper_downloads(&dummy, "downloads/networkstatus/microdesc",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_2_str);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 3 bootstrap/FLAV_NS */
+ memcpy(&(ns_dl_status_bootstrap[FLAV_NS]), &dls_sample_3,
+ sizeof(download_status_t));
+ getinfo_helper_downloads(&dummy, "downloads/networkstatus/ns/bootstrap",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_3_str);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 4 bootstrap/FLAV_MICRODESC */
+ memcpy(&(ns_dl_status_bootstrap[FLAV_MICRODESC]), &dls_sample_4,
+ sizeof(download_status_t));
+ getinfo_helper_downloads(&dummy,
+ "downloads/networkstatus/microdesc/bootstrap",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_4_str);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 5 running/FLAV_NS */
+ memcpy(&(ns_dl_status_running[FLAV_NS]), &dls_sample_5,
+ sizeof(download_status_t));
+ getinfo_helper_downloads(&dummy,
+ "downloads/networkstatus/ns/running",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_5_str);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 6 running/FLAV_MICRODESC */
+ memcpy(&(ns_dl_status_running[FLAV_MICRODESC]), &dls_sample_6,
+ sizeof(download_status_t));
+ getinfo_helper_downloads(&dummy,
+ "downloads/networkstatus/microdesc/running",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_6_str);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Now check the error case */
+ getinfo_helper_downloads(&dummy, "downloads/networkstatus/foo",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "Unknown flavor");
+ errmsg = NULL;
+
+ done:
+ clear_ns_mocks();
+ tor_free(answer);
+
+ return;
+}
+
+static void
+test_download_status_cert(void *arg)
+{
+ /* We just need one of these to pass, it doesn't matter what's in it */
+ control_connection_t dummy;
+ /* Get results out */
+ char *question = NULL;
+ char *answer = NULL;
+ const char *errmsg = NULL;
+
+ (void)arg;
+
+ setup_cert_mocks();
+
+ /*
+ * Check returning serialized dlstatuses and digest lists, and implicitly
+ * also test download_status_to_string() and digest_list_to_string().
+ */
+
+ /* Case 1 - list of authority identity fingerprints */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fps",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, auth_id_digest_expected_list);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 2 - download status for default cert for 1st auth id */
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_1_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 3 - download status for default cert for 2nd auth id */
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_2_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, auth_1_sk_digest_expected_list);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, auth_2_sk_digest_expected_list);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 6 - download status for 1st auth id, 1st sk */
+ memcpy(&auth_1_sk_1_dls, &dls_sample_3,
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_3_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 7 - download status for 1st auth id, 2nd sk */
+ memcpy(&auth_1_sk_2_dls, &dls_sample_4,
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_4_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 8 - download status for 2nd auth id, 1st sk */
+ memcpy(&auth_2_sk_1_dls, &dls_sample_5,
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_5_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 9 - download status for 2nd auth id, 2nd sk */
+ memcpy(&auth_2_sk_2_dls, &dls_sample_6,
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_6_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Now check the error cases */
+
+ /* 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_str_op(errmsg, OP_EQ, "Unknown certificate download status query");
+ errmsg = NULL;
+
+ /*
+ * Case 2 - looks like downloads/cert/fp/<fp>, but <fp> isn't even
+ * the right length for a digest.
+ */
+ getinfo_helper_downloads(&dummy, "downloads/cert/fp/2B1D36D32B2942406",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
+ errmsg = NULL;
+
+ /*
+ * Case 3 - looks like downloads/cert/fp/<fp>, and <fp> is digest-sized,
+ * but not parseable as one.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/82F52AF55D250115FE44D3GC81D49643241D56A1",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
+ errmsg = NULL;
+
+ /*
+ * Case 4 - downloads/cert/fp/<fp>, and <fp> is not a known authority
+ * identity digest
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ,
+ "Failed to get download status for this authority identity digest");
+ errmsg = NULL;
+
+ /*
+ * Case 5 - looks like downloads/cert/fp/<fp>/<anything>, but <fp> doesn't
+ * parse as a sensible digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/82F52AF55D250115FE44D3GC81D49643241D56A1/blah",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like an identity digest");
+ errmsg = NULL;
+
+ /*
+ * Case 6 - looks like downloads/cert/fp/<fp>/<anything>, but <fp> doesn't
+ * parse as a sensible digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/82F52AF55D25/blah",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like an identity digest");
+ errmsg = NULL;
+
+ /*
+ * Case 7 - downloads/cert/fp/<fp>/sks, and <fp> is not a known authority
+ * digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/sks",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ,
+ "Failed to get list of signing key digests for this authority "
+ "identity digest");
+ errmsg = NULL;
+
+ /*
+ * Case 8 - looks like downloads/cert/fp/<fp>/<sk>, but <sk> doesn't
+ * parse as a signing key digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/"
+ "82F52AF55D250115FE44D3GC81D49643241D56A1",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like a signing key digest");
+ errmsg = NULL;
+
+ /*
+ * Case 9 - looks like downloads/cert/fp/<fp>/<sk>, but <sk> doesn't
+ * parse as a signing key digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/AC4F23B5745BDD2A77997B85B1FD85D05C2E0F61/"
+ "82F52AF55D250115FE44D",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like a signing key digest");
+ errmsg = NULL;
+
+ /*
+ * Case 10 - downloads/cert/fp/<fp>/<sk>, but <fp> isn't a known
+ * authority identity digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/C6B05DF332F74DB9A13498EE3BBC7AA2F69FCB45/"
+ "3A214FC21AE25B012C2ECCB5F4EC8A3602D0545D",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ,
+ "Failed to get download status for this identity/"
+ "signing key digest pair");
+ errmsg = NULL;
+
+ /*
+ * Case 11 - downloads/cert/fp/<fp>/<sk>, but <sk> isn't a known
+ * signing key digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/63CDD326DFEF0CA020BDD3FEB45A3286FE13A061/"
+ "3A214FC21AE25B012C2ECCB5F4EC8A3602D0545D",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ,
+ "Failed to get download status for this identity/"
+ "signing key digest pair");
+ errmsg = NULL;
+
+ /*
+ * Case 12 - downloads/cert/fp/<fp>/<sk>, but <sk> is on the list for
+ * a different authority identity digest.
+ */
+ getinfo_helper_downloads(&dummy,
+ "downloads/cert/fp/63CDD326DFEF0CA020BDD3FEB45A3286FE13A061/"
+ "9451B8F1B10952384EB58B5F230C0BB701626C9B",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ,
+ "Failed to get download status for this identity/"
+ "signing key digest pair");
+ errmsg = NULL;
+
+ done:
+ clear_cert_mocks();
+ tor_free(answer);
+
+ return;
+}
+
+static void
+test_download_status_desc(void *arg)
+{
+ /* We just need one of these to pass, it doesn't matter what's in it */
+ control_connection_t dummy;
+ /* Get results out */
+ char *question = NULL;
+ char *answer = NULL;
+ const char *errmsg = NULL;
+
+ (void)arg;
+
+ setup_desc_mocks();
+
+ /*
+ * Check returning serialized dlstatuses and digest lists, and implicitly
+ * also test download_status_to_string() and digest_list_to_string().
+ */
+
+ /* Case 1 - list of router descriptor digests */
+ getinfo_helper_downloads(&dummy,
+ "downloads/desc/descs",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, descbr_expected_list);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 2 - get download status for router descriptor 1 */
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_1_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 3 - get download status for router descriptor 1 */
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_2_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Now check the error cases */
+
+ /* 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_str_op(errmsg, OP_EQ, "Unknown router descriptor download status query");
+ errmsg = NULL;
+
+ /* Case 2 - nonparseable digest-shaped thing */
+ getinfo_helper_downloads(
+ &dummy,
+ "downloads/desc/774EC52FD9A5B80A6FACZE536616E8022E3470AG",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
+ errmsg = NULL;
+
+ /* Case 3 - digest we have no descriptor for */
+ getinfo_helper_downloads(
+ &dummy,
+ "downloads/desc/B05B46135B0B2C04EBE1DD6A6AE4B12D7CD2226A",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "No such descriptor digest found");
+ errmsg = NULL;
+
+ /* Case 4 - microdescs only */
+ disable_descbr = 1;
+ getinfo_helper_downloads(&dummy,
+ "downloads/desc/descs",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ,
+ "We don't seem to have a networkstatus-flavored consensus");
+ errmsg = NULL;
+ disable_descbr = 0;
+
+ done:
+ clear_desc_mocks();
+ tor_free(answer);
+
+ return;
+}
+
+static void
+test_download_status_bridge(void *arg)
+{
+ /* We just need one of these to pass, it doesn't matter what's in it */
+ control_connection_t dummy;
+ /* Get results out */
+ char *question = NULL;
+ char *answer = NULL;
+ const char *errmsg = NULL;
+
+ (void)arg;
+
+ setup_bridge_mocks();
+
+ /*
+ * Check returning serialized dlstatuses and digest lists, and implicitly
+ * also test download_status_to_string() and digest_list_to_string().
+ */
+
+ /* Case 1 - list of bridge identity digests */
+ getinfo_helper_downloads(&dummy,
+ "downloads/bridge/bridges",
+ &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, descbr_expected_list);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 2 - get download status for bridge descriptor 1 */
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_3_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Case 3 - get download status for router descriptor 1 */
+ 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);
+ getinfo_helper_downloads(&dummy, question, &answer, &errmsg);
+ tt_assert(answer != NULL);
+ tt_assert(errmsg == NULL);
+ tt_str_op(answer, OP_EQ, dls_sample_4_str);
+ tor_free(question);
+ tor_free(answer);
+ errmsg = NULL;
+
+ /* Now check the error cases */
+
+ /* 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_str_op(errmsg, OP_EQ, "Unknown bridge descriptor download status query");
+ errmsg = NULL;
+
+ /* Case 2 - nonparseable digest-shaped thing */
+ getinfo_helper_downloads(
+ &dummy,
+ "downloads/bridge/774EC52FD9A5B80A6FACZE536616E8022E3470AG",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "That didn't look like a digest");
+ errmsg = NULL;
+
+ /* Case 3 - digest we have no descriptor for */
+ getinfo_helper_downloads(
+ &dummy,
+ "downloads/bridge/B05B46135B0B2C04EBE1DD6A6AE4B12D7CD2226A",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "No such bridge identity digest found");
+ errmsg = NULL;
+
+ /* Case 4 - bridges disabled */
+ disable_descbr = 1;
+ getinfo_helper_downloads(&dummy,
+ "downloads/bridge/bridges",
+ &answer, &errmsg);
+ tt_assert(answer == NULL);
+ tt_assert(errmsg != NULL);
+ tt_str_op(errmsg, OP_EQ, "We don't seem to be using bridges");
+ errmsg = NULL;
+ disable_descbr = 0;
+
+ done:
+ clear_bridge_mocks();
+ tor_free(answer);
+
+ return;
+}
+
struct testcase_t controller_tests[] = {
{ "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL },
{ "rend_service_parse_port_config", test_rend_service_parse_port_config, 0,
NULL, NULL },
+ { "add_onion_helper_clientauth", test_add_onion_helper_clientauth, 0, NULL,
+ NULL },
+ { "download_status_consensus", test_download_status_consensus, 0, NULL,
+ NULL },
+ { "download_status_cert", test_download_status_cert, 0, NULL,
+ NULL },
+ { "download_status_desc", test_download_status_desc, 0, NULL, NULL },
+ { "download_status_bridge", test_download_status_bridge, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 6a95e92733..64a46f7914 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -18,15 +18,12 @@
#include <openssl/evp.h>
#include <openssl/rand.h>
-extern const char AUTHORITY_SIGNKEY_3[];
-extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
-extern const char AUTHORITY_SIGNKEY_A_DIGEST256[];
-
/** Run unit tests for Diffie-Hellman functionality. */
static void
test_crypto_dh(void *arg)
{
crypto_dh_t *dh1 = crypto_dh_new(DH_TYPE_CIRCUIT);
+ crypto_dh_t *dh1_dup = NULL;
crypto_dh_t *dh2 = crypto_dh_new(DH_TYPE_CIRCUIT);
char p1[DH_BYTES];
char p2[DH_BYTES];
@@ -41,6 +38,9 @@ test_crypto_dh(void *arg)
memset(p1, 0, DH_BYTES);
memset(p2, 0, DH_BYTES);
tt_mem_op(p1,OP_EQ, p2, DH_BYTES);
+
+ tt_int_op(-1, OP_EQ, crypto_dh_get_public(dh1, p1, 6)); /* too short */
+
tt_assert(! crypto_dh_get_public(dh1, p1, DH_BYTES));
tt_mem_op(p1,OP_NE, p2, DH_BYTES);
tt_assert(! crypto_dh_get_public(dh2, p2, DH_BYTES));
@@ -54,15 +54,119 @@ test_crypto_dh(void *arg)
tt_int_op(s1len,OP_EQ, s2len);
tt_mem_op(s1,OP_EQ, s2, s1len);
+ /* test dh_dup; make sure it works the same. */
+ dh1_dup = crypto_dh_dup(dh1);
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1_dup, p2, DH_BYTES, s1, 50);
+ tt_mem_op(s1,OP_EQ, s2, s1len);
+
{
- /* XXXX Now fabricate some bad values and make sure they get caught,
- * Check 0, 1, N-1, >= N, etc.
- */
+ /* Now fabricate some bad values and make sure they get caught. */
+
+ /* 1 and 0 should both fail. */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x01", 1, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x00", 1, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ memset(p1, 0, DH_BYTES); /* 0 with padding. */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ p1[DH_BYTES-1] = 1; /* 1 with padding*/
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ /* 2 is okay, though weird. */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, "\x02", 1, s1, 50);
+ tt_int_op(50, OP_EQ, s1len);
+
+ const char P[] =
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE65381FFFFFFFFFFFFFFFF";
+
+ /* p-1, p, and so on are not okay. */
+ base16_decode(p1, sizeof(p1), P, strlen(P));
+
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ p1[DH_BYTES-1] = 0xFE; /* p-1 */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ p1[DH_BYTES-1] = 0xFD; /* p-2 works fine */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(50, OP_EQ, s1len);
+
+ const char P_plus_one[] =
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE653820000000000000000";
+
+ base16_decode(p1, sizeof(p1), P_plus_one, strlen(P_plus_one));
+
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ p1[DH_BYTES-1] = 0x01; /* p+2 */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ p1[DH_BYTES-1] = 0xff; /* p+256 */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+
+ memset(p1, 0xff, DH_BYTES), /* 2^1024-1 */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh1, p1, DH_BYTES, s1, 50);
+ tt_int_op(-1, OP_EQ, s1len);
+ }
+
+ {
+ /* provoke an error in the openssl DH_compute_key function; make sure we
+ * survive. */
+ tt_assert(! crypto_dh_get_public(dh1, p1, DH_BYTES));
+
+ crypto_dh_free(dh2);
+ dh2= crypto_dh_new(DH_TYPE_CIRCUIT); /* no private key set */
+ s1len = crypto_dh_compute_secret(LOG_WARN, dh2,
+ p1, DH_BYTES,
+ s1, 50);
+ tt_int_op(s1len, OP_EQ, -1);
}
done:
crypto_dh_free(dh1);
crypto_dh_free(dh2);
+ crypto_dh_free(dh1_dup);
+}
+
+static void
+test_crypto_openssl_version(void *arg)
+{
+ (void)arg;
+ const char *version = crypto_openssl_get_version_str();
+ const char *h_version = crypto_openssl_get_header_version_str();
+ tt_assert(version);
+ tt_assert(h_version);
+ tt_assert(!strcmpstart(version, h_version)); /* "-fips" suffix, etc */
+ tt_assert(!strstr(version, "OpenSSL"));
+ int a=-1,b=-1,c=-1;
+ if (!strcmpstart(version, "LibreSSL") || !strcmpstart(version, "BoringSSL"))
+ return;
+ int r = tor_sscanf(version, "%d.%d.%d", &a,&b,&c);
+ tt_int_op(r, OP_EQ, 3);
+ tt_int_op(a, OP_GE, 0);
+ tt_int_op(b, OP_GE, 0);
+ tt_int_op(c, OP_GE, 0);
+
+ done:
+ ;
}
/** Run unit tests for our random number generation function and its wrappers.
@@ -73,6 +177,7 @@ test_crypto_rng(void *arg)
int i, j, allok;
char data1[100], data2[100];
double d;
+ char *h=NULL;
/* Try out RNG. */
(void)arg;
@@ -104,9 +209,16 @@ test_crypto_rng(void *arg)
allok = 0;
tor_free(host);
}
+
+ /* Make sure crypto_random_hostname clips its inputs properly. */
+ h = crypto_random_hostname(20000, 9000, "www.", ".onion");
+ tt_assert(! strcmpstart(h,"www."));
+ tt_assert(! strcmpend(h,".onion"));
+ tt_int_op(63+4+6, OP_EQ, strlen(h));
+
tt_assert(allok);
done:
- ;
+ tor_free(h);
}
static void
@@ -125,14 +237,100 @@ test_crypto_rng_range(void *arg)
if (x == 8)
got_largest = 1;
}
-
/* These fail with probability 1/10^603. */
tt_assert(got_smallest);
tt_assert(got_largest);
+
+ got_smallest = got_largest = 0;
+ const uint64_t ten_billion = 10 * ((uint64_t)1000000000000);
+ for (i = 0; i < 1000; ++i) {
+ uint64_t x = crypto_rand_uint64_range(ten_billion, ten_billion+10);
+ tt_u64_op(x, OP_GE, ten_billion);
+ tt_u64_op(x, OP_LT, ten_billion+10);
+ if (x == ten_billion)
+ got_smallest = 1;
+ if (x == ten_billion+9)
+ got_largest = 1;
+ }
+
+ tt_assert(got_smallest);
+ tt_assert(got_largest);
+
+ const time_t now = time(NULL);
+ for (i = 0; i < 2000; ++i) {
+ time_t x = crypto_rand_time_range(now, now+60);
+ tt_i64_op(x, OP_GE, now);
+ tt_i64_op(x, OP_LT, now+60);
+ if (x == now)
+ got_smallest = 1;
+ if (x == now+59)
+ got_largest = 1;
+ }
+
+ tt_assert(got_smallest);
+ tt_assert(got_largest);
done:
;
}
+static void
+test_crypto_rng_strongest(void *arg)
+{
+ const char *how = arg;
+ int broken = 0;
+
+ if (how == NULL) {
+ ;
+ } else if (!strcmp(how, "nosyscall")) {
+ break_strongest_rng_syscall = 1;
+ } else if (!strcmp(how, "nofallback")) {
+ break_strongest_rng_fallback = 1;
+ } else if (!strcmp(how, "broken")) {
+ broken = break_strongest_rng_syscall = break_strongest_rng_fallback = 1;
+ }
+
+#define N 128
+ uint8_t combine_and[N];
+ uint8_t combine_or[N];
+ int i, j;
+
+ memset(combine_and, 0xff, N);
+ memset(combine_or, 0, N);
+
+ for (i = 0; i < 100; ++i) { /* 2^-100 chances just don't happen. */
+ uint8_t output[N];
+ memset(output, 0, N);
+ if (how == NULL) {
+ /* this one can't fail. */
+ crypto_strongest_rand(output, sizeof(output));
+ } else {
+ int r = crypto_strongest_rand_raw(output, sizeof(output));
+ if (r == -1) {
+ if (broken) {
+ goto done; /* we're fine. */
+ }
+ /* This function is allowed to break, but only if it always breaks. */
+ tt_int_op(i, OP_EQ, 0);
+ tt_skip();
+ } else {
+ tt_assert(! broken);
+ }
+ }
+ for (j = 0; j < N; ++j) {
+ combine_and[j] &= output[j];
+ combine_or[j] |= output[j];
+ }
+ }
+
+ for (j = 0; j < N; ++j) {
+ tt_int_op(combine_and[j], OP_EQ, 0);
+ tt_int_op(combine_or[j], OP_EQ, 0xff);
+ }
+ done:
+ ;
+#undef N
+}
+
/* Test for rectifying openssl RAND engine. */
static void
test_crypto_rng_engine(void *arg)
@@ -165,15 +363,15 @@ test_crypto_rng_engine(void *arg)
;
}
-/** Run unit tests for our AES functionality */
+/** Run unit tests for our AES128 functionality */
static void
-test_crypto_aes(void *arg)
+test_crypto_aes128(void *arg)
{
char *data1 = NULL, *data2 = NULL, *data3 = NULL;
crypto_cipher_t *env1 = NULL, *env2 = NULL;
int i, j;
char *mem_op_hex_tmp=NULL;
-
+ char key[CIPHER_KEY_LEN];
int use_evp = !strcmp(arg,"evp");
evaluate_evp_for_aes(use_evp);
evaluate_ctr_for_aes();
@@ -189,9 +387,10 @@ test_crypto_aes(void *arg)
memset(data2, 0, 1024);
memset(data3, 0, 1024);
- env1 = crypto_cipher_new(NULL);
+ crypto_rand(key, sizeof(key));
+ env1 = crypto_cipher_new(key);
tt_ptr_op(env1, OP_NE, NULL);
- env2 = crypto_cipher_new(crypto_cipher_get_key(env1));
+ env2 = crypto_cipher_new(key);
tt_ptr_op(env2, OP_NE, NULL);
/* Try encrypting 512 chars. */
@@ -222,7 +421,7 @@ test_crypto_aes(void *arg)
env2 = NULL;
memset(data3, 0, 1024);
- env2 = crypto_cipher_new(crypto_cipher_get_key(env1));
+ env2 = crypto_cipher_new(key);
tt_ptr_op(env2, OP_NE, NULL);
for (j = 0; j < 1024-16; j += 17) {
crypto_cipher_encrypt(env2, data3+j, data1+j, 17);
@@ -312,6 +511,72 @@ test_crypto_aes(void *arg)
tor_free(data3);
}
+static void
+test_crypto_aes_ctr_testvec(void *arg)
+{
+ const char *bitstr = arg;
+ char *mem_op_hex_tmp=NULL;
+ crypto_cipher_t *c=NULL;
+
+ /* from NIST SP800-38a, section F.5 */
+ const char ctr16[] = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
+ const char plaintext16[] =
+ "6bc1bee22e409f96e93d7e117393172a"
+ "ae2d8a571e03ac9c9eb76fac45af8e51"
+ "30c81c46a35ce411e5fbc1191a0a52ef"
+ "f69f2445df4f9b17ad2b417be66c3710";
+ const char *ciphertext16;
+ const char *key16;
+ int bits;
+
+ if (!strcmp(bitstr, "128")) {
+ ciphertext16 = /* section F.5.1 */
+ "874d6191b620e3261bef6864990db6ce"
+ "9806f66b7970fdff8617187bb9fffdff"
+ "5ae4df3edbd5d35e5b4f09020db03eab"
+ "1e031dda2fbe03d1792170a0f3009cee";
+ key16 = "2b7e151628aed2a6abf7158809cf4f3c";
+ bits = 128;
+ } else if (!strcmp(bitstr, "192")) {
+ ciphertext16 = /* section F.5.3 */
+ "1abc932417521ca24f2b0459fe7e6e0b"
+ "090339ec0aa6faefd5ccc2c6f4ce8e94"
+ "1e36b26bd1ebc670d1bd1d665620abf7"
+ "4f78a7f6d29809585a97daec58c6b050";
+ key16 = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b";
+ bits = 192;
+ } else if (!strcmp(bitstr, "256")) {
+ ciphertext16 = /* section F.5.5 */
+ "601ec313775789a5b7a7f504bbf3d228"
+ "f443e3ca4d62b59aca84e990cacaf5c5"
+ "2b0930daa23de94ce87017ba2d84988d"
+ "dfc9c58db67aada613c2dd08457941a6";
+ key16 =
+ "603deb1015ca71be2b73aef0857d7781"
+ "1f352c073b6108d72d9810a30914dff4";
+ bits = 256;
+ } else {
+ tt_abort_msg("AES doesn't support this number of bits.");
+ }
+
+ char key[32];
+ char iv[16];
+ char plaintext[16*4];
+ memset(key, 0xf9, sizeof(key)); /* poison extra bytes */
+ base16_decode(key, sizeof(key), key16, strlen(key16));
+ base16_decode(iv, sizeof(iv), ctr16, strlen(ctr16));
+ base16_decode(plaintext, sizeof(plaintext),
+ plaintext16, strlen(plaintext16));
+
+ c = crypto_cipher_new_with_iv_and_bits((uint8_t*)key, (uint8_t*)iv, bits);
+ crypto_cipher_crypt_inplace(c, plaintext, sizeof(plaintext));
+ test_memeq_hex(plaintext, ciphertext16);
+
+ done:
+ tor_free(mem_op_hex_tmp);
+ crypto_cipher_free(c);
+}
+
/** Run unit tests for our SHA-1 functionality */
static void
test_crypto_sha(void *arg)
@@ -764,11 +1029,11 @@ test_crypto_sha3(void *arg)
crypto_digest_free(d1);
/* Attempt to exercise the incremental hashing code by creating a randomized
- * 100 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512
+ * 30 KiB buffer, and hashing rand[1, 5 * Rate] bytes at a time. SHA3-512
* is used because it has a lowest rate of the family (the code is common,
* but the slower rate exercises more of it).
*/
- const size_t bufsz = 100 * 1024;
+ const size_t bufsz = 30 * 1024;
size_t j = 0;
large = tor_malloc(bufsz);
crypto_rand(large, bufsz);
@@ -1084,6 +1349,29 @@ test_crypto_pk_base64(void *arg)
tor_free(encoded);
}
+#ifdef HAVE_TRUNCATE
+#define do_truncate truncate
+#else
+static int
+do_truncate(const char *fname, size_t len)
+{
+ struct stat st;
+ char *bytes;
+
+ bytes = read_file_to_str(fname, RFTS_BIN, &st);
+ if (!bytes)
+ return -1;
+ /* This cast isn't so great, but it should be safe given the actual files
+ * and lengths we're using. */
+ if (st.st_size < (off_t)len)
+ len = MIN(len, (size_t)st.st_size);
+
+ int r = write_bytes_to_file(fname, bytes, len, 1);
+ tor_free(bytes);
+ return r;
+}
+#endif
+
/** Sanity check for crypto pk digests */
static void
test_crypto_digests(void *arg)
@@ -1114,6 +1402,33 @@ test_crypto_digests(void *arg)
crypto_pk_free(k);
}
+static void
+test_crypto_digest_names(void *arg)
+{
+ static const struct {
+ int a; const char *n;
+ } names[] = {
+ { DIGEST_SHA1, "sha1" },
+ { DIGEST_SHA256, "sha256" },
+ { DIGEST_SHA512, "sha512" },
+ { DIGEST_SHA3_256, "sha3-256" },
+ { DIGEST_SHA3_512, "sha3-512" },
+ { -1, NULL }
+ };
+ (void)arg;
+
+ int i;
+ for (i = 0; names[i].n; ++i) {
+ tt_str_op(names[i].n, OP_EQ,crypto_digest_algorithm_get_name(names[i].a));
+ tt_int_op(names[i].a,
+ OP_EQ,crypto_digest_algorithm_parse_name(names[i].n));
+ }
+ tt_int_op(-1, OP_EQ,
+ crypto_digest_algorithm_parse_name("TimeCubeHash-4444"));
+ done:
+ ;
+}
+
#ifndef OPENSSL_1_1_API
#define EVP_ENCODE_CTX_new() tor_malloc_zero(sizeof(EVP_ENCODE_CTX))
#define EVP_ENCODE_CTX_free(ctx) tor_free(ctx)
@@ -1236,7 +1551,7 @@ test_crypto_formats(void *arg)
strlcpy(data1, "f0d678affc000100", 1024);
i = base16_decode(data2, 8, data1, 16);
- tt_int_op(i,OP_EQ, 0);
+ tt_int_op(i,OP_EQ, 8);
tt_mem_op(data2,OP_EQ, "\xf0\xd6\x78\xaf\xfc\x00\x01\x00",8);
/* now try some failing base16 decodes */
@@ -1507,13 +1822,98 @@ test_crypto_hkdf_sha256(void *arg)
"b206fa34e5bc78d063fc291501beec53b36e5a0e434561200c"
"5f8bd13e0f88b3459600b4dc21d69363e2895321c06184879d"
"94b18f078411be70b767c7fc40679a9440a0c95ea83a23efbf");
-
done:
tor_free(mem_op_hex_tmp);
#undef EXPAND
}
static void
+test_crypto_hkdf_sha256_testvecs(void *arg)
+{
+ (void) arg;
+ /* Test vectors from RFC5869, sections A.1 through A.3 */
+ const struct {
+ const char *ikm16, *salt16, *info16;
+ int L;
+ const char *okm16;
+ } vecs[] = {
+ { /* from A.1 */
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "000102030405060708090a0b0c",
+ "f0f1f2f3f4f5f6f7f8f9",
+ 42,
+ "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf"
+ "34007208d5b887185865"
+ },
+ { /* from A.2 */
+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+ "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+ "404142434445464748494a4b4c4d4e4f",
+ "606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f"
+ "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
+ "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf",
+ "b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
+ "d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeef"
+ "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+ 82,
+ "b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c"
+ "59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71"
+ "cc30c58179ec3e87c14c01d5c1f3434f1d87"
+ },
+ { /* from A.3 */
+ "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+ "",
+ "",
+ 42,
+ "8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d"
+ "9d201395faa4b61a96c8",
+ },
+ { NULL, NULL, NULL, -1, NULL }
+ };
+
+ int i;
+ char *ikm = NULL;
+ char *salt = NULL;
+ char *info = NULL;
+ char *okm = NULL;
+ char *mem_op_hex_tmp = NULL;
+
+ for (i = 0; vecs[i].ikm16; ++i) {
+ size_t ikm_len = strlen(vecs[i].ikm16)/2;
+ size_t salt_len = strlen(vecs[i].salt16)/2;
+ size_t info_len = strlen(vecs[i].info16)/2;
+ size_t okm_len = vecs[i].L;
+
+ ikm = tor_malloc(ikm_len);
+ salt = tor_malloc(salt_len);
+ info = tor_malloc(info_len);
+ okm = tor_malloc(okm_len);
+
+ base16_decode(ikm, ikm_len, vecs[i].ikm16, strlen(vecs[i].ikm16));
+ base16_decode(salt, salt_len, vecs[i].salt16, strlen(vecs[i].salt16));
+ base16_decode(info, info_len, vecs[i].info16, strlen(vecs[i].info16));
+
+ int r = crypto_expand_key_material_rfc5869_sha256(
+ (const uint8_t*)ikm, ikm_len,
+ (const uint8_t*)salt, salt_len,
+ (const uint8_t*)info, info_len,
+ (uint8_t*)okm, okm_len);
+ tt_int_op(r, OP_EQ, 0);
+ test_memeq_hex(okm, vecs[i].okm16);
+ tor_free(ikm);
+ tor_free(salt);
+ tor_free(info);
+ tor_free(okm);
+ }
+ done:
+ tor_free(ikm);
+ tor_free(salt);
+ tor_free(info);
+ tor_free(okm);
+ tor_free(mem_op_hex_tmp);
+}
+
+static void
test_crypto_curve25519_impl(void *arg)
{
/* adapted from curve25519_donna, which adapted it from test-curve25519
@@ -1605,6 +2005,47 @@ test_crypto_curve25519_basepoint(void *arg)
}
static void
+test_crypto_curve25519_testvec(void *arg)
+{
+ (void)arg;
+ char *mem_op_hex_tmp = NULL;
+
+ /* From RFC 7748, section 6.1 */
+ /* Alice's private key, a: */
+ const char a16[] =
+ "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
+ /* Alice's public key, X25519(a, 9): */
+ const char a_pub16[] =
+ "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
+ /* Bob's private key, b: */
+ const char b16[] =
+ "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
+ /* Bob's public key, X25519(b, 9): */
+ const char b_pub16[] =
+ "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
+ /* Their shared secret, K: */
+ const char k16[] =
+ "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742";
+
+ uint8_t a[32], b[32], a_pub[32], b_pub[32], k1[32], k2[32];
+ base16_decode((char*)a, sizeof(a), a16, strlen(a16));
+ base16_decode((char*)b, sizeof(b), b16, strlen(b16));
+ curve25519_basepoint_impl(a_pub, a);
+ curve25519_basepoint_impl(b_pub, b);
+ curve25519_impl(k1, a, b_pub);
+ curve25519_impl(k2, b, a_pub);
+
+ test_memeq_hex(a, a16);
+ test_memeq_hex(b, b16);
+ test_memeq_hex(a_pub, a_pub16);
+ test_memeq_hex(b_pub, b_pub16);
+ test_memeq_hex(k1, k16);
+ test_memeq_hex(k2, k16);
+ done:
+ tor_free(mem_op_hex_tmp);
+}
+
+static void
test_crypto_curve25519_wrappers(void *arg)
{
curve25519_public_key_t pubkey1, pubkey2;
@@ -1728,24 +2169,6 @@ test_crypto_curve25519_persist(void *arg)
tor_free(tag);
}
-static void *
-ed25519_testcase_setup(const struct testcase_t *testcase)
-{
- crypto_ed25519_testing_force_impl(testcase->setup_data);
- return testcase->setup_data;
-}
-static int
-ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr)
-{
- (void)testcase;
- (void)ptr;
- crypto_ed25519_testing_restore_impl();
- return 1;
-}
-static const struct testcase_setup_t ed25519_test_setup = {
- ed25519_testcase_setup, ed25519_testcase_cleanup
-};
-
static void
test_crypto_ed25519_simple(void *arg)
{
@@ -1831,6 +2254,41 @@ test_crypto_ed25519_simple(void *arg)
tt_int_op(0, OP_EQ, ed25519_checksig_batch(NULL, ch, 2));
}
+ /* Test the string-prefixed sign/checksig functions */
+ {
+ ed25519_signature_t manual_sig;
+ char *prefixed_msg;
+
+ /* Generate a signature with a prefixed msg. */
+ tt_int_op(0, OP_EQ, ed25519_sign_prefixed(&sig1, msg, msg_len,
+ "always in the mood",
+ &kp1));
+
+ /* First, check that ed25519_sign_prefixed() returns the exact same sig as
+ if we had manually prefixed the msg ourselves. */
+ tor_asprintf(&prefixed_msg, "%s%s", "always in the mood", msg);
+ tt_int_op(0, OP_EQ, ed25519_sign(&manual_sig, (uint8_t *)prefixed_msg,
+ strlen(prefixed_msg), &kp1));
+ tor_free(prefixed_msg);
+ tt_assert(fast_memeq(sig1.sig, manual_sig.sig, sizeof(sig1.sig)));
+
+ /* Test that prefixed checksig verifies it properly. */
+ tt_int_op(0, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len,
+ "always in the mood",
+ &pub1));
+
+ /* Test that checksig with wrong prefix fails. */
+ tt_int_op(-1, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len,
+ "always in the moo",
+ &pub1));
+ tt_int_op(-1, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len,
+ "always in the moon",
+ &pub1));
+ tt_int_op(-1, OP_EQ, ed25519_checksig_prefixed(&sig1, msg, msg_len,
+ "always in the mood!",
+ &pub1));
+ }
+
done:
;
}
@@ -1896,7 +2354,67 @@ test_crypto_ed25519_test_vectors(void *arg)
"1fbc1e08682f2cc0c92efe8f4985dec61dcbd54d4b94a22547d24451271c8b00",
"0a688e79be24f866286d4646b5d81c"
},
-
+ /* These come from draft-irtf-cfrg-eddsa-05 section 7.1 */
+ {
+ "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
+ "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
+ "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155"
+ "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
+ ""
+ },
+ {
+ "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
+ "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
+ "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da"
+ "085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
+ "72"
+ },
+ {
+ "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5",
+ "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e",
+ "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350"
+ "aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03",
+ "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98"
+ "fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8"
+ "79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d"
+ "658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc"
+ "1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe"
+ "ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e"
+ "06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef"
+ "efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7"
+ "aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1"
+ "85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2"
+ "d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24"
+ "554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270"
+ "88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc"
+ "2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07"
+ "07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba"
+ "b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a"
+ "ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e"
+ "c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7"
+ "51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c"
+ "42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8"
+ "ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df"
+ "f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08"
+ "d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649"
+ "de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4"
+ "88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3"
+ "2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e"
+ "6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f"
+ "b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5"
+ "0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1"
+ "369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d"
+ "b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c"
+ "0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0"
+ },
+ {
+ "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
+ "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
+ "dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589"
+ "09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704",
+ "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a"
+ "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
+ },
{ NULL, NULL, NULL, NULL}
};
@@ -2066,8 +2584,9 @@ test_crypto_ed25519_testvectors(void *arg)
#define DECODE(p,s) base16_decode((char*)(p),sizeof(p),(s),strlen(s))
#define EQ(a,h) test_memeq_hex((const char*)(a), (h))
- tt_int_op(0, OP_EQ, DECODE(sk, ED25519_SECRET_KEYS[i]));
- tt_int_op(0, OP_EQ, DECODE(blinding_param, ED25519_BLINDING_PARAMS[i]));
+ tt_int_op(sizeof(sk), OP_EQ, DECODE(sk, ED25519_SECRET_KEYS[i]));
+ tt_int_op(sizeof(blinding_param), OP_EQ, DECODE(blinding_param,
+ ED25519_BLINDING_PARAMS[i]));
tt_int_op(0, OP_EQ, ed25519_secret_key_from_seed(&esk, sk));
EQ(esk.seckey, ED25519_EXPANDED_SECRET_KEYS[i]);
@@ -2112,74 +2631,51 @@ test_crypto_ed25519_testvectors(void *arg)
}
static void
-test_crypto_ed25519_fuzz_donna(void *arg)
+test_crypto_ed25519_storage(void *arg)
{
- const unsigned iters = 1024;
- uint8_t msg[1024];
- unsigned i;
(void)arg;
+ ed25519_keypair_t *keypair = NULL;
+ ed25519_public_key_t pub;
+ ed25519_secret_key_t sec;
+ char *fname_1 = tor_strdup(get_fname("ed_seckey_1"));
+ char *fname_2 = tor_strdup(get_fname("ed_pubkey_2"));
+ char *contents = NULL;
+ char *tag = NULL;
- tt_assert(sizeof(msg) == iters);
- crypto_rand((char*) msg, sizeof(msg));
-
- /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to
- * generate keys/sign per iteration.
- */
- for (i = 0; i < iters; ++i) {
- const int use_donna = i & 1;
- uint8_t blinding[32];
- curve25519_keypair_t ckp;
- ed25519_keypair_t kp, kp_blind, kp_curve25519;
- ed25519_public_key_t pk, pk_blind, pk_curve25519;
- ed25519_signature_t sig, sig_blind;
- int bit = 0;
-
- crypto_rand((char*) blinding, sizeof(blinding));
-
- /* Impl. A:
- * 1. Generate a keypair.
- * 2. Blinded the keypair.
- * 3. Sign a message (unblinded).
- * 4. Sign a message (blinded).
- * 5. Generate a curve25519 keypair, and convert it to Ed25519.
- */
- ed25519_set_impl_params(use_donna);
- tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1));
- tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding));
- tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp));
- tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind));
-
- tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1));
- tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair(
- &kp_curve25519, &bit, &ckp));
-
- /* Impl. B:
- * 1. Validate the public key by rederiving it.
- * 2. Validate the blinded public key by rederiving it.
- * 3. Validate the unblinded signature (and test a invalid signature).
- * 4. Validate the blinded signature.
- * 5. Validate the public key (from Curve25519) by rederiving it.
- */
- ed25519_set_impl_params(!use_donna);
- tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey));
- tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32);
-
- tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding));
- tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32);
+ keypair = tor_malloc_zero(sizeof(ed25519_keypair_t));
+ tt_int_op(0,OP_EQ,ed25519_keypair_generate(keypair, 0));
+ tt_int_op(0,OP_EQ,
+ ed25519_seckey_write_to_file(&keypair->seckey, fname_1, "foo"));
+ tt_int_op(0,OP_EQ,
+ ed25519_pubkey_write_to_file(&keypair->pubkey, fname_2, "bar"));
- tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk));
- sig.sig[0] ^= 15;
- tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk));
+ tt_int_op(-1, OP_EQ, ed25519_pubkey_read_from_file(&pub, &tag, fname_1));
+ tt_ptr_op(tag, OP_EQ, NULL);
+ tt_int_op(-1, OP_EQ, ed25519_seckey_read_from_file(&sec, &tag, fname_2));
+ tt_ptr_op(tag, OP_EQ, NULL);
- tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind));
+ tt_int_op(0, OP_EQ, ed25519_pubkey_read_from_file(&pub, &tag, fname_2));
+ tt_str_op(tag, OP_EQ, "bar");
+ tor_free(tag);
+ tt_int_op(0, OP_EQ, ed25519_seckey_read_from_file(&sec, &tag, fname_1));
+ tt_str_op(tag, OP_EQ, "foo");
+ tor_free(tag);
- tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key(
- &pk_curve25519, &ckp.pubkey, bit));
- tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32);
- }
+ /* 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(-1, OP_EQ, ed25519_pubkey_read_from_file(&pub, &tag, fname_2));
+ tt_ptr_op(tag, OP_EQ, NULL);
+ tor_free(tag);
+ tt_int_op(-1, OP_EQ, ed25519_seckey_read_from_file(&sec, &tag, fname_1));
+ tt_ptr_op(tag, OP_EQ, NULL);
done:
- ;
+ tor_free(fname_1);
+ tor_free(fname_2);
+ tor_free(contents);
+ tor_free(tag);
+ ed25519_keypair_free(keypair);
}
static void
@@ -2398,13 +2894,28 @@ struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(rng),
{ "rng_range", test_crypto_rng_range, 0, NULL, NULL },
{ "rng_engine", test_crypto_rng_engine, TT_FORK, NULL, NULL },
- { "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" },
- { "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" },
+ { "rng_strongest", test_crypto_rng_strongest, TT_FORK, NULL, NULL },
+ { "rng_strongest_nosyscall", test_crypto_rng_strongest, TT_FORK,
+ &passthrough_setup, (void*)"nosyscall" },
+ { "rng_strongest_nofallback", test_crypto_rng_strongest, TT_FORK,
+ &passthrough_setup, (void*)"nofallback" },
+ { "rng_strongest_broken", test_crypto_rng_strongest, TT_FORK,
+ &passthrough_setup, (void*)"broken" },
+ { "openssl_version", test_crypto_openssl_version, TT_FORK, NULL, NULL },
+ { "aes_AES", test_crypto_aes128, TT_FORK, &passthrough_setup, (void*)"aes" },
+ { "aes_EVP", test_crypto_aes128, TT_FORK, &passthrough_setup, (void*)"evp" },
+ { "aes128_ctr_testvec", test_crypto_aes_ctr_testvec, 0,
+ &passthrough_setup, (void*)"128" },
+ { "aes192_ctr_testvec", test_crypto_aes_ctr_testvec, 0,
+ &passthrough_setup, (void*)"192" },
+ { "aes256_ctr_testvec", test_crypto_aes_ctr_testvec, 0,
+ &passthrough_setup, (void*)"256" },
CRYPTO_LEGACY(sha),
CRYPTO_LEGACY(pk),
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
{ "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL },
CRYPTO_LEGACY(digests),
+ { "digest_names", test_crypto_digest_names, 0, NULL, NULL },
{ "sha3", test_crypto_sha3, TT_FORK, NULL, NULL},
{ "sha3_xof", test_crypto_sha3_xof, TT_FORK, NULL, NULL},
CRYPTO_LEGACY(dh),
@@ -2415,8 +2926,10 @@ struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(base32_decode),
{ "kdf_TAP", test_crypto_kdf_TAP, 0, NULL, NULL },
{ "hkdf_sha256", test_crypto_hkdf_sha256, 0, NULL, NULL },
+ { "hkdf_sha256_testvecs", test_crypto_hkdf_sha256_testvecs, 0, NULL, NULL },
{ "curve25519_impl", test_crypto_curve25519_impl, 0, NULL, NULL },
{ "curve25519_impl_hibit", test_crypto_curve25519_impl, 0, NULL, (void*)"y"},
+ { "curve25516_testvec", test_crypto_curve25519_testvec, 0, NULL, NULL },
{ "curve25519_basepoint",
test_crypto_curve25519_basepoint, TT_FORK, NULL, NULL },
{ "curve25519_wrappers", test_crypto_curve25519_wrappers, 0, NULL, NULL },
@@ -2428,7 +2941,7 @@ struct testcase_t crypto_tests[] = {
ED25519_TEST(convert, 0),
ED25519_TEST(blinding, 0),
ED25519_TEST(testvectors, 0),
- ED25519_TEST(fuzz_donna, TT_FORK),
+ { "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 },
END_OF_TESTCASES
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
index 6f3e40e0ab..0be58c9389 100644
--- a/src/test/test_crypto_slow.c
+++ b/src/test/test_crypto_slow.c
@@ -137,7 +137,8 @@ test_libscrypt_eq_openssl(void *arg)
uint8_t buf1[64];
uint8_t buf2[64];
- uint64_t N, r, p;
+ uint64_t N;
+ uint32_t r, p;
uint64_t maxmem = 0; // --> SCRYPT_MAX_MEM in OpenSSL.
int libscrypt_retval, openssl_retval;
@@ -421,12 +422,16 @@ test_crypto_pbkdf2_vectors(void *arg)
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
test_memeq_hex(out, "4b007901b765489abead49d926f721d065a429c1");
+ /* This is the very slow one here. When enabled, it accounts for roughly
+ * half the time spent in test-slow. */
+ /*
base16_decode((char*)spec, sizeof(spec),
"73616c74" "18" , 10);
memset(out, 0x00, sizeof(out));
tt_int_op(20, OP_EQ,
secret_to_key_compute_key(out, 20, spec, 5, "password", 8, 1));
test_memeq_hex(out, "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984");
+ */
base16_decode((char*)spec, sizeof(spec),
"73616c7453414c5473616c7453414c5473616c745"
@@ -503,9 +508,88 @@ test_crypto_pwbox(void *arg)
tor_free(decoded);
}
+static void
+test_crypto_ed25519_fuzz_donna(void *arg)
+{
+ const unsigned iters = 1024;
+ uint8_t msg[1024];
+ unsigned i;
+ (void)arg;
+
+ tt_assert(sizeof(msg) == iters);
+ crypto_rand((char*) msg, sizeof(msg));
+
+ /* Fuzz Ed25519-donna vs ref10, alternating the implementation used to
+ * generate keys/sign per iteration.
+ */
+ for (i = 0; i < iters; ++i) {
+ const int use_donna = i & 1;
+ uint8_t blinding[32];
+ curve25519_keypair_t ckp;
+ ed25519_keypair_t kp, kp_blind, kp_curve25519;
+ ed25519_public_key_t pk, pk_blind, pk_curve25519;
+ ed25519_signature_t sig, sig_blind;
+ int bit = 0;
+
+ crypto_rand((char*) blinding, sizeof(blinding));
+
+ /* Impl. A:
+ * 1. Generate a keypair.
+ * 2. Blinded the keypair.
+ * 3. Sign a message (unblinded).
+ * 4. Sign a message (blinded).
+ * 5. Generate a curve25519 keypair, and convert it to Ed25519.
+ */
+ ed25519_set_impl_params(use_donna);
+ tt_int_op(0, OP_EQ, ed25519_keypair_generate(&kp, i&1));
+ tt_int_op(0, OP_EQ, ed25519_keypair_blind(&kp_blind, &kp, blinding));
+ tt_int_op(0, OP_EQ, ed25519_sign(&sig, msg, i, &kp));
+ tt_int_op(0, OP_EQ, ed25519_sign(&sig_blind, msg, i, &kp_blind));
+
+ tt_int_op(0, OP_EQ, curve25519_keypair_generate(&ckp, i&1));
+ tt_int_op(0, OP_EQ, ed25519_keypair_from_curve25519_keypair(
+ &kp_curve25519, &bit, &ckp));
+
+ /* Impl. B:
+ * 1. Validate the public key by rederiving it.
+ * 2. Validate the blinded public key by rederiving it.
+ * 3. Validate the unblinded signature (and test a invalid signature).
+ * 4. Validate the blinded signature.
+ * 5. Validate the public key (from Curve25519) by rederiving it.
+ */
+ ed25519_set_impl_params(!use_donna);
+ tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pk, &kp.seckey));
+ tt_mem_op(pk.pubkey, OP_EQ, kp.pubkey.pubkey, 32);
+
+ tt_int_op(0, OP_EQ, ed25519_public_blind(&pk_blind, &kp.pubkey, blinding));
+ tt_mem_op(pk_blind.pubkey, OP_EQ, kp_blind.pubkey.pubkey, 32);
+
+ tt_int_op(0, OP_EQ, ed25519_checksig(&sig, msg, i, &pk));
+ sig.sig[0] ^= 15;
+ tt_int_op(-1, OP_EQ, ed25519_checksig(&sig, msg, sizeof(msg), &pk));
+
+ tt_int_op(0, OP_EQ, ed25519_checksig(&sig_blind, msg, i, &pk_blind));
+
+ tt_int_op(0, OP_EQ, ed25519_public_key_from_curve25519_public_key(
+ &pk_curve25519, &ckp.pubkey, bit));
+ tt_mem_op(pk_curve25519.pubkey, OP_EQ, kp_curve25519.pubkey.pubkey, 32);
+ }
+
+ done:
+ ;
+}
+
#define CRYPTO_LEGACY(name) \
{ #name, test_crypto_ ## name , 0, NULL, NULL }
+#define ED25519_TEST_ONE(name, fl, which) \
+ { #name "/ed25519_" which, test_crypto_ed25519_ ## name, (fl), \
+ &ed25519_test_setup, (void*)which }
+
+#define ED25519_TEST(name, fl) \
+ ED25519_TEST_ONE(name, (fl), "donna"), \
+ ED25519_TEST_ONE(name, (fl), "ref10")
+
struct testcase_t slow_crypto_tests[] = {
CRYPTO_LEGACY(s2k_rfc2440),
#ifdef HAVE_LIBSCRYPT
@@ -527,6 +611,7 @@ struct testcase_t slow_crypto_tests[] = {
{ "scrypt_vectors", test_crypto_scrypt_vectors, 0, NULL, NULL },
{ "pbkdf2_vectors", test_crypto_pbkdf2_vectors, 0, NULL, NULL },
{ "pwbox", test_crypto_pwbox, 0, NULL, NULL },
+ ED25519_TEST(fuzz_donna, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_data.c b/src/test/test_data.c
index 32de54bc84..788489a097 100644
--- a/src/test/test_data.c
+++ b/src/test/test_data.c
@@ -3,6 +3,8 @@
* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+#include "test.h"
+
/* Our unit test expect that the AUTHORITY_CERT_* public keys will sort
* in this order. */
#define AUTHORITY_CERT_A AUTHORITY_CERT_3
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index 26b0e72a9a..cdc56acb89 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -11,6 +11,7 @@
#define DIRVOTE_PRIVATE
#define ROUTER_PRIVATE
#define ROUTERLIST_PRIVATE
+#define ROUTERPARSE_PRIVATE
#define HIBERNATE_PRIVATE
#define NETWORKSTATUS_PRIVATE
#define RELAY_PRIVATE
@@ -30,10 +31,12 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
+#include "shared_random_state.h"
#include "test.h"
#include "test_dir_common.h"
#include "torcert.h"
#include "relay.h"
+#include "log_test_helpers.h"
#define NS_MODULE dir
@@ -114,6 +117,7 @@ test_dir_formats(void *arg)
const addr_policy_t *p;
time_t now = time(NULL);
port_cfg_t orport, dirport;
+ char cert_buf[256];
(void)arg;
pk1 = pk_generate(0);
@@ -133,6 +137,11 @@ test_dir_formats(void *arg)
tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
r1->ipv6_orport = 9999;
r1->onion_pkey = crypto_pk_dup_key(pk1);
+ /* Fake just enough of an ntor key to get by */
+ curve25519_keypair_t r1_onion_keypair;
+ curve25519_keypair_generate(&r1_onion_keypair, 0);
+ r1->onion_curve25519_pkey = tor_memdup(&r1_onion_keypair.pubkey,
+ sizeof(curve25519_public_key_t));
r1->identity_pkey = crypto_pk_dup_key(pk2);
r1->bandwidthrate = 1000;
r1->bandwidthburst = 5000;
@@ -165,11 +174,6 @@ test_dir_formats(void *arg)
&kp2.pubkey,
now, 86400,
CERT_FLAG_INCLUDE_SIGNING_KEY);
- char cert_buf[256];
- base64_encode(cert_buf, sizeof(cert_buf),
- (const char*)r2->cache_info.signing_key_cert->encoded,
- r2->cache_info.signing_key_cert->encoded_len,
- BASE64_ENCODE_MULTILINE);
r2->platform = tor_strdup(platform);
r2->cache_info.published_on = 5;
r2->or_port = 9005;
@@ -192,7 +196,7 @@ test_dir_formats(void *arg)
tt_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str,
&pk2_str_len));
- /* XXXX025 router_dump_to_string should really take this from ri.*/
+ /* XXXX+++ router_dump_to_string should really take this from ri.*/
options->ContactInfo = tor_strdup("Magri White "
"<magri@elsewhere.example.com>");
/* Skip reachability checks for DirPort and tunnelled-dir-server */
@@ -228,7 +232,6 @@ test_dir_formats(void *arg)
"platform Tor "VERSION" on ", sizeof(buf2));
strlcat(buf2, get_uname(), sizeof(buf2));
strlcat(buf2, "\n"
- "protocols Link 1 2 Circuit 1\n"
"published 1970-01-01 00:00:00\n"
"fingerprint ", sizeof(buf2));
tt_assert(!crypto_pk_get_fingerprint(pk2, fingerprint, 1));
@@ -245,6 +248,11 @@ test_dir_formats(void *arg)
strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n",
sizeof(buf2));
+ strlcat(buf2, "ntor-onion-key ", sizeof(buf2));
+ base64_encode(cert_buf, sizeof(cert_buf),
+ (const char*)r1_onion_keypair.pubkey.public_key, 32,
+ BASE64_ENCODE_MULTILINE);
+ strlcat(buf2, cert_buf, sizeof(buf2));
strlcat(buf2, "reject *:*\n", sizeof(buf2));
strlcat(buf2, "tunnelled-dir-server\nrouter-signature\n", sizeof(buf2));
buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
@@ -274,6 +282,10 @@ test_dir_formats(void *arg)
"router Fred 10.3.2.1 9005 0 0\n"
"identity-ed25519\n"
"-----BEGIN ED25519 CERT-----\n", sizeof(buf2));
+ base64_encode(cert_buf, sizeof(cert_buf),
+ (const char*)r2->cache_info.signing_key_cert->encoded,
+ r2->cache_info.signing_key_cert->encoded_len,
+ BASE64_ENCODE_MULTILINE);
strlcat(buf2, cert_buf, sizeof(buf2));
strlcat(buf2, "-----END ED25519 CERT-----\n", sizeof(buf2));
strlcat(buf2, "master-key-ed25519 ", sizeof(buf2));
@@ -288,7 +300,6 @@ test_dir_formats(void *arg)
strlcat(buf2, "platform Tor "VERSION" on ", sizeof(buf2));
strlcat(buf2, get_uname(), sizeof(buf2));
strlcat(buf2, "\n"
- "protocols Link 1 2 Circuit 1\n"
"published 1970-01-01 00:00:05\n"
"fingerprint ", sizeof(buf2));
tt_assert(!crypto_pk_get_fingerprint(pk1, fingerprint, 1));
@@ -580,7 +591,7 @@ test_dir_extrainfo_parsing(void *arg)
crypto_pk_t *pk = ri->identity_pkey = crypto_pk_new(); \
tt_assert(! crypto_pk_read_public_key_from_string(pk, \
name##_KEY, strlen(name##_KEY))); \
- tt_int_op(0,OP_EQ,base16_decode(d, 20, name##_FP, strlen(name##_FP))); \
+ tt_int_op(20,OP_EQ,base16_decode(d, 20, name##_FP, strlen(name##_FP))); \
digestmap_set((digestmap_t*)map, d, ri); \
ri = NULL; \
} while (0)
@@ -702,8 +713,8 @@ test_dir_parse_router_list(void *arg)
"9a651ee03b64325959e8f1b46f2b689b30750b4c");
/* Now tidy up */
- SMARTLIST_FOREACH(dest, routerinfo_t *, ri, routerinfo_free(ri));
- SMARTLIST_FOREACH(invalid, uint8_t *, d, tor_free(d));
+ SMARTLIST_FOREACH(dest, routerinfo_t *, rinfo, routerinfo_free(rinfo));
+ SMARTLIST_FOREACH(invalid, uint8_t *, dig, tor_free(dig));
smartlist_clear(dest);
smartlist_clear(invalid);
@@ -739,9 +750,9 @@ test_dir_parse_router_list(void *arg)
else
SMARTLIST_FOREACH(dest, extrainfo_t *, ei, extrainfo_free(ei));
smartlist_free(dest);
- SMARTLIST_FOREACH(invalid, uint8_t *, d, tor_free(d));
+ SMARTLIST_FOREACH(invalid, uint8_t *, dig, tor_free(dig));
smartlist_free(invalid);
- SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+ SMARTLIST_FOREACH(chunks, char *, chunk, tor_free(chunk));
smartlist_free(chunks);
routerinfo_free(ri);
if (map) {
@@ -1167,7 +1178,7 @@ test_dir_fp_pairs(void *arg)
tt_mem_op(pair->second,OP_EQ, "Use AES-256 instead.", DIGEST_LEN);
done:
- SMARTLIST_FOREACH(sl, fp_pair_t *, pair, tor_free(pair));
+ SMARTLIST_FOREACH(sl, fp_pair_t *, pair_to_free, tor_free(pair_to_free));
smartlist_free(sl);
}
@@ -1435,6 +1446,20 @@ test_dir_measured_bw_kb_cache(void *arg)
return;
}
+static char *
+my_dirvote_compute_params(smartlist_t *votes, int method,
+ int total_authorities)
+{
+ smartlist_t *s = dirvote_compute_params(votes, method, total_authorities);
+ tor_assert(s);
+ char *res = smartlist_join_strings(s, " ", 0, NULL);
+ SMARTLIST_FOREACH(s, char *, cp, tor_free(cp));
+ smartlist_free(s);
+ return res;
+}
+
+#define dirvote_compute_params my_dirvote_compute_params
+
static void
test_dir_param_voting(void *arg)
{
@@ -1544,6 +1569,67 @@ test_dir_param_voting(void *arg)
return;
}
+static void
+test_dir_param_voting_lookup(void *arg)
+{
+ (void)arg;
+ smartlist_t *lst = smartlist_new();
+
+ smartlist_split_string(lst,
+ "moomin=9 moomin=10 moomintroll=5 fred "
+ "jack= electricity=sdk opa=6z abc=9 abcd=99",
+ NULL, 0, 0);
+
+ tt_int_op(1000,
+ OP_EQ, dirvote_get_intermediate_param_value(lst, "ab", 1000));
+ tt_int_op(9, OP_EQ, dirvote_get_intermediate_param_value(lst, "abc", 1000));
+ tt_int_op(99, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "abcd", 1000));
+
+ /* moomin appears twice. That's a bug. */
+ tor_capture_bugs_(1);
+ tt_int_op(-100, OP_EQ,
+ 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)");
+ tor_end_capture_bugs_();
+ /* There is no 'fred=', so that is treated as not existing. */
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "fred", -100));
+ /* jack is truncated */
+ tor_capture_bugs_(1);
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "jack", -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,
+ "!(! ok)");
+ tor_end_capture_bugs_();
+ /* electricity and opa aren't integers. */
+ tor_capture_bugs_(1);
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "electricity", -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,
+ "!(! ok)");
+ tor_end_capture_bugs_();
+
+ tor_capture_bugs_(1);
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "opa", -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,
+ "!(! ok)");
+ tor_end_capture_bugs_();
+
+ done:
+ SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp));
+ smartlist_free(lst);
+ tor_end_capture_bugs_();
+}
+
+#undef dirvote_compute_params
+
/** Helper: Test that two networkstatus_voter_info_t do in fact represent the
* same voting authority, and that they do in fact have all the same
* information. */
@@ -1650,8 +1736,8 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now)
tt_int_op(rs->addr,OP_EQ, 0x99008801);
tt_int_op(rs->or_port,OP_EQ, 443);
tt_int_op(rs->dir_port,OP_EQ, 8000);
- /* no flags except "running" (16) and "v2dir" (64) */
- tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(80));
+ /* no flags except "running" (16) and "v2dir" (64) and "valid" (128) */
+ tt_u64_op(vrs->flags, OP_EQ, U64_LITERAL(0xd0));
} else if (tor_memeq(rs->identity_digest,
"\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5"
"\x5\x5\x5\x5",
@@ -1748,7 +1834,7 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
tt_assert(!rs->is_stable);
/* (If it wasn't running it wouldn't be here) */
tt_assert(rs->is_flagged_running);
- tt_assert(!rs->is_valid);
+ tt_assert(rs->is_valid);
tt_assert(!rs->is_named);
tt_assert(rs->is_v2_dir);
/* XXXX check version */
@@ -1788,6 +1874,15 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
return;
}
+static authority_cert_t *mock_cert;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
/** Run a unit tests for generating and parsing networkstatuses, with
* the supply test fns. */
static void
@@ -1831,10 +1926,30 @@ test_a_networkstatus(
tt_assert(rs_test);
tt_assert(vrs_test);
- tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3,
- &sign_skey_1, &sign_skey_2,
- &sign_skey_3));
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+ /* Parse certificates and keys. */
+ cert1 = mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(cert1);
+ cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ tt_assert(cert2);
+ cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ tt_assert(cert3);
+ sign_skey_1 = crypto_pk_new();
+ sign_skey_2 = crypto_pk_new();
+ sign_skey_3 = crypto_pk_new();
sign_skey_leg1 = pk_generate(4);
+ sr_state_init(0, 0);
+
+ tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
+ AUTHORITY_SIGNKEY_1, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_2,
+ AUTHORITY_SIGNKEY_2, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_3,
+ AUTHORITY_SIGNKEY_3, -1));
+
+ tt_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key));
+ tt_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key));
tt_assert(!dir_common_construct_vote_1(&vote, cert1, sign_skey_1, vrs_gen,
&v1, &n_vrs, now, 1));
@@ -1959,9 +2074,9 @@ test_a_networkstatus(
tt_int_op(4,OP_EQ, smartlist_len(con->voters)); /*3 voters, 1 legacy key.*/
/* The voter id digests should be in this order. */
- tt_assert(memcmp(cert2->cache_info.identity_digest,
+ tt_assert(fast_memcmp(cert2->cache_info.identity_digest,
cert1->cache_info.identity_digest,DIGEST_LEN)<0);
- tt_assert(memcmp(cert1->cache_info.identity_digest,
+ tt_assert(fast_memcmp(cert1->cache_info.identity_digest,
cert3->cache_info.identity_digest,DIGEST_LEN)<0);
test_same_voter(smartlist_get(con->voters, 1),
smartlist_get(v2->voters, 0));
@@ -2196,56 +2311,57 @@ test_dir_scale_bw(void *testdata)
1.0/7,
12.0,
24.0 };
- u64_dbl_t vals[8];
+ double vals_dbl[8];
+ uint64_t vals_u64[8];
uint64_t total;
int i;
(void) testdata;
for (i=0; i<8; ++i)
- vals[i].dbl = v[i];
+ vals_dbl[i] = v[i];
- scale_array_elements_to_u64(vals, 8, &total);
+ scale_array_elements_to_u64(vals_u64, vals_dbl, 8, &total);
tt_int_op((int)total, OP_EQ, 48);
total = 0;
for (i=0; i<8; ++i) {
- total += vals[i].u64;
+ total += vals_u64[i];
}
tt_assert(total >= (U64_LITERAL(1)<<60));
tt_assert(total <= (U64_LITERAL(1)<<62));
for (i=0; i<8; ++i) {
/* vals[2].u64 is the scaled value of 1.0 */
- double ratio = ((double)vals[i].u64) / vals[2].u64;
+ double ratio = ((double)vals_u64[i]) / vals_u64[2];
tt_double_op(fabs(ratio - v[i]), OP_LT, .00001);
}
/* test handling of no entries */
total = 1;
- scale_array_elements_to_u64(vals, 0, &total);
+ scale_array_elements_to_u64(vals_u64, vals_dbl, 0, &total);
tt_assert(total == 0);
/* make sure we don't read the array when we have no entries
* may require compiler flags to catch NULL dereferences */
total = 1;
- scale_array_elements_to_u64(NULL, 0, &total);
+ scale_array_elements_to_u64(NULL, NULL, 0, &total);
tt_assert(total == 0);
- scale_array_elements_to_u64(NULL, 0, NULL);
+ scale_array_elements_to_u64(NULL, NULL, 0, NULL);
/* test handling of zero totals */
total = 1;
- vals[0].dbl = 0.0;
- scale_array_elements_to_u64(vals, 1, &total);
+ vals_dbl[0] = 0.0;
+ scale_array_elements_to_u64(vals_u64, vals_dbl, 1, &total);
tt_assert(total == 0);
- tt_assert(vals[0].u64 == 0);
+ tt_assert(vals_u64[0] == 0);
- vals[0].dbl = 0.0;
- vals[1].dbl = 0.0;
- scale_array_elements_to_u64(vals, 2, NULL);
- tt_assert(vals[0].u64 == 0);
- tt_assert(vals[1].u64 == 0);
+ vals_dbl[0] = 0.0;
+ vals_dbl[1] = 0.0;
+ scale_array_elements_to_u64(vals_u64, vals_dbl, 2, NULL);
+ tt_assert(vals_u64[0] == 0);
+ tt_assert(vals_u64[1] == 0);
done:
;
@@ -2256,7 +2372,7 @@ test_dir_random_weighted(void *testdata)
{
int histogram[10];
uint64_t vals[10] = {3,1,2,4,6,0,7,5,8,9}, total=0;
- u64_dbl_t inp[10];
+ uint64_t inp_u64[10];
int i, choice;
const int n = 50000;
double max_sq_error;
@@ -2266,12 +2382,12 @@ test_dir_random_weighted(void *testdata)
* in a scrambled order to make sure we don't depend on order. */
memset(histogram,0,sizeof(histogram));
for (i=0; i<10; ++i) {
- inp[i].u64 = vals[i];
+ inp_u64[i] = vals[i];
total += vals[i];
}
tt_u64_op(total, OP_EQ, 45);
for (i=0; i<n; ++i) {
- choice = choose_array_element_by_weight(inp, 10);
+ choice = choose_array_element_by_weight(inp_u64, 10);
tt_int_op(choice, OP_GE, 0);
tt_int_op(choice, OP_LT, 10);
histogram[choice]++;
@@ -2298,16 +2414,16 @@ test_dir_random_weighted(void *testdata)
/* Now try a singleton; do we choose it? */
for (i = 0; i < 100; ++i) {
- choice = choose_array_element_by_weight(inp, 1);
+ choice = choose_array_element_by_weight(inp_u64, 1);
tt_int_op(choice, OP_EQ, 0);
}
/* Now try an array of zeros. We should choose randomly. */
memset(histogram,0,sizeof(histogram));
for (i = 0; i < 5; ++i)
- inp[i].u64 = 0;
+ inp_u64[i] = 0;
for (i = 0; i < n; ++i) {
- choice = choose_array_element_by_weight(inp, 5);
+ choice = choose_array_element_by_weight(inp_u64, 5);
tt_int_op(choice, OP_GE, 0);
tt_int_op(choice, OP_LT, 5);
histogram[choice]++;
@@ -2847,7 +2963,7 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
(void)arg;
/* Init options */
- mock_options = malloc(sizeof(or_options_t));
+ mock_options = tor_malloc(sizeof(or_options_t));
reset_options(mock_options, &mock_get_options_calls);
MOCK(get_options, mock_get_options);
@@ -2865,10 +2981,10 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
routerset_parse(routerset_none, ROUTERSET_NONE_STR, "No routers");
/* Init routerstatuses */
- routerstatus_t *rs_a = malloc(sizeof(routerstatus_t));
+ routerstatus_t *rs_a = tor_malloc(sizeof(routerstatus_t));
reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4);
- routerstatus_t *rs_b = malloc(sizeof(routerstatus_t));
+ routerstatus_t *rs_b = tor_malloc(sizeof(routerstatus_t));
reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4);
/* Sanity check that routersets correspond to routerstatuses.
@@ -3053,7 +3169,7 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
tt_assert(rs_b->is_hs_dir == 1);
done:
- free(mock_options);
+ tor_free(mock_options);
mock_options = NULL;
UNMOCK(get_options);
@@ -3062,8 +3178,8 @@ test_dir_dirserv_set_routerstatus_testing(void *arg)
routerset_free(routerset_a);
routerset_free(routerset_none);
- free(rs_a);
- free(rs_b);
+ tor_free(rs_a);
+ tor_free(rs_b);
}
static void
@@ -3151,6 +3267,7 @@ static void
test_dir_fetch_type(void *arg)
{
(void)arg;
+
tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_BRIDGE,
NULL), OP_EQ, EXTRAINFO_DIRINFO | BRIDGE_DIRINFO);
tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_EXTRAINFO, ROUTER_PURPOSE_GENERAL,
@@ -3176,9 +3293,14 @@ test_dir_fetch_type(void *arg)
tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_MICRODESC, ROUTER_PURPOSE_GENERAL,
NULL), OP_EQ, MICRODESC_DIRINFO);
+ /* This will give a warning, because this function isn't supposed to be
+ * used for HS descriptors. */
+ setup_full_capture_of_logs(LOG_WARN);
tt_int_op(dir_fetch_type(DIR_PURPOSE_FETCH_RENDDESC_V2,
ROUTER_PURPOSE_GENERAL, NULL), OP_EQ, NO_DIRINFO);
- done: ;
+ expect_single_log_msg_containing("Unexpected purpose");
+ done:
+ teardown_capture_of_logs();
}
static void
@@ -3332,13 +3454,16 @@ test_dir_download_status_schedule(void *arg)
(void)arg;
download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE };
+ DL_SCHED_INCREMENT_FAILURE,
+ DL_SCHED_DETERMINISTIC, 0, 0 };
download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_CONSENSUS,
DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_ATTEMPT};
+ DL_SCHED_INCREMENT_ATTEMPT,
+ DL_SCHED_DETERMINISTIC, 0, 0 };
download_status_t dls_bridge = { 0, 0, 0, DL_SCHED_BRIDGE,
DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE};
+ DL_SCHED_INCREMENT_FAILURE,
+ DL_SCHED_DETERMINISTIC, 0, 0 };
int increment = -1;
int expected_increment = -1;
time_t current_time = time(NULL);
@@ -3354,6 +3479,7 @@ test_dir_download_status_schedule(void *arg)
delay1 = 1000;
increment = download_status_schedule_get_delay(&dls_failure,
schedule,
+ 0, INT_MAX,
TIME_MIN);
expected_increment = delay1;
tt_assert(increment == expected_increment);
@@ -3362,6 +3488,7 @@ test_dir_download_status_schedule(void *arg)
delay1 = INT_MAX;
increment = download_status_schedule_get_delay(&dls_failure,
schedule,
+ 0, INT_MAX,
-1);
expected_increment = delay1;
tt_assert(increment == expected_increment);
@@ -3370,6 +3497,7 @@ test_dir_download_status_schedule(void *arg)
delay1 = 0;
increment = download_status_schedule_get_delay(&dls_attempt,
schedule,
+ 0, INT_MAX,
0);
expected_increment = delay1;
tt_assert(increment == expected_increment);
@@ -3378,6 +3506,7 @@ test_dir_download_status_schedule(void *arg)
delay1 = 1000;
increment = download_status_schedule_get_delay(&dls_attempt,
schedule,
+ 0, INT_MAX,
1);
expected_increment = delay1;
tt_assert(increment == expected_increment);
@@ -3386,6 +3515,7 @@ test_dir_download_status_schedule(void *arg)
delay1 = INT_MAX;
increment = download_status_schedule_get_delay(&dls_bridge,
schedule,
+ 0, INT_MAX,
current_time);
expected_increment = delay1;
tt_assert(increment == expected_increment);
@@ -3394,6 +3524,7 @@ test_dir_download_status_schedule(void *arg)
delay1 = 1;
increment = download_status_schedule_get_delay(&dls_bridge,
schedule,
+ 0, INT_MAX,
TIME_MAX);
expected_increment = delay1;
tt_assert(increment == expected_increment);
@@ -3406,6 +3537,7 @@ test_dir_download_status_schedule(void *arg)
delay2 = 100;
increment = download_status_schedule_get_delay(&dls_attempt,
schedule,
+ 0, INT_MAX,
current_time);
expected_increment = delay2;
tt_assert(increment == expected_increment);
@@ -3414,6 +3546,7 @@ test_dir_download_status_schedule(void *arg)
delay2 = 1;
increment = download_status_schedule_get_delay(&dls_bridge,
schedule,
+ 0, INT_MAX,
current_time);
expected_increment = delay2;
tt_assert(increment == expected_increment);
@@ -3426,6 +3559,7 @@ test_dir_download_status_schedule(void *arg)
delay2 = 5;
increment = download_status_schedule_get_delay(&dls_attempt,
schedule,
+ 0, INT_MAX,
current_time);
expected_increment = delay2;
tt_assert(increment == expected_increment);
@@ -3434,6 +3568,7 @@ test_dir_download_status_schedule(void *arg)
delay2 = 17;
increment = download_status_schedule_get_delay(&dls_bridge,
schedule,
+ 0, INT_MAX,
current_time);
expected_increment = delay2;
tt_assert(increment == expected_increment);
@@ -3446,6 +3581,7 @@ test_dir_download_status_schedule(void *arg)
delay2 = 35;
increment = download_status_schedule_get_delay(&dls_attempt,
schedule,
+ 0, INT_MAX,
current_time);
expected_increment = INT_MAX;
tt_assert(increment == expected_increment);
@@ -3454,6 +3590,7 @@ test_dir_download_status_schedule(void *arg)
delay2 = 99;
increment = download_status_schedule_get_delay(&dls_bridge,
schedule,
+ 0, INT_MAX,
current_time);
expected_increment = INT_MAX;
tt_assert(increment == expected_increment);
@@ -3465,15 +3602,58 @@ test_dir_download_status_schedule(void *arg)
}
static void
+test_dir_download_status_random_backoff(void *arg)
+{
+ 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;
+ 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;
+ do {
+ increment = download_status_schedule_get_delay(&dls_random,
+ NULL,
+ min_delay, max_delay,
+ current_time);
+ /* 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);
+
+ /* Try another maybe */
+ old_increment = increment;
+ } while (increment < max_delay);
+
+ done:
+ return;
+}
+
+static void
test_dir_download_status_increment(void *arg)
{
(void)arg;
download_status_t dls_failure = { 0, 0, 0, DL_SCHED_GENERIC,
DL_WANT_AUTHORITY,
- DL_SCHED_INCREMENT_FAILURE };
+ DL_SCHED_INCREMENT_FAILURE,
+ DL_SCHED_DETERMINISTIC, 0, 0 };
download_status_t dls_attempt = { 0, 0, 0, DL_SCHED_BRIDGE,
DL_WANT_ANY_DIRSERVER,
- DL_SCHED_INCREMENT_ATTEMPT};
+ DL_SCHED_INCREMENT_ATTEMPT,
+ DL_SCHED_DETERMINISTIC, 0, 0 };
int delay0 = -1;
int delay1 = -1;
int delay2 = -1;
@@ -3552,14 +3732,14 @@ test_dir_download_status_increment(void *arg)
current_time + delay1 + 10,
0) == 0);
- /* Check that failure increments don't happen on 503 for clients, but that
- * attempt increments do. */
+ /* 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_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) == 2);
+ 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);
/* Check that failure increments do happen on 503 for servers */
@@ -3567,7 +3747,7 @@ test_dir_download_status_increment(void *arg)
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) == 2);
+ 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);
@@ -3576,7 +3756,7 @@ test_dir_download_status_increment(void *arg)
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) == 3);
+ 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);
@@ -3738,8 +3918,13 @@ test_dir_download_status_increment(void *arg)
/* Check that attempt increments don't happen on failure-based schedules,
* and that the attempt is set at the end of time */
mock_get_options_calls = 0;
+ setup_full_capture_of_logs(LOG_WARN);
next_at = download_status_increment_attempt(&dls_failure, "test",
current_time);
+ expect_single_log_msg_containing(
+ "Tried to launch an attempt-based connection on a failure-based "
+ "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);
@@ -3751,6 +3936,7 @@ test_dir_download_status_increment(void *arg)
UNMOCK(get_options);
mock_options = NULL;
mock_get_options_calls = 0;
+ teardown_capture_of_logs();
}
static void
@@ -3809,9 +3995,14 @@ test_dir_conn_purpose_to_string(void *data)
EXPECT_CONN_PURPOSE(DIR_PURPOSE_UPLOAD_RENDDESC_V2,
"hidden-service v2 descriptor upload");
EXPECT_CONN_PURPOSE(DIR_PURPOSE_FETCH_MICRODESC, "microdescriptor fetch");
+
+ /* This will give a warning, because there is no purpose 1024. */
+ setup_full_capture_of_logs(LOG_WARN);
EXPECT_CONN_PURPOSE(1024, "(unknown)");
+ expect_single_log_msg_containing("Called with unknown purpose 1024");
- done: ;
+ done:
+ teardown_capture_of_logs();
}
NS_DECL(int,
@@ -4042,6 +4233,1003 @@ test_dir_choose_compression_level(void* data)
done: ;
}
+/*
+ * Mock check_private_dir(), and always succeed - no need to actually
+ * look at or create anything on the filesystem.
+ */
+
+static int
+mock_check_private_dir(const char *dirname, cpd_check_t check,
+ const char *effective_user)
+{
+ (void)dirname;
+ (void)check;
+ (void)effective_user;
+
+ return 0;
+}
+
+/*
+ * This really mocks options_get_datadir_fname2_suffix(), but for testing
+ * dump_desc(), we only care about get_datadir_fname(sub1), which is defined
+ * in config.h as:
+ *
+ * options_get_datadir_fname2_suffix(get_options(), sub1, NULL, NULL)
+ */
+
+static char *
+mock_get_datadir_fname(const or_options_t *options,
+ const char *sub1, const char *sub2,
+ const char *suffix)
+{
+ char *rv = NULL;
+
+ /*
+ * 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);
+ /*
+ * No particular assertions about sub2, since we could be in the
+ * get_datadir_fname() or get_datadir_fname2() case.
+ */
+ tt_assert(suffix == NULL);
+
+ /* Just duplicate the basename and return it for this mock */
+ if (sub2) {
+ /* If we have sub2, it's the basename, otherwise sub1 */
+ rv = tor_strdup(sub2);
+ } else {
+ rv = tor_strdup(sub1);
+ }
+
+ done:
+ return rv;
+}
+
+static char *last_unlinked_path = NULL;
+static int unlinked_count = 0;
+
+static void
+mock_unlink_reset(void)
+{
+ tor_free(last_unlinked_path);
+ unlinked_count = 0;
+}
+
+static int
+mock_unlink(const char *path)
+{
+ tt_assert(path != NULL);
+
+ tor_free(last_unlinked_path);
+ last_unlinked_path = tor_strdup(path);
+ ++unlinked_count;
+
+ done:
+ return 0;
+}
+
+static char *last_write_str_path = NULL;
+static uint8_t last_write_str_hash[DIGEST256_LEN];
+static int write_str_count = 0;
+
+static void
+mock_write_str_to_file_reset(void)
+{
+ tor_free(last_write_str_path);
+ write_str_count = 0;
+}
+
+static int
+mock_write_str_to_file(const char *path, const char *str, int bin)
+{
+ size_t len;
+ uint8_t hash[DIGEST256_LEN];
+
+ (void)bin;
+
+ tt_assert(path != NULL);
+ tt_assert(str != NULL);
+
+ len = strlen(str);
+ crypto_digest256((char *)hash, str, len, DIGEST_SHA256);
+
+ tor_free(last_write_str_path);
+ last_write_str_path = tor_strdup(path);
+ memcpy(last_write_str_hash, hash, sizeof(last_write_str_hash));
+ ++write_str_count;
+
+ done:
+ return 0;
+}
+
+static void
+test_dir_dump_unparseable_descriptors(void *data)
+{
+ /*
+ * These bogus descriptors look nothing at all like real bogus descriptors
+ * we might see, but we're only testing dump_desc() here, not the parser.
+ */
+ const char *test_desc_type = "squamous";
+ /* strlen(test_desc_1) = 583 bytes */
+ const char *test_desc_1 =
+ "The most merciful thing in the world, I think, is the inability of the "
+ "human mind to correlate all its contents. We live on a placid island of"
+ " ignorance in the midst of black seas of infinity, and it was not meant"
+ " that we should voyage far. The sciences, each straining in its own dir"
+ "ection, have hitherto harmed us little; but some day the piecing togeth"
+ "er of dissociated knowledge will open up such terrifying vistas of real"
+ "ity, and of our frightful position therein, that we shall either go mad"
+ "from the revelation or flee from the light into the peace and safety of"
+ "a new dark age.";
+ uint8_t test_desc_1_hash[DIGEST256_LEN];
+ char test_desc_1_hash_str[HEX_DIGEST256_LEN+1];
+ /* strlen(test_desc_2) = 650 bytes */
+ const char *test_desc_2 =
+ "I think their predominant colour was a greyish-green, though they had w"
+ "hite bellies. They were mostly shiny and slippery, but the ridges of th"
+ "eir backs were scaly. Their forms vaguely suggested the anthropoid, whi"
+ "le their heads were the heads of fish, with prodigious bulging eyes tha"
+ "t never closed. At the sides of their necks were palpitating gills, and"
+ "their long paws were webbed. They hopped irregularly, sometimes on two "
+ "legs and sometimes on four. I was somehow glad that they had no more th"
+ "an four limbs. Their croaking, baying voices, clearly wed tar articulat"
+ "e speech, held all the dark shades of expression which their staring fa"
+ "ces lacked.";
+ uint8_t test_desc_2_hash[DIGEST256_LEN];
+ char test_desc_2_hash_str[HEX_DIGEST256_LEN+1];
+ /* strlen(test_desc_3) = 700 bytes */
+ const char *test_desc_3 =
+ "Without knowing what futurism is like, Johansen achieved something very"
+ "close to it when he spoke of the city; for instead of describing any de"
+ "finite structure or building, he dwells only on broad impressions of va"
+ "st angles and stone surfaces - surfaces too great to belong to anything"
+ "right or proper for this earth, and impious with horrible images and hi"
+ "eroglyphs. I mention his talk about angles because it suggests somethin"
+ "g Wilcox had told me of his awful dreams. He said that the geometry of "
+ "the dream-place he saw was abnormal, non-Euclidean, and loathsomely red"
+ "olent of spheres and dimensions apart from ours. Now an unlettered seam"
+ "an felt the same thing whilst gazing at the terrible reality.";
+ uint8_t test_desc_3_hash[DIGEST256_LEN];
+ char test_desc_3_hash_str[HEX_DIGEST256_LEN+1];
+ /* strlen(test_desc_3) = 604 bytes */
+ const char *test_desc_4 =
+ "So we glanced back simultaneously, it would appear; though no doubt the"
+ "incipient motion of one prompted the imitation of the other. As we did "
+ "so we flashed both torches full strength at the momentarily thinned mis"
+ "t; either from sheer primitive anxiety to see all we could, or in a les"
+ "s primitive but equally unconscious effort to dazzle the entity before "
+ "we dimmed our light and dodged among the penguins of the labyrinth cent"
+ "er ahead. Unhappy act! Not Orpheus himself, or Lot's wife, paid much mo"
+ "re dearly for a backward glance. And again came that shocking, wide-ran"
+ "ged piping - \"Tekeli-li! Tekeli-li!\"";
+ uint8_t test_desc_4_hash[DIGEST256_LEN];
+ char test_desc_4_hash_str[HEX_DIGEST256_LEN+1];
+ (void)data;
+
+ /*
+ * Set up options mock so we can force a tiny FIFO size and generate
+ * cleanups.
+ */
+ mock_options = tor_malloc(sizeof(or_options_t));
+ reset_options(mock_options, &mock_get_options_calls);
+ mock_options->MaxUnparseableDescSizeToLog = 1536;
+ MOCK(get_options, mock_get_options);
+ MOCK(check_private_dir, mock_check_private_dir);
+ MOCK(options_get_datadir_fname2_suffix,
+ mock_get_datadir_fname);
+
+ /*
+ * Set up unlink and write mocks
+ */
+ MOCK(tor_unlink, mock_unlink);
+ mock_unlink_reset();
+ MOCK(write_str_to_file, mock_write_str_to_file);
+ mock_write_str_to_file_reset();
+
+ /*
+ * Compute hashes we'll need to recognize which descriptor is which
+ */
+ crypto_digest256((char *)test_desc_1_hash, test_desc_1,
+ strlen(test_desc_1), DIGEST_SHA256);
+ base16_encode(test_desc_1_hash_str, sizeof(test_desc_1_hash_str),
+ (const char *)test_desc_1_hash,
+ sizeof(test_desc_1_hash));
+ crypto_digest256((char *)test_desc_2_hash, test_desc_2,
+ strlen(test_desc_2), DIGEST_SHA256);
+ base16_encode(test_desc_2_hash_str, sizeof(test_desc_2_hash_str),
+ (const char *)test_desc_2_hash,
+ sizeof(test_desc_2_hash));
+ crypto_digest256((char *)test_desc_3_hash, test_desc_3,
+ strlen(test_desc_3), DIGEST_SHA256);
+ base16_encode(test_desc_3_hash_str, sizeof(test_desc_3_hash_str),
+ (const char *)test_desc_3_hash,
+ sizeof(test_desc_3_hash));
+ crypto_digest256((char *)test_desc_4_hash, test_desc_4,
+ strlen(test_desc_4), DIGEST_SHA256);
+ base16_encode(test_desc_4_hash_str, sizeof(test_desc_4_hash_str),
+ (const char *)test_desc_4_hash,
+ sizeof(test_desc_4_hash));
+
+ /*
+ * Reset the FIFO and check its state
+ */
+ dump_desc_fifo_cleanup();
+ tt_u64_op(len_descs_dumped, ==, 0);
+ tt_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * (1) Fire off dump_desc() once; these descriptors should all be safely
+ * smaller than configured FIFO size.
+ */
+
+ dump_desc(test_desc_1, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ mock_write_str_to_file_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(write_str_count, ==, 0);
+
+ /*
+ * (2) Fire off dump_desc() twice; this still should trigger no cleanup.
+ */
+
+ /* First time */
+ dump_desc(test_desc_2, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
+
+ /* Second time */
+ dump_desc(test_desc_3, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ mock_write_str_to_file_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(write_str_count, ==, 0);
+
+ /*
+ * (3) Three calls to dump_desc cause a FIFO cleanup
+ */
+
+ /* First time */
+ dump_desc(test_desc_4, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
+
+ /* Second time */
+ dump_desc(test_desc_1, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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 */
+ dump_desc(test_desc_2, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ mock_write_str_to_file_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(write_str_count, ==, 0);
+
+ /*
+ * (4) But repeating one (A B B) doesn't overflow and cleanup
+ */
+
+ /* First time */
+ dump_desc(test_desc_3, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
+
+ /* Second time */
+ dump_desc(test_desc_4, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
+
+ /* Third time */
+ dump_desc(test_desc_4, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ mock_write_str_to_file_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(write_str_count, ==, 0);
+
+ /*
+ * (5) Same for the (A B A) repetition
+ */
+
+ /* First time */
+ dump_desc(test_desc_1, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_1_hash, DIGEST_SHA256);
+
+ /* Second time */
+ dump_desc(test_desc_2, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
+
+ /* Third time */
+ dump_desc(test_desc_1, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ mock_write_str_to_file_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(write_str_count, ==, 0);
+
+ /*
+ * (6) (A B B C) triggering overflow on C causes A, not B to be unlinked
+ */
+
+ /* First time */
+ dump_desc(test_desc_3, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
+
+ /* Second time */
+ dump_desc(test_desc_4, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_4_hash, DIGEST_SHA256);
+
+ /* Third time */
+ dump_desc(test_desc_4, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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 */
+ dump_desc(test_desc_1, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ mock_write_str_to_file_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(write_str_count, ==, 0);
+
+ /*
+ * (7) (A B A C) triggering overflow on C causes B, not A to be unlinked
+ */
+
+ /* First time */
+ dump_desc(test_desc_2, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_2_hash, DIGEST_SHA256);
+
+ /* Second time */
+ dump_desc(test_desc_3, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_mem_op(last_write_str_hash, OP_EQ, test_desc_3_hash, DIGEST_SHA256);
+
+ /* Third time */
+ dump_desc(test_desc_2, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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 */
+ dump_desc(test_desc_4, test_desc_type);
+
+ /*
+ * Assert things about the FIFO state
+ */
+ tt_u64_op(len_descs_dumped, ==, 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_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_assert(descs_dumped == NULL || smartlist_len(descs_dumped) == 0);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ mock_write_str_to_file_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ tt_int_op(write_str_count, ==, 0);
+
+ done:
+
+ /* Clean up the fifo */
+ dump_desc_fifo_cleanup();
+
+ /* Remove mocks */
+ UNMOCK(tor_unlink);
+ mock_unlink_reset();
+ UNMOCK(write_str_to_file);
+ mock_write_str_to_file_reset();
+ UNMOCK(options_get_datadir_fname2_suffix);
+ UNMOCK(check_private_dir);
+ UNMOCK(get_options);
+ tor_free(mock_options);
+ mock_options = NULL;
+
+ return;
+}
+
+/* Variables for reset_read_file_to_str_mock() */
+
+static int enforce_expected_filename = 0;
+static char *expected_filename = NULL;
+static char *file_content = NULL;
+static size_t file_content_len = 0;
+static struct stat file_stat;
+static int read_count = 0, read_call_count = 0;
+
+static void
+reset_read_file_to_str_mock(void)
+{
+ tor_free(expected_filename);
+ tor_free(file_content);
+ file_content_len = 0;
+ memset(&file_stat, 0, sizeof(file_stat));
+ read_count = 0;
+ read_call_count = 0;
+}
+
+static char *
+read_file_to_str_mock(const char *filename, int flags,
+ struct stat *stat_out) {
+ char *result = NULL;
+
+ /* Insist we got a filename */
+ tt_assert(filename != NULL);
+
+ /* We ignore flags */
+ (void)flags;
+
+ /* Bump the call count */
+ ++read_call_count;
+
+ if (enforce_expected_filename) {
+ tt_assert(expected_filename);
+ tt_str_op(filename, OP_EQ, expected_filename);
+ }
+
+ if (expected_filename != NULL &&
+ file_content != NULL &&
+ strcmp(filename, expected_filename) == 0) {
+ /* You asked for it, you got it */
+
+ /*
+ * This is the same behavior as the real read_file_to_str();
+ * if there's a NUL, the real size ends up in stat_out.
+ */
+ result = tor_malloc(file_content_len + 1);
+ if (file_content_len > 0) {
+ memcpy(result, file_content, file_content_len);
+ }
+ result[file_content_len] = '\0';
+
+ /* Do we need to set up stat_out? */
+ if (stat_out != NULL) {
+ memcpy(stat_out, &file_stat, sizeof(file_stat));
+ /* We always return the correct length here */
+ stat_out->st_size = file_content_len;
+ }
+
+ /* Wooo, we have a return value - bump the counter */
+ ++read_count;
+ }
+ /* else no match, return NULL */
+
+ done:
+ return result;
+}
+
+/* This one tests dump_desc_populate_one_file() */
+static void
+test_dir_populate_dump_desc_fifo(void *data)
+{
+ const char *dirname = "foo";
+ const char *fname = NULL;
+ dumped_desc_t *ent;
+
+ (void)data;
+
+ /*
+ * Set up unlink and read_file_to_str mocks
+ */
+ MOCK(tor_unlink, mock_unlink);
+ mock_unlink_reset();
+ MOCK(read_file_to_str, read_file_to_str_mock);
+ reset_read_file_to_str_mock();
+
+ /* Check state of unlink mock */
+ tt_int_op(unlinked_count, ==, 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ 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);
+
+ /* 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);
+
+ /* This one has a correctly formed filename and should try reading */
+
+ /* Read fails */
+ ent = dump_desc_populate_one_file(
+ 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);
+
+ /* This read will succeed but the digest won't match the file content */
+ fname =
+ "unparseable-desc."
+ "DF0981323F3A70D02B55AB54B44B04F287D72F7B72F242E85C8CB0EDA8854A99";
+ enforce_expected_filename = 1;
+ tor_asprintf(&expected_filename, "%s%s%s", dirname, PATH_SEPARATOR, fname);
+ file_content = tor_strdup("hanc culpam maiorem an illam dicam?");
+ file_content_len = strlen(file_content);
+ 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);
+ tor_free(expected_filename);
+ tor_free(file_content);
+
+ /* This one will match */
+ fname =
+ "unparseable-desc."
+ "0786C7173447B7FB033FFCA2FC47C3CF71C30DD47CA8236D3FC7FF35853271C6";
+ tor_asprintf(&expected_filename, "%s%s%s", dirname, PATH_SEPARATOR, fname);
+ file_content = tor_strdup("hanc culpam maiorem an illam dicam?");
+ 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_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);
+ tor_free(ent->filename);
+ tor_free(ent);
+ tor_free(expected_filename);
+
+ /*
+ * Reset the mocks and check their state
+ */
+ mock_unlink_reset();
+ tt_int_op(unlinked_count, ==, 0);
+ reset_read_file_to_str_mock();
+ tt_int_op(read_count, ==, 0);
+
+ done:
+
+ UNMOCK(tor_unlink);
+ mock_unlink_reset();
+ UNMOCK(read_file_to_str);
+ reset_read_file_to_str_mock();
+
+ tor_free(file_content);
+
+ return;
+}
+
+static smartlist_t *
+listdir_mock(const char *dname)
+{
+ smartlist_t *l;
+
+ /* Ignore the name, always return this list */
+ (void)dname;
+
+ l = smartlist_new();
+ smartlist_add(l, tor_strdup("foo"));
+ smartlist_add(l, tor_strdup("bar"));
+ smartlist_add(l, tor_strdup("baz"));
+
+ return l;
+}
+
+static dumped_desc_t *
+pop_one_mock(const char *dirname, const char *f)
+{
+ dumped_desc_t *ent = NULL;
+
+ if (dirname != NULL && strcmp(dirname, "d") == 0) {
+ if (f != NULL && strcmp(f, "foo") == 0) {
+ ent = tor_malloc_zero(sizeof(*ent));
+ ent->filename = tor_strdup("d/foo");
+ ent->len = 123;
+ ent->digest_sha256[0] = 1;
+ ent->when = 1024;
+ } else if (f != NULL && strcmp(f, "bar") == 0) {
+ ent = tor_malloc_zero(sizeof(*ent));
+ ent->filename = tor_strdup("d/bar");
+ ent->len = 456;
+ ent->digest_sha256[0] = 2;
+ /*
+ * Note that the timestamps are in a different order than
+ * listdir_mock() returns; we're testing the sort order.
+ */
+ ent->when = 512;
+ } else if (f != NULL && strcmp(f, "baz") == 0) {
+ ent = tor_malloc_zero(sizeof(*ent));
+ ent->filename = tor_strdup("d/baz");
+ ent->len = 789;
+ ent->digest_sha256[0] = 3;
+ ent->when = 768;
+ }
+ }
+
+ return ent;
+}
+
+/* This one tests dump_desc_populate_fifo_from_directory() */
+static void
+test_dir_populate_dump_desc_fifo_2(void *data)
+{
+ dumped_desc_t *ent = NULL;
+
+ (void)data;
+
+ /* Set up the mocks */
+ MOCK(tor_listdir, listdir_mock);
+ MOCK(dump_desc_populate_one_file, pop_one_mock);
+
+ /* Run dump_desc_populate_fifo_from_directory() */
+ descs_dumped = NULL;
+ len_descs_dumped = 0;
+ dump_desc_populate_fifo_from_directory("d");
+ tt_assert(descs_dumped != NULL);
+ tt_int_op(smartlist_len(descs_dumped), OP_EQ, 3);
+ tt_u64_op(len_descs_dumped, OP_EQ, 1368);
+ ent = smartlist_get(descs_dumped, 0);
+ tt_str_op(ent->filename, OP_EQ, "d/bar");
+ tt_int_op(ent->len, OP_EQ, 456);
+ tt_int_op(ent->when, OP_EQ, 512);
+ ent = smartlist_get(descs_dumped, 1);
+ tt_str_op(ent->filename, OP_EQ, "d/baz");
+ tt_int_op(ent->len, OP_EQ, 789);
+ tt_int_op(ent->when, OP_EQ, 768);
+ ent = smartlist_get(descs_dumped, 2);
+ tt_str_op(ent->filename, OP_EQ, "d/foo");
+ tt_int_op(ent->len, OP_EQ, 123);
+ tt_int_op(ent->when, OP_EQ, 1024);
+
+ done:
+ dump_desc_fifo_cleanup();
+
+ UNMOCK(dump_desc_populate_one_file);
+ UNMOCK(tor_listdir);
+
+ return;
+}
+
static int mock_networkstatus_consensus_is_bootstrapping_value = 0;
static int
mock_networkstatus_consensus_is_bootstrapping(time_t now)
@@ -4093,7 +5281,7 @@ test_dir_find_dl_schedule(void* data)
smartlist_t client_boot_auth_only_cons, client_boot_auth_cons;
smartlist_t client_boot_fallback_cons, bridge;
- mock_options = malloc(sizeof(or_options_t));
+ mock_options = tor_malloc(sizeof(or_options_t));
reset_options(mock_options, &mock_get_options_calls);
MOCK(get_options, mock_get_options);
@@ -4202,10 +5390,53 @@ test_dir_find_dl_schedule(void* data)
UNMOCK(networkstatus_consensus_is_bootstrapping);
UNMOCK(networkstatus_consensus_can_use_extra_fallbacks);
UNMOCK(get_options);
- free(mock_options);
+ tor_free(mock_options);
mock_options = NULL;
}
+static void
+test_dir_assumed_flags(void *arg)
+{
+ (void)arg;
+ smartlist_t *tokens = smartlist_new();
+ memarea_t *area = memarea_new();
+ routerstatus_t *rs = NULL;
+
+ /* First, we should always assume that the Running flag is set, even
+ * when it isn't listed, since the consensus method is always
+ * higher than 4. */
+ const char *str1 =
+ "r example hereiswhereyouridentitygoes 2015-08-30 12:00:00 "
+ "192.168.0.1 9001 0\n"
+ "m thisoneislongerbecauseitisa256bitmddigest33\n"
+ "s Fast Guard Stable\n";
+
+ const char *cp = str1;
+ rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL,
+ 23, FLAV_MICRODESC);
+ tt_assert(rs);
+ tt_assert(rs->is_flagged_running);
+ tt_assert(! rs->is_valid);
+ tt_assert(! rs->is_exit);
+ tt_assert(rs->is_fast);
+ routerstatus_free(rs);
+
+ /* With method 24 or later, we can assume "valid" is set. */
+ cp = str1;
+ rs = routerstatus_parse_entry_from_string(area, &cp, tokens, NULL, NULL,
+ 24, FLAV_MICRODESC);
+ tt_assert(rs);
+ tt_assert(rs->is_flagged_running);
+ tt_assert(rs->is_valid);
+ tt_assert(! rs->is_exit);
+ tt_assert(rs->is_fast);
+
+ done:
+ smartlist_free(tokens);
+ memarea_drop_all(area);
+ routerstatus_free(rs);
+}
+
#define DIR_LEGACY(name) \
{ #name, test_dir_ ## name , TT_FORK, NULL, NULL }
@@ -4230,6 +5461,7 @@ struct testcase_t dir_tests[] = {
DIR_LEGACY(measured_bw_kb),
DIR_LEGACY(measured_bw_kb_cache),
DIR_LEGACY(param_voting),
+ DIR(param_voting_lookup, 0),
DIR_LEGACY(v3_networkstatus),
DIR(random_weighted, 0),
DIR(scale_bw, 0),
@@ -4242,6 +5474,7 @@ struct testcase_t dir_tests[] = {
DIR(fetch_type, 0),
DIR(packages, 0),
DIR(download_status_schedule, 0),
+ DIR(download_status_random_backoff, 0),
DIR(download_status_increment, 0),
DIR(authdir_type_to_string, 0),
DIR(conn_purpose_to_string, 0),
@@ -4250,10 +5483,14 @@ struct testcase_t dir_tests[] = {
DIR(should_not_init_request_to_dir_auths_without_v3_info, 0),
DIR(should_init_request_to_dir_auths, 0),
DIR(choose_compression_level, 0),
+ 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(assumed_flags, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c
index 0b446c2dfd..ca43dd4c04 100644
--- a/src/test/test_dir_common.c
+++ b/src/test/test_dir_common.c
@@ -21,13 +21,6 @@ networkstatus_t * dir_common_add_rs_and_parse(networkstatus_t *vote,
crypto_pk_t *sign_skey, int *n_vrs,
time_t now, int clear_rl);
-extern const char AUTHORITY_CERT_1[];
-extern const char AUTHORITY_SIGNKEY_1[];
-extern const char AUTHORITY_CERT_2[];
-extern const char AUTHORITY_SIGNKEY_2[];
-extern const char AUTHORITY_CERT_3[];
-extern const char AUTHORITY_SIGNKEY_3[];
-
/** Initialize and set auth certs and keys
* Returns 0 on success, -1 on failure. Clean up handled by caller.
*/
@@ -96,6 +89,7 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now)
/* all flags but running and v2dir cleared */
rs->is_flagged_running = 1;
rs->is_v2_dir = 1;
+ rs->is_valid = 1; /* xxxxx */
break;
case 1:
/* Generate the second routerstatus. */
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 05657ca452..a0f22f1f0c 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -38,7 +38,15 @@
#include <dirent.h>
#endif
+#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
+DISABLE_GCC_WARNING(overlength-strings)
+/* We allow huge string constants in the unit tests, but not in the code
+ * at large. */
+#endif
#include "vote_descriptors.inc"
+#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
+ENABLE_GCC_WARNING(overlength-strings)
+#endif
#define NS_MODULE dir_handle_get
@@ -61,7 +69,13 @@ connection_write_to_buf_mock(const char *string, size_t len,
#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
"Consensus not signed by sufficient number of requested authorities\r\n\r\n"
-static tor_addr_t MOCK_TOR_ADDR;
+static dir_connection_t *
+new_dir_conn(void)
+{
+ dir_connection_t *conn = dir_connection_new(AF_INET);
+ tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001);
+ return conn;
+}
static void
test_dir_handle_get_bad_request(void *data)
@@ -72,7 +86,7 @@ test_dir_handle_get_bad_request(void *data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(directory_handle_command_get(conn, "", NULL, 0), OP_EQ, 0);
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
@@ -95,7 +109,7 @@ test_dir_handle_get_v1_command_not_found(void *data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
// no frontpage configured
tt_ptr_op(get_dirportfrontpage(), OP_EQ, NULL);
@@ -137,7 +151,7 @@ test_dir_handle_get_v1_command(void *data)
exp_body = get_dirportfrontpage();
body_len = strlen(exp_body);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(directory_handle_command_get(conn, GET("/tor/"), NULL, 0),
OP_EQ, 0);
@@ -172,7 +186,7 @@ test_dir_handle_get_not_found(void *data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
/* Unrecognized path */
tt_int_op(directory_handle_command_get(conn, GET("/anything"), NULL, 0),
@@ -199,7 +213,7 @@ test_dir_handle_get_robots_txt(void *data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(directory_handle_command_get(conn, GET("/tor/robots.txt"),
NULL, 0), OP_EQ, 0);
@@ -224,51 +238,6 @@ test_dir_handle_get_robots_txt(void *data)
tor_free(body);
}
-static void
-test_dir_handle_get_bytes_txt(void *data)
-{
- dir_connection_t *conn = NULL;
- char *header = NULL;
- char *body = NULL;
- size_t body_used = 0, body_len = 0;
- char buff[30];
- char *exp_body = NULL;
- (void) data;
-
- exp_body = directory_dump_request_log();
- body_len = strlen(exp_body);
-
- MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
-
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
-
- tt_int_op(directory_handle_command_get(conn, GET("/tor/bytes.txt"), NULL, 0),
- OP_EQ, 0);
- fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
- &body, &body_used, body_len+1, 0);
-
- tt_assert(header);
- tt_assert(body);
-
- tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
- tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
- tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
- tt_assert(strstr(header, "Pragma: no-cache\r\n"));
-
- tor_snprintf(buff, sizeof(buff), "Content-Length: %ld\r\n", (long) body_len);
- tt_assert(strstr(header, buff));
-
- tt_int_op(body_used, OP_EQ, strlen(body));
- tt_str_op(body, OP_EQ, exp_body);
-
- done:
- UNMOCK(connection_write_to_buf_impl_);
- connection_free_(TO_CONN(conn));
- tor_free(header);
- tor_free(body);
- tor_free(exp_body);
-}
-
#define RENDEZVOUS2_GET(descid) GET("/tor/rendezvous2/" descid)
static void
test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
@@ -279,7 +248,7 @@ test_dir_handle_get_rendezvous2_not_found_if_not_encrypted(void *data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
// connection is not encrypted
tt_assert(!connection_dir_is_encrypted(conn))
@@ -306,7 +275,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_with_invalid_desc_id(
(void) data;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
// connection is encrypted
TO_CONN(conn)->linked = 1;
@@ -333,7 +302,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_not_well_formed(void *data)
(void) data;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
// connection is encrypted
TO_CONN(conn)->linked = 1;
@@ -366,7 +335,7 @@ test_dir_handle_get_rendezvous2_not_found(void *data)
(void) data;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
rend_cache_init();
@@ -432,13 +401,13 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id,
DIGEST_LEN);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
// connection is encrypted
TO_CONN(conn)->linked = 1;
tt_assert(connection_dir_is_encrypted(conn));
- sprintf(req, RENDEZVOUS2_GET("%s"), desc_id_base32);
+ tor_snprintf(req, sizeof(req), RENDEZVOUS2_GET("%s"), desc_id_base32);
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -453,7 +422,7 @@ test_dir_handle_get_rendezvous2_on_encrypted_conn_success(void *data)
tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
tt_assert(strstr(header, "Pragma: no-cache\r\n"));
- sprintf(buff, "Content-Length: %ld\r\n", (long) body_len);
+ tor_snprintf(buff, sizeof(buff), "Content-Length: %ld\r\n", (long) body_len);
tt_assert(strstr(header, buff));
tt_int_op(body_used, OP_EQ, strlen(body));
@@ -483,7 +452,7 @@ test_dir_handle_get_micro_d_not_found(void *data)
#define B64_256_1 "8/Pz8/u7vz8/Pz+7vz8/Pz+7u/Pz8/P7u/Pz8/P7u78"
#define B64_256_2 "zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMw"
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = MICRODESC_GET(B64_256_1 "-" B64_256_2);
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -504,7 +473,7 @@ static or_options_t *mock_options = NULL;
static void
init_mock_options(void)
{
- mock_options = malloc(sizeof(or_options_t));
+ mock_options = tor_malloc(sizeof(or_options_t));
memset(mock_options, 0, sizeof(or_options_t));
mock_options->TestingTorNetwork = 1;
}
@@ -563,9 +532,9 @@ test_dir_handle_get_micro_d(void *data)
tt_int_op(1, OP_EQ, smartlist_len(list));
/* Make the request */
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
- sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tor_snprintf(path, sizeof(path), MICRODESC_GET("%s"), digest_base64);
tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
@@ -633,9 +602,9 @@ test_dir_handle_get_micro_d_server_busy(void *data)
mock_options->CountPrivateBandwidth = 1;
/* Make the request */
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
- sprintf(path, MICRODESC_GET("%s"), digest_base64);
+ tor_snprintf(path, sizeof(path), MICRODESC_GET("%s"), digest_base64);
tt_int_op(directory_handle_command_get(conn, path, NULL, 0), OP_EQ, 0);
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
@@ -670,7 +639,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_without_auth(void *data)
mock_options->BridgeAuthoritativeDir = 1;
mock_options->BridgePassword_AuthDigest_ = tor_strdup("digest");
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
TO_CONN(conn)->linked = 1;
const char *req = GET(BRIDGES_PATH);
@@ -706,7 +675,7 @@ test_dir_handle_get_networkstatus_bridges(void *data)
crypto_digest256(mock_options->BridgePassword_AuthDigest_,
"abcdefghijklm12345", 18, DIGEST_SHA256);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
TO_CONN(conn)->linked = 1;
const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
@@ -746,7 +715,7 @@ test_dir_handle_get_networkstatus_bridges_not_found_wrong_auth(void *data)
crypto_digest256(mock_options->BridgePassword_AuthDigest_,
"abcdefghijklm12345", 18, DIGEST_SHA256);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
TO_CONN(conn)->linked = 1;
const char *req = "GET " BRIDGES_PATH " HTTP/1.0\r\n"
@@ -776,7 +745,7 @@ test_dir_handle_get_server_descriptors_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = SERVER_DESC_GET("invalid");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -819,7 +788,7 @@ test_dir_handle_get_server_descriptors_all(void* data)
/* Treat "all" requests as if they were unencrypted */
mock_routerinfo->cache_info.send_unencrypted = 1;
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = SERVER_DESC_GET("all");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -925,7 +894,7 @@ test_dir_handle_get_server_descriptors_authority(void* data)
strlen(TEST_DESCRIPTOR) - annotation_len;;
mock_routerinfo->cache_info.annotations_len = annotation_len;
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = SERVER_DESC_GET("authority");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -989,7 +958,7 @@ test_dir_handle_get_server_descriptors_fp(void* data)
strlen(TEST_DESCRIPTOR) - annotation_len;
mock_routerinfo->cache_info.annotations_len = annotation_len;
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
#define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
#define HEX2 "Deadbeef99999991111119999911111111f00ba4"
@@ -997,7 +966,8 @@ test_dir_handle_get_server_descriptors_fp(void* data)
DIGEST_LEN);
char req[155];
- sprintf(req, SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2), hex_digest);
+ tor_snprintf(req, sizeof(req), SERVER_DESC_GET("fp/%s+" HEX1 "+" HEX2),
+ hex_digest);
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
//TODO: Is this a BUG?
@@ -1054,10 +1024,11 @@ test_dir_handle_get_server_descriptors_d(void* data)
const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
DIGEST_LEN);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
- char req_header[155];
- sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ char req_header[155]; /* XXX Why 155? What kind of number is that?? */
+ tor_snprintf(req_header, sizeof(req_header),
+ SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
//TODO: Is this a BUG?
@@ -1121,12 +1092,13 @@ test_dir_handle_get_server_descriptors_busy(void* data)
const char *hex_digest = hex_str(router->cache_info.signed_descriptor_digest,
DIGEST_LEN);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
#define HEX1 "Fe0daff89127389bc67558691231234551193EEE"
#define HEX2 "Deadbeef99999991111119999911111111f00ba4"
- char req_header[155];
- sprintf(req_header, SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
+ char req_header[155]; /* XXX 155? Why 155? */
+ tor_snprintf(req_header, sizeof(req_header),
+ SERVER_DESC_GET("d/%s+" HEX1 "+" HEX2), hex_digest);
tt_int_op(directory_handle_command_get(conn, req_header, NULL, 0), OP_EQ, 0);
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
@@ -1159,7 +1131,7 @@ test_dir_handle_get_server_keys_bad_req(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1185,7 +1157,7 @@ test_dir_handle_get_server_keys_all_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/all");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1204,8 +1176,6 @@ test_dir_handle_get_server_keys_all_not_found(void* data)
#define TEST_CERTIFICATE AUTHORITY_CERT_3
#define TEST_SIGNING_KEY AUTHORITY_SIGNKEY_A_DIGEST
-extern const char AUTHORITY_CERT_3[];
-extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
static const char TEST_CERT_IDENT_KEY[] =
"D867ACF56A9D229B35C25F0090BC9867E906BE69";
@@ -1237,9 +1207,9 @@ test_dir_handle_get_server_keys_all(void* data)
base16_decode(ds->v3_identity_digest, DIGEST_LEN,
TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
- TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/all");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1276,7 +1246,7 @@ test_dir_handle_get_server_keys_authority_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/authority");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1316,7 +1286,7 @@ test_dir_handle_get_server_keys_authority(void* data)
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/authority");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1352,7 +1322,7 @@ test_dir_handle_get_server_keys_fp_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/fp/somehex");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1396,11 +1366,12 @@ test_dir_handle_get_server_keys_fp(void* data)
TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
- TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
char req[71];
- sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tor_snprintf(req, sizeof(req),
+ GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
@@ -1434,7 +1405,7 @@ test_dir_handle_get_server_keys_sk_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/sk/somehex");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1468,11 +1439,12 @@ test_dir_handle_get_server_keys_sk(void* data)
routerlist_free_all();
tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
- TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
char req[71];
- sprintf(req, GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY);
+ tor_snprintf(req, sizeof(req),
+ GET("/tor/keys/sk/%s"), TEST_SIGNING_KEY);
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
@@ -1506,7 +1478,7 @@ test_dir_handle_get_server_keys_fpsk_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
const char *req = GET("/tor/keys/fp-sk/somehex");
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1550,13 +1522,14 @@ test_dir_handle_get_server_keys_fpsk(void* data)
dir_server_add(ds);
tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
- TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
char req[115];
- sprintf(req, GET("/tor/keys/fp-sk/%s-%s"),
- TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY);
+ tor_snprintf(req, sizeof(req),
+ GET("/tor/keys/fp-sk/%s-%s"),
+ TEST_CERT_IDENT_KEY, TEST_SIGNING_KEY);
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
@@ -1606,7 +1579,7 @@ test_dir_handle_get_server_keys_busy(void* data)
dir_server_add(ds);
tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
- TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
MOCK(get_options, mock_get_options);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
@@ -1615,9 +1588,9 @@ test_dir_handle_get_server_keys_busy(void* data)
init_mock_options();
mock_options->CountPrivateBandwidth = 1;
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
char req[71];
- sprintf(req, GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
+ tor_snprintf(req, sizeof(req), GET("/tor/keys/fp/%s"), TEST_CERT_IDENT_KEY);
tt_int_op(directory_handle_command_get(conn, req, NULL, 0), OP_EQ, 0);
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
@@ -1669,7 +1642,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d)
mock_options->DirReqStatistics = 1;
geoip_dirreq_stats_init(time(NULL));
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/current/consensus-ns/" HEX1 "+" HEX2), NULL, 0));
@@ -1714,7 +1687,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
mock_options->DirReqStatistics = 1;
geoip_dirreq_stats_init(time(NULL));
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/current/consensus-ns"), NULL, 0));
@@ -1767,7 +1740,7 @@ status_vote_current_consensus_ns_test(char **header, char **body,
geoip_parse_entry("10,50,AB", AF_INET);
tt_str_op("ab", OP_EQ, geoip_get_country_name(1));
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
TO_CONN(conn)->address = tor_strdup("127.0.0.1");
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
@@ -1890,7 +1863,7 @@ test_dir_handle_get_status_vote_current_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/current/" HEX1), NULL, 0));
@@ -1914,7 +1887,7 @@ status_vote_current_d_test(char **header, char **body, size_t *body_l)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/current/d/" VOTE_DIGEST), NULL, 0));
@@ -1934,7 +1907,7 @@ status_vote_next_d_test(char **header, char **body, size_t *body_l)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/next/d/" VOTE_DIGEST), NULL, 0));
@@ -2058,7 +2031,7 @@ test_dir_handle_get_status_vote_next_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/next/" HEX1), NULL, 0));
@@ -2080,7 +2053,7 @@ status_vote_next_consensus_test(char **header, char **body, size_t *body_used)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/next/consensus"), NULL, 0));
@@ -2117,7 +2090,7 @@ test_dir_handle_get_status_vote_current_authority_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/current/authority"), NULL, 0));
@@ -2141,7 +2114,7 @@ test_dir_handle_get_status_vote_next_authority_not_found(void* data)
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/next/authority"), NULL, 0));
@@ -2226,7 +2199,7 @@ status_vote_next_consensus_signatures_test(char **header, char **body,
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/next/consensus-signatures"), NULL, 0));
@@ -2344,7 +2317,7 @@ test_dir_handle_get_status_vote_next_authority(void* data)
base16_decode(ds->v3_identity_digest, DIGEST_LEN,
TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
- TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
init_mock_options();
mock_options->AuthoritativeDir = 1;
@@ -2364,7 +2337,7 @@ test_dir_handle_get_status_vote_next_authority(void* data)
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/next/authority"), NULL, 0));
@@ -2423,7 +2396,7 @@ test_dir_handle_get_status_vote_current_authority(void* data)
TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
- TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1));
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
init_mock_options();
mock_options->AuthoritativeDir = 1;
@@ -2446,7 +2419,7 @@ test_dir_handle_get_status_vote_current_authority(void* data)
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
- conn = dir_connection_new(tor_addr_family(&MOCK_TOR_ADDR));
+ conn = new_dir_conn();
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
GET("/tor/status-vote/current/authority"), NULL, 0));
@@ -2484,7 +2457,6 @@ struct testcase_t dir_handle_get_tests[] = {
DIR_HANDLE_CMD(v1_command_not_found, 0),
DIR_HANDLE_CMD(v1_command, 0),
DIR_HANDLE_CMD(robots_txt, 0),
- DIR_HANDLE_CMD(bytes_txt, 0),
DIR_HANDLE_CMD(rendezvous2_not_found_if_not_encrypted, 0),
DIR_HANDLE_CMD(rendezvous2_not_found, 0),
DIR_HANDLE_CMD(rendezvous2_on_encrypted_conn_with_invalid_desc_id, 0),
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index 5289ca58ff..6a8e92cb47 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -16,30 +16,11 @@ NS(test_main)(void *arg)
{
(void)arg;
- uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_TTL / 2;
+ 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 - 1),==,MIN_DNS_TTL);
- tt_int_op(dns_clip_ttl(ttl_mid),==,ttl_mid);
- tt_int_op(dns_clip_ttl(MAX_DNS_TTL + 1),==,MAX_DNS_TTL);
-
- done:
- return;
-}
-
-#undef NS_SUBMODULE
-
-#define NS_SUBMODULE expiry_ttl
-
-static void
-NS(test_main)(void *arg)
-{
- (void)arg;
-
- uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_ENTRY_AGE / 2;
-
- tt_int_op(dns_get_expiry_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL);
- tt_int_op(dns_get_expiry_ttl(ttl_mid),==,ttl_mid);
- tt_int_op(dns_get_expiry_ttl(MAX_DNS_ENTRY_AGE + 1),==,MAX_DNS_ENTRY_AGE);
+ 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);
done:
return;
@@ -52,7 +33,7 @@ NS(test_main)(void *arg)
static int resolve_retval = 0;
static int resolve_made_conn_pending = 0;
static char *resolved_name = NULL;
-static cached_resolve_t *cache_entry = NULL;
+static cached_resolve_t *cache_entry_mock = NULL;
static int n_fake_impl = 0;
@@ -85,8 +66,8 @@ NS(dns_resolve_impl)(edge_connection_t *exitconn, int is_resolve,
if (hostname_out && resolved_name)
*hostname_out = tor_strdup(resolved_name);
- if (resolve_out && cache_entry)
- *resolve_out = cache_entry;
+ if (resolve_out && cache_entry_mock)
+ *resolve_out = cache_entry_mock;
n_fake_impl++;
@@ -213,7 +194,7 @@ NS(test_main)(void *arg)
exitconn->on_circuit = &(on_circuit->base_);
- cache_entry = fake_resolved;
+ cache_entry_mock = fake_resolved;
prev_n_send_resolved_cell_replacement =
n_send_resolved_cell_replacement;
@@ -749,7 +730,6 @@ NS(test_main)(void *arg)
struct testcase_t dns_tests[] = {
TEST_CASE(clip_ttl),
- TEST_CASE(expiry_ttl),
TEST_CASE(resolve),
TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve),
TEST_CASE_ASPECT(resolve_impl, non_exit),
diff --git a/src/test/test_guardfraction.c b/src/test/test_guardfraction.c
index 300590a3d9..8173e44d47 100644
--- a/src/test/test_guardfraction.c
+++ b/src/test/test_guardfraction.c
@@ -17,6 +17,7 @@
#include "test.h"
#include "test_helpers.h"
+#include "log_test_helpers.h"
/** Generate a vote_routerstatus_t for a router with identity digest
* <b>digest_in_hex</b>. */
@@ -40,7 +41,7 @@ gen_vote_routerstatus_for_tests(const char *digest_in_hex, int is_guard)
tt_int_op(strlen(digest_in_hex), ==, HEX_DIGEST_LEN);
retval = base16_decode(digest_tmp, sizeof(digest_tmp),
digest_in_hex, HEX_DIGEST_LEN);
- tt_int_op(retval, ==, 0);
+ tt_int_op(retval, ==, sizeof(digest_tmp));
memcpy(rs->identity_digest, digest_tmp, DIGEST_LEN);
}
@@ -304,11 +305,15 @@ test_parse_guardfraction_consensus(void *arg)
memset(&rs_no_guard, 0, sizeof(routerstatus_t));
tt_assert(!rs_no_guard.is_possible_guard);
+ setup_full_capture_of_logs(LOG_WARN);
retval = routerstatus_parse_guardfraction(guardfraction_str_good,
NULL, NULL,
&rs_no_guard);
tt_int_op(retval, ==, 0);
tt_assert(!rs_no_guard.has_guardfraction);
+ expect_single_log_msg_containing("Got GuardFraction for non-guard . "
+ "This is not supposed to happen.");
+ teardown_capture_of_logs();
}
{ /* Bad GuardFraction. Function should fail and not apply. */
@@ -334,7 +339,7 @@ test_parse_guardfraction_consensus(void *arg)
}
done:
- ;
+ teardown_capture_of_logs();
}
/** Make sure that we use GuardFraction information when we should,
diff --git a/src/test/test_handles.c b/src/test/test_handles.c
new file mode 100644
index 0000000000..536a478689
--- /dev/null
+++ b/src/test/test_handles.c
@@ -0,0 +1,95 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#include "test.h"
+
+#include "util.h"
+#include "handles.h"
+
+typedef struct demo_t {
+ HANDLE_ENTRY(demo, demo_t);
+ int val;
+} demo_t;
+
+HANDLE_DECL(demo, demo_t, static)
+HANDLE_IMPL(demo, demo_t, static)
+
+static demo_t *
+demo_new(int val)
+{
+ demo_t *d = tor_malloc_zero(sizeof(demo_t));
+ d->val = val;
+ return d;
+}
+
+static void
+demo_free(demo_t *d)
+{
+ if (d == NULL)
+ return;
+ demo_handles_clear(d);
+ tor_free(d);
+}
+
+static void
+test_handle_basic(void *arg)
+{
+ (void) arg;
+ demo_t *d1 = NULL, *d2 = NULL;
+ demo_handle_t *wr1 = NULL, *wr2 = NULL, *wr3 = NULL, *wr4 = NULL;
+
+ d1 = demo_new(9000);
+ d2 = demo_new(9009);
+
+ wr1 = demo_handle_new(d1);
+ wr2 = demo_handle_new(d1);
+ wr3 = demo_handle_new(d1);
+ wr4 = demo_handle_new(d2);
+
+ tt_assert(wr1);
+ tt_assert(wr2);
+ tt_assert(wr3);
+ tt_assert(wr4);
+
+ tt_ptr_op(demo_handle_get(wr1), OP_EQ, d1);
+ tt_ptr_op(demo_handle_get(wr2), OP_EQ, d1);
+ tt_ptr_op(demo_handle_get(wr3), OP_EQ, d1);
+ tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
+
+ demo_handle_free(wr1);
+ wr1 = NULL;
+ tt_ptr_op(demo_handle_get(wr2), OP_EQ, d1);
+ tt_ptr_op(demo_handle_get(wr3), OP_EQ, d1);
+ tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
+
+ demo_free(d1);
+ d1 = NULL;
+ tt_ptr_op(demo_handle_get(wr2), OP_EQ, NULL);
+ tt_ptr_op(demo_handle_get(wr3), OP_EQ, NULL);
+ tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
+
+ demo_handle_free(wr2);
+ wr2 = NULL;
+ tt_ptr_op(demo_handle_get(wr3), OP_EQ, NULL);
+ tt_ptr_op(demo_handle_get(wr4), OP_EQ, d2);
+
+ demo_handle_free(wr3);
+ wr3 = NULL;
+ done:
+ demo_handle_free(wr1);
+ demo_handle_free(wr2);
+ demo_handle_free(wr3);
+ demo_handle_free(wr4);
+ demo_free(d1);
+ demo_free(d2);
+}
+
+#define HANDLE_TEST(name, flags) \
+ { #name, test_handle_ ##name, (flags), NULL, NULL }
+
+struct testcase_t handle_tests[] = {
+ HANDLE_TEST(basic, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c
index c6daaf220a..ae9fc7a243 100644
--- a/src/test/test_helpers.c
+++ b/src/test/test_helpers.c
@@ -16,7 +16,15 @@
#include "test.h"
#include "test_helpers.h"
+#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
+DISABLE_GCC_WARNING(overlength-strings)
+/* We allow huge string constants in the unit tests, but not in the code
+ * at large. */
+#endif
#include "test_descriptors.inc"
+#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
+ENABLE_GCC_WARNING(overlength-strings)
+#endif
/* Return a statically allocated string representing yesterday's date
* in ISO format. We use it so that state file items are not found to
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 49939a53cf..8237bbc50e 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -8,12 +8,14 @@
#define CONTROL_PRIVATE
#define CIRCUITBUILD_PRIVATE
+#define RENDSERVICE_PRIVATE
#include "or.h"
#include "test.h"
#include "control.h"
#include "config.h"
#include "rendcommon.h"
+#include "rendservice.h"
#include "routerset.h"
#include "circuitbuild.h"
#include "test_helpers.h"
@@ -435,6 +437,347 @@ test_hs_rend_data(void *arg)
rend_data_free(client_dup);
}
+/* Test encoding and decoding service authorization cookies */
+static void
+test_hs_auth_cookies(void *arg)
+{
+#define TEST_COOKIE_RAW ((const uint8_t *) "abcdefghijklmnop")
+#define TEST_COOKIE_ENCODED "YWJjZGVmZ2hpamtsbW5vcA"
+#define TEST_COOKIE_ENCODED_STEALTH "YWJjZGVmZ2hpamtsbW5vcB"
+#define TEST_COOKIE_ENCODED_INVALID "YWJjZGVmZ2hpamtsbW5vcD"
+
+ char *encoded_cookie;
+ uint8_t raw_cookie[REND_DESC_COOKIE_LEN];
+ rend_auth_type_t auth_type;
+ char *err_msg;
+ int re;
+
+ (void)arg;
+
+ /* Test that encoding gives the expected result */
+ encoded_cookie = rend_auth_encode_cookie(TEST_COOKIE_RAW, REND_BASIC_AUTH);
+ tt_str_op(encoded_cookie, OP_EQ, TEST_COOKIE_ENCODED);
+ tor_free(encoded_cookie);
+
+ encoded_cookie = rend_auth_encode_cookie(TEST_COOKIE_RAW, REND_STEALTH_AUTH);
+ tt_str_op(encoded_cookie, OP_EQ, TEST_COOKIE_ENCODED_STEALTH);
+ tor_free(encoded_cookie);
+
+ /* Decoding should give the original value */
+ re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED, raw_cookie, &auth_type,
+ &err_msg);
+ tt_assert(!re);
+ tt_assert(!err_msg);
+ 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));
+
+ re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED_STEALTH, raw_cookie,
+ &auth_type, &err_msg);
+ tt_assert(!re);
+ tt_assert(!err_msg);
+ 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));
+
+ /* Decoding with padding characters should also work */
+ re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED "==", raw_cookie, NULL,
+ &err_msg);
+ tt_assert(!re);
+ tt_assert(!err_msg);
+ tt_mem_op(raw_cookie, OP_EQ, TEST_COOKIE_RAW, REND_DESC_COOKIE_LEN);
+
+ /* Decoding with an unknown type should fail */
+ re = rend_auth_decode_cookie(TEST_COOKIE_ENCODED_INVALID, raw_cookie,
+ &auth_type, &err_msg);
+ tt_int_op(re, OP_LT, 0);
+ tt_assert(err_msg);
+ tor_free(err_msg);
+
+ done:
+ return;
+}
+
+static int mock_get_options_calls = 0;
+static or_options_t *mock_options = NULL;
+
+static void
+reset_options(or_options_t *options, int *get_options_calls)
+{
+ memset(options, 0, sizeof(or_options_t));
+ options->TestingTorNetwork = 1;
+
+ *get_options_calls = 0;
+}
+
+static const or_options_t *
+mock_get_options(void)
+{
+ ++mock_get_options_calls;
+ tor_assert(mock_options);
+ return mock_options;
+}
+
+/* arg can't be 0 (the test fails) or 2 (the test is skipped) */
+#define CREATE_HS_DIR_NONE ((intptr_t)0x04)
+#define CREATE_HS_DIR1 ((intptr_t)0x08)
+#define CREATE_HS_DIR2 ((intptr_t)0x10)
+
+/* Test that single onion poisoning works. */
+static void
+test_single_onion_poisoning(void *arg)
+{
+ or_options_t opt;
+ mock_options = &opt;
+ reset_options(mock_options, &mock_get_options_calls);
+ MOCK(get_options, mock_get_options);
+
+ int ret = -1;
+ intptr_t create_dir_mask = (intptr_t)arg;
+ /* Get directories with a random suffix so we can repeat the tests */
+ mock_options->DataDirectory = tor_strdup(get_fname_rnd("test_data_dir"));
+ rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t));
+ char *dir1 = tor_strdup(get_fname_rnd("test_hs_dir1"));
+ rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t));
+ char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2"));
+ smartlist_t *services = smartlist_new();
+ char *poison_path = 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);
+ if (create_dir_mask & CREATE_HS_DIR1) {
+ ret = check_private_dir(dir1, CPD_CREATE, NULL);
+ tt_assert(ret == 0);
+ }
+ if (create_dir_mask & CREATE_HS_DIR2) {
+ ret = check_private_dir(dir2, CPD_CREATE, NULL);
+ tt_assert(ret == 0);
+ }
+
+ service_1->directory = dir1;
+ service_2->directory = dir2;
+ /* The services own the directory pointers now */
+ dir1 = dir2 = NULL;
+ /* Add port to service 1 */
+ service_1->ports = smartlist_new();
+ service_2->ports = smartlist_new();
+ char *err_msg = NULL;
+ rend_service_port_config_t *port1 = rend_service_parse_port_config("80", " ",
+ &err_msg);
+ tt_assert(port1);
+ tt_assert(!err_msg);
+ 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);
+ 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 0);
+
+ /* Add the first service */
+ ret = rend_service_check_dir_and_add(services, mock_options, service_1, 0);
+ tt_assert(ret == 0);
+ /* 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ /* Poisoning twice is a no-op. */
+ ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 0);
+
+ /* Now add some keys, and we'll have a problem. */
+ ret = rend_service_load_all_keys(services);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ /* And it keeps the poison. */
+ ret = rend_service_verify_single_onion_poison(service_1, mock_options);
+ tt_assert(ret == 0);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+
+ /* 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+
+ /* 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
+ tt_assert(ret == 0);
+ /* And the old directory remains unpoisoned. */
+ ret = rend_service_verify_single_onion_poison(service_1, mock_options);
+ tt_assert(ret < 0);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 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);
+ ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
+ tt_assert(ret == 0);
+ /* And the old directory remains unpoisoned. */
+ ret = rend_service_verify_single_onion_poison(service_1, mock_options);
+ tt_assert(ret < 0);
+ ret = rend_service_verify_single_onion_poison(service_2, mock_options);
+ tt_assert(ret == 0);
+
+ done:
+ /* The test harness deletes the directories at exit */
+ tor_free(poison_path);
+ tor_free(dir1);
+ tor_free(dir2);
+ smartlist_free(services);
+ rend_service_free(service_1);
+ rend_service_free(service_2);
+ UNMOCK(get_options);
+ tor_free(mock_options->DataDirectory);
+}
+
struct testcase_t hs_tests[] = {
{ "hs_rend_data", test_hs_rend_data, TT_FORK,
NULL, NULL },
@@ -445,6 +788,16 @@ struct testcase_t hs_tests[] = {
{ "pick_bad_tor2web_rendezvous_node",
test_pick_bad_tor2web_rendezvous_node, TT_FORK,
NULL, NULL },
+ { "hs_auth_cookies", test_hs_auth_cookies, TT_FORK,
+ NULL, NULL },
+ { "single_onion_poisoning_create_dir_none", test_single_onion_poisoning,
+ TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR_NONE) },
+ { "single_onion_poisoning_create_dir1", test_single_onion_poisoning,
+ TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1) },
+ { "single_onion_poisoning_create_dir2", test_single_onion_poisoning,
+ TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR2) },
+ { "single_onion_poisoning_create_dir_both", test_single_onion_poisoning,
+ TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1 | CREATE_HS_DIR2) },
END_OF_TESTCASES
};
diff --git a/src/test/test_introduce.c b/src/test/test_introduce.c
index 9c7a86da66..810b03c93d 100644
--- a/src/test/test_introduce.c
+++ b/src/test/test_introduce.c
@@ -9,8 +9,6 @@
#define RENDSERVICE_PRIVATE
#include "rendservice.h"
-extern const char AUTHORITY_SIGNKEY_1[];
-
static uint8_t v0_test_plaintext[] =
/* 20 bytes of rendezvous point nickname */
{ 0x4e, 0x69, 0x63, 0x6b, 0x6e, 0x61, 0x6d, 0x65,
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index e8856c60de..ddf66f4d34 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -15,8 +15,9 @@
#include "scheduler.h"
#include "test.h"
+#include "log_test_helpers.h"
-var_cell_t *mock_got_var_cell = NULL;
+static var_cell_t *mock_got_var_cell = NULL;
static void
mock_write_var_cell(const var_cell_t *vc, or_connection_t *conn)
@@ -65,6 +66,14 @@ mock_send_authenticate(or_connection_t *conn, int type)
return 0;
}
+static tor_x509_cert_t *mock_own_cert = NULL;
+static tor_x509_cert_t *
+mock_get_own_cert(tor_tls_t *tls)
+{
+ (void)tls;
+ return tor_x509_cert_dup(mock_own_cert);
+}
+
/* Test good certs cells */
static void
test_link_handshake_certs_ok(void *arg)
@@ -83,6 +92,7 @@ test_link_handshake_certs_ok(void *arg)
MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell);
MOCK(connection_or_send_netinfo, mock_send_netinfo);
+ MOCK(tor_tls_get_own_cert, mock_get_own_cert);
key1 = pk_generate(2);
key2 = pk_generate(3);
@@ -93,6 +103,12 @@ test_link_handshake_certs_ok(void *arg)
tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
key1, key2, 86400), ==, 0);
+ {
+ const tor_x509_cert_t *link_cert = NULL;
+ tt_assert(!tor_tls_get_my_certs(1, &link_cert, NULL));
+ mock_own_cert = tor_x509_cert_dup(link_cert);
+ }
+
c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
c1->link_proto = 3;
tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0);
@@ -173,6 +189,11 @@ test_link_handshake_certs_ok(void *arg)
UNMOCK(tor_tls_cert_matches_key);
UNMOCK(connection_or_write_var_cell_to_buf);
UNMOCK(connection_or_send_netinfo);
+ UNMOCK(tor_tls_get_own_cert);
+ tor_x509_cert_free(mock_own_cert);
+ mock_own_cert = NULL;
+ memset(c1->identity_digest, 0, sizeof(c1->identity_digest));
+ memset(c2->identity_digest, 0, sizeof(c2->identity_digest));
connection_free_(TO_CONN(c1));
connection_free_(TO_CONN(c2));
tor_free(cell1);
@@ -209,6 +230,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj)
if (d) {
tor_free(d->cell);
certs_cell_free(d->ccell);
+ connection_or_remove_from_identity_map(d->c);
connection_free_(TO_CONN(d->c));
circuitmux_free(d->chan->base_.cmux);
tor_free(d->chan);
@@ -332,30 +354,50 @@ test_link_handshake_recv_certs_ok_server(void *arg)
test_link_handshake_recv_certs_ ## name(void *arg) \
{ \
certs_data_t *d = arg; \
+ const char *require_failure_message = NULL; \
+ setup_capture_of_logs(LOG_INFO); \
{ code ; } \
channel_tls_process_certs_cell(d->cell, d->chan); \
tt_int_op(1, ==, mock_close_called); \
tt_int_op(0, ==, mock_send_authenticate_called); \
tt_int_op(0, ==, mock_send_netinfo_called); \
+ if (require_failure_message) { \
+ expect_log_msg_containing(require_failure_message); \
+ } \
done: \
- ; \
+ teardown_capture_of_logs(); \
}
-CERTS_FAIL(badstate, d->c->base_.state = OR_CONN_STATE_CONNECTING)
-CERTS_FAIL(badproto, d->c->link_proto = 2)
-CERTS_FAIL(duplicate, d->c->handshake_state->received_certs_cell = 1)
+CERTS_FAIL(badstate,
+ require_failure_message = "We're not doing a v3 handshake!";
+ d->c->base_.state = OR_CONN_STATE_CONNECTING;)
+CERTS_FAIL(badproto,
+ require_failure_message = "not using link protocol >= 3";
+ d->c->link_proto = 2)
+CERTS_FAIL(duplicate,
+ require_failure_message = "We already got one";
+ d->c->handshake_state->received_certs_cell = 1)
CERTS_FAIL(already_authenticated,
+ require_failure_message = "We're already authenticated!";
d->c->handshake_state->authenticated = 1)
-CERTS_FAIL(empty, d->cell->payload_len = 0)
-CERTS_FAIL(bad_circid, d->cell->circ_id = 1)
-CERTS_FAIL(truncated_1, d->cell->payload[0] = 5)
+CERTS_FAIL(empty,
+ require_failure_message = "It had no body";
+ d->cell->payload_len = 0)
+CERTS_FAIL(bad_circid,
+ require_failure_message = "It had a nonzero circuit ID";
+ d->cell->circ_id = 1)
+CERTS_FAIL(truncated_1,
+ require_failure_message = "It couldn't be parsed";
+ d->cell->payload[0] = 5)
CERTS_FAIL(truncated_2,
{
+ require_failure_message = "It couldn't be parsed";
d->cell->payload_len = 4;
memcpy(d->cell->payload, "\x01\x01\x00\x05", 4);
})
CERTS_FAIL(truncated_3,
{
+ require_failure_message = "It couldn't be parsed";
d->cell->payload_len = 7;
memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7);
})
@@ -367,30 +409,35 @@ CERTS_FAIL(truncated_3,
CERTS_FAIL(not_x509,
{
+ require_failure_message = "Received undecodable certificate";
certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 0), 3);
certs_cell_get_certs(d->ccell, 0)->cert_len = 3;
REENCODE();
})
CERTS_FAIL(both_link,
{
+ require_failure_message = "Duplicate x509 certificate";
certs_cell_get_certs(d->ccell, 0)->cert_type = 1;
certs_cell_get_certs(d->ccell, 1)->cert_type = 1;
REENCODE();
})
CERTS_FAIL(both_id_rsa,
{
+ require_failure_message = "Duplicate x509 certificate";
certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
certs_cell_get_certs(d->ccell, 1)->cert_type = 2;
REENCODE();
})
CERTS_FAIL(both_auth,
{
+ require_failure_message = "Duplicate x509 certificate";
certs_cell_get_certs(d->ccell, 0)->cert_type = 3;
certs_cell_get_certs(d->ccell, 1)->cert_type = 3;
REENCODE();
})
CERTS_FAIL(wrong_labels_1,
{
+ require_failure_message = "The link certificate was not valid";
certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
certs_cell_get_certs(d->ccell, 1)->cert_type = 1;
REENCODE();
@@ -401,6 +448,7 @@ CERTS_FAIL(wrong_labels_2,
const tor_x509_cert_t *b;
const uint8_t *enca;
size_t lena;
+ require_failure_message = "The link certificate was not valid";
tor_tls_get_my_certs(1, &a, &b);
tor_x509_cert_get_der(a, &enca, &lena);
certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 1), lena);
@@ -411,16 +459,20 @@ CERTS_FAIL(wrong_labels_2,
})
CERTS_FAIL(wrong_labels_3,
{
+ require_failure_message = "The certs we wanted were missing";
certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
certs_cell_get_certs(d->ccell, 1)->cert_type = 3;
REENCODE();
})
CERTS_FAIL(server_missing_certs,
{
+ require_failure_message = "The certs we wanted were missing";
d->c->handshake_state->started_here = 0;
})
CERTS_FAIL(server_wrong_labels_1,
{
+ require_failure_message =
+ "The authentication certificate was not valid";
d->c->handshake_state->started_here = 0;
certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
certs_cell_get_certs(d->ccell, 1)->cert_type = 3;
@@ -579,38 +631,55 @@ test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg)
test_link_handshake_recv_authchallenge_ ## name(void *arg) \
{ \
authchallenge_data_t *d = arg; \
+ const char *require_failure_message = NULL; \
+ setup_capture_of_logs(LOG_INFO); \
{ code ; } \
channel_tls_process_auth_challenge_cell(d->cell, d->chan); \
tt_int_op(1, ==, mock_close_called); \
tt_int_op(0, ==, mock_send_authenticate_called); \
tt_int_op(0, ==, mock_send_netinfo_called); \
+ if (require_failure_message) { \
+ expect_log_msg_containing(require_failure_message); \
+ } \
done: \
- ; \
+ teardown_capture_of_logs(); \
}
AUTHCHALLENGE_FAIL(badstate,
+ require_failure_message = "We're not currently doing a "
+ "v3 handshake";
d->c->base_.state = OR_CONN_STATE_CONNECTING)
AUTHCHALLENGE_FAIL(badproto,
+ require_failure_message = "not using link protocol >= 3";
d->c->link_proto = 2)
AUTHCHALLENGE_FAIL(as_server,
+ require_failure_message = "We didn't originate this "
+ "connection";
d->c->handshake_state->started_here = 0;)
AUTHCHALLENGE_FAIL(duplicate,
+ require_failure_message = "We already received one";
d->c->handshake_state->received_auth_challenge = 1)
AUTHCHALLENGE_FAIL(nocerts,
+ require_failure_message = "We haven't gotten a CERTS "
+ "cell yet";
d->c->handshake_state->received_certs_cell = 0)
AUTHCHALLENGE_FAIL(tooshort,
+ require_failure_message = "It was not well-formed";
d->cell->payload_len = 33)
AUTHCHALLENGE_FAIL(truncated,
+ require_failure_message = "It was not well-formed";
d->cell->payload_len = 34)
AUTHCHALLENGE_FAIL(nonzero_circid,
+ require_failure_message = "It had a nonzero circuit ID";
d->cell->circ_id = 1337)
static tor_x509_cert_t *mock_peer_cert = NULL;
+
static tor_x509_cert_t *
mock_get_peer_cert(tor_tls_t *tls)
{
(void)tls;
- return mock_peer_cert;
+ return tor_x509_cert_dup(mock_peer_cert);
}
static int
@@ -644,12 +713,15 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg)
(void) test;
UNMOCK(connection_or_write_var_cell_to_buf);
UNMOCK(tor_tls_get_peer_cert);
+ UNMOCK(tor_tls_get_own_cert);
UNMOCK(tor_tls_get_tlssecrets);
UNMOCK(connection_or_close_for_error);
UNMOCK(channel_set_circid_type);
authenticate_data_t *d = arg;
if (d) {
tor_free(d->cell);
+ connection_or_remove_from_identity_map(d->c1);
+ connection_or_remove_from_identity_map(d->c2);
connection_free_(TO_CONN(d->c1));
connection_free_(TO_CONN(d->c2));
circuitmux_free(d->chan2->base_.cmux);
@@ -658,7 +730,10 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg)
crypto_pk_free(d->key2);
tor_free(d);
}
+ tor_x509_cert_free(mock_peer_cert);
+ tor_x509_cert_free(mock_own_cert);
mock_peer_cert = NULL;
+ mock_own_cert = NULL;
return 1;
}
@@ -672,11 +747,14 @@ authenticate_data_setup(const struct testcase_t *test)
MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell);
MOCK(tor_tls_get_peer_cert, mock_get_peer_cert);
+ MOCK(tor_tls_get_own_cert, mock_get_own_cert);
MOCK(tor_tls_get_tlssecrets, mock_get_tlssecrets);
MOCK(connection_or_close_for_error, mock_close_for_err);
MOCK(channel_set_circid_type, mock_set_circid_type);
d->c1 = or_connection_new(CONN_TYPE_OR, AF_INET);
d->c2 = or_connection_new(CONN_TYPE_OR, AF_INET);
+ tor_addr_from_ipv4h(&d->c1->base_.addr, 0x01020304);
+ tor_addr_from_ipv4h(&d->c2->base_.addr, 0x05060708);
d->key1 = pk_generate(2);
d->key2 = pk_generate(3);
@@ -719,6 +797,8 @@ authenticate_data_setup(const struct testcase_t *test)
tor_x509_cert_get_der(link_cert, &der, &sz);
mock_peer_cert = tor_x509_cert_decode(der, sz);
tt_assert(mock_peer_cert);
+ mock_own_cert = tor_x509_cert_decode(der, sz);
+ tt_assert(mock_own_cert);
tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert));
tor_x509_cert_get_der(auth_cert, &der, &sz);
d->c2->handshake_state->auth_cert = tor_x509_cert_decode(der, sz);
@@ -798,57 +878,83 @@ test_link_handshake_auth_cell(void *arg)
test_link_handshake_auth_ ## name(void *arg) \
{ \
authenticate_data_t *d = arg; \
+ const char *require_failure_message = NULL; \
+ setup_capture_of_logs(LOG_INFO); \
{ code ; } \
tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \
channel_tls_process_authenticate_cell(d->cell, d->chan2); \
tt_int_op(mock_close_called, ==, 1); \
tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \
- done: \
- ; \
+ if (require_failure_message) { \
+ expect_log_msg_containing(require_failure_message); \
+ } \
+ done: \
+ teardown_capture_of_logs(); \
}
AUTHENTICATE_FAIL(badstate,
+ require_failure_message = "We're not doing a v3 handshake";
d->c2->base_.state = OR_CONN_STATE_CONNECTING)
AUTHENTICATE_FAIL(badproto,
+ require_failure_message = "not using link protocol >= 3";
d->c2->link_proto = 2)
AUTHENTICATE_FAIL(atclient,
+ require_failure_message = "We originated this connection";
d->c2->handshake_state->started_here = 1)
AUTHENTICATE_FAIL(duplicate,
+ require_failure_message = "We already got one";
d->c2->handshake_state->received_authenticate = 1)
static void
test_link_handshake_auth_already_authenticated(void *arg)
{
authenticate_data_t *d = arg;
+ setup_capture_of_logs(LOG_INFO);
d->c2->handshake_state->authenticated = 1;
channel_tls_process_authenticate_cell(d->cell, d->chan2);
tt_int_op(mock_close_called, ==, 1);
tt_int_op(d->c2->handshake_state->authenticated, ==, 1);
+ expect_log_msg_containing("The peer is already authenticated");
done:
- ;
+ teardown_capture_of_logs();
}
+
AUTHENTICATE_FAIL(nocerts,
+ require_failure_message = "We never got a certs cell";
d->c2->handshake_state->received_certs_cell = 0)
AUTHENTICATE_FAIL(noidcert,
+ require_failure_message = "We never got an identity "
+ "certificate";
tor_x509_cert_free(d->c2->handshake_state->id_cert);
d->c2->handshake_state->id_cert = NULL)
AUTHENTICATE_FAIL(noauthcert,
+ require_failure_message = "We never got an authentication "
+ "certificate";
tor_x509_cert_free(d->c2->handshake_state->auth_cert);
d->c2->handshake_state->auth_cert = NULL)
AUTHENTICATE_FAIL(tooshort,
+ require_failure_message = "Cell was way too short";
d->cell->payload_len = 3)
AUTHENTICATE_FAIL(badtype,
+ require_failure_message = "Authenticator type was not "
+ "recognized";
d->cell->payload[0] = 0xff)
AUTHENTICATE_FAIL(truncated_1,
+ require_failure_message = "Authenticator was truncated";
d->cell->payload[2]++)
AUTHENTICATE_FAIL(truncated_2,
+ require_failure_message = "Authenticator was truncated";
d->cell->payload[3]++)
AUTHENTICATE_FAIL(tooshort_1,
+ require_failure_message = "Authenticator was too short";
tt_int_op(d->cell->payload_len, >=, 260);
d->cell->payload[2] -= 1;
d->cell->payload_len -= 256;)
AUTHENTICATE_FAIL(badcontent,
+ require_failure_message = "Some field in the AUTHENTICATE "
+ "cell body was not as expected";
d->cell->payload[10] ^= 0xff)
AUTHENTICATE_FAIL(badsig_1,
+ require_failure_message = "Signature wasn't valid";
d->cell->payload[d->cell->payload_len - 5] ^= 0xff)
#define TEST(name, flags) \
diff --git a/src/test/test_logging.c b/src/test/test_logging.c
index eb294fe6f8..15471e46d0 100644
--- a/src/test/test_logging.c
+++ b/src/test/test_logging.c
@@ -127,9 +127,47 @@ test_sigsafe_err(void *arg)
smartlist_free(lines);
}
+static void
+test_ratelim(void *arg)
+{
+ (void) arg;
+ ratelim_t ten_min = RATELIM_INIT(10*60);
+
+ const time_t start = 1466091600;
+ time_t now = start;
+ /* Initially, we're ready. */
+
+ char *msg = NULL;
+
+ msg = rate_limit_log(&ten_min, now);
+ tt_assert(msg != NULL);
+ tt_str_op(msg, OP_EQ, ""); /* nothing was suppressed. */
+
+ tt_int_op(ten_min.last_allowed, OP_EQ, now);
+ tor_free(msg);
+
+ int i;
+ for (i = 0; i < 9; ++i) {
+ now += 60; /* one minute has passed. */
+ msg = rate_limit_log(&ten_min, now);
+ tt_assert(msg == 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_str_op(msg, OP_EQ,
+ " [9 similar message(s) suppressed in last 600 seconds]");
+ done:
+ tor_free(msg);
+}
+
struct testcase_t logging_tests[] = {
{ "sigsafe_err_fds", test_get_sigsafe_err_fds, TT_FORK, NULL, NULL },
{ "sigsafe_err", test_sigsafe_err, TT_FORK, NULL, NULL },
+ { "ratelim", test_ratelim, 0, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index dbd1e5ac48..2ae605b8db 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -14,30 +14,11 @@
#include "test.h"
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#endif
-
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic push
-#endif
-/* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice.
- * Suppress the GCC warning so we can build with -Wredundant-decl. */
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#endif
-
+DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
-
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic pop
-#else
-#pragma GCC diagnostic warning "-Wredundant-decls"
-#endif
-#endif
+ENABLE_GCC_WARNING(redundant-decls)
#ifdef _WIN32
/* For mkdir() */
@@ -511,6 +492,11 @@ test_md_generate(void *arg)
routerinfo_free(ri);
}
+#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
+DISABLE_GCC_WARNING(overlength-strings)
+/* We allow huge string constants in the unit tests, but not in the code
+ * at large. */
+#endif
/* Taken at random from my ~/.tor/cached-microdescs file and then
* hand-munged */
static const char MD_PARSE_TEST_DATA[] =
@@ -666,6 +652,9 @@ static const char MD_PARSE_TEST_DATA[] =
"id rsa1024 2A8wYpHxnkKJ92orocvIQBzeHlE\n"
"p6 allow 80\n"
;
+#ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS
+ENABLE_GCC_WARNING(overlength-strings)
+#endif
/** More tests for parsing different kinds of microdescriptors, and getting
* invalid digests trackd from them. */
@@ -727,7 +716,7 @@ test_md_parse(void *arg)
tt_int_op(md->ipv6_orport, OP_EQ, 9090);
done:
- SMARTLIST_FOREACH(mds, microdesc_t *, md, microdesc_free(md));
+ SMARTLIST_FOREACH(mds, microdesc_t *, mdsc, microdesc_free(mdsc));
smartlist_free(mds);
SMARTLIST_FOREACH(invalid, char *, cp, tor_free(cp));
smartlist_free(invalid);
@@ -794,7 +783,8 @@ test_md_reject_cache(void *arg)
mc = get_microdesc_cache();
#define ADD(hex) \
do { \
- tt_int_op(0,OP_EQ,base16_decode(buf,sizeof(buf),hex,strlen(hex))); \
+ tt_int_op(sizeof(buf),OP_EQ,base16_decode(buf,sizeof(buf), \
+ hex,strlen(hex)));\
smartlist_add(wanted, tor_memdup(buf, DIGEST256_LEN)); \
} while (0)
diff --git a/src/test/test_ntor_cl.c b/src/test/test_ntor_cl.c
index 6df123162e..a560e5fc5e 100644
--- a/src/test/test_ntor_cl.c
+++ b/src/test/test_ntor_cl.c
@@ -21,7 +21,7 @@
} STMT_END
#define BASE16(idx, var, n) STMT_BEGIN { \
const char *s = argv[(idx)]; \
- if (base16_decode((char*)var, n, s, strlen(s)) < 0 ) { \
+ if (base16_decode((char*)var, n, s, strlen(s)) < (int)n ) { \
fprintf(stderr, "couldn't decode argument %d (%s)\n",idx,s); \
return 1; \
} \
@@ -153,7 +153,10 @@ main(int argc, char **argv)
if (argc < 2) {
fprintf(stderr, "I need arguments. Read source for more info.\n");
return 1;
- } else if (!strcmp(argv[1], "client1")) {
+ }
+
+ curve25519_init();
+ if (!strcmp(argv[1], "client1")) {
return client1(argc, argv);
} else if (!strcmp(argv[1], "server1")) {
return server1(argc, argv);
diff --git a/src/test/test_oom.c b/src/test/test_oom.c
index 2569b6e00f..6102af01f5 100644
--- a/src/test/test_oom.c
+++ b/src/test/test_oom.c
@@ -77,14 +77,14 @@ dummy_origin_circuit_new(int n_cells)
}
static void
-add_bytes_to_buf(generic_buffer_t *buf, size_t n_bytes)
+add_bytes_to_buf(buf_t *buf, size_t n_bytes)
{
char b[3000];
while (n_bytes) {
size_t this_add = n_bytes > sizeof(b) ? sizeof(b) : n_bytes;
crypto_rand(b, this_add);
- generic_buffer_add(buf, b, this_add);
+ write_to_buf(b, this_add, buf);
n_bytes -= this_add;
}
}
@@ -94,20 +94,15 @@ dummy_edge_conn_new(circuit_t *circ,
int type, size_t in_bytes, size_t out_bytes)
{
edge_connection_t *conn;
- generic_buffer_t *inbuf, *outbuf;
+ buf_t *inbuf, *outbuf;
if (type == CONN_TYPE_EXIT)
conn = edge_connection_new(type, AF_INET);
else
conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET));
-#ifdef USE_BUFFEREVENTS
- inbuf = bufferevent_get_input(TO_CONN(conn)->bufev);
- outbuf = bufferevent_get_output(TO_CONN(conn)->bufev);
-#else
inbuf = TO_CONN(conn)->inbuf;
outbuf = TO_CONN(conn)->outbuf;
-#endif
/* We add these bytes directly to the buffers, to avoid all the
* edge connection read/write machinery. */
@@ -134,10 +129,12 @@ test_oom_circbuf(void *arg)
{
or_options_t *options = get_options_mutable();
circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL;
- struct timeval tv = { 1389631048, 0 };
+ uint64_t now_ns = 1389631048 * (uint64_t)1000000000;
+ const uint64_t start_ns = now_ns;
(void) arg;
+ monotime_enable_test_mocking();
MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_);
/* Far too low for real life. */
@@ -150,11 +147,11 @@ test_oom_circbuf(void *arg)
/* Now we're going to fake up some circuits and get them added to the global
circuit list. */
- tv.tv_usec = 0;
- tor_gettimeofday_cache_set(&tv);
+ monotime_coarse_set_mock_time_nsec(now_ns);
c1 = dummy_origin_circuit_new(30);
- tv.tv_usec = 10*1000;
- tor_gettimeofday_cache_set(&tv);
+
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
c2 = dummy_or_circuit_new(20, 20);
tt_int_op(packed_cell_mem_cost(), OP_EQ,
@@ -163,15 +160,15 @@ test_oom_circbuf(void *arg)
packed_cell_mem_cost() * 70);
tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */
- tv.tv_usec = 20*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
c3 = dummy_or_circuit_new(100, 85);
tt_int_op(cell_queues_check_size(), OP_EQ, 0); /* We are still not OOM */
tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
packed_cell_mem_cost() * 255);
- tv.tv_usec = 30*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
/* Adding this cell will trigger our OOM handler. */
c4 = dummy_or_circuit_new(2, 0);
@@ -189,12 +186,12 @@ test_oom_circbuf(void *arg)
packed_cell_mem_cost() * (257 - 30));
circuit_free(c1);
- tv.tv_usec = 0;
- tor_gettimeofday_cache_set(&tv); /* go back in time */
+
+ monotime_coarse_set_mock_time_nsec(start_ns); /* go back in time */
c1 = dummy_or_circuit_new(90, 0);
- tv.tv_usec = 40*1000; /* go back to the future */
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
tt_int_op(cell_queues_check_size(), OP_EQ, 1); /* We are now OOM */
@@ -213,6 +210,7 @@ test_oom_circbuf(void *arg)
circuit_free(c4);
UNMOCK(circuit_mark_for_close_);
+ monotime_disable_test_mocking();
}
/** Run unit tests for buffers.c */
@@ -221,12 +219,14 @@ test_oom_streambuf(void *arg)
{
or_options_t *options = get_options_mutable();
circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL;
- struct timeval tv = { 1389641159, 0 };
uint32_t tvms;
int i;
smartlist_t *edgeconns = smartlist_new();
+ const uint64_t start_ns = 1389641159 * (uint64_t)1000000000;
+ uint64_t now_ns = start_ns;
(void) arg;
+ monotime_enable_test_mocking();
MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_);
@@ -238,54 +238,56 @@ test_oom_streambuf(void *arg)
tt_int_op(cell_queues_get_total_allocation(), OP_EQ, 0);
tt_int_op(buf_get_total_allocation(), OP_EQ, 0);
+ monotime_coarse_set_mock_time_nsec(start_ns);
+
/* Start all circuits with a bit of data queued in cells */
- tv.tv_usec = 500*1000; /* go halfway into the second. */
- tor_gettimeofday_cache_set(&tv);
+
+ /* go halfway into the second. */
+ monotime_coarse_set_mock_time_nsec(start_ns + 500 * 1000000);
c1 = dummy_or_circuit_new(10,10);
- tv.tv_usec = 510*1000;
- tor_gettimeofday_cache_set(&tv);
+
+ monotime_coarse_set_mock_time_nsec(start_ns + 510 * 1000000);
c2 = dummy_origin_circuit_new(20);
- tv.tv_usec = 520*1000;
- tor_gettimeofday_cache_set(&tv);
+ monotime_coarse_set_mock_time_nsec(start_ns + 520 * 1000000);
c3 = dummy_or_circuit_new(20,20);
- tv.tv_usec = 530*1000;
- tor_gettimeofday_cache_set(&tv);
+ monotime_coarse_set_mock_time_nsec(start_ns + 530 * 1000000);
c4 = dummy_or_circuit_new(0,0);
tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
packed_cell_mem_cost() * 80);
- tv.tv_usec = 600*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns = start_ns + 600 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
/* Add some connections to c1...c4. */
for (i = 0; i < 4; ++i) {
edge_connection_t *ec;
/* link it to a circuit */
- tv.tv_usec += 10*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
ec = dummy_edge_conn_new(c1, CONN_TYPE_EXIT, 1000, 1000);
tt_assert(ec);
smartlist_add(edgeconns, ec);
- tv.tv_usec += 10*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
ec = dummy_edge_conn_new(c2, CONN_TYPE_AP, 1000, 1000);
tt_assert(ec);
smartlist_add(edgeconns, ec);
- tv.tv_usec += 10*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); /* Yes, 4 twice*/
tt_assert(ec);
smartlist_add(edgeconns, ec);
- tv.tv_usec += 10*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 10 * 1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
smartlist_add(edgeconns, ec);
tt_assert(ec);
}
- tv.tv_sec += 1;
- tv.tv_usec = 0;
- tvms = (uint32_t) tv_to_msec(&tv);
+ now_ns -= now_ns % 1000000000;
+ now_ns += 1000000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
+ tvms = (uint32_t) monotime_coarse_absolute_msec();
tt_int_op(circuit_max_queued_cell_age(c1, tvms), OP_EQ, 500);
tt_int_op(circuit_max_queued_cell_age(c2, tvms), OP_EQ, 490);
@@ -309,9 +311,8 @@ test_oom_streambuf(void *arg)
/* Now give c4 a very old buffer of modest size */
{
edge_connection_t *ec;
- tv.tv_sec -= 1;
- tv.tv_usec = 0;
- tor_gettimeofday_cache_set(&tv);
+ now_ns -= 1000000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
tt_assert(ec);
smartlist_add(edgeconns, ec);
@@ -322,8 +323,8 @@ test_oom_streambuf(void *arg)
tt_int_op(cell_queues_check_size(), OP_EQ, 0);
/* And run over the limit. */
- tv.tv_usec = 800*1000;
- tor_gettimeofday_cache_set(&tv);
+ now_ns += 800*1000000;
+ monotime_coarse_set_mock_time_nsec(now_ns);
c5 = dummy_or_circuit_new(0,5);
tt_int_op(cell_queues_get_total_allocation(), OP_EQ,
@@ -355,6 +356,7 @@ test_oom_streambuf(void *arg)
smartlist_free(edgeconns);
UNMOCK(circuit_mark_for_close_);
+ monotime_disable_test_mocking();
}
struct testcase_t oom_tests[] = {
diff --git a/src/test/test_oos.c b/src/test/test_oos.c
new file mode 100644
index 0000000000..db06625116
--- /dev/null
+++ b/src/test/test_oos.c
@@ -0,0 +1,456 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/* Unit tests for OOS handler */
+
+#define CONNECTION_PRIVATE
+
+#include "or.h"
+#include "config.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "main.h"
+#include "test.h"
+
+static or_options_t mock_options;
+
+static void
+reset_options_mock(void)
+{
+ memset(&mock_options, 0, sizeof(or_options_t));
+}
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mock_options;
+}
+
+static int moribund_calls = 0;
+static int moribund_conns = 0;
+
+static int
+mock_connection_count_moribund(void)
+{
+ ++moribund_calls;
+
+ return moribund_conns;
+}
+
+/*
+ * For unit test purposes it's sufficient to tell that
+ * kill_conn_list_for_oos() was called with an approximately
+ * sane argument; it's just the thing we returned from the
+ * mock for pick_oos_victims().
+ */
+
+static int kill_conn_list_calls = 0;
+static int kill_conn_list_killed = 0;
+
+static void
+kill_conn_list_mock(smartlist_t *conns)
+{
+ ++kill_conn_list_calls;
+
+ tt_assert(conns != NULL);
+
+ kill_conn_list_killed += smartlist_len(conns);
+
+ done:
+ return;
+}
+
+static int pick_oos_mock_calls = 0;
+static int pick_oos_mock_fail = 0;
+static int pick_oos_mock_last_n = 0;
+
+static smartlist_t *
+pick_oos_victims_mock(int n)
+{
+ smartlist_t *l = NULL;
+ int i;
+
+ ++pick_oos_mock_calls;
+
+ tt_int_op(n, OP_GT, 0);
+
+ if (!pick_oos_mock_fail) {
+ /*
+ * connection_check_oos() just passes the list onto
+ * kill_conn_list_for_oos(); we don't need to simulate
+ * its content for this mock, just its existence, but
+ * we do need to check the parameter.
+ */
+ l = smartlist_new();
+ for (i = 0; i < n; ++i) smartlist_add(l, NULL);
+ } else {
+ l = NULL;
+ }
+
+ pick_oos_mock_last_n = n;
+
+ done:
+ return l;
+}
+
+/** Unit test for the logic in connection_check_oos(), which is concerned
+ * with comparing thresholds and connection counts to decide if an OOS has
+ * occurred and if so, how many connections to try to kill, and then using
+ * pick_oos_victims() and kill_conn_list_for_oos() to carry out its grim
+ * duty.
+ */
+static void
+test_oos_connection_check_oos(void *arg)
+{
+ (void)arg;
+
+ /* Set up mocks */
+ reset_options_mock();
+ /* OOS handling is only sensitive to these fields */
+ mock_options.ConnLimit = 32;
+ mock_options.ConnLimit_ = 64;
+ mock_options.ConnLimit_high_thresh = 60;
+ mock_options.ConnLimit_low_thresh = 50;
+ MOCK(get_options, mock_get_options);
+ moribund_calls = 0;
+ moribund_conns = 0;
+ MOCK(connection_count_moribund, mock_connection_count_moribund);
+ kill_conn_list_calls = 0;
+ kill_conn_list_killed = 0;
+ MOCK(kill_conn_list_for_oos, kill_conn_list_mock);
+ pick_oos_mock_calls = 0;
+ pick_oos_mock_fail = 0;
+ MOCK(pick_oos_victims, pick_oos_victims_mock);
+
+ /* No OOS case */
+ connection_check_oos(50, 0);
+ tt_int_op(moribund_calls, OP_EQ, 0);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 0);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 0);
+
+ /* OOS from socket count, nothing moribund */
+ connection_check_oos(62, 0);
+ tt_int_op(moribund_calls, OP_EQ, 1);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 1);
+ /* 12 == 62 - ConnLimit_low_thresh */
+ tt_int_op(pick_oos_mock_last_n, OP_EQ, 12);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 1);
+ tt_int_op(kill_conn_list_killed, OP_EQ, 12);
+
+ /* OOS from socket count, some are moribund */
+ kill_conn_list_killed = 0;
+ moribund_conns = 5;
+ connection_check_oos(62, 0);
+ tt_int_op(moribund_calls, OP_EQ, 2);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 2);
+ /* 7 == 62 - ConnLimit_low_thresh - moribund_conns */
+ tt_int_op(pick_oos_mock_last_n, OP_EQ, 7);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 2);
+ tt_int_op(kill_conn_list_killed, OP_EQ, 7);
+
+ /* OOS from socket count, but pick fails */
+ kill_conn_list_killed = 0;
+ moribund_conns = 0;
+ pick_oos_mock_fail = 1;
+ connection_check_oos(62, 0);
+ tt_int_op(moribund_calls, OP_EQ, 3);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 3);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 2);
+ tt_int_op(kill_conn_list_killed, OP_EQ, 0);
+ pick_oos_mock_fail = 0;
+
+ /*
+ * OOS from socket count with so many moribund conns
+ * we have none to kill.
+ */
+ kill_conn_list_killed = 0;
+ moribund_conns = 15;
+ connection_check_oos(62, 0);
+ tt_int_op(moribund_calls, OP_EQ, 4);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 3);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 2);
+
+ /*
+ * OOS from socket exhaustion; OOS handler will try to
+ * kill 1/10 (5) of the connections.
+ */
+ kill_conn_list_killed = 0;
+ moribund_conns = 0;
+ connection_check_oos(50, 1);
+ tt_int_op(moribund_calls, OP_EQ, 5);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 4);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 3);
+ tt_int_op(kill_conn_list_killed, OP_EQ, 5);
+
+ /* OOS from socket exhaustion with moribund conns */
+ kill_conn_list_killed = 0;
+ moribund_conns = 2;
+ connection_check_oos(50, 1);
+ tt_int_op(moribund_calls, OP_EQ, 6);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 5);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 4);
+ tt_int_op(kill_conn_list_killed, OP_EQ, 3);
+
+ /* OOS from socket exhaustion with many moribund conns */
+ kill_conn_list_killed = 0;
+ moribund_conns = 7;
+ connection_check_oos(50, 1);
+ tt_int_op(moribund_calls, OP_EQ, 7);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 5);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 4);
+
+ /* OOS with both socket exhaustion and above-threshold */
+ kill_conn_list_killed = 0;
+ moribund_conns = 0;
+ connection_check_oos(62, 1);
+ tt_int_op(moribund_calls, OP_EQ, 8);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 6);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 5);
+ tt_int_op(kill_conn_list_killed, OP_EQ, 12);
+
+ /*
+ * OOS with both socket exhaustion and above-threshold with some
+ * moribund conns
+ */
+ kill_conn_list_killed = 0;
+ moribund_conns = 5;
+ connection_check_oos(62, 1);
+ tt_int_op(moribund_calls, OP_EQ, 9);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 7);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 6);
+ tt_int_op(kill_conn_list_killed, OP_EQ, 7);
+
+ /*
+ * OOS with both socket exhaustion and above-threshold with many
+ * moribund conns
+ */
+ kill_conn_list_killed = 0;
+ moribund_conns = 15;
+ connection_check_oos(62, 1);
+ tt_int_op(moribund_calls, OP_EQ, 10);
+ tt_int_op(pick_oos_mock_calls, OP_EQ, 7);
+ tt_int_op(kill_conn_list_calls, OP_EQ, 6);
+
+ done:
+
+ UNMOCK(pick_oos_victims);
+ UNMOCK(kill_conn_list_for_oos);
+ UNMOCK(connection_count_moribund);
+ UNMOCK(get_options);
+
+ return;
+}
+
+static int cfe_calls = 0;
+
+static void
+close_for_error_mock(or_connection_t *orconn, int flush)
+{
+ (void)flush;
+
+ tt_assert(orconn != NULL);
+ ++cfe_calls;
+
+ done:
+ return;
+}
+
+static int mark_calls = 0;
+
+static void
+mark_for_close_oos_mock(connection_t *conn,
+ int line, const char *file)
+{
+ (void)line;
+ (void)file;
+
+ tt_assert(conn != NULL);
+ ++mark_calls;
+
+ done:
+ return;
+}
+
+static void
+test_oos_kill_conn_list(void *arg)
+{
+ connection_t *c1, *c2;
+ or_connection_t *or_c1 = NULL;
+ dir_connection_t *dir_c2 = NULL;
+ smartlist_t *l = NULL;
+ (void)arg;
+
+ /* Set up mocks */
+ mark_calls = 0;
+ MOCK(connection_mark_for_close_internal_, mark_for_close_oos_mock);
+ cfe_calls = 0;
+ MOCK(connection_or_close_for_error, close_for_error_mock);
+
+ /* Make fake conns */
+ or_c1 = tor_malloc_zero(sizeof(*or_c1));
+ or_c1->base_.magic = OR_CONNECTION_MAGIC;
+ or_c1->base_.type = CONN_TYPE_OR;
+ c1 = TO_CONN(or_c1);
+ dir_c2 = tor_malloc_zero(sizeof(*dir_c2));
+ dir_c2->base_.magic = DIR_CONNECTION_MAGIC;
+ dir_c2->base_.type = CONN_TYPE_DIR;
+ dir_c2->base_.state = DIR_CONN_STATE_MIN_;
+ dir_c2->base_.purpose = DIR_PURPOSE_MIN_;
+ c2 = TO_CONN(dir_c2);
+
+ tt_assert(c1 != NULL);
+ tt_assert(c2 != NULL);
+
+ /* Make list */
+ l = smartlist_new();
+ smartlist_add(l, c1);
+ smartlist_add(l, c2);
+
+ /* Run kill_conn_list_for_oos() */
+ kill_conn_list_for_oos(l);
+
+ /* Check call counters */
+ tt_int_op(mark_calls, OP_EQ, 1);
+ tt_int_op(cfe_calls, OP_EQ, 1);
+
+ done:
+
+ UNMOCK(connection_or_close_for_error);
+ UNMOCK(connection_mark_for_close_internal_);
+
+ if (l) smartlist_free(l);
+ tor_free(or_c1);
+ tor_free(dir_c2);
+
+ return;
+}
+
+static smartlist_t *conns_for_mock = NULL;
+
+static smartlist_t *
+get_conns_mock(void)
+{
+ return conns_for_mock;
+}
+
+/*
+ * For this mock, we pretend all conns have either zero or one circuits,
+ * depending on if this appears on the list of things to say have a circuit.
+ */
+
+static smartlist_t *conns_with_circs = NULL;
+
+static int
+get_num_circuits_mock(or_connection_t *conn)
+{
+ int circs = 0;
+
+ tt_assert(conn != NULL);
+
+ if (conns_with_circs &&
+ smartlist_contains(conns_with_circs, TO_CONN(conn))) {
+ circs = 1;
+ }
+
+ done:
+ return circs;
+}
+
+static void
+test_oos_pick_oos_victims(void *arg)
+{
+ (void)arg;
+ or_connection_t *ortmp;
+ dir_connection_t *dirtmp;
+ smartlist_t *picked;
+
+ /* Set up mocks */
+ conns_for_mock = smartlist_new();
+ MOCK(get_connection_array, get_conns_mock);
+ conns_with_circs = smartlist_new();
+ MOCK(connection_or_get_num_circuits, get_num_circuits_mock);
+
+ /* Make some fake connections */
+ ortmp = tor_malloc_zero(sizeof(*ortmp));
+ ortmp->base_.magic = OR_CONNECTION_MAGIC;
+ ortmp->base_.type = CONN_TYPE_OR;
+ smartlist_add(conns_for_mock, TO_CONN(ortmp));
+ /* We'll pretend this one has a circuit too */
+ smartlist_add(conns_with_circs, TO_CONN(ortmp));
+ /* Next one */
+ ortmp = tor_malloc_zero(sizeof(*ortmp));
+ ortmp->base_.magic = OR_CONNECTION_MAGIC;
+ ortmp->base_.type = CONN_TYPE_OR;
+ smartlist_add(conns_for_mock, TO_CONN(ortmp));
+ /* Next one is moribund */
+ ortmp = tor_malloc_zero(sizeof(*ortmp));
+ ortmp->base_.magic = OR_CONNECTION_MAGIC;
+ ortmp->base_.type = CONN_TYPE_OR;
+ ortmp->base_.marked_for_close = 1;
+ smartlist_add(conns_for_mock, TO_CONN(ortmp));
+ /* Last one isn't an orconn */
+ dirtmp = tor_malloc_zero(sizeof(*dirtmp));
+ dirtmp->base_.magic = DIR_CONNECTION_MAGIC;
+ dirtmp->base_.type = CONN_TYPE_DIR;
+ smartlist_add(conns_for_mock, TO_CONN(dirtmp));
+
+ /* Try picking one */
+ picked = pick_oos_victims(1);
+ /* It should be the one with circuits */
+ tt_assert(picked != NULL);
+ tt_int_op(smartlist_len(picked), OP_EQ, 1);
+ tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0)));
+ smartlist_free(picked);
+
+ /* Try picking none */
+ picked = pick_oos_victims(0);
+ /* We should get an empty list */
+ tt_assert(picked != NULL);
+ tt_int_op(smartlist_len(picked), OP_EQ, 0);
+ smartlist_free(picked);
+
+ /* Try picking two */
+ picked = pick_oos_victims(2);
+ /* We should get both active orconns */
+ tt_assert(picked != NULL);
+ tt_int_op(smartlist_len(picked), OP_EQ, 2);
+ tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0)));
+ tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1)));
+ smartlist_free(picked);
+
+ /* Try picking three - only two are eligible */
+ picked = pick_oos_victims(3);
+ tt_int_op(smartlist_len(picked), OP_EQ, 2);
+ tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 0)));
+ tt_assert(smartlist_contains(picked, smartlist_get(conns_for_mock, 1)));
+ smartlist_free(picked);
+
+ done:
+
+ /* Free leftover stuff */
+ if (conns_with_circs) {
+ smartlist_free(conns_with_circs);
+ conns_with_circs = NULL;
+ }
+
+ UNMOCK(connection_or_get_num_circuits);
+
+ if (conns_for_mock) {
+ SMARTLIST_FOREACH(conns_for_mock, connection_t *, c, tor_free(c));
+ smartlist_free(conns_for_mock);
+ conns_for_mock = NULL;
+ }
+
+ UNMOCK(get_connection_array);
+
+ return;
+}
+
+struct testcase_t oos_tests[] = {
+ { "connection_check_oos", test_oos_connection_check_oos,
+ TT_FORK, NULL, NULL },
+ { "kill_conn_list", test_oos_kill_conn_list, TT_FORK, NULL, NULL },
+ { "pick_oos_victims", test_oos_pick_oos_victims, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 4f24757a85..e85e11805b 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -12,7 +12,7 @@
#define ROUTERSET_PRIVATE
#include "routerset.h"
-
+#include "main.h"
#include "log_test_helpers.h"
#include "sandbox.h"
@@ -121,7 +121,7 @@ test_options_validate_impl(const char *configuration,
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ r = config_assign(&options_format, opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
r = options_validate(NULL, opt, dflt, 0, &msg);
@@ -223,7 +223,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ r = config_assign(&options_format, opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 300 MB RAM available, DirCache enabled */
@@ -246,7 +246,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ r = config_assign(&options_format, opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 300 MB RAM available, DirCache enabled, Bridge */
@@ -269,7 +269,7 @@ test_have_enough_mem_for_dircache(void *arg)
r = config_get_lines(configuration, &cl, 1);
tt_int_op(r, OP_EQ, 0);
- r = config_assign(&options_format, opt, cl, 0, 0, &msg);
+ r = config_assign(&options_format, opt, cl, 0, &msg);
tt_int_op(r, OP_EQ, 0);
/* 200 MB RAM available, DirCache disabled */
@@ -354,7 +354,7 @@ get_options_test_data(const char *conf)
result->def_opt = options_new();
rv = config_get_lines(conf, &cl, 1);
tt_assert(rv == 0);
- rv = config_assign(&options_format, result->opt, cl, 0, 0, &msg);
+ 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, "");
@@ -365,7 +365,7 @@ get_options_test_data(const char *conf)
result->opt->TokenBucketRefillInterval = 1;
rv = config_get_lines(TEST_OPTIONS_OLD_VALUES, &cl, 1);
tt_assert(rv == 0);
- rv = config_assign(&options_format, result->def_opt, cl, 0, 0, &msg);
+ 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, "");
@@ -394,14 +394,6 @@ free_options_test_data(options_test_data_t *td)
tor_free(td);
}
-#define expect_log_msg(str) \
- tt_assert_msg(mock_saved_log_has_message(str), \
- "expected log to contain " # str);
-
-#define expect_no_log_msg(str) \
- tt_assert_msg(!mock_saved_log_has_message(str), \
- "expected log to not contain " # str);
-
static void
test_options_validate__uname_for_server(void *ignored)
{
@@ -409,7 +401,7 @@ test_options_validate__uname_for_server(void *ignored)
char *msg;
options_test_data_t *tdata = get_options_test_data(
"ORListenAddress 127.0.0.1:5555");
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
MOCK(get_uname, fixed_get_uname);
fixed_get_uname_result = "Windows 95";
@@ -445,7 +437,7 @@ test_options_validate__uname_for_server(void *ignored)
UNMOCK(get_uname);
free_options_test_data(tdata);
tor_free(msg);
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
}
static void
@@ -513,8 +505,9 @@ test_options_validate__nickname(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,
- "Nickname 'ThisNickNameIsABitTooLong' is wrong length or"
- " contains illegal characters.");
+ "Nickname 'ThisNickNameIsABitTooLong', nicknames must be between "
+ "1 and 19 characters inclusive, and must contain only the "
+ "characters [a-zA-Z0-9].");
tor_free(msg);
free_options_test_data(tdata);
@@ -542,7 +535,7 @@ test_options_validate__contactinfo(void *ignored)
char *msg;
options_test_data_t *tdata = get_options_test_data(
"ORListenAddress 127.0.0.1:5555\nORPort 955");
- int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ setup_capture_of_logs(LOG_DEBUG);
tdata->opt->ContactInfo = NULL;
ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
@@ -566,13 +559,11 @@ test_options_validate__contactinfo(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
-extern int quiet_level;
-
static void
test_options_validate__logs(void *ignored)
{
@@ -656,7 +647,7 @@ test_options_validate__authdir(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_INFO);
+ setup_capture_of_logs(LOG_INFO);
options_test_data_t *tdata = get_options_test_data(
"AuthoritativeDirectory 1\n"
"Address this.should.not_exist.example.org");
@@ -949,7 +940,7 @@ test_options_validate__authdir(void *ignored)
/* "but ClientOnly also set."); */
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
// sandbox_free_getaddrinfo_cache();
free_options_test_data(tdata);
tor_free(msg);
@@ -960,7 +951,7 @@ test_options_validate__relay_with_hidden_services(void *ignored)
{
(void)ignored;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ setup_capture_of_logs(LOG_DEBUG);
options_test_data_t *tdata = get_options_test_data(
"ORListenAddress 127.0.0.1:5555\n"
"ORPort 955\n"
@@ -977,7 +968,7 @@ test_options_validate__relay_with_hidden_services(void *ignored)
"https://trac.torproject.org/8742\n");
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -990,7 +981,7 @@ test_options_validate__relay_with_hidden_services(void *ignored)
/* (void)ignored; */
/* int ret; */
/* char *msg; */
-/* int previous_log = setup_capture_of_logs(LOG_WARN); */
+/* setup_capture_of_logs(LOG_WARN); */
/* options_test_data_t *tdata = get_options_test_data(""); */
/* ret = options_validate(tdata->old_opt, tdata->opt, */
/* tdata->def_opt, 0, &msg); */
@@ -999,7 +990,7 @@ test_options_validate__relay_with_hidden_services(void *ignored)
/* "configured. " */
/* " Tor will still run, but probably won't do anything.\n"); */
/* done: */
-/* teardown_capture_of_logs(previous_log); */
+/* teardown_capture_of_logs(); */
/* free_options_test_data(tdata); */
/* tor_free(msg); */
/* } */
@@ -1059,7 +1050,7 @@ test_options_validate__transproxy(void *ignored)
tt_int_op(ret, OP_EQ, -1);
#ifndef KERNEL_MAY_SUPPORT_IPFW
- tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specificand OS X/Darwin-specific "
+ tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specific and OS X/Darwin-specific "
"feature.");
#else
tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW);
@@ -1080,26 +1071,38 @@ test_options_validate__transproxy(void *ignored)
free_options_test_data(tdata);
tdata = NULL;
-#if defined(linux)
+#if defined(__linux__)
tdata = get_options_test_data("TransProxyType tproxy\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);
- tt_assert(!msg);
-#endif
-#if defined(__FreeBSD_kernel__) || defined( DARWIN )
+ if (msg) {
+ TT_DIE(("Expected NULL but got '%s'", msg));
+ }
+#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);
- tt_assert(!msg);
-#endif
-#if defined(__OpenBSD__)
+ if (msg) {
+ TT_DIE(("Expected NULL but got '%s'", 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);
- tt_assert(!msg);
+ if (msg) {
+ TT_DIE(("Expected NULL but got '%s'", 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
// Assert that a test has run for some TransProxyType
@@ -1140,7 +1143,7 @@ test_options_validate__exclude_nodes(void *ignored)
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
options_test_data_t *tdata = get_options_test_data(
"ExcludeExitNodes {us}\n");
@@ -1205,7 +1208,7 @@ test_options_validate__exclude_nodes(void *ignored)
done:
NS_UNMOCK(geoip_get_country);
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -1216,7 +1219,7 @@ test_options_validate__scheduler(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ setup_capture_of_logs(LOG_DEBUG);
options_test_data_t *tdata = get_options_test_data(
"SchedulerLowWaterMark__ 0\n");
@@ -1248,7 +1251,7 @@ test_options_validate__scheduler(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -1311,7 +1314,7 @@ test_options_validate__tlsec(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ setup_capture_of_logs(LOG_DEBUG);
options_test_data_t *tdata = get_options_test_data(
"TLSECGroup ed25519\n"
"SchedulerHighWaterMark__ 42\n"
@@ -1348,7 +1351,7 @@ test_options_validate__tlsec(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -1386,7 +1389,7 @@ test_options_validate__recommended_packages(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ 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"
@@ -1400,7 +1403,7 @@ test_options_validate__recommended_packages(void *ignored)
done:
escaped(NULL); // This will free the leaking memory from the previous escaped
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -1478,7 +1481,7 @@ test_options_validate__paths_needed(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
options_test_data_t *tdata = get_options_test_data(
"PathsNeededToBuildCircuits 0.1\n"
"ConnLimit 1\n"
@@ -1523,7 +1526,7 @@ test_options_validate__paths_needed(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -1660,7 +1663,7 @@ test_options_validate__reachable_addresses(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_NOTICE);
+ setup_capture_of_logs(LOG_NOTICE);
options_test_data_t *tdata = get_options_test_data(
"FascistFirewall 1\n"
"MaxClientCircuitsPending 1\n"
@@ -1874,7 +1877,7 @@ test_options_validate__reachable_addresses(void *ignored)
tt_ptr_op(msg, OP_EQ, NULL);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -2124,7 +2127,7 @@ test_options_validate__publish_server_descriptor(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
options_test_data_t *tdata = get_options_test_data(
"PublishServerDescriptor bridge\n" TEST_OPTIONS_DEFAULT_VALUES
);
@@ -2188,7 +2191,7 @@ test_options_validate__publish_server_descriptor(void *ignored)
tt_assert(!tdata->opt->DirPort_set);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
policies_free_all();
free_options_test_data(tdata);
tor_free(msg);
@@ -2275,7 +2278,7 @@ test_options_validate__hidserv(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
options_test_data_t *tdata = get_options_test_data(
TEST_OPTIONS_DEFAULT_VALUES);
@@ -2310,7 +2313,7 @@ test_options_validate__hidserv(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
policies_free_all();
free_options_test_data(tdata);
tor_free(msg);
@@ -2322,7 +2325,7 @@ test_options_validate__predicted_ports(void *ignored)
(void)ignored;
int ret;
char *msg;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
options_test_data_t *tdata = get_options_test_data(
"PredictedPortsRelevanceTime 100000000\n"
@@ -2334,7 +2337,7 @@ test_options_validate__predicted_ports(void *ignored)
tt_int_op(tdata->opt->PredictedPortsRelevanceTime, OP_EQ, 3600);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
policies_free_all();
free_options_test_data(tdata);
tor_free(msg);
@@ -2548,7 +2551,7 @@ test_options_validate__circuits(void *ignored)
(void)ignored;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -2625,7 +2628,7 @@ test_options_validate__circuits(void *ignored)
done:
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -2698,7 +2701,7 @@ test_options_validate__rend(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -2753,7 +2756,156 @@ test_options_validate__rend(void *ignored)
done:
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
+test_options_validate__single_onion(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ setup_capture_of_logs(LOG_WARN);
+
+ /* Test that HiddenServiceSingleHopMode must come with
+ * HiddenServiceNonAnonymousMode */
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\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, "HiddenServiceSingleHopMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceNonAnonymousMode set to 1.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 0\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, "HiddenServiceSingleHopMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceNonAnonymousMode set to 1.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ /* Test that SOCKSPort must come with Tor2webMode if
+ * HiddenServiceSingleHopMode is 1 */
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 5000\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ "Tor2webMode 0\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, "HiddenServiceNonAnonymousMode is incompatible with "
+ "using Tor as an anonymous client. Please set "
+ "Socks/Trans/NATD/DNSPort to 0, or HiddenServiceNonAnonymousMode "
+ "to 0, or use the non-anonymous Tor2webMode.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ "Tor2webMode 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 5000\n"
+ "HiddenServiceSingleHopMode 0\n"
+ "Tor2webMode 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 5000\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ "Tor2webMode 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ /* Test that a hidden service can't be run with Tor2web
+ * Use HiddenServiceNonAnonymousMode instead of Tor2webMode, because
+ * Tor2webMode requires a compilation #define */
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceNonAnonymousMode 1\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\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, "HiddenServiceNonAnonymousMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceSingleHopMode set to 1.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceNonAnonymousMode 1\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, "HiddenServiceNonAnonymousMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceSingleHopMode set to 1.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceNonAnonymousMode 1\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "SOCKSPort 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ done:
+ policies_free_all();
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -2765,7 +2917,7 @@ test_options_validate__accounting(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -2870,7 +3022,7 @@ test_options_validate__accounting(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
policies_free_all();
free_options_test_data(tdata);
tor_free(msg);
@@ -2884,7 +3036,7 @@ test_options_validate__proxy(void *ignored)
char *msg;
options_test_data_t *tdata = NULL;
sandbox_disable_getaddrinfo_cache();
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -3200,7 +3352,7 @@ test_options_validate__proxy(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
policies_free_all();
// sandbox_free_getaddrinfo_cache();
@@ -3214,7 +3366,7 @@ test_options_validate__control(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -3428,7 +3580,7 @@ test_options_validate__control(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
policies_free_all();
free_options_test_data(tdata);
tor_free(msg);
@@ -3441,7 +3593,7 @@ test_options_validate__families(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -3500,7 +3652,7 @@ test_options_validate__families(void *ignored)
tor_free(msg);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
policies_free_all();
free_options_test_data(tdata);
tor_free(msg);
@@ -3537,7 +3689,7 @@ test_options_validate__dir_auth(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -3612,7 +3764,7 @@ test_options_validate__dir_auth(void *ignored)
done:
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -3624,7 +3776,7 @@ test_options_validate__transport(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_NOTICE);
+ setup_capture_of_logs(LOG_NOTICE);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -3737,7 +3889,7 @@ test_options_validate__transport(void *ignored)
done:
escaped(NULL); // This will free the leaking memory from the previous escaped
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -3749,7 +3901,7 @@ test_options_validate__constrained_sockets(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -3820,7 +3972,7 @@ test_options_validate__constrained_sockets(void *ignored)
done:
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -3832,7 +3984,7 @@ test_options_validate__v3_auth(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -4039,7 +4191,7 @@ test_options_validate__v3_auth(void *ignored)
done:
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -4084,7 +4236,7 @@ test_options_validate__exits(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
free_options_test_data(tdata);
tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
@@ -4114,7 +4266,7 @@ test_options_validate__exits(void *ignored)
done:
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -4126,7 +4278,7 @@ test_options_validate__testing_options(void *ignored)
int ret;
char *msg;
options_test_data_t *tdata = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
#define TEST_TESTING_OPTION(name, low_val, high_val, err_low) \
STMT_BEGIN \
@@ -4282,7 +4434,7 @@ test_options_validate__testing_options(void *ignored)
done:
policies_free_all();
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
free_options_test_data(tdata);
tor_free(msg);
}
@@ -4380,6 +4532,7 @@ struct testcase_t options_tests[] = {
LOCAL_VALIDATE_TEST(port_forwarding),
LOCAL_VALIDATE_TEST(tor2web),
LOCAL_VALIDATE_TEST(rend),
+ LOCAL_VALIDATE_TEST(single_onion),
LOCAL_VALIDATE_TEST(accounting),
LOCAL_VALIDATE_TEST(proxy),
LOCAL_VALIDATE_TEST(control),
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index a939ebf54f..1ffdc2cd51 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -32,12 +32,14 @@ test_short_policy_parse(const char *input,
short_policy_free(short_policy);
}
-/** Helper: Parse the exit policy string in <b>policy_str</b>, and make sure
- * that policies_summarize() produces the string <b>expected_summary</b> from
- * it. */
+/** Helper: Parse the exit policy string in <b>policy_str</b> with
+ * <b>options</b>, and make sure that policies_summarize() produces the string
+ * <b>expected_summary</b> from it when called with family. */
static void
-test_policy_summary_helper(const char *policy_str,
- const char *expected_summary)
+test_policy_summary_helper_family_flags(const char *policy_str,
+ const char *expected_summary,
+ sa_family_t family,
+ exit_policy_parser_cfg_t options)
{
config_line_t line;
smartlist_t *policy = smartlist_new();
@@ -45,17 +47,17 @@ test_policy_summary_helper(const char *policy_str,
char *summary_after = NULL;
int r;
short_policy_t *short_policy = NULL;
+ int success = 0;
line.key = (char*)"foo";
line.value = (char *)policy_str;
line.next = NULL;
r = policies_parse_exit_policy(&line, &policy,
- EXIT_POLICY_IPV6_ENABLED |
- EXIT_POLICY_ADD_DEFAULT, NULL);
+ options, NULL);
tt_int_op(r,OP_EQ, 0);
- summary = policy_summarize(policy, AF_INET);
+ summary = policy_summarize(policy, family);
tt_assert(summary != NULL);
tt_str_op(summary,OP_EQ, expected_summary);
@@ -65,7 +67,12 @@ test_policy_summary_helper(const char *policy_str,
summary_after = write_short_policy(short_policy);
tt_str_op(summary,OP_EQ, summary_after);
+ success = 1;
done:
+ /* If we don't print the flags on failure, it's very hard to diagnose bugs */
+ if (!success)
+ TT_DECLARE("CTXT", ("\n IPv%d\n Options: %x\n Policy: %s",
+ family == AF_INET ? 4 : 6, options, policy_str));
tor_free(summary_after);
tor_free(summary);
if (policy)
@@ -73,6 +80,50 @@ test_policy_summary_helper(const char *policy_str,
short_policy_free(short_policy);
}
+/** Like test_policy_summary_helper_family_flags, but tries all the different
+ * flag combinations */
+static void
+test_policy_summary_helper_family(const char *policy_str,
+ const char *expected_summary,
+ sa_family_t family)
+{
+ for (exit_policy_parser_cfg_t opt = 0;
+ opt <= EXIT_POLICY_OPTION_ALL;
+ opt++) {
+ if (family == AF_INET6 && !(opt & EXIT_POLICY_IPV6_ENABLED))
+ /* Skip the test: IPv6 addresses need IPv6 enabled */
+ continue;
+
+ if (opt & EXIT_POLICY_REJECT_LOCAL_INTERFACES)
+ /* Skip the test: local interfaces are machine-specific */
+ continue;
+
+ test_policy_summary_helper_family_flags(policy_str, expected_summary,
+ family, opt);
+ }
+}
+
+/** Like test_policy_summary_helper_family, but uses expected_summary for
+ * both IPv4 and IPv6. */
+static void
+test_policy_summary_helper(const char *policy_str,
+ const char *expected_summary)
+{
+ test_policy_summary_helper_family(policy_str, expected_summary, AF_INET);
+ test_policy_summary_helper_family(policy_str, expected_summary, AF_INET6);
+}
+
+/** Like test_policy_summary_helper_family, but uses expected_summary4 for
+ * IPv4 and expected_summary6 for IPv6. */
+static void
+test_policy_summary_helper6(const char *policy_str,
+ const char *expected_summary4,
+ const char *expected_summary6)
+{
+ test_policy_summary_helper_family(policy_str, expected_summary4, AF_INET);
+ test_policy_summary_helper_family(policy_str, expected_summary6, AF_INET6);
+}
+
/** Run unit tests for generating summary lines of exit policies */
static void
test_policies_general(void *arg)
@@ -253,19 +304,21 @@ test_policies_general(void *arg)
tt_assert(!exit_policy_is_general_exit(policy10));
tt_assert(!exit_policy_is_general_exit(policy11));
- tt_assert(cmp_addr_policies(policy, policy2));
- tt_assert(cmp_addr_policies(policy, NULL));
- tt_assert(!cmp_addr_policies(policy2, policy2));
- tt_assert(!cmp_addr_policies(NULL, NULL));
-
- tt_assert(!policy_is_reject_star(policy2, AF_INET));
- tt_assert(policy_is_reject_star(policy, AF_INET));
- tt_assert(policy_is_reject_star(policy10, AF_INET));
- tt_assert(!policy_is_reject_star(policy10, AF_INET6));
- tt_assert(policy_is_reject_star(policy11, AF_INET));
- tt_assert(policy_is_reject_star(policy11, AF_INET6));
- tt_assert(policy_is_reject_star(NULL, AF_INET));
- tt_assert(policy_is_reject_star(NULL, AF_INET6));
+ tt_assert(!addr_policies_eq(policy, policy2));
+ tt_assert(!addr_policies_eq(policy, NULL));
+ tt_assert(addr_policies_eq(policy2, policy2));
+ tt_assert(addr_policies_eq(NULL, NULL));
+
+ tt_assert(!policy_is_reject_star(policy2, AF_INET, 1));
+ tt_assert(policy_is_reject_star(policy, AF_INET, 1));
+ tt_assert(policy_is_reject_star(policy10, AF_INET, 1));
+ tt_assert(!policy_is_reject_star(policy10, AF_INET6, 1));
+ tt_assert(policy_is_reject_star(policy11, AF_INET, 1));
+ tt_assert(policy_is_reject_star(policy11, AF_INET6, 1));
+ tt_assert(policy_is_reject_star(NULL, AF_INET, 1));
+ tt_assert(policy_is_reject_star(NULL, AF_INET6, 1));
+ tt_assert(!policy_is_reject_star(NULL, AF_INET, 0));
+ tt_assert(!policy_is_reject_star(NULL, AF_INET6, 0));
addr_policy_list_free(policy);
policy = NULL;
@@ -392,13 +445,14 @@ test_policies_general(void *arg)
"reject 14.0.0.0/9:80,"
"reject 15.0.0.0:81,"
"accept *:*", "accept 1-65535");
- test_policy_summary_helper("reject 11.0.0.0/9:80,"
- "reject 12.0.0.0/9:80,"
- "reject 13.0.0.0/9:80,"
- "reject 14.0.0.0/9:80,"
- "reject 15.0.0.0:80,"
- "accept *:*",
- "reject 80");
+ test_policy_summary_helper6("reject 11.0.0.0/9:80,"
+ "reject 12.0.0.0/9:80,"
+ "reject 13.0.0.0/9:80,"
+ "reject 14.0.0.0/9:80,"
+ "reject 15.0.0.0:80,"
+ "accept *:*",
+ "reject 80",
+ "accept 1-65535");
/* no exits */
test_policy_summary_helper("accept 11.0.0.0/9:80,"
"reject *:*",
@@ -429,6 +483,458 @@ test_policies_general(void *arg)
"reject *:7,"
"accept *:*",
"reject 1,3,5,7");
+ /* long policies */
+ /* standard long policy on many exits */
+ test_policy_summary_helper("accept *:20-23,"
+ "accept *:43,"
+ "accept *:53,"
+ "accept *:79-81,"
+ "accept *:88,"
+ "accept *:110,"
+ "accept *:143,"
+ "accept *:194,"
+ "accept *:220,"
+ "accept *:389,"
+ "accept *:443,"
+ "accept *:464,"
+ "accept *:531,"
+ "accept *:543-544,"
+ "accept *:554,"
+ "accept *:563,"
+ "accept *:636,"
+ "accept *:706,"
+ "accept *:749,"
+ "accept *:873,"
+ "accept *:902-904,"
+ "accept *:981,"
+ "accept *:989-995,"
+ "accept *:1194,"
+ "accept *:1220,"
+ "accept *:1293,"
+ "accept *:1500,"
+ "accept *:1533,"
+ "accept *:1677,"
+ "accept *:1723,"
+ "accept *:1755,"
+ "accept *:1863,"
+ "accept *:2082,"
+ "accept *:2083,"
+ "accept *:2086-2087,"
+ "accept *:2095-2096,"
+ "accept *:2102-2104,"
+ "accept *:3128,"
+ "accept *:3389,"
+ "accept *:3690,"
+ "accept *:4321,"
+ "accept *:4643,"
+ "accept *:5050,"
+ "accept *:5190,"
+ "accept *:5222-5223,"
+ "accept *:5228,"
+ "accept *:5900,"
+ "accept *:6660-6669,"
+ "accept *:6679,"
+ "accept *:6697,"
+ "accept *:8000,"
+ "accept *:8008,"
+ "accept *:8074,"
+ "accept *:8080,"
+ "accept *:8087-8088,"
+ "accept *:8332-8333,"
+ "accept *:8443,"
+ "accept *:8888,"
+ "accept *:9418,"
+ "accept *:9999,"
+ "accept *:10000,"
+ "accept *:11371,"
+ "accept *:12350,"
+ "accept *:19294,"
+ "accept *:19638,"
+ "accept *:23456,"
+ "accept *:33033,"
+ "accept *:64738,"
+ "reject *:*",
+ "accept 20-23,43,53,79-81,88,110,143,194,220,389,"
+ "443,464,531,543-544,554,563,636,706,749,873,"
+ "902-904,981,989-995,1194,1220,1293,1500,1533,"
+ "1677,1723,1755,1863,2082-2083,2086-2087,"
+ "2095-2096,2102-2104,3128,3389,3690,4321,4643,"
+ "5050,5190,5222-5223,5228,5900,6660-6669,6679,"
+ "6697,8000,8008,8074,8080,8087-8088,8332-8333,"
+ "8443,8888,9418,9999-10000,11371,12350,19294,"
+ "19638,23456,33033,64738");
+ /* short policy with configured addresses */
+ test_policy_summary_helper("reject 149.56.1.1:*,"
+ "reject [2607:5300:1:1::1:0]:*,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "accept 80,443");
+ /* short policy with configured and local interface addresses */
+ test_policy_summary_helper("reject 149.56.1.0:*,"
+ "reject 149.56.1.1:*,"
+ "reject 149.56.1.2:*,"
+ "reject 149.56.1.3:*,"
+ "reject 149.56.1.4:*,"
+ "reject 149.56.1.5:*,"
+ "reject 149.56.1.6:*,"
+ "reject 149.56.1.7:*,"
+ "reject [2607:5300:1:1::1:0]:*,"
+ "reject [2607:5300:1:1::1:1]:*,"
+ "reject [2607:5300:1:1::1:2]:*,"
+ "reject [2607:5300:1:1::1:3]:*,"
+ "reject [2607:5300:1:1::2:0]:*,"
+ "reject [2607:5300:1:1::2:1]:*,"
+ "reject [2607:5300:1:1::2:2]:*,"
+ "reject [2607:5300:1:1::2:3]:*,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "accept 80,443");
+ /* short policy with configured netblocks */
+ test_policy_summary_helper("reject 149.56.0.0/16,"
+ "reject6 2607:5300::/32,"
+ "reject6 2608:5300::/64,"
+ "reject6 2609:5300::/96,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "accept 80,443");
+ /* short policy with large netblocks that do not count as a rejection */
+ test_policy_summary_helper("reject 148.0.0.0/7,"
+ "reject6 2600::/16,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "accept 80,443");
+ /* short policy with large netblocks that count as a rejection */
+ test_policy_summary_helper("reject 148.0.0.0/6,"
+ "reject6 2600::/15,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "reject 1-65535");
+ /* short policy with huge netblocks that count as a rejection */
+ test_policy_summary_helper("reject 128.0.0.0/1,"
+ "reject6 8000::/1,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "reject 1-65535");
+ /* short policy which blocks everything using netblocks */
+ test_policy_summary_helper("reject 0.0.0.0/0,"
+ "reject6 ::/0,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "reject 1-65535");
+ /* short policy which has repeated redundant netblocks */
+ test_policy_summary_helper("reject 0.0.0.0/0,"
+ "reject 0.0.0.0/0,"
+ "reject 0.0.0.0/0,"
+ "reject 0.0.0.0/0,"
+ "reject 0.0.0.0/0,"
+ "reject6 ::/0,"
+ "reject6 ::/0,"
+ "reject6 ::/0,"
+ "reject6 ::/0,"
+ "reject6 ::/0,"
+ "accept *:80,"
+ "accept *:443,"
+ "reject *:*",
+ "reject 1-65535");
+
+ /* longest possible policy
+ * (1-2,4-5,... is longer, but gets reduced to 3,6,... )
+ * Going all the way to 65535 is incredibly slow, so we just go slightly
+ * more than the expected length */
+ test_policy_summary_helper("accept *:1,"
+ "accept *:3,"
+ "accept *:5,"
+ "accept *:7,"
+ "accept *:9,"
+ "accept *:11,"
+ "accept *:13,"
+ "accept *:15,"
+ "accept *:17,"
+ "accept *:19,"
+ "accept *:21,"
+ "accept *:23,"
+ "accept *:25,"
+ "accept *:27,"
+ "accept *:29,"
+ "accept *:31,"
+ "accept *:33,"
+ "accept *:35,"
+ "accept *:37,"
+ "accept *:39,"
+ "accept *:41,"
+ "accept *:43,"
+ "accept *:45,"
+ "accept *:47,"
+ "accept *:49,"
+ "accept *:51,"
+ "accept *:53,"
+ "accept *:55,"
+ "accept *:57,"
+ "accept *:59,"
+ "accept *:61,"
+ "accept *:63,"
+ "accept *:65,"
+ "accept *:67,"
+ "accept *:69,"
+ "accept *:71,"
+ "accept *:73,"
+ "accept *:75,"
+ "accept *:77,"
+ "accept *:79,"
+ "accept *:81,"
+ "accept *:83,"
+ "accept *:85,"
+ "accept *:87,"
+ "accept *:89,"
+ "accept *:91,"
+ "accept *:93,"
+ "accept *:95,"
+ "accept *:97,"
+ "accept *:99,"
+ "accept *:101,"
+ "accept *:103,"
+ "accept *:105,"
+ "accept *:107,"
+ "accept *:109,"
+ "accept *:111,"
+ "accept *:113,"
+ "accept *:115,"
+ "accept *:117,"
+ "accept *:119,"
+ "accept *:121,"
+ "accept *:123,"
+ "accept *:125,"
+ "accept *:127,"
+ "accept *:129,"
+ "accept *:131,"
+ "accept *:133,"
+ "accept *:135,"
+ "accept *:137,"
+ "accept *:139,"
+ "accept *:141,"
+ "accept *:143,"
+ "accept *:145,"
+ "accept *:147,"
+ "accept *:149,"
+ "accept *:151,"
+ "accept *:153,"
+ "accept *:155,"
+ "accept *:157,"
+ "accept *:159,"
+ "accept *:161,"
+ "accept *:163,"
+ "accept *:165,"
+ "accept *:167,"
+ "accept *:169,"
+ "accept *:171,"
+ "accept *:173,"
+ "accept *:175,"
+ "accept *:177,"
+ "accept *:179,"
+ "accept *:181,"
+ "accept *:183,"
+ "accept *:185,"
+ "accept *:187,"
+ "accept *:189,"
+ "accept *:191,"
+ "accept *:193,"
+ "accept *:195,"
+ "accept *:197,"
+ "accept *:199,"
+ "accept *:201,"
+ "accept *:203,"
+ "accept *:205,"
+ "accept *:207,"
+ "accept *:209,"
+ "accept *:211,"
+ "accept *:213,"
+ "accept *:215,"
+ "accept *:217,"
+ "accept *:219,"
+ "accept *:221,"
+ "accept *:223,"
+ "accept *:225,"
+ "accept *:227,"
+ "accept *:229,"
+ "accept *:231,"
+ "accept *:233,"
+ "accept *:235,"
+ "accept *:237,"
+ "accept *:239,"
+ "accept *:241,"
+ "accept *:243,"
+ "accept *:245,"
+ "accept *:247,"
+ "accept *:249,"
+ "accept *:251,"
+ "accept *:253,"
+ "accept *:255,"
+ "accept *:257,"
+ "accept *:259,"
+ "accept *:261,"
+ "accept *:263,"
+ "accept *:265,"
+ "accept *:267,"
+ "accept *:269,"
+ "accept *:271,"
+ "accept *:273,"
+ "accept *:275,"
+ "accept *:277,"
+ "accept *:279,"
+ "accept *:281,"
+ "accept *:283,"
+ "accept *:285,"
+ "accept *:287,"
+ "accept *:289,"
+ "accept *:291,"
+ "accept *:293,"
+ "accept *:295,"
+ "accept *:297,"
+ "accept *:299,"
+ "accept *:301,"
+ "accept *:303,"
+ "accept *:305,"
+ "accept *:307,"
+ "accept *:309,"
+ "accept *:311,"
+ "accept *:313,"
+ "accept *:315,"
+ "accept *:317,"
+ "accept *:319,"
+ "accept *:321,"
+ "accept *:323,"
+ "accept *:325,"
+ "accept *:327,"
+ "accept *:329,"
+ "accept *:331,"
+ "accept *:333,"
+ "accept *:335,"
+ "accept *:337,"
+ "accept *:339,"
+ "accept *:341,"
+ "accept *:343,"
+ "accept *:345,"
+ "accept *:347,"
+ "accept *:349,"
+ "accept *:351,"
+ "accept *:353,"
+ "accept *:355,"
+ "accept *:357,"
+ "accept *:359,"
+ "accept *:361,"
+ "accept *:363,"
+ "accept *:365,"
+ "accept *:367,"
+ "accept *:369,"
+ "accept *:371,"
+ "accept *:373,"
+ "accept *:375,"
+ "accept *:377,"
+ "accept *:379,"
+ "accept *:381,"
+ "accept *:383,"
+ "accept *:385,"
+ "accept *:387,"
+ "accept *:389,"
+ "accept *:391,"
+ "accept *:393,"
+ "accept *:395,"
+ "accept *:397,"
+ "accept *:399,"
+ "accept *:401,"
+ "accept *:403,"
+ "accept *:405,"
+ "accept *:407,"
+ "accept *:409,"
+ "accept *:411,"
+ "accept *:413,"
+ "accept *:415,"
+ "accept *:417,"
+ "accept *:419,"
+ "accept *:421,"
+ "accept *:423,"
+ "accept *:425,"
+ "accept *:427,"
+ "accept *:429,"
+ "accept *:431,"
+ "accept *:433,"
+ "accept *:435,"
+ "accept *:437,"
+ "accept *:439,"
+ "accept *:441,"
+ "accept *:443,"
+ "accept *:445,"
+ "accept *:447,"
+ "accept *:449,"
+ "accept *:451,"
+ "accept *:453,"
+ "accept *:455,"
+ "accept *:457,"
+ "accept *:459,"
+ "accept *:461,"
+ "accept *:463,"
+ "accept *:465,"
+ "accept *:467,"
+ "accept *:469,"
+ "accept *:471,"
+ "accept *:473,"
+ "accept *:475,"
+ "accept *:477,"
+ "accept *:479,"
+ "accept *:481,"
+ "accept *:483,"
+ "accept *:485,"
+ "accept *:487,"
+ "accept *:489,"
+ "accept *:491,"
+ "accept *:493,"
+ "accept *:495,"
+ "accept *:497,"
+ "accept *:499,"
+ "accept *:501,"
+ "accept *:503,"
+ "accept *:505,"
+ "accept *:507,"
+ "accept *:509,"
+ "accept *:511,"
+ "accept *:513,"
+ "accept *:515,"
+ "accept *:517,"
+ "accept *:519,"
+ "accept *:521,"
+ "accept *:523,"
+ "accept *:525,"
+ "accept *:527,"
+ "accept *:529,"
+ "reject *:*",
+ "accept 1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,"
+ "31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,"
+ "63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,"
+ "95,97,99,101,103,105,107,109,111,113,115,117,"
+ "119,121,123,125,127,129,131,133,135,137,139,141,"
+ "143,145,147,149,151,153,155,157,159,161,163,165,"
+ "167,169,171,173,175,177,179,181,183,185,187,189,"
+ "191,193,195,197,199,201,203,205,207,209,211,213,"
+ "215,217,219,221,223,225,227,229,231,233,235,237,"
+ "239,241,243,245,247,249,251,253,255,257,259,261,"
+ "263,265,267,269,271,273,275,277,279,281,283,285,"
+ "287,289,291,293,295,297,299,301,303,305,307,309,"
+ "311,313,315,317,319,321,323,325,327,329,331,333,"
+ "335,337,339,341,343,345,347,349,351,353,355,357,"
+ "359,361,363,365,367,369,371,373,375,377,379,381,"
+ "383,385,387,389,391,393,395,397,399,401,403,405,"
+ "407,409,411,413,415,417,419,421,423,425,427,429,"
+ "431,433,435,437,439,441,443,445,447,449,451,453,"
+ "455,457,459,461,463,465,467,469,471,473,475,477,"
+ "479,481,483,485,487,489,491,493,495,497,499,501,"
+ "503,505,507,509,511,513,515,517,519,521,523");
/* Short policies with unrecognized formats should get accepted. */
test_short_policy_parse("accept fred,2,3-5", "accept 2,3-5");
@@ -523,18 +1029,17 @@ test_policies_general(void *arg)
/* Test a too-long policy. */
{
- int i;
- char *policy = NULL;
+ char *policy_strng = NULL;
smartlist_t *chunks = smartlist_new();
smartlist_add(chunks, tor_strdup("accept "));
for (i=1; i<10000; ++i)
smartlist_add_asprintf(chunks, "%d,", i);
smartlist_add(chunks, tor_strdup("20000"));
- policy = smartlist_join_strings(chunks, "", 0, NULL);
+ policy_strng = smartlist_join_strings(chunks, "", 0, NULL);
SMARTLIST_FOREACH(chunks, char *, ch, tor_free(ch));
smartlist_free(chunks);
- short_parsed = parse_short_policy(policy);/* shouldn't be accepted */
- tor_free(policy);
+ short_parsed = parse_short_policy(policy_strng);/* shouldn't be accepted */
+ tor_free(policy_strng);
tt_ptr_op(NULL, OP_EQ, short_parsed);
}
@@ -778,8 +1283,8 @@ test_policies_reject_port_address(void *arg)
UNMOCK(get_configured_ports);
}
-smartlist_t *mock_ipv4_addrs = NULL;
-smartlist_t *mock_ipv6_addrs = NULL;
+static smartlist_t *mock_ipv4_addrs = NULL;
+static smartlist_t *mock_ipv6_addrs = NULL;
/* mock get_interface_address6_list, returning a deep copy of the template
* address list ipv4_interface_address_list or ipv6_interface_address_list */
@@ -804,7 +1309,7 @@ mock_get_interface_address6_list(int severity,
tt_assert(template_list);
SMARTLIST_FOREACH_BEGIN(template_list, tor_addr_t *, src_addr) {
- tor_addr_t *dest_addr = malloc(sizeof(tor_addr_t));
+ tor_addr_t *dest_addr = tor_malloc(sizeof(tor_addr_t));
memset(dest_addr, 0, sizeof(*dest_addr));
tor_addr_copy_tight(dest_addr, src_addr);
smartlist_add(clone_list, dest_addr);
@@ -1082,10 +1587,32 @@ test_policies_getinfo_helper_policies(void *arg)
append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*");
mock_options.IPv6Exit = 1;
- mock_options.ExitPolicyRejectPrivate = 1;
tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR);
tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR);
+ mock_options.ExitPolicyRejectPrivate = 1;
+ mock_options.ExitPolicyRejectLocalInterfaces = 1;
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ mock_options.ExitPolicyRejectPrivate = 1;
+ mock_options.ExitPolicyRejectLocalInterfaces = 0;
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) > 0);
+ tor_free(answer);
+
+ mock_options.ExitPolicyRejectPrivate = 0;
+ mock_options.ExitPolicyRejectLocalInterfaces = 1;
+
rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
&answer, &errmsg);
tt_assert(rv == 0);
@@ -1093,6 +1620,16 @@ test_policies_getinfo_helper_policies(void *arg)
tt_assert(strlen(answer) > 0);
tor_free(answer);
+ mock_options.ExitPolicyRejectPrivate = 0;
+ mock_options.ExitPolicyRejectLocalInterfaces = 0;
+
+ rv = getinfo_helper_policies(NULL, "exit-policy/reject-private/relay",
+ &answer, &errmsg);
+ tt_assert(rv == 0);
+ tt_assert(answer != NULL);
+ tt_assert(strlen(answer) == 0);
+ tor_free(answer);
+
rv = getinfo_helper_policies(NULL, "exit-policy/ipv4", &answer,
&errmsg);
tt_assert(rv == 0);
diff --git a/src/test/test_protover.c b/src/test/test_protover.c
new file mode 100644
index 0000000000..f00955d1b4
--- /dev/null
+++ b/src/test/test_protover.c
@@ -0,0 +1,195 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define PROTOVER_PRIVATE
+
+#include "orconfig.h"
+#include "test.h"
+
+#include "protover.h"
+
+static void
+test_protover_parse(void *arg)
+{
+ (void) arg;
+ char *re_encoded = NULL;
+
+ const char *orig = "Foo=1,3 Bar=3 Baz= Quux=9-12,14,15-16,900";
+ smartlist_t *elts = parse_protocol_list(orig);
+
+ tt_assert(elts);
+ tt_int_op(smartlist_len(elts), OP_EQ, 4);
+
+ const proto_entry_t *e;
+ const proto_range_t *r;
+ e = smartlist_get(elts, 0);
+ tt_str_op(e->name, OP_EQ, "Foo");
+ tt_int_op(smartlist_len(e->ranges), OP_EQ, 2);
+ {
+ r = smartlist_get(e->ranges, 0);
+ tt_int_op(r->low, OP_EQ, 1);
+ tt_int_op(r->high, OP_EQ, 1);
+
+ r = smartlist_get(e->ranges, 1);
+ tt_int_op(r->low, OP_EQ, 3);
+ tt_int_op(r->high, OP_EQ, 3);
+ }
+
+ e = smartlist_get(elts, 1);
+ tt_str_op(e->name, OP_EQ, "Bar");
+ tt_int_op(smartlist_len(e->ranges), OP_EQ, 1);
+ {
+ r = smartlist_get(e->ranges, 0);
+ tt_int_op(r->low, OP_EQ, 3);
+ tt_int_op(r->high, OP_EQ, 3);
+ }
+
+ e = smartlist_get(elts, 2);
+ tt_str_op(e->name, OP_EQ, "Baz");
+ tt_int_op(smartlist_len(e->ranges), OP_EQ, 0);
+
+ e = smartlist_get(elts, 3);
+ tt_str_op(e->name, OP_EQ, "Quux");
+ tt_int_op(smartlist_len(e->ranges), OP_EQ, 4);
+ {
+ r = smartlist_get(e->ranges, 0);
+ tt_int_op(r->low, OP_EQ, 9);
+ tt_int_op(r->high, OP_EQ, 12);
+
+ r = smartlist_get(e->ranges, 1);
+ tt_int_op(r->low, OP_EQ, 14);
+ tt_int_op(r->high, OP_EQ, 14);
+
+ r = smartlist_get(e->ranges, 2);
+ tt_int_op(r->low, OP_EQ, 15);
+ tt_int_op(r->high, OP_EQ, 16);
+
+ r = smartlist_get(e->ranges, 3);
+ tt_int_op(r->low, OP_EQ, 900);
+ tt_int_op(r->high, OP_EQ, 900);
+ }
+
+ re_encoded = encode_protocol_list(elts);
+ tt_assert(re_encoded);
+ tt_str_op(re_encoded, OP_EQ, orig);
+
+ done:
+ if (elts)
+ SMARTLIST_FOREACH(elts, proto_entry_t *, ent, proto_entry_free(ent));
+ smartlist_free(elts);
+ tor_free(re_encoded);
+}
+
+static void
+test_protover_parse_fail(void *arg)
+{
+ (void)arg;
+ smartlist_t *elts;
+
+ /* random junk */
+ elts = parse_protocol_list("!!3@*");
+ tt_assert(elts == NULL);
+
+ /* Missing equals sign in an entry */
+ elts = parse_protocol_list("Link=4 Haprauxymatyve Desc=9");
+ tt_assert(elts == NULL);
+
+ /* Missing word. */
+ elts = parse_protocol_list("Link=4 =3 Desc=9");
+ tt_assert(elts == NULL);
+
+ /* Broken numbers */
+ elts = parse_protocol_list("Link=fred");
+ tt_assert(elts == NULL);
+ elts = parse_protocol_list("Link=1,fred");
+ tt_assert(elts == NULL);
+ elts = parse_protocol_list("Link=1,fred,3");
+ tt_assert(elts == NULL);
+
+ /* Broken range */
+ elts = parse_protocol_list("Link=1,9-8,3");
+ tt_assert(elts == NULL);
+
+ done:
+ ;
+}
+
+static void
+test_protover_vote(void *arg)
+{
+ (void) arg;
+
+ smartlist_t *lst = smartlist_new();
+ char *result = protover_compute_vote(lst, 1);
+
+ tt_str_op(result, OP_EQ, "");
+ tor_free(result);
+
+ smartlist_add(lst, (void*) "Foo=1-10,500 Bar=1,3-7,8");
+ result = protover_compute_vote(lst, 1);
+ tt_str_op(result, OP_EQ, "Bar=1,3-8 Foo=1-10,500");
+ tor_free(result);
+
+ smartlist_add(lst, (void*) "Quux=123-456,78 Bar=2-6,8 Foo=9");
+ result = protover_compute_vote(lst, 1);
+ tt_str_op(result, OP_EQ, "Bar=1-8 Foo=1-10,500 Quux=78,123-456");
+ tor_free(result);
+
+ result = protover_compute_vote(lst, 2);
+ tt_str_op(result, OP_EQ, "Bar=3-6,8 Foo=9");
+ tor_free(result);
+
+ done:
+ tor_free(result);
+ smartlist_free(lst);
+}
+
+static void
+test_protover_all_supported(void *arg)
+{
+ (void)arg;
+ char *msg = NULL;
+
+ tt_assert(protover_all_supported(NULL, &msg));
+ tt_assert(msg == NULL);
+
+ tt_assert(protover_all_supported("", &msg));
+ tt_assert(msg == NULL);
+
+ // Some things that we do support
+ tt_assert(protover_all_supported("Link=3-4", &msg));
+ tt_assert(msg == NULL);
+ tt_assert(protover_all_supported("Link=3-4 Desc=2", &msg));
+ tt_assert(msg == NULL);
+
+ // Some things we don't support
+ tt_assert(! protover_all_supported("Wombat=9", &msg));
+ tt_str_op(msg, OP_EQ, "Wombat=9");
+ tor_free(msg);
+ tt_assert(! protover_all_supported("Link=999", &msg));
+ tt_str_op(msg, OP_EQ, "Link=999");
+ tor_free(msg);
+
+ // Mix of things we support and things we don't
+ tt_assert(! protover_all_supported("Link=3-4 Wombat=9", &msg));
+ tt_str_op(msg, OP_EQ, "Wombat=9");
+ tor_free(msg);
+ tt_assert(! protover_all_supported("Link=3-999", &msg));
+ tt_str_op(msg, OP_EQ, "Link=3-999");
+ tor_free(msg);
+
+ done:
+ tor_free(msg);
+}
+
+#define PV_TEST(name, flags) \
+ { #name, test_protover_ ##name, (flags), NULL, NULL }
+
+struct testcase_t protover_tests[] = {
+ PV_TEST(parse, 0),
+ PV_TEST(parse_fail, 0),
+ PV_TEST(vote, 0),
+ PV_TEST(all_supported, 0),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_pt.c b/src/test/test_pt.c
index ab8447dcd7..e5cdc5f3cd 100644
--- a/src/test/test_pt.c
+++ b/src/test/test_pt.c
@@ -107,12 +107,12 @@ test_pt_parsing(void *arg)
tt_assert(parse_smethod_line(line, mp) == 0);
tt_int_op(1, OP_EQ, smartlist_len(mp->transports));
{
- const transport_t *transport = smartlist_get(mp->transports, 0);
- tt_assert(transport);
- tt_str_op(transport->name, OP_EQ, "trebuchet");
- tt_int_op(transport->port, OP_EQ, 9999);
- tt_str_op(fmt_addr(&transport->addr), OP_EQ, "127.0.0.1");
- tt_str_op(transport->extra_info_args, OP_EQ,
+ const transport_t *transport_ = smartlist_get(mp->transports, 0);
+ tt_assert(transport_);
+ tt_str_op(transport_->name, OP_EQ, "trebuchet");
+ tt_int_op(transport_->port, OP_EQ, 9999);
+ tt_str_op(fmt_addr(&transport_->addr), OP_EQ, "127.0.0.1");
+ tt_str_op(transport_->extra_info_args, OP_EQ,
"counterweight=3,sling=snappy");
}
reset_mp(mp);
diff --git a/src/test/test_pubsub.c b/src/test/test_pubsub.c
new file mode 100644
index 0000000000..547d6c6b32
--- /dev/null
+++ b/src/test/test_pubsub.c
@@ -0,0 +1,85 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_pubsub.c
+ * \brief Unit tests for publish-subscribe abstraction.
+ **/
+
+#include "or.h"
+#include "test.h"
+#include "pubsub.h"
+
+DECLARE_PUBSUB_STRUCT_TYPES(foobar)
+DECLARE_PUBSUB_TOPIC(foobar)
+DECLARE_NOTIFY_PUBSUB_TOPIC(static, foobar)
+IMPLEMENT_PUBSUB_TOPIC(static, foobar)
+
+struct foobar_event_data_t {
+ unsigned u;
+ const char *s;
+};
+
+struct foobar_subscriber_data_t {
+ const char *name;
+ long l;
+};
+
+static int
+foobar_sub1(foobar_event_data_t *ev, foobar_subscriber_data_t *mine)
+{
+ ev->u += 10;
+ mine->l += 100;
+ return 0;
+}
+
+static int
+foobar_sub2(foobar_event_data_t *ev, foobar_subscriber_data_t *mine)
+{
+ ev->u += 5;
+ mine->l += 50;
+ return 0;
+}
+
+static void
+test_pubsub_basic(void *arg)
+{
+ (void)arg;
+ foobar_subscriber_data_t subdata1 = { "hi", 0 };
+ foobar_subscriber_data_t subdata2 = { "wow", 0 };
+ const foobar_subscriber_t *sub1;
+ const foobar_subscriber_t *sub2;
+ foobar_event_data_t ed = { 0, "x" };
+ foobar_event_data_t ed2 = { 0, "y" };
+ sub1 = foobar_subscribe(foobar_sub1, &subdata1, SUBSCRIBE_ATSTART, 100);
+ tt_assert(sub1);
+
+ foobar_notify(&ed, 0);
+ tt_int_op(subdata1.l, OP_EQ, 100);
+ tt_int_op(subdata2.l, OP_EQ, 0);
+ tt_int_op(ed.u, OP_EQ, 10);
+
+ sub2 = foobar_subscribe(foobar_sub2, &subdata2, 0, 5);
+ tt_assert(sub2);
+
+ foobar_notify(&ed2, 0);
+ tt_int_op(subdata1.l, OP_EQ, 200);
+ tt_int_op(subdata2.l, OP_EQ, 50);
+ tt_int_op(ed2.u, OP_EQ, 15);
+
+ foobar_unsubscribe(sub1);
+
+ foobar_notify(&ed, 0);
+ tt_int_op(subdata1.l, OP_EQ, 200);
+ tt_int_op(subdata2.l, OP_EQ, 100);
+ tt_int_op(ed.u, OP_EQ, 15);
+
+ done:
+ foobar_clear();
+}
+
+struct testcase_t pubsub_tests[] = {
+ { "pubsub_basic", test_pubsub_basic, TT_FORK, NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_relay.c b/src/test/test_relay.c
index a7fcad5401..4713c79ea5 100644
--- a/src/test/test_relay.c
+++ b/src/test/test_relay.c
@@ -74,6 +74,10 @@ test_relay_append_cell_to_circuit_queue(void *arg)
/* Make a fake orcirc */
orcirc = new_fake_orcirc(nchan, pchan);
tt_assert(orcirc);
+ circuitmux_attach_circuit(nchan->cmux, TO_CIRCUIT(orcirc),
+ CELL_DIRECTION_OUT);
+ circuitmux_attach_circuit(pchan->cmux, TO_CIRCUIT(orcirc),
+ CELL_DIRECTION_IN);
/* Make a cell */
cell = tor_malloc_zero(sizeof(cell_t));
@@ -109,8 +113,12 @@ test_relay_append_cell_to_circuit_queue(void *arg)
done:
tor_free(cell);
- cell_queue_clear(&orcirc->base_.n_chan_cells);
- cell_queue_clear(&orcirc->p_chan_cells);
+ if (orcirc) {
+ circuitmux_detach_circuit(nchan->cmux, TO_CIRCUIT(orcirc));
+ circuitmux_detach_circuit(pchan->cmux, TO_CIRCUIT(orcirc));
+ cell_queue_clear(&orcirc->base_.n_chan_cells);
+ cell_queue_clear(&orcirc->p_chan_cells);
+ }
tor_free(orcirc);
free_fake_channel(nchan);
free_fake_channel(pchan);
diff --git a/src/test/test_relaycell.c b/src/test/test_relaycell.c
index 1cd9ff064b..fb6748965a 100644
--- a/src/test/test_relaycell.c
+++ b/src/test/test_relaycell.c
@@ -95,7 +95,7 @@ test_relaycell_resolved(void *arg)
tt_int_op(srm_ncalls, OP_EQ, 1); \
tt_ptr_op(srm_conn, OP_EQ, entryconn); \
tt_int_op(srm_atype, OP_EQ, (atype)); \
- if (answer) { \
+ if ((answer) != NULL) { \
tt_int_op(srm_alen, OP_EQ, sizeof(answer)-1); \
tt_int_op(srm_alen, OP_LT, 512); \
tt_int_op(srm_answer_is_set, OP_EQ, 1); \
diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c
index d1b52649b2..a5d3f351f8 100644
--- a/src/test/test_rendcache.c
+++ b/src/test/test_rendcache.c
@@ -12,18 +12,14 @@
#include "config.h"
#include <openssl/rsa.h>
#include "rend_test_helpers.h"
+#include "log_test_helpers.h"
#define NS_MODULE rend_cache
static const int RECENT_TIME = -10;
static const int TIME_IN_THE_PAST = -(REND_CACHE_MAX_AGE + \
- REND_CACHE_MAX_SKEW + 10);
-static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 10;
-
-extern strmap_t *rend_cache;
-extern digestmap_t *rend_cache_v2_dir;
-extern strmap_t *rend_cache_failure;
-extern size_t rend_cache_total_allocation;
+ 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)
@@ -660,15 +656,19 @@ test_rend_cache_decrement_allocation(void *data)
// Test when there are not enough allocations
rend_cache_total_allocation = 1;
+ setup_full_capture_of_logs(LOG_WARN);
rend_cache_decrement_allocation(2);
tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
+ expect_single_log_msg_containing(
+ "Underflow in rend_cache_decrement_allocation");
+ teardown_capture_of_logs();
// And again
rend_cache_decrement_allocation(2);
tt_int_op(rend_cache_total_allocation, OP_EQ, 0);
done:
- (void)0;
+ teardown_capture_of_logs();
}
static void
@@ -683,15 +683,19 @@ test_rend_cache_increment_allocation(void *data)
// Test when there are too many allocations
rend_cache_total_allocation = SIZE_MAX-1;
+ setup_full_capture_of_logs(LOG_WARN);
rend_cache_increment_allocation(2);
tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
+ expect_single_log_msg_containing(
+ "Overflow in rend_cache_increment_allocation");
+ teardown_capture_of_logs();
// And again
rend_cache_increment_allocation(2);
tt_u64_op(rend_cache_total_allocation, OP_EQ, SIZE_MAX);
done:
- (void)0;
+ teardown_capture_of_logs();
}
static void
@@ -976,7 +980,7 @@ test_rend_cache_entry_free(void *data)
// Handles non-NULL descriptor correctly
e = tor_malloc_zero(sizeof(rend_cache_entry_t));
- e->desc = (char *)malloc(10);
+ e->desc = tor_malloc(10);
rend_cache_entry_free(e);
/* done: */
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 2cffa6e801..088bd257c3 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -19,20 +19,24 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "shared_random.h"
#include "test.h"
#include "test_dir_common.h"
-extern const char AUTHORITY_CERT_1[];
-extern const char AUTHORITY_SIGNKEY_1[];
-extern const char AUTHORITY_CERT_2[];
-extern const char AUTHORITY_SIGNKEY_2[];
-extern const char AUTHORITY_CERT_3[];
-extern const char AUTHORITY_SIGNKEY_3[];
-
void construct_consensus(char **consensus_text_md);
+static authority_cert_t *mock_cert;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
/* 4 digests + 3 sep + pre + post + NULL */
static char output[4*BASE64_DIGEST256_LEN+3+2+2+1];
@@ -234,6 +238,12 @@ test_router_pick_directory_server_impl(void *arg)
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
+ /* Init SR subsystem. */
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ sr_init(0);
+ UNMOCK(get_my_v3_authority_cert);
+
/* No consensus available, fail early */
rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
tt_assert(rs == NULL);
@@ -423,7 +433,7 @@ test_router_pick_directory_server_impl(void *arg)
networkstatus_vote_free(con_md);
}
-connection_t *mocked_connection = NULL;
+static connection_t *mocked_connection = NULL;
/* Mock connection_get_by_type_addr_port_purpose by returning
* mocked_connection. */
diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c
index 74b39c0486..1b526d430b 100644
--- a/src/test/test_routerset.c
+++ b/src/test/test_routerset.c
@@ -432,7 +432,7 @@ NS(test_main)(void *arg)
NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
(const char *s, int assume_action, int *malformed_list));
-addr_policy_t *NS(mock_addr_policy);
+static addr_policy_t *NS(mock_addr_policy);
static void
NS(test_main)(void *arg)
@@ -480,7 +480,7 @@ NS(router_parse_addr_policy_item_from_string)(const char *s,
NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
(const char *s, int assume_action, int *bogus));
-addr_policy_t *NS(mock_addr_policy);
+static addr_policy_t *NS(mock_addr_policy);
static void
NS(test_main)(void *arg)
@@ -527,7 +527,7 @@ NS(router_parse_addr_policy_item_from_string)(const char *s, int assume_action,
NS_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
(const char *s, int assume_action, int *bad));
-addr_policy_t *NS(mock_addr_policy);
+static addr_policy_t *NS(mock_addr_policy);
static void
NS(test_main)(void *arg)
@@ -1477,7 +1477,7 @@ NS(test_main)(void *arg)
* routerset or routerinfo.
*/
-node_t NS(mock_node);
+static node_t NS(mock_node);
static void
NS(test_main)(void *arg)
@@ -1504,7 +1504,7 @@ NS(test_main)(void *arg)
* routerset and no routerinfo.
*/
-node_t NS(mock_node);
+static node_t NS(mock_node);
static void
NS(test_main)(void *arg)
@@ -1603,7 +1603,7 @@ NS(test_main)(void *arg)
NS_DECL(const node_t *, node_get_by_nickname,
(const char *nickname, int warn_if_unused));
-const char *NS(mock_nickname);
+static const char *NS(mock_nickname);
static void
NS(test_main)(void *arg)
@@ -1652,8 +1652,8 @@ 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 *NS(mock_nickname);
-node_t NS(mock_node);
+static const char *NS(mock_nickname);
+static node_t NS(mock_node);
static void
NS(test_main)(void *arg)
@@ -1702,8 +1702,8 @@ 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));
-char *NS(mock_nickname);
-node_t NS(mock_node);
+static char *NS(mock_nickname);
+static node_t NS(mock_node);
static void
NS(test_main)(void *arg)
@@ -1754,7 +1754,7 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused)
NS_DECL(smartlist_t *, nodelist_get_list, (void));
-smartlist_t *NS(mock_smartlist);
+static smartlist_t *NS(mock_smartlist);
static void
NS(test_main)(void *arg)
@@ -1800,8 +1800,8 @@ NS(nodelist_get_list)(void)
NS_DECL(smartlist_t *, nodelist_get_list, (void));
-smartlist_t *NS(mock_smartlist);
-node_t NS(mock_node);
+static smartlist_t *NS(mock_smartlist);
+static node_t NS(mock_node);
static void
NS(test_main)(void *arg)
diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c
index 6e9889b48b..05ea8e86e8 100644
--- a/src/test/test_scheduler.c
+++ b/src/test/test_scheduler.c
@@ -1,16 +1,10 @@
/* Copyright (c) 2014-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#include <math.h>
-
#include "orconfig.h"
-/* Libevent stuff */
-#ifdef HAVE_EVENT2_EVENT_H
+#include <math.h>
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
#define TOR_CHANNEL_INTERNAL_
#define CHANNEL_PRIVATE_
@@ -24,12 +18,6 @@
#include "test.h"
#include "fakechans.h"
-/* Statics in scheduler.c exposed to the test suite */
-extern smartlist_t *channels_pending;
-extern struct event *run_sched_ev;
-extern uint64_t queue_heuristic;
-extern time_t queue_heuristic_timestamp;
-
/* Event base for scheduelr tests */
static struct event_base *mock_event_base = NULL;
@@ -96,9 +84,7 @@ mock_event_free_all(void)
static void
mock_event_init(void)
{
-#ifdef HAVE_EVENT2_EVENT_H
struct event_config *cfg = NULL;
-#endif
tt_ptr_op(mock_event_base, ==, NULL);
@@ -108,7 +94,6 @@ mock_event_init(void)
*/
if (!mock_event_base) {
-#ifdef HAVE_EVENT2_EVENT_H
cfg = event_config_new();
#if LIBEVENT_VERSION_NUMBER >= V(2,0,9)
/* We can enable changelist support with epoll, since we don't give
@@ -117,9 +102,6 @@ mock_event_init(void)
#endif
mock_event_base = event_base_new_with_config(cfg);
event_config_free(cfg);
-#else
- mock_event_base = event_init();
-#endif
}
tt_assert(mock_event_base != NULL);
@@ -156,7 +138,7 @@ channel_flush_some_cells_mock_free_all(void)
static void
channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells)
{
- flush_mock_channel_t *flush_mock_ch = NULL;
+ int found = 0;
if (!chan) return;
if (num_cells <= 0) return;
@@ -172,6 +154,7 @@ channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells)
if (flush_mock_ch->chan == chan) {
/* Found it */
flush_mock_ch->cells = num_cells;
+ found = 1;
break;
}
} else {
@@ -181,8 +164,9 @@ channel_flush_some_cells_mock_set(channel_t *chan, ssize_t num_cells)
}
} SMARTLIST_FOREACH_END(flush_mock_ch);
- if (!flush_mock_ch) {
+ if (! found) {
/* The loop didn't find it */
+ flush_mock_channel_t *flush_mock_ch;
flush_mock_ch = tor_malloc_zero(sizeof(*flush_mock_ch));
flush_mock_ch->chan = chan;
flush_mock_ch->cells = num_cells;
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
new file mode 100644
index 0000000000..056f199b94
--- /dev/null
+++ b/src/test/test_shared_random.c
@@ -0,0 +1,1290 @@
+#define SHARED_RANDOM_PRIVATE
+#define SHARED_RANDOM_STATE_PRIVATE
+#define CONFIG_PRIVATE
+#define DIRVOTE_PRIVATE
+
+#include "or.h"
+#include "test.h"
+#include "config.h"
+#include "dirvote.h"
+#include "shared_random.h"
+#include "shared_random_state.h"
+#include "routerkeys.h"
+#include "routerlist.h"
+#include "router.h"
+#include "routerparse.h"
+#include "networkstatus.h"
+#include "log_test_helpers.h"
+
+static authority_cert_t *mock_cert;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
+static dir_server_t ds;
+
+static dir_server_t *
+trusteddirserver_get_by_v3_auth_digest_m(const char *digest)
+{
+ (void) digest;
+ /* The shared random code only need to know if a valid pointer to a dir
+ * server object has been found so this is safe because it won't use the
+ * pointer at all never. */
+ return &ds;
+}
+
+/* Setup a minimal dirauth environment by initializing the SR state and
+ * making sure the options are set to be an authority directory. */
+static void
+init_authority_state(void)
+{
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+ or_options_t *options = get_options_mutable();
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(mock_cert);
+ options->AuthoritativeDir = 1;
+ tt_int_op(0, ==, load_ed_keys(options, time(NULL)));
+ sr_state_init(0, 0);
+ /* It's possible a commit has been generated in our state depending on
+ * the phase we are currently in which uses "now" as the starting
+ * timestamp. Delete it before we do any testing below. */
+ sr_state_delete_commits();
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+}
+
+static void
+test_get_sr_protocol_phase(void *arg)
+{
+ time_t the_time;
+ sr_phase_t phase;
+ int retval;
+
+ (void) arg;
+
+ /* Initialize SR state */
+ init_authority_state();
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 23:59:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:01 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 11:59:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:01 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 13:00:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ done:
+ ;
+}
+
+static networkstatus_t *mock_consensus = NULL;
+
+static void
+test_get_state_valid_until_time(void *arg)
+{
+ time_t current_time;
+ time_t valid_until_time;
+ char tbuf[ISO_TIME_LEN + 1];
+ int retval;
+
+ (void) arg;
+
+ {
+ /* 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);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 19:22:00 UTC",
+ &current_time);
+ tt_int_op(retval, ==, 0);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:00 UTC",
+ &current_time);
+ tt_int_op(retval, ==, 0);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ &current_time);
+ tt_int_op(retval, ==, 0);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ 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;
+}
+
+/** Test the get_next_valid_after_time() function. */
+static void
+test_get_next_valid_after_time(void *arg)
+{
+ time_t current_time;
+ time_t valid_after_time;
+ char tbuf[ISO_TIME_LEN + 1];
+ int retval;
+
+ (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));
+
+ 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, 13 Jan 2016 15:00:00 UTC",
+ &mock_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+ }
+
+ {
+ /* Get the valid after time if called at 00:00:00 */
+ 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);
+
+ /* 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);
+ }
+
+ {
+ /* 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);
+ valid_after_time = get_next_valid_after_time(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);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:30:01 UTC",
+ &current_time);
+ tt_int_op(retval, ==, 0);
+ valid_after_time = get_next_valid_after_time(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);
+ }
+
+ done:
+ networkstatus_vote_free(mock_consensus);
+}
+
+/* In this test we are going to generate a sr_commit_t object and validate
+ * it. We first generate our values, and then we parse them as if they were
+ * received from the network. After we parse both the commit and the reveal,
+ * we verify that they indeed match. */
+static void
+test_sr_commit(void *arg)
+{
+ authority_cert_t *auth_cert = NULL;
+ time_t now = time(NULL);
+ sr_commit_t *our_commit = NULL;
+ smartlist_t *args = smartlist_new();
+ sr_commit_t *parsed_commit = NULL;
+
+ (void) arg;
+
+ { /* Setup a minimal dirauth environment for this test */
+ or_options_t *options = get_options_mutable();
+
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(auth_cert);
+
+ options->AuthoritativeDir = 1;
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ }
+
+ /* Generate our commit object and validate it has the appropriate field
+ * that we can then use to build a representation that we'll find in a
+ * vote coming from the network. */
+ {
+ sr_commit_t test_commit;
+ our_commit = sr_generate_our_commit(now, auth_cert);
+ tt_assert(our_commit);
+ /* Default and only supported algorithm for now. */
+ tt_assert(our_commit->alg == DIGEST_SHA3_256);
+ /* We should have a reveal value. */
+ tt_assert(commit_has_reveal_value(our_commit));
+ /* We should have a random value. */
+ 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);
+ /* 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,
+ &test_commit));
+ tt_int_op(0, ==, commit_decode(our_commit->encoded_commit,
+ &test_commit));
+ tt_int_op(0, ==, verify_commit_and_reveal(our_commit));
+ }
+
+ /* Let's make sure our verify commit and reveal function works. We'll
+ * make it fail a bit with known failure case. */
+ {
+ /* Copy our commit so we don't alter it for the rest of testing. */
+ sr_commit_t test_commit;
+ memcpy(&test_commit, our_commit, sizeof(test_commit));
+
+ /* 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));
+ 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));
+
+ /* 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));
+ 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));
+ }
+
+ /* We'll build a list of values from our commit that our parsing function
+ * takes from a vote line and see if we can parse it correctly. */
+ {
+ smartlist_add(args, tor_strdup("1"));
+ smartlist_add(args,
+ tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg)));
+ smartlist_add(args, tor_strdup(sr_commit_get_rsa_fpr(our_commit)));
+ smartlist_add(args, tor_strdup(our_commit->encoded_commit));
+ smartlist_add(args, tor_strdup(our_commit->encoded_reveal));
+ parsed_commit = sr_parse_commit(args);
+ tt_assert(parsed_commit);
+ /* That parsed commit should be _EXACTLY_ like our original commit (we
+ * have to explicitly set the valid flag though). */
+ parsed_commit->valid = 1;
+ tt_mem_op(parsed_commit, OP_EQ, our_commit, sizeof(*parsed_commit));
+ /* Cleanup */
+ }
+
+ done:
+ teardown_capture_of_logs();
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ sr_commit_free(our_commit);
+ sr_commit_free(parsed_commit);
+ authority_cert_free(auth_cert);
+}
+
+/* Test the encoding and decoding function for commit and reveal values. */
+static void
+test_encoding(void *arg)
+{
+ (void) arg;
+ int ret;
+ /* Random number is 32 bytes. */
+ char raw_rand[32];
+ time_t ts = 1454333590;
+ char hashed_rand[DIGEST256_LEN], hashed_reveal[DIGEST256_LEN];
+ sr_commit_t parsed_commit;
+
+ /* Those values were generated by sr_commit_calc_ref.py where the random
+ * value is 32 'A' and timestamp is the one in ts. */
+ static const char *encoded_reveal =
+ "AAAAAFavXpZJxbwTupvaJCTeIUCQmOPxAMblc7ChL5H2nZKuGchdaA==";
+ static const char *encoded_commit =
+ "AAAAAFavXpbkBMzMQG7aNoaGLFNpm2Wkk1ozXhuWWqL//GynltxVAg==";
+
+ /* Set up our raw random bytes array. */
+ memset(raw_rand, 'A', sizeof(raw_rand));
+ /* Hash random number because we don't expose bytes of the RNG. */
+ ret = crypto_digest256(hashed_rand, raw_rand,
+ sizeof(raw_rand), SR_DIGEST_ALG);
+ tt_int_op(0, ==, ret);
+ /* Hash reveal value. */
+ tt_int_op(SR_REVEAL_BASE64_LEN, ==, 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));
+
+ /* 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_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_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,
+ sizeof(hashed_reveal));
+ }
+
+ /* Test our commit/reveal encode functions. */
+ {
+ /* Test the reveal encode. */
+ char encoded[SR_REVEAL_BASE64_LEN + 1];
+ parsed_commit.reveal_ts = ts;
+ 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_mem_op(encoded_reveal, OP_EQ, encoded, strlen(encoded_reveal));
+ }
+
+ {
+ /* Test the commit encode. */
+ char encoded[SR_COMMIT_BASE64_LEN + 1];
+ parsed_commit.commit_ts = ts;
+ 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_mem_op(encoded_commit, OP_EQ, encoded, strlen(encoded_commit));
+ }
+
+ done:
+ ;
+}
+
+/** Setup some SRVs in our SR state. If <b>also_current</b> is set, then set
+ * both current and previous SRVs.
+ * Helper of test_vote() and test_sr_compute_srv(). */
+static void
+test_sr_setup_srv(int also_current)
+{
+ sr_srv_t *srv = tor_malloc_zero(sizeof(sr_srv_t));
+ srv->num_reveals = 42;
+ memcpy(srv->value,
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+ sizeof(srv->value));
+
+ sr_state_set_previous_srv(srv);
+
+ if (also_current) {
+ srv = tor_malloc_zero(sizeof(sr_srv_t));
+ srv->num_reveals = 128;
+ memcpy(srv->value,
+ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN",
+ sizeof(srv->value));
+
+ sr_state_set_current_srv(srv);
+ }
+}
+
+/* Test anything that has to do with SR protocol and vote. */
+static void
+test_vote(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+ sr_commit_t *our_commit = NULL;
+
+ (void) arg;
+
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
+ { /* Setup a minimal dirauth environment for this test */
+ init_authority_state();
+ /* Set ourself in reveal phase so we can parse the reveal value in the
+ * vote as well. */
+ set_sr_phase(SR_PHASE_REVEAL);
+ }
+
+ /* Generate our commit object and validate it has the appropriate field
+ * that we can then use to build a representation that we'll find in a
+ * vote coming from the network. */
+ {
+ sr_commit_t *saved_commit;
+ our_commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(our_commit);
+ sr_state_add_commit(our_commit);
+ /* Make sure it's there. */
+ saved_commit = sr_state_get_commit(our_commit->rsa_identity);
+ tt_assert(saved_commit);
+ }
+
+ /* Also setup the SRVs */
+ test_sr_setup_srv(1);
+
+ { /* Now test the vote generation */
+ smartlist_t *chunks = smartlist_new();
+ smartlist_t *tokens = smartlist_new();
+ /* Get our vote line and validate it. */
+ char *lines = sr_get_string_for_vote();
+ 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_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:
+ * "shared-rand-commitment" SP version SP algname SP identity
+ * SP COMMIT [SP REVEAL] NL
+ */
+ 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_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,
+ crypto_digest_algorithm_get_name(DIGEST_SHA3_256));
+ 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_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)
+;
+ /* Finally, does this vote line creates a valid commit object? */
+ smartlist_t *args = smartlist_new();
+ smartlist_add(args, smartlist_get(tokens, 1));
+ smartlist_add(args, smartlist_get(tokens, 2));
+ smartlist_add(args, smartlist_get(tokens, 3));
+ smartlist_add(args, smartlist_get(tokens, 4));
+ smartlist_add(args, smartlist_get(tokens, 5));
+ sr_commit_t *parsed_commit = sr_parse_commit(args);
+ tt_assert(parsed_commit);
+ /* 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));
+
+ /* minor cleanup */
+ SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+ smartlist_clear(tokens);
+
+ /* Now test the previous SRV */
+ 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_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,
+ "WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlo=");
+
+ /* minor cleanup */
+ SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+ smartlist_clear(tokens);
+
+ /* Now test the current SRV */
+ 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_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,
+ "Tk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk4=");
+
+ /* Clean up */
+ sr_commit_free(parsed_commit);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+ smartlist_free(tokens);
+ smartlist_clear(args);
+ smartlist_free(args);
+ tor_free(lines);
+ }
+
+ done:
+ sr_commit_free(our_commit);
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+static const char *sr_state_str = "Version 1\n"
+ "TorVersion 0.2.9.0-alpha-dev\n"
+ "ValidAfter 2037-04-19 07:16:00\n"
+ "ValidUntil 2037-04-20 07:16:00\n"
+ "Commit 1 sha3-256 FA3CEC2C99DC68D3166B9B6E4FA21A4026C2AB1C "
+ "7M8GdubCAAdh7WUG0DiwRyxTYRKji7HATa7LLJEZ/UAAAAAAVmfUSg== "
+ "AAAAAFZn1EojfIheIw42bjK3VqkpYyjsQFSbv/dxNna3Q8hUEPKpOw==\n"
+ "Commit 1 sha3-256 41E89EDFBFBA44983E21F18F2230A4ECB5BFB543 "
+ "17aUsYuMeRjd2N1r8yNyg7aHqRa6gf4z7QPoxxAZbp0AAAAAVmfUSg==\n"
+ "Commit 1 sha3-256 36637026573A04110CF3E6B1D201FB9A98B88734 "
+ "DDDYtripvdOU+XPEUm5xpU64d9IURSds1xSwQsgeB8oAAAAAVmfUSg==\n"
+ "SharedRandPreviousValue 4 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=\n"
+ "SharedRandCurrentValue 3 8dWeW12KEzTGEiLGgO1UVJ7Z91CekoRcxt6Q9KhnOFI=\n";
+
+/** Create an SR disk state, parse it and validate that the parsing went
+ * well. Yes! */
+static void
+test_state_load_from_disk(void *arg)
+{
+ int ret;
+ char *dir = tor_strdup(get_fname("test_sr_state"));
+ char *sr_state_path = tor_strdup(get_fname("test_sr_state/sr_state"));
+ sr_state_t *the_sr_state = NULL;
+
+ (void) arg;
+
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
+ /* First try with a nonexistent path. */
+ ret = disk_state_load_from_disk_impl("NONEXISTENTNONEXISTENT");
+ tt_assert(ret == -ENOENT);
+
+ /* Now create a mock state directory and state file */
+#ifdef _WIN32
+ ret = mkdir(dir);
+#else
+ ret = mkdir(dir, 0700);
+#endif
+ tt_assert(ret == 0);
+ ret = write_str_to_file(sr_state_path, sr_state_str, 0);
+ tt_assert(ret == 0);
+
+ /* Try to load the directory itself. Should fail. */
+ ret = disk_state_load_from_disk_impl(dir);
+ tt_int_op(ret, OP_LT, 0);
+
+ /* State should be non-existent at this point. */
+ the_sr_state = get_sr_state();
+ tt_assert(!the_sr_state);
+
+ /* Now try to load the correct file! */
+ ret = disk_state_load_from_disk_impl(sr_state_path);
+ tt_assert(ret == 0);
+
+ /* Check the content of the state */
+ /* XXX check more deeply!!! */
+ the_sr_state = get_sr_state();
+ tt_assert(the_sr_state);
+ tt_assert(the_sr_state->version == 1);
+ tt_assert(digestmap_size(the_sr_state->commits) == 3);
+ tt_assert(the_sr_state->current_srv);
+ tt_assert(the_sr_state->current_srv->num_reveals == 3);
+ tt_assert(the_sr_state->previous_srv);
+
+ /* XXX Now also try loading corrupted state files and make sure parsing
+ fails */
+
+ done:
+ tor_free(dir);
+ tor_free(sr_state_path);
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+/** Generate three specially crafted commits (based on the test
+ * vector at sr_srv_calc_ref.py). Helper of test_sr_compute_srv(). */
+static void
+test_sr_setup_commits(void)
+{
+ time_t now = time(NULL);
+ sr_commit_t *commit_a, *commit_b, *commit_c, *commit_d;
+ sr_commit_t *place_holder = tor_malloc_zero(sizeof(*place_holder));
+ authority_cert_t *auth_cert = NULL;
+
+ { /* Setup a minimal dirauth environment for this test */
+ or_options_t *options = get_options_mutable();
+
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(auth_cert);
+
+ options->AuthoritativeDir = 1;
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ }
+
+ /* Generate three dummy commits according to sr_srv_calc_ref.py . Then
+ register them to the SR state. Also register a fourth commit 'd' with no
+ reveal info, to make sure that it will get ignored during SRV
+ calculation. */
+
+ { /* Commit from auth 'a' */
+ commit_a = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_a);
+
+ /* Do some surgery on the commit */
+ memset(commit_a->rsa_identity, 'A', sizeof(commit_a->rsa_identity));
+ base16_encode(commit_a->rsa_identity_hex,
+ sizeof(commit_a->rsa_identity_hex), commit_a->rsa_identity,
+ sizeof(commit_a->rsa_identity));
+ strlcpy(commit_a->encoded_reveal,
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ sizeof(commit_a->encoded_reveal));
+ memcpy(commit_a->hashed_reveal,
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ sizeof(commit_a->hashed_reveal));
+ }
+
+ { /* Commit from auth 'b' */
+ commit_b = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_b);
+
+ /* Do some surgery on the commit */
+ memset(commit_b->rsa_identity, 'B', sizeof(commit_b->rsa_identity));
+ base16_encode(commit_b->rsa_identity_hex,
+ sizeof(commit_b->rsa_identity_hex), commit_b->rsa_identity,
+ sizeof(commit_b->rsa_identity));
+ strlcpy(commit_b->encoded_reveal,
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+ sizeof(commit_b->encoded_reveal));
+ memcpy(commit_b->hashed_reveal,
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+ sizeof(commit_b->hashed_reveal));
+ }
+
+ { /* Commit from auth 'c' */
+ commit_c = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_c);
+
+ /* Do some surgery on the commit */
+ memset(commit_c->rsa_identity, 'C', sizeof(commit_c->rsa_identity));
+ base16_encode(commit_c->rsa_identity_hex,
+ sizeof(commit_c->rsa_identity_hex), commit_c->rsa_identity,
+ sizeof(commit_c->rsa_identity));
+ strlcpy(commit_c->encoded_reveal,
+ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
+ sizeof(commit_c->encoded_reveal));
+ memcpy(commit_c->hashed_reveal,
+ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
+ sizeof(commit_c->hashed_reveal));
+ }
+
+ { /* Commit from auth 'd' */
+ commit_d = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_d);
+
+ /* Do some surgery on the commit */
+ memset(commit_d->rsa_identity, 'D', sizeof(commit_d->rsa_identity));
+ base16_encode(commit_d->rsa_identity_hex,
+ sizeof(commit_d->rsa_identity_hex), commit_d->rsa_identity,
+ sizeof(commit_d->rsa_identity));
+ strlcpy(commit_d->encoded_reveal,
+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+ sizeof(commit_d->encoded_reveal));
+ memcpy(commit_d->hashed_reveal,
+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+ sizeof(commit_d->hashed_reveal));
+ /* Clean up its reveal info */
+ memcpy(place_holder, commit_d, sizeof(*place_holder));
+ memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal));
+ tt_assert(!commit_has_reveal_value(commit_d));
+ }
+
+ /* Register commits to state (during commit phase) */
+ set_sr_phase(SR_PHASE_COMMIT);
+ save_commit_to_state(commit_a);
+ 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);
+
+ /* Now during REVEAL phase save commit D by restoring its reveal. */
+ set_sr_phase(SR_PHASE_REVEAL);
+ save_commit_to_state(place_holder);
+ tt_str_op(commit_d->encoded_reveal, OP_EQ,
+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
+ /* Go back to an empty encoded reveal value. */
+ memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal));
+ memset(commit_d->random_number, 0, sizeof(commit_d->random_number));
+ tt_assert(!commit_has_reveal_value(commit_d));
+
+ done:
+ authority_cert_free(auth_cert);
+}
+
+/** Verify that the SRV generation procedure is proper by testing it against
+ * the test vector from ./sr_srv_calc_ref.py. */
+static void
+test_sr_compute_srv(void *arg)
+{
+ (void) arg;
+ const sr_srv_t *current_srv = NULL;
+
+#define SRV_TEST_VECTOR \
+ "2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D"
+
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
+ init_authority_state();
+
+ /* Setup the commits for this unittest */
+ test_sr_setup_commits();
+ test_sr_setup_srv(0);
+
+ /* Now switch to reveal phase */
+ set_sr_phase(SR_PHASE_REVEAL);
+
+ /* Compute the SRV */
+ sr_compute_srv();
+
+ /* 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_str_op(hex_str((char*)current_srv->value, 32),
+ ==,
+ SRV_TEST_VECTOR);
+
+ done:
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+/** Return a minimal vote document with a current SRV value set to
+ * <b>srv</b>. */
+static networkstatus_t *
+get_test_vote_with_curr_srv(const char *srv)
+{
+ networkstatus_t *vote = tor_malloc_zero(sizeof(networkstatus_t));
+
+ vote->type = NS_TYPE_VOTE;
+ vote->sr_info.participate = 1;
+ vote->sr_info.current_srv = tor_malloc_zero(sizeof(sr_srv_t));
+ vote->sr_info.current_srv->num_reveals = 42;
+ memcpy(vote->sr_info.current_srv->value,
+ srv,
+ sizeof(vote->sr_info.current_srv->value));
+
+ return vote;
+}
+
+/* Test the function that picks the right SRV given a bunch of votes. Make sure
+ * that the function returns an SRV iff the majority/agreement requirements are
+ * met. */
+static void
+test_sr_get_majority_srv_from_votes(void *arg)
+{
+ sr_srv_t *chosen_srv;
+ smartlist_t *votes = smartlist_new();
+
+#define SRV_1 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+#define SRV_2 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+
+ (void) arg;
+
+ init_authority_state();
+ /* Make sure our SRV is fresh so we can consider the super majority with
+ * the consensus params of number of agreements needed. */
+ sr_state_set_fresh_srv();
+
+ /* The test relies on the dirauth list being initialized. */
+ clear_dir_servers();
+ add_default_trusted_dir_authorities(V3_DIRINFO);
+
+ { /* Prepare voting environment with just a single vote. */
+ networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+ smartlist_add(votes, vote);
+ }
+
+ /* 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);
+
+ { /* Now put in 8 more votes. Let SRV_1 have majority. */
+ int i;
+ /* Now 7 votes believe in SRV_1 */
+ for (i = 0; i < 3; i++) {
+ networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+ smartlist_add(votes, vote);
+ }
+ /* and 2 votes believe in SRV_2 */
+ for (i = 0; i < 2; i++) {
+ networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_2);
+ smartlist_add(votes, vote);
+ }
+ for (i = 0; i < 3; i++) {
+ networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+ smartlist_add(votes, vote);
+ }
+
+ tt_int_op(smartlist_len(votes), ==, 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);
+
+ /* 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_mem_op(chosen_srv->value, OP_EQ, SRV_1, sizeof(chosen_srv->value));
+
+ done:
+ SMARTLIST_FOREACH(votes, networkstatus_t *, vote,
+ networkstatus_vote_free(vote));
+ smartlist_free(votes);
+}
+
+static void
+test_utils(void *arg)
+{
+ (void) arg;
+
+ /* Testing srv_dup(). */
+ {
+ sr_srv_t *srv = NULL, *dup_srv = NULL;
+ const char *srv_value =
+ "1BDB7C3E973936E4D13A49F37C859B3DC69C429334CF9412E3FEF6399C52D47A";
+ srv = tor_malloc_zero(sizeof(*srv));
+ srv->num_reveals = 42;
+ 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_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
+ tor_free(srv);
+ tor_free(dup_srv);
+ }
+
+ /* Testing commitments_are_the_same(). Currently, the check is to test the
+ * value of the encoded commit so let's make sure that actually works. */
+ {
+ /* Payload of 57 bytes that is the length of sr_commit_t->encoded_commit.
+ * 56 bytes of payload and a NUL terminated byte at the end ('\x00')
+ * which comes down to SR_COMMIT_BASE64_LEN + 1. */
+ const char *payload =
+ "\x5d\xb9\x60\xb6\xcc\x51\x68\x52\x31\xd9\x88\x88\x71\x71\xe0\x30"
+ "\x59\x55\x7f\xcd\x61\xc0\x4b\x05\xb8\xcd\xc1\x48\xe9\xcd\x16\x1f"
+ "\x70\x15\x0c\xfc\xd3\x1a\x75\xd0\x93\x6c\xc4\xe0\x5c\xbe\xe2\x18"
+ "\xc7\xaf\x72\xb6\x7c\x9b\x52\x00";
+ 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);
+ /* 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);
+ }
+
+ /* Testing commit_is_authoritative(). */
+ {
+ crypto_pk_t *k = crypto_pk_new();
+ char digest[DIGEST_LEN];
+ sr_commit_t commit;
+
+ tt_assert(!crypto_pk_generate_key(k));
+
+ tt_int_op(0, ==, crypto_pk_get_digest(k, digest));
+ memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), ==, 1);
+ /* Change the pubkey. */
+ memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), ==, 0);
+ crypto_pk_free(k);
+ }
+
+ /* Testing get_phase_str(). */
+ {
+ tt_str_op(get_phase_str(SR_PHASE_REVEAL), ==, "reveal");
+ tt_str_op(get_phase_str(SR_PHASE_COMMIT), ==, "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);
+ 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);
+ /* Junk. */
+ tt_int_op(is_phase_transition(42), ==, 1);
+ }
+
+ done:
+ return;
+}
+
+static void
+test_state_transition(void *arg)
+{
+ sr_state_t *state = NULL;
+ time_t now = time(NULL);
+
+ (void) arg;
+
+ { /* Setup a minimal dirauth environment for this test */
+ init_authority_state();
+ state = get_sr_state();
+ tt_assert(state);
+ }
+
+ /* Test our state reset for a new protocol run. */
+ {
+ /* Add a commit to the state so we can test if the reset cleans the
+ * commits. Also, change all params that we expect to be updated. */
+ 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);
+ /* Let's test our delete feature. */
+ sr_state_delete_commits();
+ tt_int_op(digestmap_size(state->commits), ==, 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);
+ 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);
+ }
+
+ /* Test SRV rotation in our state. */
+ {
+ const sr_srv_t *cur, *prev;
+ test_sr_setup_srv(1);
+ cur = sr_state_get_current_srv();
+ tt_assert(cur);
+ /* After, current srv should be the previous and then set to NULL. */
+ state_rotate_srv();
+ prev = sr_state_get_previous_srv();
+ tt_assert(prev == cur);
+ tt_assert(!sr_state_get_current_srv());
+ sr_state_clean_srvs();
+ }
+
+ /* New protocol run. */
+ {
+ const sr_srv_t *cur;
+ /* Setup some new SRVs so we can confirm that a new protocol run
+ * actually makes them rotate and compute new ones. */
+ test_sr_setup_srv(1);
+ cur = sr_state_get_current_srv();
+ tt_assert(cur);
+ set_sr_phase(SR_PHASE_REVEAL);
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ new_protocol_run(now);
+ UNMOCK(get_my_v3_authority_cert);
+ /* Rotation happened. */
+ tt_assert(sr_state_get_previous_srv() == cur);
+ /* We are going into COMMIT phase so we had to rotate our SRVs. Usually
+ * our current SRV would be NULL but a new protocol run should make us
+ * compute a new SRV. */
+ tt_assert(sr_state_get_current_srv());
+ /* 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);
+ /* 46 here since we were at 45 just before. */
+ tt_u64_op(state->n_protocol_runs, ==, 46);
+ }
+
+ /* Cleanup of SRVs. */
+ {
+ sr_state_clean_srvs();
+ tt_assert(!sr_state_get_current_srv());
+ tt_assert(!sr_state_get_previous_srv());
+ }
+
+ done:
+ return;
+}
+
+static void
+test_keep_commit(void *arg)
+{
+ char fp[FINGERPRINT_LEN + 1];
+ sr_commit_t *commit = NULL, *dup_commit = NULL;
+ sr_state_t *state;
+ time_t now = time(NULL);
+ crypto_pk_t *k = NULL;
+
+ (void) arg;
+
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
+ {
+ k = pk_generate(1);
+ /* Setup a minimal dirauth environment for this test */
+ /* Have a key that is not the one from our commit. */
+ init_authority_state();
+ state = get_sr_state();
+ }
+
+ /* 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. */
+ commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(commit);
+ /* 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);
+ /* 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);
+ /* 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);
+ /* 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);
+
+ /* Let's reset our commit and go into REVEAL phase. */
+ sr_commit_free(commit);
+ commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(commit);
+ /* Dup the commit so we have one with and one without a reveal value. */
+ dup_commit = tor_malloc_zero(sizeof(*dup_commit));
+ memcpy(dup_commit, commit, sizeof(*dup_commit));
+ 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);
+ /* 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);
+ /* Important to add the commit _without_ the reveal here. */
+ sr_state_add_commit(dup_commit);
+ tt_int_op(digestmap_size(state->commits), ==, 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);
+ /* The commit shouldn't be kept if it's not verified that is no matchin
+ * hashed reveal. */
+ {
+ /* Let's save the hash reveal so we can restore it. */
+ sr_commit_t place_holder;
+ memcpy(place_holder.hashed_reveal, commit->hashed_reveal,
+ sizeof(place_holder.hashed_reveal));
+ 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);
+ 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,
+ "expected 2 log entries");
+ teardown_capture_of_logs();
+ memcpy(commit->hashed_reveal, place_holder.hashed_reveal,
+ sizeof(commit->hashed_reveal));
+ }
+ /* 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);
+ /* 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);
+
+ done:
+ teardown_capture_of_logs();
+ sr_commit_free(commit);
+ sr_commit_free(dup_commit);
+ crypto_pk_free(k);
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+static void
+test_state_update(void *arg)
+{
+ time_t commit_phase_time = 1452076000;
+ time_t reveal_phase_time = 1452086800;
+ sr_state_t *state;
+
+ (void) arg;
+
+ {
+ init_authority_state();
+ state = get_sr_state();
+ set_sr_phase(SR_PHASE_COMMIT);
+ /* We'll cheat a bit here and reset the creation time of the state which
+ * will avoid us to compute a valid_after time that fits the commit
+ * phase. */
+ state->valid_after = 0;
+ state->n_reveal_rounds = 0;
+ state->n_commit_rounds = 0;
+ state->n_protocol_runs = 0;
+ }
+
+ /* We need to mock for the state update function call. */
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+ /* 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);
+
+ /* 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);
+ /* Only our commit should be in there. */
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(state->n_reveal_rounds, ==, 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);
+
+ /* 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_assert(state->current_srv);
+
+ done:
+ sr_state_free();
+ UNMOCK(get_my_v3_authority_cert);
+}
+
+struct testcase_t sr_tests[] = {
+ { "get_sr_protocol_phase", test_get_sr_protocol_phase, TT_FORK,
+ NULL, NULL },
+ { "sr_commit", test_sr_commit, TT_FORK,
+ NULL, NULL },
+ { "keep_commit", test_keep_commit, TT_FORK,
+ NULL, NULL },
+ { "encoding", test_encoding, TT_FORK,
+ NULL, NULL },
+ { "get_next_valid_after_time", test_get_next_valid_after_time, TT_FORK,
+ NULL, NULL },
+ { "get_state_valid_until_time", test_get_state_valid_until_time, TT_FORK,
+ NULL, NULL },
+ { "vote", test_vote, TT_FORK,
+ NULL, NULL },
+ { "state_load_from_disk", test_state_load_from_disk, TT_FORK,
+ NULL, NULL },
+ { "sr_compute_srv", test_sr_compute_srv, TT_FORK, NULL, NULL },
+ { "sr_get_majority_srv_from_votes", test_sr_get_majority_srv_from_votes,
+ TT_FORK, NULL, NULL },
+ { "utils", test_utils, TT_FORK, NULL, NULL },
+ { "state_transition", test_state_transition, TT_FORK, NULL, NULL },
+ { "state_update", test_state_update, TT_FORK,
+ NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_slow.c b/src/test/test_slow.c
index c1d2e81914..7c9f0b1cc2 100644
--- a/src/test/test_slow.c
+++ b/src/test/test_slow.c
@@ -18,9 +18,6 @@
#include "or.h"
#include "test.h"
-extern struct testcase_t slow_crypto_tests[];
-extern struct testcase_t slow_util_tests[];
-
struct testgroup_t testgroups[] = {
{ "slow/crypto/", slow_crypto_tests },
{ "slow/util/", slow_util_tests },
diff --git a/src/test/test_socks.c b/src/test/test_socks.c
index 6da09fd653..62ff12fe15 100644
--- a/src/test/test_socks.c
+++ b/src/test/test_socks.c
@@ -34,7 +34,7 @@ socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
return 1;
}
-const struct testcase_setup_t socks_setup = {
+static const struct testcase_setup_t socks_setup = {
socks_test_setup, socks_test_cleanup
};
diff --git a/src/test/test_status.c b/src/test/test_status.c
index 84a0f6c024..a3b1a2af87 100644
--- a/src/test/test_status.c
+++ b/src/test/test_status.c
@@ -3,6 +3,8 @@
#define LOG_PRIVATE
#define REPHIST_PRIVATE
+#include "orconfig.h"
+
#include <float.h>
#include <math.h>
@@ -310,8 +312,6 @@ NS_DECL(void, logv, (int severity, log_domain_mask_t domain,
NS_DECL(int, server_mode, (const or_options_t *options));
static routerinfo_t *mock_routerinfo;
-extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1];
-extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1];
static void
NS(test_main)(void *arg)
diff --git a/src/test/test_switch_id.sh b/src/test/test_switch_id.sh
index 1b4e0998b5..79c44f2eb1 100755
--- a/src/test/test_switch_id.sh
+++ b/src/test/test_switch_id.sh
@@ -10,6 +10,10 @@ if test "`id -u nobody`" = ""; then
exit 1
fi
+if test "$OVERRIDE_GCDA_PERMISSIONS_HACK" = "yes"; then
+ find src -type f -name '*gcda' -print0 | xargs -0 chmod 0666
+fi
+
"${builddir:-.}/src/test/test-switch-id" nobody setuid || exit 1
"${builddir:-.}/src/test/test-switch-id" nobody root-bind-low || exit 1
"${builddir:-.}/src/test/test-switch-id" nobody setuid-strict || exit 1
@@ -19,6 +23,9 @@ fi
"${builddir:-.}/src/test/test-switch-id" nobody have-caps || exit 1
"${builddir:-.}/src/test/test-switch-id" nobody setuid-keepcaps || exit 1
+if test "$OVERRIDE_GCDA_PERMISSIONS_HACK" = "yes"; then
+ find src -type f -name '*gcda' -print0 | xargs -0 chmod 0644
+fi
echo "All okay"
diff --git a/src/test/test_threads.c b/src/test/test_threads.c
index 1bbe6f5508..ebbc95c7ca 100644
--- a/src/test/test_threads.c
+++ b/src/test/test_threads.c
@@ -87,11 +87,6 @@ test_threads_basic(void *arg)
char *s1 = NULL, *s2 = NULL;
int done = 0, timedout = 0;
time_t started;
-#ifndef _WIN32
- struct timeval tv;
- tv.tv_sec=0;
- tv.tv_usec=100*1000;
-#endif
(void) arg;
tt_int_op(tor_threadlocal_init(&count), OP_EQ, 0);
@@ -120,10 +115,8 @@ test_threads_basic(void *arg)
timedout = done = 1;
}
tor_mutex_release(thread_test_mutex_);
-#ifndef _WIN32
/* Prevent the main thread from starving the worker threads. */
- select(0, NULL, NULL, NULL, &tv);
-#endif
+ tor_sleep_msec(10);
}
tor_mutex_acquire(thread_test_start1_);
tor_mutex_release(thread_test_start1_);
@@ -286,16 +279,7 @@ test_threads_conditionvar(void *arg)
if (!timeout) {
tt_int_op(ti->n_shutdown, ==, 4);
} else {
-#ifdef _WIN32
- Sleep(500); /* msec */
-#elif defined(HAVE_USLEEP)
- usleep(500*1000); /* usec */
-#else
- {
- struct tv = { 0, 500*1000 };
- select(0, NULL, NULL, NULL, &tv);
- }
-#endif
+ tor_sleep_msec(200);
tor_mutex_acquire(ti->mutex);
tt_int_op(ti->n_shutdown, ==, 2);
tt_int_op(ti->n_timeouts, ==, 2);
diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c
index b9b74a1e96..47455cff83 100644
--- a/src/test/test_tortls.c
+++ b/src/test/test_tortls.c
@@ -8,19 +8,13 @@
#ifdef _WIN32
#include <winsock2.h>
#endif
+#include <math.h>
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#endif
+#include "compat.h"
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic push
-#endif
/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in
* srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#endif
+DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/opensslv.h>
@@ -33,13 +27,7 @@
#include <openssl/evp.h>
#include <openssl/bn.h>
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic pop
-#else
-#pragma GCC diagnostic warning "-Wredundant-decls"
-#endif
-#endif
+ENABLE_GCC_WARNING(redundant-decls)
#include "or.h"
#include "torlog.h"
@@ -50,12 +38,11 @@
#include "log_test_helpers.h"
#define NS_MODULE tortls
-extern tor_tls_context_t *server_tls_context;
-extern tor_tls_context_t *client_tls_context;
-
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0) \
- && !defined(LIBRESSL_VERSION_NUMBER)
+#ifndef HAVE_SSL_STATE
#define OPENSSL_OPAQUE
+#endif
+
+#if defined(OPENSSL_OPAQUE) && !defined(LIBRESSL_VERSION_NUMBER)
#define SSL_STATE_STR "before SSL initialization"
#else
#define SSL_STATE_STR "before/accept initialization"
@@ -277,8 +264,6 @@ test_tortls_get_state_description(void *ignored)
tor_free(tls);
}
-extern int tor_tls_object_ex_data_index;
-
static void
test_tortls_get_by_ssl(void *ignored)
{
@@ -341,7 +326,7 @@ test_tortls_log_one_error(void *ignored)
ctx = SSL_CTX_new(SSLv23_method());
tls = tor_malloc_zero(sizeof(tor_tls_t));
- int previous_log = setup_capture_of_logs(LOG_INFO);
+ setup_capture_of_logs(LOG_INFO);
tor_tls_log_one_error(NULL, 0, LOG_WARN, 0, "something");
expect_log_msg("TLS error while something: "
@@ -410,7 +395,7 @@ test_tortls_log_one_error(void *ignored)
" (in (null):(null):" SSL_STATE_STR ")\n");
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
SSL_free(ssl);
SSL_CTX_free(ctx);
if (tls && tls->ssl)
@@ -433,7 +418,7 @@ test_tortls_get_error(void *ignored)
SSL_load_error_strings();
ctx = SSL_CTX_new(SSLv23_method());
- int previous_log = setup_capture_of_logs(LOG_INFO);
+ setup_capture_of_logs(LOG_INFO);
tls = tor_malloc_zero(sizeof(tor_tls_t));
tls->ssl = SSL_new(ctx);
SSL_set_bio(tls->ssl, BIO_new(BIO_s_mem()), NULL);
@@ -499,7 +484,7 @@ test_tortls_get_error(void *ignored)
"connect:before/accept initialization)\n");
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
SSL_free(tls->ssl);
tor_free(tls);
SSL_CTX_free(ctx);
@@ -740,6 +725,26 @@ test_tortls_get_my_certs(void *ignored)
(void)1;
}
+#ifndef HAVE_SSL_GET_CLIENT_CIPHERS
+static SSL_CIPHER *
+get_cipher_by_name(const char *name)
+{
+ int i;
+ const SSL_METHOD *method = SSLv23_method();
+ int num = method->num_ciphers();
+
+ for (i = 0; i < num; ++i) {
+ const SSL_CIPHER *cipher = method->get_cipher(i);
+ const char *ciphername = SSL_CIPHER_get_name(cipher);
+ if (!strcmp(ciphername, name)) {
+ return (SSL_CIPHER *)cipher;
+ }
+ }
+
+ return NULL;
+}
+#endif
+
#ifndef OPENSSL_OPAQUE
static void
test_tortls_get_ciphersuite_name(void *ignored)
@@ -759,23 +764,6 @@ test_tortls_get_ciphersuite_name(void *ignored)
}
static SSL_CIPHER *
-get_cipher_by_name(const char *name)
-{
- int i;
- const SSL_METHOD *method = SSLv23_method();
- int num = method->num_ciphers();
- for (i = 0; i < num; ++i) {
- const SSL_CIPHER *cipher = method->get_cipher(i);
- const char *ciphername = SSL_CIPHER_get_name(cipher);
- if (!strcmp(ciphername, name)) {
- return (SSL_CIPHER *)cipher;
- }
- }
-
- return NULL;
-}
-
-static SSL_CIPHER *
get_cipher_by_id(uint16_t id)
{
int i;
@@ -791,8 +779,6 @@ get_cipher_by_id(uint16_t id)
return NULL;
}
-extern uint16_t v2_cipher_list[];
-
static void
test_tortls_classify_client_ciphers(void *ignored)
{
@@ -853,9 +839,9 @@ test_tortls_classify_client_ciphers(void *ignored)
sk_SSL_CIPHER_zero(ciphers);
- one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
one->id = 0x00ff;
- two = get_cipher_by_name("ECDH-RSA-AES128-GCM-SHA256");
+ two = get_cipher_by_name("ECDHE-RSA-AES128-GCM-SHA256");
two->id = 0x0000;
sk_SSL_CIPHER_push(ciphers, one);
tls->client_cipher_list_type = 0;
@@ -925,7 +911,7 @@ test_tortls_client_is_using_v2_ciphers(void *ignored)
tt_int_op(ret, OP_EQ, 0);
ciphers = sk_SSL_CIPHER_new_null();
- SSL_CIPHER *one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ SSL_CIPHER *one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
one->id = 0x00ff;
sk_SSL_CIPHER_push(ciphers, one);
sess->ciphers = ciphers;
@@ -1185,9 +1171,6 @@ test_tortls_get_forced_write_size(void *ignored)
tor_free(tls);
}
-extern uint64_t total_bytes_written_over_tls;
-extern uint64_t total_bytes_written_by_tls;
-
static void
test_tortls_get_write_overhead_ratio(void *ignored)
{
@@ -1196,17 +1179,17 @@ test_tortls_get_write_overhead_ratio(void *ignored)
total_bytes_written_over_tls = 0;
ret = tls_get_write_overhead_ratio();
- tt_int_op(ret, OP_EQ, 1.0);
+ tt_double_op(fabs(ret - 1.0), OP_LT, 1E-12);
total_bytes_written_by_tls = 10;
total_bytes_written_over_tls = 1;
ret = tls_get_write_overhead_ratio();
- tt_int_op(ret, OP_EQ, 10.0);
+ tt_double_op(fabs(ret - 10.0), OP_LT, 1E-12);
total_bytes_written_by_tls = 10;
total_bytes_written_over_tls = 2;
ret = tls_get_write_overhead_ratio();
- tt_int_op(ret, OP_EQ, 5.0);
+ tt_double_op(fabs(ret - 5.0), OP_LT, 1E-12);
done:
(void)0;
@@ -1573,7 +1556,7 @@ test_tortls_session_secret_cb(void *ignored)
tor_tls_session_secret_cb(tls->ssl, NULL, NULL, NULL, NULL, NULL);
tt_assert(!tls->ssl->tls_session_secret_cb);
- one = get_cipher_by_name("ECDH-RSA-AES256-GCM-SHA384");
+ one = get_cipher_by_name("ECDHE-RSA-AES256-GCM-SHA384");
one->id = 0x00ff;
ciphers = sk_SSL_CIPHER_new_null();
sk_SSL_CIPHER_push(ciphers, one);
@@ -1812,7 +1795,7 @@ test_tortls_debug_state_callback(void *ignored)
char *buf = tor_malloc_zero(1000);
int n;
- int previous_log = setup_capture_of_logs(LOG_DEBUG);
+ setup_capture_of_logs(LOG_DEBUG);
ssl = tor_malloc_zero(sizeof(SSL));
@@ -1825,7 +1808,7 @@ test_tortls_debug_state_callback(void *ignored)
expect_log_msg(buf);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
tor_free(buf);
tor_free(ssl);
}
@@ -1839,7 +1822,6 @@ test_tortls_server_info_callback(void *ignored)
tor_tls_t *tls;
SSL_CTX *ctx;
SSL *ssl;
- int previous_log = setup_capture_of_logs(LOG_WARN);
SSL_library_init();
SSL_load_error_strings();
@@ -1853,22 +1835,22 @@ test_tortls_server_info_callback(void *ignored)
tls->magic = TOR_TLS_MAGIC;
tls->ssl = ssl;
- tor_tls_server_info_callback(NULL, 0, 0);
-
+ setup_full_capture_of_logs(LOG_WARN);
SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_A);
mock_clean_saved_logs();
tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+ expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
mock_clean_saved_logs();
tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
- expect_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
+ expect_single_log_msg("Couldn't look up the tls for an SSL*. How odd!\n");
SSL_set_state(ssl, 99);
mock_clean_saved_logs();
tor_tls_server_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 0);
expect_no_log_entry();
+ teardown_capture_of_logs();
SSL_set_ex_data(tls->ssl, tor_tls_object_ex_data_index, tls);
SSL_set_state(ssl, SSL3_ST_SW_SRVR_HELLO_B);
@@ -1889,7 +1871,7 @@ test_tortls_server_info_callback(void *ignored)
tt_int_op(tls->wasV2Handshake, OP_EQ, 0);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
SSL_free(ssl);
SSL_CTX_free(ctx);
tor_free(tls);
@@ -1951,7 +1933,7 @@ test_tortls_shutdown(void *ignored)
int ret;
tor_tls_t *tls;
SSL_METHOD *method = give_me_a_test_method();
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
tls = tor_malloc_zero(sizeof(tor_tls_t));
tls->ssl = tor_malloc_zero(sizeof(SSL));
@@ -2034,7 +2016,7 @@ test_tortls_shutdown(void *ignored)
#endif
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
tor_free(method);
tor_free(tls->ssl);
tor_free(tls);
@@ -2058,7 +2040,7 @@ test_tortls_read(void *ignored)
tor_tls_t *tls;
char buf[100];
SSL_METHOD *method = give_me_a_test_method();
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
tls = tor_malloc_zero(sizeof(tor_tls_t));
tls->ssl = tor_malloc_zero(sizeof(SSL));
@@ -2106,7 +2088,7 @@ test_tortls_read(void *ignored)
// TODO: fill up
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
tor_free(tls->ssl);
tor_free(tls);
tor_free(method);
@@ -2131,7 +2113,7 @@ test_tortls_write(void *ignored)
tor_tls_t *tls;
SSL_METHOD *method = give_me_a_test_method();
char buf[100];
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
tls = tor_malloc_zero(sizeof(tor_tls_t));
tls->ssl = tor_malloc_zero(sizeof(SSL));
@@ -2171,7 +2153,7 @@ test_tortls_write(void *ignored)
tt_int_op(ret, OP_EQ, TOR_TLS_WANTWRITE);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
BIO_free(tls->ssl->rbio);
tor_free(tls->ssl);
tor_free(tls);
@@ -2216,7 +2198,7 @@ test_tortls_handshake(void *ignored)
tor_tls_t *tls;
SSL_CTX *ctx;
SSL_METHOD *method = give_me_a_test_method();
- int previous_log = setup_capture_of_logs(LOG_INFO);
+ setup_capture_of_logs(LOG_INFO);
SSL_library_init();
SSL_load_error_strings();
@@ -2279,7 +2261,7 @@ test_tortls_handshake(void *ignored)
expect_log_severity(LOG_WARN);
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
SSL_free(tls->ssl);
SSL_CTX_free(ctx);
tor_free(tls);
@@ -2313,9 +2295,13 @@ test_tortls_finish_handshake(void *ignored)
tls->isServer = 1;
tls->wasV2Handshake = 0;
+ setup_full_capture_of_logs(LOG_WARN);
ret = tor_tls_finish_handshake(tls);
tt_int_op(ret, OP_EQ, 0);
tt_int_op(tls->wasV2Handshake, OP_EQ, 1);
+ expect_single_log_msg_containing("For some reason, wasV2Handshake didn't "
+ "get set.");
+ teardown_capture_of_logs();
tls->wasV2Handshake = 1;
ret = tor_tls_finish_handshake(tls);
@@ -2354,6 +2340,7 @@ test_tortls_finish_handshake(void *ignored)
tor_free(tls);
SSL_CTX_free(ctx);
tor_free(method);
+ teardown_capture_of_logs();
}
#endif
@@ -2440,6 +2427,8 @@ test_tortls_context_new(void *ignored)
ret = tor_tls_context_new(NULL, 0, 0, 0);
tt_assert(!ret);
+ /* note: we already override this in testing_common.c, so we
+ * run this unit test in a subprocess. */
MOCK(crypto_pk_generate_key_with_bits,
fixed_crypto_pk_generate_key_with_bits);
fixed_crypto_pk_new_result_index = 0;
@@ -2826,7 +2815,7 @@ struct testcase_t tortls_tests[] = {
INTRUSIVE_TEST_CASE(find_cipher_by_id, 0),
INTRUSIVE_TEST_CASE(session_secret_cb, 0),
INTRUSIVE_TEST_CASE(debug_state_callback, 0),
- INTRUSIVE_TEST_CASE(context_new, 0),
+ INTRUSIVE_TEST_CASE(context_new, TT_FORK /* redundant */),
LOCAL_TEST_CASE(create_certificate, 0),
LOCAL_TEST_CASE(cert_new, 0),
LOCAL_TEST_CASE(cert_is_valid, 0),
diff --git a/src/test/test_util.c b/src/test/test_util.c
index d534cc0b52..0b707caeeb 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -5,6 +5,7 @@
#include "orconfig.h"
#define COMPAT_PRIVATE
+#define COMPAT_TIME_PRIVATE
#define CONTROL_PRIVATE
#define UTIL_PRIVATE
#include "or.h"
@@ -13,6 +14,7 @@
#include "test.h"
#include "memarea.h"
#include "util_process.h"
+#include "log_test_helpers.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
@@ -30,6 +32,9 @@
#include <ctype.h>
#include <float.h>
+#define INFINITY_DBL ((double)INFINITY)
+#define NAN_DBL ((double)NAN)
+
/* XXXX this is a minimal wrapper to make the unit tests compile with the
* changed tor_timegm interface. */
static time_t
@@ -258,7 +263,7 @@ test_util_time(void *arg)
int i;
struct timeval tv;
- /* Test tv_udiff */
+ /* Test tv_udiff and tv_mdiff */
(void)arg;
start.tv_sec = 5;
@@ -268,22 +273,312 @@ test_util_time(void *arg)
end.tv_usec = 5000;
tt_int_op(0L,OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(0L,OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(0L,OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(0L,OP_EQ, tv_mdiff(&end, &start));
end.tv_usec = 7000;
tt_int_op(2000L,OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(2L,OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-2000L,OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-2L,OP_EQ, tv_mdiff(&end, &start));
end.tv_sec = 6;
tt_int_op(1002000L,OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(1002L,OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-1002000L,OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-1002L,OP_EQ, tv_mdiff(&end, &start));
end.tv_usec = 0;
tt_int_op(995000L,OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(995L,OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-995000L,OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-995L,OP_EQ, tv_mdiff(&end, &start));
end.tv_sec = 4;
tt_int_op(-1005000L,OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(-1005L,OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(1005000L,OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(1005L,OP_EQ, tv_mdiff(&end, &start));
+
+ /* Negative tv_sec values, these will break on platforms where tv_sec is
+ * unsigned */
+
+ end.tv_sec = -10;
+
+ tt_int_op(-15005000L,OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(-15005L,OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(15005000L,OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(15005L,OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = -100;
+
+ tt_int_op(89995000L,OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(89995L,OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-89995000L,OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-89995L,OP_EQ, tv_mdiff(&end, &start));
+
+ /* Test that tv_usec values round away from zero when converted to msec */
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = 10;
+ end.tv_usec = 499;
+
+ tt_int_op(10000499L, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(10000L, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-10000499L, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-10000L, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = 10;
+ end.tv_usec = 500;
+
+ tt_int_op(10000500L, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(10001L, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-10000500L, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-10000L, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = 10;
+ end.tv_usec = 501;
+
+ tt_int_op(10000501L, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(10001L, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-10000501L, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-10001L, OP_EQ, tv_mdiff(&end, &start));
+
+ /* Overflow conditions */
+
+#ifdef _WIN32
+ /* Would you believe that tv_sec is a long on windows? Of course you would.*/
+#define TV_SEC_MAX LONG_MAX
+#define TV_SEC_MIN LONG_MIN
+#else
+ /* Some BSDs have struct timeval.tv_sec 64-bit, but time_t (and long) 32-bit
+ * Which means TIME_MAX is not actually the maximum value of tv_sec.
+ * But that's ok for the moment, because the code correctly performs 64-bit
+ * calculations internally, then catches the overflow. */
+#define TV_SEC_MAX TIME_MAX
+#define TV_SEC_MIN TIME_MIN
+#endif
+
+/* Assume tv_usec is an unsigned integer until proven otherwise */
+#define TV_USEC_MAX UINT_MAX
+#define TOR_USEC_PER_SEC 1000000
+
+ /* Overflows in the result type */
+
+ /* All comparisons work */
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = LONG_MAX/1000 - 2;
+ end.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(end.tv_sec*1000L, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-end.tv_sec*1000L, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = LONG_MAX/1000000 - 1;
+ end.tv_usec = 0;
+
+ tt_int_op(end.tv_sec*1000000L, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(end.tv_sec*1000L, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(-end.tv_sec*1000000L, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-end.tv_sec*1000L, OP_EQ, tv_mdiff(&end, &start));
+
+ /* No comparisons work */
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = LONG_MAX/1000 + 1;
+ end.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = LONG_MAX/1000000 + 1;
+ end.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(end.tv_sec*1000L, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-end.tv_sec*1000L, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = LONG_MAX/1000;
+ end.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+ end.tv_sec = LONG_MAX/1000000;
+ end.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op((end.tv_sec + 1)*1000L, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(-(end.tv_sec + 1)*1000L, OP_EQ, tv_mdiff(&end, &start));
+
+ /* Overflows on comparison to zero */
+
+ start.tv_sec = 0;
+ start.tv_usec = 0;
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = 0;
+ end.tv_usec = TV_USEC_MAX;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = TV_USEC_MAX;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = 0;
+ end.tv_usec = 0;
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = TV_USEC_MAX;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ /* overflows on comparison to maxima / minima */
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = 0;
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = 0;
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ /* overflows on comparison to maxima / minima with extra usec */
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = TOR_USEC_PER_SEC;
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ end.tv_sec = TV_SEC_MAX;
+ end.tv_usec = TOR_USEC_PER_SEC;
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = 0;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
+
+ start.tv_sec = TV_SEC_MIN;
+ start.tv_usec = TOR_USEC_PER_SEC;
+
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&start, &end));
+ tt_int_op(LONG_MAX, OP_EQ, tv_udiff(&end, &start));
+ tt_int_op(LONG_MAX, OP_EQ, tv_mdiff(&end, &start));
/* Test tor_timegm & tor_gmtime_r */
@@ -349,31 +644,57 @@ test_util_time(void *arg)
/* Test tor_timegm out of range */
+ /* The below tests will all cause a BUG message, so we capture, suppress,
+ * and detect. */
+#define CAPTURE() do { \
+ 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())); \
+ teardown_capture_of_logs(); \
+ } while (0)
+
+#define CHECK_TIMEGM_ARG_OUT_OF_RANGE(msg) \
+ CHECK_TIMEGM_WARNING("Out-of-range argument to tor_timegm")
+
/* year */
/* Wrong year < 1970 */
a_time.tm_year = 1969-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = -1-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
#if SIZEOF_INT == 4 || SIZEOF_INT == 8
a_time.tm_year = -1*(1 << 16);
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* one of the smallest tm_year values my 64 bit system supports:
* t_res = -9223372036854775LL without clamping */
a_time.tm_year = -292275055-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = INT32_MIN;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
#endif
#if SIZEOF_INT == 8
a_time.tm_year = -1*(1 << 48);
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* while unlikely, the system's gmtime(_r) could return
* a "correct" retrospective gregorian negative year value,
@@ -381,25 +702,35 @@ test_util_time(void *arg)
* -1*(2^63)/60/60/24*2000/730485 + 1970 = -292277022657
* 730485 is the number of days in two millenia, including leap days */
a_time.tm_year = -292277022657-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = INT64_MIN;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
#endif
/* Wrong year >= INT32_MAX - 1900 */
#if SIZEOF_INT == 4 || SIZEOF_INT == 8
a_time.tm_year = INT32_MAX-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = INT32_MAX;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
#endif
#if SIZEOF_INT == 8
/* one of the largest tm_year values my 64 bit system supports */
a_time.tm_year = 292278994-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* while unlikely, the system's gmtime(_r) could return
* a "correct" proleptic gregorian year value,
@@ -407,72 +738,104 @@ test_util_time(void *arg)
* (2^63-1)/60/60/24*2000/730485 + 1970 = 292277026596
* 730485 is the number of days in two millenia, including leap days */
a_time.tm_year = 292277026596-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = INT64_MAX-1900;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = INT64_MAX;
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
#endif
/* month */
a_time.tm_year = 2007-1900; /* restore valid year */
a_time.tm_mon = 12; /* Wrong month, it's 0-based */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_mon = -1; /* Wrong month */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* day */
a_time.tm_mon = 6; /* Try July */
a_time.tm_mday = 32; /* Wrong day */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_mon = 5; /* Try June */
a_time.tm_mday = 31; /* Wrong day */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = 2008-1900; /* Try a leap year */
a_time.tm_mon = 1; /* in feb. */
a_time.tm_mday = 30; /* Wrong day */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_year = 2011-1900; /* Try a non-leap year */
a_time.tm_mon = 1; /* in feb. */
a_time.tm_mday = 29; /* Wrong day */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_mday = 0; /* Wrong day, it's 1-based (to be different) */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* hour */
a_time.tm_mday = 3; /* restore valid month day */
a_time.tm_hour = 24; /* Wrong hour, it's 0-based */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_hour = -1; /* Wrong hour */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* minute */
a_time.tm_hour = 22; /* restore valid hour */
a_time.tm_min = 60; /* Wrong minute, it's 0-based */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_min = -1; /* Wrong minute */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* second */
a_time.tm_min = 37; /* restore valid minute */
a_time.tm_sec = 61; /* Wrong second: 0-based with leap seconds */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
a_time.tm_sec = -1; /* Wrong second */
+ CAPTURE();
tt_int_op((time_t) -1,OP_EQ, tor_timegm(&a_time));
+ CHECK_TIMEGM_ARG_OUT_OF_RANGE();
/* Test tor_gmtime_r out of range */
@@ -512,9 +875,17 @@ test_util_time(void *arg)
* 730485 is the number of days in two millenia, including leap days
* (int64_t)b_time.tm_year == (-292277022657LL-1900LL) without clamping */
t_res = INT64_MIN;
+ CAPTURE();
tor_gmtime_r(&t_res, &b_time);
- tt_assert(b_time.tm_year == (1970-1900) ||
- b_time.tm_year == (1-1900));
+ if (! (b_time.tm_year == (1970-1900) ||
+ b_time.tm_year == (1-1900))) {
+ tt_int_op(b_time.tm_year, OP_EQ, 1970-1900);
+ }
+ if (b_time.tm_year != 1970-1900) {
+ CHECK_TIMEGM_WARNING("Rounding up to ");
+ } else {
+ teardown_capture_of_logs();
+ }
}
#endif
@@ -550,7 +921,10 @@ test_util_time(void *arg)
* 730485 is the number of days in two millenia, including leap days
* (int64_t)b_time.tm_year == (292277026596L-1900L) without clamping */
t_res = INT64_MAX;
+ CAPTURE();
tor_gmtime_r(&t_res, &b_time);
+ CHECK_TIMEGM_WARNING("Rounding down to ");
+
tt_assert(b_time.tm_year == (2037-1900) ||
b_time.tm_year == (9999-1900));
}
@@ -622,9 +996,16 @@ test_util_time(void *arg)
parse_rfc1123_time("Wed, 30 Ene 2011 23:59:59 GMT", &t_res));
tt_int_op(-1,OP_EQ,
parse_rfc1123_time("Wed, 30 Mar 2011 23:59:59 GM", &t_res));
+ tt_int_op(-1,OP_EQ,
+ parse_rfc1123_time("Wed, 30 Mar 1900 23:59:59 GMT", &t_res));
+ /* Leap year. */
tt_int_op(-1,OP_EQ,
parse_rfc1123_time("Wed, 29 Feb 2011 16:00:00 GMT", &t_res));
+ tt_int_op(0,OP_EQ,
+ parse_rfc1123_time("Wed, 29 Feb 2012 16:00:00 GMT", &t_res));
+
+ /* Leap second plus one */
tt_int_op(-1,OP_EQ,
parse_rfc1123_time("Wed, 30 Mar 2011 23:59:61 GMT", &t_res));
@@ -732,8 +1113,11 @@ test_util_time(void *arg)
#endif
#endif
+#undef CAPTURE
+#undef CHECK_TIMEGM_ARG_OUT_OF_RANGE
+
done:
- ;
+ teardown_capture_of_logs();
}
static void
@@ -865,106 +1249,106 @@ test_util_config_line(void *arg)
, sizeof(buf));
str = buf;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k");
tt_str_op(v,OP_EQ, "v");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "key value with"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "key");
tt_str_op(v,OP_EQ, "value with spaces");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "keykey"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "keykey");
tt_str_op(v,OP_EQ, "val");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "k2\n"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k2");
tt_str_op(v,OP_EQ, "");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "k3 \n"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k3");
tt_str_op(v,OP_EQ, "");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "#comment"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k4");
tt_str_op(v,OP_EQ, "");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "k5#abc"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k5");
tt_str_op(v,OP_EQ, "");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "k6"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k6");
tt_str_op(v,OP_EQ, "val");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "kseven"));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "kseven");
tt_str_op(v,OP_EQ, "a quoted \'string");
tor_free(k); tor_free(v);
tt_assert(!strcmpstart(str, "k8 "));
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k8");
tt_str_op(v,OP_EQ, "a quoted\n\"str\\ing\t\x01\x01\x01\"");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k9");
tt_str_op(v,OP_EQ, "a line that spans two lines.");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k10");
tt_str_op(v,OP_EQ, "more than one continuation");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k11");
tt_str_op(v,OP_EQ, "continuation at the start");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k12");
tt_str_op(v,OP_EQ, "line with a embedded");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k13");
tt_str_op(v,OP_EQ, "continuation at the very start");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k14");
tt_str_op(v,OP_EQ, "a line that has a comment and" );
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k15");
tt_str_op(v,OP_EQ, "this should be the next new line");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k16");
tt_str_op(v,OP_EQ, "a line that has a comment and" );
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k17");
tt_str_op(v,OP_EQ, "this should be the next new line");
tor_free(k); tor_free(v);
@@ -999,32 +1383,36 @@ test_util_config_line_quotes(void *arg)
, sizeof(buf4));
str = buf1;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "kTrailingSpace");
tt_str_op(v,OP_EQ, "quoted value");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
str = buf2;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
str = buf3;
- str = parse_config_line_from_str(str, &k, &v);
+ const char *err = NULL;
+ str = parse_config_line_from_str_verbose(str, &k, &v, &err);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
+ tt_str_op(err, OP_EQ, "Invalid escape sequence in quoted string");
str = buf4;
- str = parse_config_line_from_str(str, &k, &v);
+ err = NULL;
+ str = parse_config_line_from_str_verbose(str, &k, &v, &err);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
+ tt_str_op(err, OP_EQ, "Invalid escape sequence in quoted string");
done:
tor_free(k);
@@ -1046,12 +1434,12 @@ test_util_config_line_comment_character(void *arg)
, sizeof(buf));
str = buf;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k1");
tt_str_op(v,OP_EQ, "# in quotes");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "k2");
tt_str_op(v,OP_EQ, "some value");
tor_free(k); tor_free(v);
@@ -1059,7 +1447,7 @@ test_util_config_line_comment_character(void *arg)
tt_str_op(str,OP_EQ, "k3 /home/user/myTorNetwork#2\n");
#if 0
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
test_streq(k, "k3");
test_streq(v, "/home/user/myTorNetwork#2");
tor_free(k); tor_free(v);
@@ -1116,57 +1504,57 @@ test_util_config_line_escaped_content(void *arg)
str = buf1;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "HexadecimalLower");
tt_str_op(v,OP_EQ, "*");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "HexadecimalUpper");
tt_str_op(v,OP_EQ, "*");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "HexadecimalUpperX");
tt_str_op(v,OP_EQ, "*");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "Octal");
tt_str_op(v,OP_EQ, "*");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "Newline");
tt_str_op(v,OP_EQ, "\n");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "Tab");
tt_str_op(v,OP_EQ, "\t");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "CarriageReturn");
tt_str_op(v,OP_EQ, "\r");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "DoubleQuote");
tt_str_op(v,OP_EQ, "\"");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "SimpleQuote");
tt_str_op(v,OP_EQ, "'");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "Backslash");
tt_str_op(v,OP_EQ, "\\");
tor_free(k); tor_free(v);
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_str_op(k,OP_EQ, "Mix");
tt_str_op(v,OP_EQ, "This is a \"star\":\t'*'\nAnd second line");
tor_free(k); tor_free(v);
@@ -1174,41 +1562,115 @@ test_util_config_line_escaped_content(void *arg)
str = buf2;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
str = buf3;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
str = buf4;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
#if 0
str = buf5;
- str = parse_config_line_from_str(str, &k, &v);
+ 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
str = buf6;
- str = parse_config_line_from_str(str, &k, &v);
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
+ tt_ptr_op(str,OP_EQ, NULL);
+ tor_free(k); tor_free(v);
+
+ /* more things to try. */
+ /* Bad hex: */
+ strlcpy(buf1, "Foo \"\\x9g\"\n", sizeof(buf1));
+ strlcpy(buf2, "Foo \"\\xg0\"\n", sizeof(buf2));
+ strlcpy(buf3, "Foo \"\\xf\"\n", sizeof(buf3));
+ /* bad escape */
+ strlcpy(buf4, "Foo \"\\q\"\n", sizeof(buf4));
+ /* missing endquote */
+ strlcpy(buf5, "Foo \"hello\n", sizeof(buf5));
+ /* extra stuff */
+ strlcpy(buf6, "Foo \"hello\" world\n", sizeof(buf6));
+
+ str=buf1;
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
+ tt_ptr_op(str,OP_EQ, NULL);
+ tor_free(k); tor_free(v);
+
+ str=buf2;
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
+ tt_ptr_op(str,OP_EQ, NULL);
+ tor_free(k); tor_free(v);
+
+ str=buf3;
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
+ tt_ptr_op(str,OP_EQ, NULL);
+ tor_free(k); tor_free(v);
+
+ str=buf4;
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
+ tt_ptr_op(str,OP_EQ, NULL);
+ tor_free(k); tor_free(v);
+
+ str=buf5;
+
+ str = parse_config_line_from_str_verbose(str, &k, &v, NULL);
tt_ptr_op(str,OP_EQ, NULL);
tor_free(k); tor_free(v);
+ str=buf6;
+ const char *err = NULL;
+ str = parse_config_line_from_str_verbose(str, &k, &v, &err);
+ tt_ptr_op(str,OP_EQ, NULL);
+ tor_free(k); tor_free(v);
+ tt_str_op(err,OP_EQ, "Excess data after quoted string");
+
done:
tor_free(k);
tor_free(v);
}
+static void
+test_util_config_line_crlf(void *arg)
+{
+ char *k=NULL, *v=NULL;
+ const char *err = NULL;
+ (void)arg;
+ const char *str =
+ "Hello world\r\n"
+ "Hello \"nice big world\"\r\n";
+
+ 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,"world");
+ tt_assert(!err);
+ 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);
+ tor_free(k); tor_free(v);
+ tt_str_op(str,OP_EQ, "");
+
+ done:
+ tor_free(k); tor_free(v);
+}
+
#ifndef _WIN32
static void
test_util_expand_filename(void *arg)
@@ -1372,8 +1834,7 @@ static void
test_util_strmisc(void *arg)
{
char buf[1024];
- int i;
- char *cp, *cp_tmp = NULL;
+ char *cp_tmp = NULL;
/* Test strl operations */
(void)arg;
@@ -1398,117 +1859,6 @@ test_util_strmisc(void *arg)
tor_strstrip(buf, "!? ");
tt_str_op(buf,OP_EQ, "Testing123");
- /* Test parse_long */
- /* Empty/zero input */
- tt_int_op(0L,OP_EQ, tor_parse_long("",10,0,100,&i,NULL));
- tt_int_op(0,OP_EQ, i);
- tt_int_op(0L,OP_EQ, tor_parse_long("0",10,0,100,&i,NULL));
- tt_int_op(1,OP_EQ, i);
- /* Normal cases */
- tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,100,&i,NULL));
- tt_int_op(1,OP_EQ, i);
- tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,10,&i,NULL));
- tt_int_op(1,OP_EQ, i);
- tt_int_op(10L,OP_EQ, tor_parse_long("10",10,10,100,&i,NULL));
- tt_int_op(1,OP_EQ, i);
- tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,100,&i,NULL));
- tt_int_op(1,OP_EQ, i);
- tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,0,&i,NULL));
- tt_int_op(1,OP_EQ, i);
- tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-50,0,&i,NULL));
- tt_int_op(1,OP_EQ, i);
- /* Extra garbage */
- tt_int_op(0L,OP_EQ, tor_parse_long("10m",10,0,100,&i,NULL));
- tt_int_op(0,OP_EQ, i);
- tt_int_op(0L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,NULL));
- tt_int_op(0,OP_EQ, i);
- tt_int_op(10L,OP_EQ, tor_parse_long("10m",10,0,100,&i,&cp));
- tt_int_op(1,OP_EQ, i);
- tt_str_op(cp,OP_EQ, "m");
- tt_int_op(-50L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,&cp));
- tt_int_op(1,OP_EQ, i);
- tt_str_op(cp,OP_EQ, " plus garbage");
- /* Out of bounds */
- tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,100,&i,NULL));
- tt_int_op(0,OP_EQ, i);
- tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,0,100,&i,NULL));
- tt_int_op(0,OP_EQ, i);
- /* Base different than 10 */
- tt_int_op(2L,OP_EQ, tor_parse_long("10",2,0,100,NULL,NULL));
- tt_int_op(0L,OP_EQ, tor_parse_long("2",2,0,100,NULL,NULL));
- tt_int_op(0L,OP_EQ, tor_parse_long("10",-2,0,100,NULL,NULL));
- tt_int_op(68284L,OP_EQ, tor_parse_long("10abc",16,0,70000,NULL,NULL));
- tt_int_op(68284L,OP_EQ, tor_parse_long("10ABC",16,0,70000,NULL,NULL));
- tt_int_op(0,OP_EQ, tor_parse_long("10ABC",-1,0,70000,&i,NULL));
- tt_int_op(i,OP_EQ, 0);
-
- /* Test parse_ulong */
- tt_int_op(0UL,OP_EQ, tor_parse_ulong("",10,0,100,NULL,NULL));
- tt_int_op(0UL,OP_EQ, tor_parse_ulong("0",10,0,100,NULL,NULL));
- tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,100,NULL,NULL));
- tt_int_op(0UL,OP_EQ, tor_parse_ulong("10",10,50,100,NULL,NULL));
- tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,10,NULL,NULL));
- tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,10,100,NULL,NULL));
- tt_int_op(0UL,OP_EQ, tor_parse_ulong("8",8,0,100,NULL,NULL));
- tt_int_op(50UL,OP_EQ, tor_parse_ulong("50",10,50,100,NULL,NULL));
- tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,-100,100,NULL,NULL));
- tt_int_op(0UL,OP_EQ, tor_parse_ulong("50",-1,50,100,&i,NULL));
- tt_int_op(0,OP_EQ, i);
-
- /* Test parse_uint64 */
- tt_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp));
- tt_int_op(1,OP_EQ, i);
- tt_str_op(cp,OP_EQ, " x");
- tt_assert(U64_LITERAL(12345678901) ==
- tor_parse_uint64("12345678901",10,0,UINT64_MAX, &i, &cp));
- tt_int_op(1,OP_EQ, i);
- tt_str_op(cp,OP_EQ, "");
- tt_assert(U64_LITERAL(0) ==
- tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp));
- tt_int_op(0,OP_EQ, i);
- tt_assert(U64_LITERAL(0) ==
- tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp));
- tt_int_op(0,OP_EQ, i);
-
- {
- /* Test parse_double */
- double d = tor_parse_double("10", 0, UINT64_MAX,&i,NULL);
- tt_int_op(1,OP_EQ, i);
- tt_assert(DBL_TO_U64(d) == 10);
- d = tor_parse_double("0", 0, UINT64_MAX,&i,NULL);
- tt_int_op(1,OP_EQ, i);
- tt_assert(DBL_TO_U64(d) == 0);
- d = tor_parse_double(" ", 0, UINT64_MAX,&i,NULL);
- tt_int_op(0,OP_EQ, i);
- d = tor_parse_double(".0a", 0, UINT64_MAX,&i,NULL);
- tt_int_op(0,OP_EQ, i);
- d = tor_parse_double(".0a", 0, UINT64_MAX,&i,&cp);
- tt_int_op(1,OP_EQ, i);
- d = tor_parse_double("-.0", 0, UINT64_MAX,&i,NULL);
- tt_int_op(1,OP_EQ, i);
- tt_assert(DBL_TO_U64(d) == 0);
- d = tor_parse_double("-10", -100.0, 100.0,&i,NULL);
- tt_int_op(1,OP_EQ, i);
- tt_int_op(-10.0,OP_EQ, d);
- }
-
- {
- /* Test tor_parse_* where we overflow/underflow the underlying type. */
- /* This string should overflow 64-bit ints. */
-#define TOOBIG "100000000000000000000000000"
- tt_int_op(0L, OP_EQ,
- tor_parse_long(TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL));
- tt_int_op(i,OP_EQ, 0);
- tt_int_op(0L,OP_EQ,
- tor_parse_long("-"TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL));
- tt_int_op(i,OP_EQ, 0);
- tt_int_op(0UL,OP_EQ, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL));
- tt_int_op(i,OP_EQ, 0);
- tt_u64_op(U64_LITERAL(0), OP_EQ, tor_parse_uint64(TOOBIG, 10,
- 0, UINT64_MAX, &i, NULL));
- tt_int_op(i,OP_EQ, 0);
- }
-
/* Test snprintf */
/* Returning -1 when there's not enough room in the output buffer */
tt_int_op(-1,OP_EQ, tor_snprintf(buf, 0, "Foo"));
@@ -1583,6 +1933,17 @@ test_util_strmisc(void *arg)
tt_str_op("\"z\\001abc\\277d\"",OP_EQ, escaped("z\001abc\277d"));
tt_str_op("\"z\\336\\255 ;foo\"",OP_EQ, escaped("z\xde\xad\x20;foo"));
+ /* Other cases of esc_for_log{,_len} */
+ cp_tmp = esc_for_log(NULL);
+ tt_str_op(cp_tmp, OP_EQ, "(null)");
+ tor_free(cp_tmp);
+ cp_tmp = esc_for_log_len("abcdefg", 3);
+ tt_str_op(cp_tmp, OP_EQ, "\"abc\"");
+ tor_free(cp_tmp);
+ cp_tmp = esc_for_log_len("abcdefg", 100);
+ tt_str_op(cp_tmp, OP_EQ, "\"abcdefg\"");
+ tor_free(cp_tmp);
+
/* Test strndup and memdup */
{
const char *s = "abcdefghijklmnopqrstuvwxyz";
@@ -1642,9 +2003,9 @@ test_util_strmisc(void *arg)
/* Test hex_str */
{
char binary_data[68];
- size_t i;
- for (i = 0; i < sizeof(binary_data); ++i)
- binary_data[i] = i;
+ size_t idx;
+ for (idx = 0; idx < sizeof(binary_data); ++idx)
+ binary_data[idx] = idx;
tt_str_op(hex_str(binary_data, 0),OP_EQ, "");
tt_str_op(hex_str(binary_data, 1),OP_EQ, "00");
tt_str_op(hex_str(binary_data, 17),OP_EQ,
@@ -1687,6 +2048,144 @@ test_util_strmisc(void *arg)
}
static void
+test_util_parse_integer(void *arg)
+{
+ (void)arg;
+ int i;
+ char *cp;
+
+ /* Test parse_long */
+ /* Empty/zero input */
+ tt_int_op(0L,OP_EQ, tor_parse_long("",10,0,100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ tt_int_op(0L,OP_EQ, tor_parse_long("0",10,0,100,&i,NULL));
+ tt_int_op(1,OP_EQ, i);
+ /* Normal cases */
+ tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,100,&i,NULL));
+ tt_int_op(1,OP_EQ, i);
+ tt_int_op(10L,OP_EQ, tor_parse_long("10",10,0,10,&i,NULL));
+ tt_int_op(1,OP_EQ, i);
+ tt_int_op(10L,OP_EQ, tor_parse_long("10",10,10,100,&i,NULL));
+ tt_int_op(1,OP_EQ, i);
+ tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,100,&i,NULL));
+ tt_int_op(1,OP_EQ, i);
+ tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-100,0,&i,NULL));
+ tt_int_op(1,OP_EQ, i);
+ tt_int_op(-50L,OP_EQ, tor_parse_long("-50",10,-50,0,&i,NULL));
+ tt_int_op(1,OP_EQ, i);
+ /* Extra garbage */
+ tt_int_op(0L,OP_EQ, tor_parse_long("10m",10,0,100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ tt_int_op(0L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ tt_int_op(10L,OP_EQ, tor_parse_long("10m",10,0,100,&i,&cp));
+ tt_int_op(1,OP_EQ, i);
+ tt_str_op(cp,OP_EQ, "m");
+ tt_int_op(-50L,OP_EQ, tor_parse_long("-50 plus garbage",10,-100,100,&i,&cp));
+ tt_int_op(1,OP_EQ, i);
+ tt_str_op(cp,OP_EQ, " plus garbage");
+ /* Illogical min max */
+ tor_capture_bugs_(1);
+ tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,4,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_()));
+ tt_str_op("!(max < min)", OP_EQ,
+ smartlist_get(tor_get_captured_bug_log_(), 0));
+ tor_end_capture_bugs_();
+ tor_capture_bugs_(1);
+ tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,100,-100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ tt_int_op(1, OP_EQ, smartlist_len(tor_get_captured_bug_log_()));
+ tt_str_op("!(max < min)", OP_EQ,
+ smartlist_get(tor_get_captured_bug_log_(), 0));
+ tor_end_capture_bugs_();
+ /* Out of bounds */
+ tt_int_op(0L,OP_EQ, tor_parse_long("10",10,50,100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ tt_int_op(0L,OP_EQ, tor_parse_long("-50",10,0,100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ /* Base different than 10 */
+ tt_int_op(2L,OP_EQ, tor_parse_long("10",2,0,100,NULL,NULL));
+ tt_int_op(0L,OP_EQ, tor_parse_long("2",2,0,100,NULL,NULL));
+ tt_int_op(0L,OP_EQ, tor_parse_long("10",-2,0,100,NULL,NULL));
+ tt_int_op(68284L,OP_EQ, tor_parse_long("10abc",16,0,70000,NULL,NULL));
+ tt_int_op(68284L,OP_EQ, tor_parse_long("10ABC",16,0,70000,NULL,NULL));
+ tt_int_op(0,OP_EQ, tor_parse_long("10ABC",-1,0,70000,&i,NULL));
+ tt_int_op(i,OP_EQ, 0);
+
+ /* Test parse_ulong */
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong("",10,0,100,NULL,NULL));
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong("0",10,0,100,NULL,NULL));
+ tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,100,NULL,NULL));
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong("10",10,50,100,NULL,NULL));
+ tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,0,10,NULL,NULL));
+ tt_int_op(10UL,OP_EQ, tor_parse_ulong("10",10,10,100,NULL,NULL));
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong("8",8,0,100,NULL,NULL));
+ tt_int_op(50UL,OP_EQ, tor_parse_ulong("50",10,50,100,NULL,NULL));
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,0,100,NULL,NULL));
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong("50",-1,50,100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong("-50",10,0,100,&i,NULL));
+ tt_int_op(0,OP_EQ, i);
+
+ /* Test parse_uint64 */
+ tt_assert(U64_LITERAL(10) == tor_parse_uint64("10 x",10,0,100, &i, &cp));
+ tt_int_op(1,OP_EQ, i);
+ tt_str_op(cp,OP_EQ, " x");
+ tt_assert(U64_LITERAL(12345678901) ==
+ tor_parse_uint64("12345678901",10,0,UINT64_MAX, &i, &cp));
+ tt_int_op(1,OP_EQ, i);
+ tt_str_op(cp,OP_EQ, "");
+ tt_assert(U64_LITERAL(0) ==
+ tor_parse_uint64("12345678901",10,500,INT32_MAX, &i, &cp));
+ tt_int_op(0,OP_EQ, i);
+ tt_assert(U64_LITERAL(0) ==
+ tor_parse_uint64("123",-1,0,INT32_MAX, &i, &cp));
+ tt_int_op(0,OP_EQ, i);
+
+ {
+ /* Test parse_double */
+ double d = tor_parse_double("10", 0, (double)UINT64_MAX,&i,NULL);
+ tt_int_op(1,OP_EQ, i);
+ tt_assert(DBL_TO_U64(d) == 10);
+ d = tor_parse_double("0", 0, (double)UINT64_MAX,&i,NULL);
+ tt_int_op(1,OP_EQ, i);
+ tt_assert(DBL_TO_U64(d) == 0);
+ d = tor_parse_double(" ", 0, (double)UINT64_MAX,&i,NULL);
+ tt_int_op(0,OP_EQ, i);
+ d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,NULL);
+ tt_int_op(0,OP_EQ, i);
+ d = tor_parse_double(".0a", 0, (double)UINT64_MAX,&i,&cp);
+ tt_int_op(1,OP_EQ, i);
+ d = tor_parse_double("-.0", 0, (double)UINT64_MAX,&i,NULL);
+ tt_int_op(1,OP_EQ, i);
+ tt_assert(DBL_TO_U64(d) == 0);
+ d = tor_parse_double("-10", -100.0, 100.0,&i,NULL);
+ tt_int_op(1,OP_EQ, i);
+ tt_double_op(fabs(d - -10.0),OP_LT, 1E-12);
+ }
+
+ {
+ /* Test tor_parse_* where we overflow/underflow the underlying type. */
+ /* This string should overflow 64-bit ints. */
+#define TOOBIG "100000000000000000000000000"
+ tt_int_op(0L, OP_EQ,
+ tor_parse_long(TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL));
+ tt_int_op(i,OP_EQ, 0);
+ tt_int_op(0L,OP_EQ,
+ tor_parse_long("-"TOOBIG, 10, LONG_MIN, LONG_MAX, &i, NULL));
+ tt_int_op(i,OP_EQ, 0);
+ tt_int_op(0UL,OP_EQ, tor_parse_ulong(TOOBIG, 10, 0, ULONG_MAX, &i, NULL));
+ tt_int_op(i,OP_EQ, 0);
+ tt_u64_op(U64_LITERAL(0), OP_EQ, tor_parse_uint64(TOOBIG, 10,
+ 0, UINT64_MAX, &i, NULL));
+ tt_int_op(i,OP_EQ, 0);
+ }
+ done:
+ tor_end_capture_bugs_();
+}
+
+static void
test_util_pow2(void *arg)
{
/* Test tor_log2(). */
@@ -1737,22 +2236,21 @@ test_util_gzip(void *arg)
(void)arg;
buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ");
tt_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD);
- if (is_gzip_supported()) {
- tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
- GZIP_METHOD));
- tt_assert(buf2);
- tt_assert(len1 < strlen(buf1));
- tt_assert(detect_compression_method(buf2, len1) == GZIP_METHOD);
-
- tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1,
- GZIP_METHOD, 1, LOG_INFO));
- tt_assert(buf3);
- tt_int_op(strlen(buf1) + 1,OP_EQ, len2);
- tt_str_op(buf1,OP_EQ, buf3);
-
- tor_free(buf2);
- tor_free(buf3);
- }
+
+ tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
+ GZIP_METHOD));
+ tt_assert(buf2);
+ tt_assert(len1 < strlen(buf1));
+ tt_assert(detect_compression_method(buf2, len1) == GZIP_METHOD);
+
+ tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1,
+ GZIP_METHOD, 1, LOG_INFO));
+ tt_assert(buf3);
+ tt_int_op(strlen(buf1) + 1,OP_EQ, len2);
+ tt_str_op(buf1,OP_EQ, buf3);
+
+ tor_free(buf2);
+ tor_free(buf3);
tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
ZLIB_METHOD));
@@ -1836,6 +2334,58 @@ test_util_gzip(void *arg)
tor_free(buf1);
}
+static void
+test_util_gzip_compression_bomb(void *arg)
+{
+ /* A 'compression bomb' is a very small object that uncompresses to a huge
+ * one. Most compression formats support them, but they can be a DOS vector.
+ * In Tor we try not to generate them, and we don't accept them.
+ */
+ (void) arg;
+ size_t one_million = 1<<20;
+ char *one_mb = tor_malloc_zero(one_million);
+ char *result = NULL;
+ size_t result_len = 0;
+ tor_zlib_state_t *state = NULL;
+
+ /* Make sure we can't produce a compression bomb */
+ setup_full_capture_of_logs(LOG_WARN);
+ tt_int_op(-1, OP_EQ, tor_gzip_compress(&result, &result_len,
+ one_mb, one_million,
+ ZLIB_METHOD));
+ expect_single_log_msg_containing(
+ "We compressed something and got an insanely high "
+ "compression factor; other Tors would think this "
+ "was a zlib bomb.");
+ teardown_capture_of_logs();
+
+ /* Here's a compression bomb that we made manually. */
+ const char compression_bomb[1039] =
+ { 0x78, 0xDA, 0xED, 0xC1, 0x31, 0x01, 0x00, 0x00, 0x00, 0xC2,
+ 0xA0, 0xF5, 0x4F, 0x6D, 0x08, 0x5F, 0xA0 /* .... */ };
+ tt_int_op(-1, OP_EQ, tor_gzip_uncompress(&result, &result_len,
+ compression_bomb, 1039,
+ ZLIB_METHOD, 0, LOG_WARN));
+
+ /* Now try streaming that. */
+ state = tor_zlib_new(0, ZLIB_METHOD, HIGH_COMPRESSION);
+ tor_zlib_output_t r;
+ const char *inp = compression_bomb;
+ size_t inlen = 1039;
+ do {
+ char *outp = one_mb;
+ size_t outleft = 4096; /* small on purpose */
+ r = tor_zlib_process(state, &outp, &outleft, &inp, &inlen, 0);
+ tt_int_op(inlen, OP_NE, 0);
+ } while (r == TOR_ZLIB_BUF_FULL);
+
+ tt_int_op(r, OP_EQ, TOR_ZLIB_ERR);
+
+ done:
+ tor_free(one_mb);
+ tor_zlib_free(state);
+}
+
/** Run unit tests for mmap() wrapper functionality. */
static void
test_util_mmap(void *arg)
@@ -1950,7 +2500,7 @@ test_util_sscanf(void *arg)
{
unsigned u1, u2, u3;
unsigned long ulng;
- char s1[20], s2[10], s3[10], ch;
+ char s1[20], s2[10], s3[10], ch, *huge = NULL;
int r;
long lng1,lng2;
int int1, int2;
@@ -1962,7 +2512,13 @@ test_util_sscanf(void *arg)
tt_int_op(-1,OP_EQ,
tor_sscanf("wrong", "%5c", s1)); /* %c cannot have a number. */
tt_int_op(-1,OP_EQ, tor_sscanf("hello", "%s", s1)); /* %s needs a number. */
- tt_int_op(-1,OP_EQ, tor_sscanf("prettylongstring", "%999999s", s1));
+ /* this will fail because we don't allow widths longer than 9999 */
+ {
+ huge = tor_malloc(1000000);
+ r = tor_sscanf("prettylongstring", "%99999s", huge);
+ tor_free(huge);
+ tt_int_op(-1,OP_EQ, r);
+ }
#if 0
/* GCC thinks these two are illegal. */
test_eq(-1, tor_sscanf("prettylongstring", "%0s", s1));
@@ -2068,8 +2624,13 @@ test_util_sscanf(void *arg)
tt_int_op(2,OP_EQ, tor_sscanf("76trombones", "%6u%9s", &u1, s1)); /* %u%s */
tt_int_op(76,OP_EQ, u1);
tt_str_op(s1,OP_EQ, "trombones");
- tt_int_op(1,OP_EQ, tor_sscanf("prettylongstring", "%999s", s1));
- tt_str_op(s1,OP_EQ, "prettylongstring");
+ {
+ huge = tor_malloc(1000);
+ r = tor_sscanf("prettylongstring", "%999s", huge);
+ tt_int_op(1,OP_EQ, r);
+ tt_str_op(huge,OP_EQ, "prettylongstring");
+ tor_free(huge);
+ }
/* %s doesn't eat spaces */
tt_int_op(2,OP_EQ, tor_sscanf("hello world", "%9s %9s", s1, s2));
tt_str_op(s1,OP_EQ, "hello");
@@ -2293,7 +2854,7 @@ test_util_sscanf(void *arg)
test_feq(d4, 3.2);
done:
- ;
+ tor_free(huge);
}
#define tt_char_op(a,op,b) tt_assert_op_type(a,op,b,char,"%c")
@@ -2842,19 +3403,40 @@ test_util_memarea(void *arg)
p1 = memarea_alloc(area, 1);
tt_ptr_op(p1,OP_EQ, p1_orig);
memarea_clear(area);
+ size_t total = 0, initial_allocation, allocation2, dummy;
+ memarea_get_stats(area, &initial_allocation, &dummy);
/* Check for running over an area's size. */
- for (i = 0; i < 512; ++i) {
- p1 = memarea_alloc(area, crypto_rand_int(5)+1);
+ for (i = 0; i < 4096; ++i) {
+ size_t n = crypto_rand_int(6);
+ p1 = memarea_alloc(area, n);
+ total += n;
tt_assert(memarea_owns_ptr(area, p1));
}
memarea_assert_ok(area);
+ memarea_get_stats(area, &allocation2, &dummy);
/* Make sure we can allocate a too-big object. */
p1 = memarea_alloc_zero(area, 9000);
p2 = memarea_alloc_zero(area, 16);
+ total += 9000;
+ total += 16;
tt_assert(memarea_owns_ptr(area, p1));
tt_assert(memarea_owns_ptr(area, p2));
+ /* Now test stats... */
+ size_t allocated = 0, used = 0;
+ memarea_get_stats(area, &allocated, &used);
+ tt_int_op(used, OP_LE, allocated);
+ tt_int_op(used, OP_GE, total); /* not EQ, because of alignment and headers*/
+ tt_int_op(allocated, OP_GT, allocation2);
+
+ tt_int_op(allocation2, OP_GT, initial_allocation);
+
+ memarea_clear(area);
+ memarea_get_stats(area, &allocated, &used);
+ tt_int_op(used, OP_LT, 128); /* Not 0, because of header */
+ tt_int_op(allocated, OP_EQ, initial_allocation);
+
done:
memarea_drop_all(area);
tor_free(malloced_ptr);
@@ -3244,6 +3826,21 @@ test_util_ftruncate(void *ptr)
tor_free(buf);
}
+static void
+test_util_num_cpus(void *arg)
+{
+ (void)arg;
+ int num = compute_num_cpus();
+ if (num < 0)
+ tt_skip();
+
+ tt_int_op(num, OP_GE, 1);
+ tt_int_op(num, OP_LE, 16);
+
+ done:
+ ;
+}
+
#ifdef _WIN32
static void
test_util_load_win_lib(void *ptr)
@@ -4209,6 +4806,31 @@ test_util_mathlog(void *arg)
}
static void
+test_util_fraction(void *arg)
+{
+ uint64_t a,b;
+ (void)arg;
+
+ a = 99; b = 30;
+ simplify_fraction64(&a,&b);
+ tt_u64_op(a, OP_EQ, 33);
+ tt_u64_op(b, OP_EQ, 10);
+
+ a = 3000000; b = 10000000;
+ simplify_fraction64(&a,&b);
+ tt_u64_op(a, OP_EQ, 3);
+ tt_u64_op(b, OP_EQ, 10);
+
+ a = 0; b = 15;
+ simplify_fraction64(&a,&b);
+ tt_u64_op(a, OP_EQ, 0);
+ tt_u64_op(b, OP_EQ, 1);
+
+ done:
+ ;
+}
+
+static void
test_util_round_to_next_multiple_of(void *arg)
{
(void)arg;
@@ -4223,21 +4845,6 @@ test_util_round_to_next_multiple_of(void *arg)
tt_u64_op(round_uint64_to_next_multiple_of(UINT64_MAX,2), ==,
UINT64_MAX);
- tt_i64_op(round_int64_to_next_multiple_of(0,1), ==, 0);
- tt_i64_op(round_int64_to_next_multiple_of(0,7), ==, 0);
-
- tt_i64_op(round_int64_to_next_multiple_of(99,1), ==, 99);
- tt_i64_op(round_int64_to_next_multiple_of(99,7), ==, 105);
- tt_i64_op(round_int64_to_next_multiple_of(99,9), ==, 99);
-
- tt_i64_op(round_int64_to_next_multiple_of(-99,1), ==, -99);
- tt_i64_op(round_int64_to_next_multiple_of(-99,7), ==, -98);
- tt_i64_op(round_int64_to_next_multiple_of(-99,9), ==, -99);
-
- tt_i64_op(round_int64_to_next_multiple_of(INT64_MIN,2), ==, INT64_MIN);
- tt_i64_op(round_int64_to_next_multiple_of(INT64_MAX,2), ==,
- INT64_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);
@@ -4407,7 +5014,7 @@ test_util_clamp_double_to_int64(void *arg)
{
(void)arg;
- tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY));
+ tt_i64_op(INT64_MIN, ==, clamp_double_to_int64(-INFINITY_DBL));
tt_i64_op(INT64_MIN, ==,
clamp_double_to_int64(-1.0 * pow(2.0, 64.0) - 1.0));
tt_i64_op(INT64_MIN, ==,
@@ -4420,7 +5027,7 @@ test_util_clamp_double_to_int64(void *arg)
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));
+ 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));
@@ -4432,7 +5039,7 @@ test_util_clamp_double_to_int64(void *arg)
clamp_double_to_int64(pow(2.0, 63.0)));
tt_i64_op(INT64_MAX, ==,
clamp_double_to_int64(pow(2.0, 64.0)));
- tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY));
+ tt_i64_op(INT64_MAX, ==, clamp_double_to_int64(INFINITY_DBL));
done:
;
@@ -4507,25 +5114,61 @@ test_util_socket(void *arg)
tt_int_op(fd_is_nonblocking(fd4), OP_EQ, 1);
#endif
- tor_close_socket(fd1);
- tor_close_socket(fd2);
+ tor_assert(tor_close_socket == tor_close_socket__real);
+
+ /* we use close_socket__real here so that coverity can tell that we are
+ * really closing these sockets. */
+ tor_close_socket__real(fd1);
+ tor_close_socket__real(fd2);
fd1 = fd2 = TOR_INVALID_SOCKET;
tt_int_op(get_n_open_sockets(), OP_EQ, n + 2);
- tor_close_socket(fd3);
- tor_close_socket(fd4);
+ tor_close_socket__real(fd3);
+ tor_close_socket__real(fd4);
fd3 = fd4 = TOR_INVALID_SOCKET;
tt_int_op(get_n_open_sockets(), OP_EQ, n);
done:
if (SOCKET_OK(fd1))
- tor_close_socket(fd1);
+ tor_close_socket__real(fd1);
if (SOCKET_OK(fd2))
- tor_close_socket(fd2);
+ tor_close_socket__real(fd2);
if (SOCKET_OK(fd3))
- tor_close_socket(fd3);
+ tor_close_socket__real(fd3);
if (SOCKET_OK(fd4))
- tor_close_socket(fd4);
+ tor_close_socket__real(fd4);
+}
+
+#if 0
+static int
+is_there_a_localhost(int family)
+{
+ tor_socket_t s;
+ s = tor_open_socket(family, SOCK_STREAM, IPPROTO_TCP);
+ tor_assert(SOCKET_OK(s));
+
+ int result = 0;
+ if (family == AF_INET) {
+ struct sockaddr_in s_in;
+ memset(&s_in, 0, sizeof(s_in));
+ s_in.sin_family = AF_INET;
+ s_in.sin_addr.s_addr = htonl(0x7f000001);
+ s_in.sin_port = 0;
+
+ if (bind(s, (void*)&s_in, sizeof(s_in)) == 0) {
+ result = 1;
+ }
+ } else if (family == AF_INET6) {
+ struct sockaddr_in6 sin6;
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr.s6_addr[15] = 1;
+ sin6.sin6_port = 0;
+ }
+ tor_close_socket(s);
+
+ return result;
}
+#endif
/* Test for socketpair and ersatz_socketpair(). We test them both, since
* the latter is a tolerably good way to exersize tor_accept_socket(). */
@@ -4541,15 +5184,18 @@ test_util_socketpair(void *arg)
int socketpair_result = 0;
socketpair_result = tor_socketpair_fn(family, SOCK_STREAM, 0, fds);
- /* If there is no 127.0.0.1 or ::1, tor_ersatz_socketpair will and must fail.
+
+#ifdef __FreeBSD__
+ /* If there is no 127.0.0.1, tor_ersatz_socketpair will and must fail.
* Otherwise, we risk exposing a socketpair on a routable IP address. (Some
* BSD jails use a routable address for localhost. Fortunately, they have
* the real AF_UNIX socketpair.) */
- if (ersatz && ERRNO_IS_EPROTO(-socketpair_result)) {
+ if (ersatz && socketpair_result < 0) {
/* In my testing, an IPv6-only FreeBSD jail without ::1 returned EINVAL.
* Assume we're on a machine without 127.0.0.1 or ::1 and give up now. */
- goto done;
+ tt_skip();
}
+#endif
tt_int_op(0, OP_EQ, socketpair_result);
tt_assert(SOCKET_OK(fds[0]));
@@ -4780,12 +5426,203 @@ test_util_pwdb(void *arg)
dir = get_user_homedir(name);
tt_assert(dir != NULL);
+ /* Try failing cases. First find a user that doesn't exist by name */
+ char randbytes[4];
+ char badname[9];
+ int i, found=0;
+ for (i = 0; i < 100; ++i) {
+ crypto_rand(randbytes, sizeof(randbytes));
+ base16_encode(badname, sizeof(badname), randbytes, sizeof(randbytes));
+ if (tor_getpwnam(badname) == NULL) {
+ found = 1;
+ break;
+ }
+ }
+ tt_assert(found);
+ tor_free(dir);
+
+ /* We should do a LOG_ERR */
+ setup_full_capture_of_logs(LOG_ERR);
+ dir = get_user_homedir(badname);
+ tt_assert(dir == NULL);
+ expect_log_msg_containing("not found");
+ tt_int_op(smartlist_len(mock_saved_logs()), OP_EQ, 1);
+ teardown_capture_of_logs();
+
+ /* Now try to find a user that doesn't exist by ID. */
+ found = 0;
+ for (i = 0; i < 1000; ++i) {
+ uid_t u;
+ crypto_rand((char*)&u, sizeof(u));
+ if (tor_getpwuid(u) == NULL) {
+ found = 1;
+ break;
+ }
+ }
+ tt_assert(found);
+
done:
tor_free(name);
tor_free(dir);
+ teardown_capture_of_logs();
}
#endif
+static void
+test_util_calloc_check(void *arg)
+{
+ (void) arg;
+ /* Easy cases that are good. */
+ tt_assert(size_mul_check__(0,0));
+ tt_assert(size_mul_check__(0,100));
+ tt_assert(size_mul_check__(100,0));
+ tt_assert(size_mul_check__(100,100));
+
+ /* Harder cases that are still good. */
+ tt_assert(size_mul_check__(SIZE_MAX, 1));
+ tt_assert(size_mul_check__(1, SIZE_MAX));
+ tt_assert(size_mul_check__(SIZE_MAX / 10, 9));
+ tt_assert(size_mul_check__(11, SIZE_MAX / 12));
+ const size_t sqrt_size_max_p1 = ((size_t)1) << (sizeof(size_t) * 4);
+ tt_assert(size_mul_check__(sqrt_size_max_p1, sqrt_size_max_p1 - 1));
+
+ /* Cases that overflow */
+ tt_assert(! size_mul_check__(SIZE_MAX, 2));
+ tt_assert(! size_mul_check__(2, SIZE_MAX));
+ tt_assert(! size_mul_check__(SIZE_MAX / 10, 11));
+ tt_assert(! size_mul_check__(11, SIZE_MAX / 10));
+ tt_assert(! size_mul_check__(SIZE_MAX / 8, 9));
+ tt_assert(! size_mul_check__(sqrt_size_max_p1, sqrt_size_max_p1));
+
+ done:
+ ;
+}
+
+static void
+test_util_monotonic_time(void *arg)
+{
+ (void)arg;
+
+ monotime_t mt1, mt2;
+ monotime_coarse_t mtc1, mtc2;
+ uint64_t nsec1, nsec2, usec1, msec1;
+ uint64_t nsecc1, nsecc2, usecc1, msecc1;
+
+ monotime_init();
+
+ monotime_get(&mt1);
+ monotime_coarse_get(&mtc1);
+ nsec1 = monotime_absolute_nsec();
+ usec1 = monotime_absolute_usec();
+ msec1 = monotime_absolute_msec();
+ nsecc1 = monotime_coarse_absolute_nsec();
+ usecc1 = monotime_coarse_absolute_usec();
+ msecc1 = monotime_coarse_absolute_msec();
+
+ tor_sleep_msec(200);
+
+ monotime_get(&mt2);
+ monotime_coarse_get(&mtc2);
+ nsec2 = monotime_absolute_nsec();
+ nsecc2 = monotime_coarse_absolute_nsec();
+
+ /* We need to be a little careful here since we don't know the system load.
+ */
+ tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_GE, 175);
+ tt_i64_op(monotime_diff_msec(&mt1, &mt2), OP_LT, 1000);
+ tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_GE, 125);
+ tt_i64_op(monotime_coarse_diff_msec(&mtc1, &mtc2), OP_LT, 1000);
+ tt_u64_op(nsec2-nsec1, OP_GE, 175000000);
+ tt_u64_op(nsec2-nsec1, OP_LT, 1000000000);
+ tt_u64_op(nsecc2-nsecc1, OP_GE, 125000000);
+ tt_u64_op(nsecc2-nsecc1, OP_LT, 1000000000);
+
+ tt_u64_op(msec1, OP_GE, nsec1 / 1000000);
+ tt_u64_op(usec1, OP_GE, nsec1 / 1000);
+ tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000);
+ tt_u64_op(usecc1, OP_GE, nsecc1 / 1000);
+ tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1);
+ tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 1000);
+ tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1);
+ tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 1000);
+
+ done:
+ ;
+}
+
+static void
+test_util_monotonic_time_ratchet(void *arg)
+{
+ (void)arg;
+ monotime_init();
+ monotime_reset_ratchets_for_testing();
+
+ /* win32, performance counter ratchet. */
+ tt_i64_op(100, OP_EQ, ratchet_performance_counter(100));
+ tt_i64_op(101, OP_EQ, ratchet_performance_counter(101));
+ tt_i64_op(2000, OP_EQ, ratchet_performance_counter(2000));
+ tt_i64_op(2000, OP_EQ, ratchet_performance_counter(100));
+ tt_i64_op(2005, OP_EQ, ratchet_performance_counter(105));
+ tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1105));
+ tt_i64_op(3005, OP_EQ, ratchet_performance_counter(1000));
+ tt_i64_op(3010, OP_EQ, ratchet_performance_counter(1005));
+
+ /* win32, GetTickCounts32 ratchet-and-rollover-detector. */
+ const int64_t R = ((int64_t)1) << 32;
+ tt_i64_op(5, OP_EQ, ratchet_coarse_performance_counter(5));
+ tt_i64_op(1000, OP_EQ, ratchet_coarse_performance_counter(1000));
+ tt_i64_op(5+R, OP_EQ, ratchet_coarse_performance_counter(5));
+ tt_i64_op(10+R, OP_EQ, ratchet_coarse_performance_counter(10));
+ tt_i64_op(4+R*2, OP_EQ, ratchet_coarse_performance_counter(4));
+
+ /* gettimeofday regular ratchet. */
+ struct timeval tv_in = {0,0}, tv_out;
+ tv_in.tv_usec = 9000;
+
+ ratchet_timeval(&tv_in, &tv_out);
+ tt_int_op(tv_out.tv_usec, OP_EQ, 9000);
+ tt_i64_op(tv_out.tv_sec, OP_EQ, 0);
+
+ tv_in.tv_sec = 1337;
+ tv_in.tv_usec = 0;
+ ratchet_timeval(&tv_in, &tv_out);
+ tt_int_op(tv_out.tv_usec, OP_EQ, 0);
+ tt_i64_op(tv_out.tv_sec, OP_EQ, 1337);
+
+ tv_in.tv_sec = 1336;
+ tv_in.tv_usec = 500000;
+ ratchet_timeval(&tv_in, &tv_out);
+ tt_int_op(tv_out.tv_usec, OP_EQ, 0);
+ tt_i64_op(tv_out.tv_sec, OP_EQ, 1337);
+
+ tv_in.tv_sec = 1337;
+ tv_in.tv_usec = 0;
+ ratchet_timeval(&tv_in, &tv_out);
+ tt_int_op(tv_out.tv_usec, OP_EQ, 500000);
+ tt_i64_op(tv_out.tv_sec, OP_EQ, 1337);
+
+ tv_in.tv_sec = 1337;
+ tv_in.tv_usec = 600000;
+ ratchet_timeval(&tv_in, &tv_out);
+ tt_int_op(tv_out.tv_usec, OP_EQ, 100000);
+ tt_i64_op(tv_out.tv_sec, OP_EQ, 1338);
+
+ tv_in.tv_sec = 1000;
+ tv_in.tv_usec = 1000;
+ ratchet_timeval(&tv_in, &tv_out);
+ tt_int_op(tv_out.tv_usec, OP_EQ, 100000);
+ tt_i64_op(tv_out.tv_sec, OP_EQ, 1338);
+
+ tv_in.tv_sec = 2000;
+ tv_in.tv_usec = 2000;
+ ratchet_timeval(&tv_in, &tv_out);
+ tt_int_op(tv_out.tv_usec, OP_EQ, 101000);
+ tt_i64_op(tv_out.tv_sec, OP_EQ, 2338);
+
+ done:
+ ;
+}
+
#define UTIL_LEGACY(name) \
{ #name, test_util_ ## name , 0, NULL, NULL }
@@ -4809,17 +5646,20 @@ struct testcase_t util_tests[] = {
UTIL_LEGACY(config_line_quotes),
UTIL_LEGACY(config_line_comment_character),
UTIL_LEGACY(config_line_escaped_content),
+ UTIL_LEGACY(config_line_crlf),
UTIL_LEGACY_NO_WIN(expand_filename),
UTIL_LEGACY(escape_string_socks),
UTIL_LEGACY(string_is_key_value),
UTIL_LEGACY(strmisc),
+ UTIL_TEST(parse_integer, 0),
UTIL_LEGACY(pow2),
UTIL_LEGACY(gzip),
+ UTIL_TEST(gzip_compression_bomb, TT_FORK),
UTIL_LEGACY(datadir),
UTIL_LEGACY(memarea),
UTIL_LEGACY(control_formats),
UTIL_LEGACY(mmap),
- UTIL_LEGACY(sscanf),
+ UTIL_TEST(sscanf, TT_FORK),
UTIL_LEGACY(format_time_interval),
UTIL_LEGACY(path_is_relative),
UTIL_LEGACY(strtok),
@@ -4834,6 +5674,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(listdir, 0),
UTIL_TEST(parent_dir, 0),
UTIL_TEST(ftruncate, 0),
+ UTIL_TEST(num_cpus, 0),
UTIL_TEST_WIN_ONLY(load_win_lib, 0),
UTIL_TEST_NO_WIN(exit_status, 0),
UTIL_TEST_NO_WIN(fgets_eagain, 0),
@@ -4855,6 +5696,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(read_file_eof_zero_bytes, 0),
UTIL_TEST(write_chunks_to_file, 0),
UTIL_TEST(mathlog, 0),
+ UTIL_TEST(fraction, 0),
UTIL_TEST(weak_random, 0),
{ "socket_ipv4", test_util_socket, TT_FORK, &passthrough_setup,
(void*)"4" },
@@ -4871,6 +5713,9 @@ struct testcase_t util_tests[] = {
UTIL_TEST(get_avail_disk_space, 0),
UTIL_TEST(touch_file, 0),
UTIL_TEST_NO_WIN(pwdb, TT_FORK),
+ UTIL_TEST(calloc_check, 0),
+ UTIL_TEST(monotonic_time, 0),
+ UTIL_TEST(monotonic_time_ratchet, TT_FORK),
END_OF_TESTCASES
};
diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c
index 3d02930983..63a668238c 100644
--- a/src/test/test_util_format.c
+++ b/src/test/test_util_format.c
@@ -263,14 +263,14 @@ test_util_format_base16_decode(void *ignored)
res = base16_decode(dst, 1, src, 10);
tt_int_op(res, OP_EQ, -1);
- res = base16_decode(dst, SIZE_T_CEILING+2, src, 10);
+ res = base16_decode(dst, ((size_t)INT_MAX)+1, src, 10);
tt_int_op(res, OP_EQ, -1);
res = base16_decode(dst, 1000, "", 0);
tt_int_op(res, OP_EQ, 0);
res = base16_decode(dst, 1000, "aabc", 4);
- tt_int_op(res, OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 2);
tt_mem_op(dst, OP_EQ, "\xaa\xbc", 2);
res = base16_decode(dst, 1000, "aabcd", 6);
@@ -280,7 +280,7 @@ test_util_format_base16_decode(void *ignored)
tt_int_op(res, OP_EQ, -1);
res = base16_decode(real_dst, 10, real_src, 14);
- tt_int_op(res, OP_EQ, 0);
+ tt_int_op(res, OP_EQ, 7);
tt_mem_op(real_dst, OP_EQ, expected, 7);
done:
@@ -289,6 +289,95 @@ test_util_format_base16_decode(void *ignored)
tor_free(real_dst);
}
+static void
+test_util_format_base32_encode(void *arg)
+{
+ (void) arg;
+ size_t real_dstlen = 32;
+ char *dst = tor_malloc_zero(real_dstlen);
+
+ /* Basic use case that doesn't require a source length correction. */
+ {
+ /* Length of 10 bytes. */
+ const char *src = "blahbleh12";
+ size_t srclen = strlen(src);
+ /* Expected result encoded base32. This was created using python as
+ * such (and same goes for all test case.):
+ *
+ * b = bytes("blahbleh12", 'utf-8')
+ * base64.b32encode(b)
+ * (result in lower case)
+ */
+ const char *expected = "mjwgc2dcnrswqmjs";
+
+ base32_encode(dst, base32_encoded_size(srclen), src, srclen);
+ tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+ /* Encode but to a larger size destination. */
+ memset(dst, 0, real_dstlen);
+ base32_encode(dst, real_dstlen, src, srclen);
+ tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+ }
+
+ /* Non multiple of 5 for the source buffer length. */
+ {
+ /* Length of 8 bytes. */
+ const char *expected = "mjwgc2dcnrswq";
+ const char *src = "blahbleh";
+ size_t srclen = strlen(src);
+
+ memset(dst, 0, real_dstlen);
+ base32_encode(dst, base32_encoded_size(srclen), src, srclen);
+ tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+ }
+
+ done:
+ tor_free(dst);
+}
+
+static void
+test_util_format_base32_decode(void *arg)
+{
+ (void) arg;
+ int ret;
+ size_t real_dstlen = 32;
+ char *dst = tor_malloc_zero(real_dstlen);
+
+ /* Basic use case. */
+ {
+ /* Length of 10 bytes. */
+ const char *expected = "blahbleh12";
+ /* Expected result encoded base32. */
+ const char *src = "mjwgc2dcnrswqmjs";
+
+ ret = base32_decode(dst, strlen(expected), src, strlen(src));
+ tt_int_op(ret, ==, 0);
+ tt_str_op(expected, OP_EQ, dst);
+ }
+
+ /* Non multiple of 5 for the source buffer length. */
+ {
+ /* Length of 8 bytes. */
+ const char *expected = "blahbleh";
+ const char *src = "mjwgc2dcnrswq";
+
+ ret = base32_decode(dst, strlen(expected), src, strlen(src));
+ tt_int_op(ret, ==, 0);
+ tt_mem_op(expected, OP_EQ, dst, strlen(expected));
+ }
+
+ /* Invalid values. */
+ {
+ /* Invalid character '#'. */
+ ret = base32_decode(dst, real_dstlen, "#abcde", 6);
+ tt_int_op(ret, ==, -1);
+ /* Make sure the destination buffer has been zeroed even on error. */
+ tt_int_op(tor_mem_is_zero(dst, real_dstlen), ==, 1);
+ }
+
+ done:
+ tor_free(dst);
+}
+
struct testcase_t util_format_tests[] = {
{ "unaligned_accessors", test_util_format_unaligned_accessors, 0,
NULL, NULL },
@@ -297,6 +386,10 @@ struct testcase_t util_format_tests[] = {
NULL, NULL },
{ "base64_decode", test_util_format_base64_decode, 0, NULL, NULL },
{ "base16_decode", test_util_format_base16_decode, 0, NULL, NULL },
+ { "base32_encode", test_util_format_base32_encode, 0,
+ NULL, NULL },
+ { "base32_decode", test_util_format_base32_decode, 0,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_util_process.c b/src/test/test_util_process.c
index 45c22ef47f..4e75b97f3d 100644
--- a/src/test/test_util_process.c
+++ b/src/test/test_util_process.c
@@ -26,7 +26,7 @@ test_util_process_set_waitpid_callback(void *ignored)
{
(void)ignored;
waitpid_callback_t *res1 = NULL, *res2 = NULL;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_full_capture_of_logs(LOG_WARN);
pid_t pid = (pid_t)42;
res1 = set_waitpid_callback(pid, temp_callback, NULL);
@@ -34,11 +34,12 @@ test_util_process_set_waitpid_callback(void *ignored)
res2 = set_waitpid_callback(pid, temp_callback, NULL);
tt_assert(res2);
- expect_log_msg("Replaced a waitpid monitor on pid 42. That should be "
+ expect_single_log_msg(
+ "Replaced a waitpid monitor on pid 42. That should be "
"impossible.\n");
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
clear_waitpid_callback(res1);
clear_waitpid_callback(res2);
}
@@ -48,7 +49,7 @@ test_util_process_clear_waitpid_callback(void *ignored)
{
(void)ignored;
waitpid_callback_t *res;
- int previous_log = setup_capture_of_logs(LOG_WARN);
+ setup_capture_of_logs(LOG_WARN);
pid_t pid = (pid_t)43;
clear_waitpid_callback(NULL);
@@ -64,7 +65,7 @@ test_util_process_clear_waitpid_callback(void *ignored)
#endif
done:
- teardown_capture_of_logs(previous_log);
+ teardown_capture_of_logs();
}
#endif /* _WIN32 */
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index cbcf596b22..ccb8d0c8ca 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -12,11 +12,7 @@
#include "compat_libevent.h"
#include <stdio.h>
-#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
-#else
-#include <event.h>
-#endif
#define MAX_INFLIGHT (1<<16)
@@ -400,6 +396,9 @@ main(int argc, char **argv)
}
rq = replyqueue_new(as_flags);
+ if (as_flags && rq == NULL)
+ return 77; // 77 means "skipped".
+
tor_assert(rq);
tp = threadpool_new(opt_n_threads,
rq, new_state, free_state, NULL);
diff --git a/src/test/test_workqueue_cancel.sh b/src/test/test_workqueue_cancel.sh
new file mode 100755
index 0000000000..f7c663171e
--- /dev/null
+++ b/src/test/test_workqueue_cancel.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+${builddir:-.}/src/test/test_workqueue -C 1
+
diff --git a/src/test/test_workqueue_efd.sh b/src/test/test_workqueue_efd.sh
new file mode 100755
index 0000000000..4d89396819
--- /dev/null
+++ b/src/test/test_workqueue_efd.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+${builddir:-.}/src/test/test_workqueue \
+ --no-eventfd2 --no-pipe2 --no-pipe --no-socketpair
diff --git a/src/test/test_workqueue_efd2.sh b/src/test/test_workqueue_efd2.sh
new file mode 100755
index 0000000000..7cfff45ff3
--- /dev/null
+++ b/src/test/test_workqueue_efd2.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+${builddir:-.}/src/test/test_workqueue \
+ --no-eventfd --no-pipe2 --no-pipe --no-socketpair
diff --git a/src/test/test_workqueue_pipe.sh b/src/test/test_workqueue_pipe.sh
new file mode 100755
index 0000000000..afcef87853
--- /dev/null
+++ b/src/test/test_workqueue_pipe.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+${builddir:-.}/src/test/test_workqueue \
+ --no-eventfd2 --no-eventfd --no-pipe2 --no-socketpair
diff --git a/src/test/test_workqueue_pipe2.sh b/src/test/test_workqueue_pipe2.sh
new file mode 100755
index 0000000000..a20a1427e0
--- /dev/null
+++ b/src/test/test_workqueue_pipe2.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+${builddir:-.}/src/test/test_workqueue \
+ --no-eventfd2 --no-eventfd --no-pipe --no-socketpair
diff --git a/src/test/test_workqueue_socketpair.sh b/src/test/test_workqueue_socketpair.sh
new file mode 100755
index 0000000000..76af79746d
--- /dev/null
+++ b/src/test/test_workqueue_socketpair.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+${builddir:-.}/src/test/test_workqueue \
+ --no-eventfd2 --no-eventfd --no-pipe2 --no-pipe
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 39c3d02ab1..9c6580f788 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -3,6 +3,8 @@
* Copyright (c) 2007-2016, The Tor Project, Inc. */
/* See LICENSE for licensing information */
+extern const char tor_git_revision[];
+
/* Ordinarily defined in tor_main.c; this bit is just here to provide one
* since we're not linking to tor_main.c */
const char tor_git_revision[] = "";
@@ -100,18 +102,41 @@ setup_directory(void)
temp_dir_setup_in_pid = getpid();
}
-/** Return a filename relative to our testing temporary directory */
-const char *
-get_fname(const char *name)
+/** Return a filename relative to our testing temporary directory, based on
+ * name and suffix. If name is NULL, return the name of the testing temporary
+ * directory. */
+static const char *
+get_fname_suffix(const char *name, const char *suffix)
{
static char buf[1024];
setup_directory();
if (!name)
return temp_dir;
- tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name);
+ tor_snprintf(buf,sizeof(buf),"%s/%s%s%s",temp_dir,name,suffix ? "_" : "",
+ suffix ? suffix : "");
return buf;
}
+/** Return a filename relative to our testing temporary directory. If name is
+ * NULL, return the name of the testing temporary directory. */
+const char *
+get_fname(const char *name)
+{
+ return get_fname_suffix(name, NULL);
+}
+
+/** Return a filename with a random suffix, relative to our testing temporary
+ * directory. If name is NULL, return the name of the testing temporary
+ * directory, without any suffix. */
+const char *
+get_fname_rnd(const char *name)
+{
+ char rnd[256], rnd32[256];
+ crypto_rand(rnd, RAND_PATH_BYTES);
+ base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES);
+ return get_fname_suffix(name, rnd32);
+}
+
/* Remove a directory and all of its subdirectories */
static void
rm_rf(const char *dir)
@@ -154,37 +179,51 @@ remove_directory(void)
}
/** Define this if unit tests spend too much time generating public keys*/
-#undef CACHE_GENERATED_KEYS
+#define CACHE_GENERATED_KEYS
-static crypto_pk_t *pregen_keys[5] = {NULL, NULL, NULL, NULL, NULL};
-#define N_PREGEN_KEYS ARRAY_LENGTH(pregen_keys)
+#define N_PREGEN_KEYS 11
+static crypto_pk_t *pregen_keys[N_PREGEN_KEYS];
+static int next_key_idx;
/** Generate and return a new keypair for use in unit tests. If we're using
- * the key cache optimization, we might reuse keys: we only guarantee that
- * keys made with distinct values for <b>idx</b> are different. The value of
- * <b>idx</b> must be at least 0, and less than N_PREGEN_KEYS. */
+ * the key cache optimization, we might reuse keys. "idx" is ignored.
+ * Our only guarantee is that we won't reuse a key till this function has been
+ * called several times. The order in which keys are returned is slightly
+ * randomized, so that tests that depend on a particular order will not be
+ * reliable. */
crypto_pk_t *
pk_generate(int idx)
{
- int res;
+ (void) idx;
#ifdef CACHE_GENERATED_KEYS
- tor_assert(idx < N_PREGEN_KEYS);
- if (! pregen_keys[idx]) {
- pregen_keys[idx] = crypto_pk_new();
- res = crypto_pk_generate_key(pregen_keys[idx]);
- tor_assert(!res);
- }
- return crypto_pk_dup_key(pregen_keys[idx]);
+ /* Either skip 1 or 2 keys. */
+ next_key_idx += crypto_rand_int_range(1,3);
+ next_key_idx %= N_PREGEN_KEYS;
+ return crypto_pk_dup_key(pregen_keys[next_key_idx]);
#else
crypto_pk_t *result;
- (void) idx;
+ int res;
result = crypto_pk_new();
- res = crypto_pk_generate_key(result);
+ res = crypto_pk_generate_key__real(result);
tor_assert(!res);
return result;
#endif
}
+#ifdef CACHE_GENERATED_KEYS
+static int
+crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits)
+{
+ if (bits != 1024)
+ return crypto_pk_generate_key_with_bits__real(env, bits);
+
+ crypto_pk_t *newkey = pk_generate(0);
+ crypto_pk_assign_(env, newkey);
+ crypto_pk_free(newkey);
+ return 0;
+}
+#endif
+
/** Free all storage used for the cached key optimization. */
static void
free_pregenerated_keys(void)
@@ -201,6 +240,9 @@ free_pregenerated_keys(void)
static void *
passthrough_test_setup(const struct testcase_t *testcase)
{
+ /* Make sure the passthrough doesn't unintentionally fail or skip tests */
+ tor_assert(testcase->setup_data);
+ tor_assert(testcase->setup_data != (void*)TT_SKIP);
return testcase->setup_data;
}
static int
@@ -211,11 +253,33 @@ passthrough_test_cleanup(const struct testcase_t *testcase, void *ptr)
return 1;
}
+static void *
+ed25519_testcase_setup(const struct testcase_t *testcase)
+{
+ crypto_ed25519_testing_force_impl(testcase->setup_data);
+ return testcase->setup_data;
+}
+static int
+ed25519_testcase_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+ (void)testcase;
+ (void)ptr;
+ crypto_ed25519_testing_restore_impl();
+ return 1;
+}
+const struct testcase_setup_t ed25519_test_setup = {
+ ed25519_testcase_setup, ed25519_testcase_cleanup
+};
+
const struct testcase_setup_t passthrough_setup = {
passthrough_test_setup, passthrough_test_cleanup
};
-extern struct testgroup_t testgroups[];
+static void
+an_assertion_failed(void)
+{
+ tinytest_set_test_failed_();
+}
/** Main entry point for unit test code: parse the command line, and run
* some unit tests. */
@@ -244,6 +308,8 @@ main(int c, const char **v)
network_init();
+ monotime_init();
+
struct tor_libevent_cfg cfg;
memset(&cfg, 0, sizeof(cfg));
tor_libevent_initialize(&cfg);
@@ -272,6 +338,8 @@ main(int c, const char **v)
log_severity_list_t s;
memset(&s, 0, sizeof(s));
set_log_severity_config(loglevel, LOG_ERR, &s);
+ /* ALWAYS log bug warnings. */
+ s.masks[LOG_WARN-LOG_ERR] |= LD_BUG;
add_stream_log(&s, "", fileno(stdout));
}
@@ -295,6 +363,17 @@ main(int c, const char **v)
tor_free(errmsg);
return 1;
}
+ tor_set_failed_assertion_callback(an_assertion_failed);
+
+#ifdef CACHE_GENERATED_KEYS
+ for (i = 0; i < N_PREGEN_KEYS; ++i) {
+ pregen_keys[i] = crypto_pk_new();
+ int r = crypto_pk_generate_key(pregen_keys[i]);
+ tor_assert(r == 0);
+ }
+ MOCK(crypto_pk_generate_key_with_bits,
+ crypto_pk_generate_key_with_bits__get_cached);
+#endif
atexit(remove_directory);
diff --git a/src/test/vote_descriptors.inc b/src/test/vote_descriptors.inc
index c5ce21f744..895dc6c65c 100644
--- a/src/test/vote_descriptors.inc
+++ b/src/test/vote_descriptors.inc
@@ -1,4 +1,4 @@
-const char* VOTE_BODY_V3 =
+static const char* VOTE_BODY_V3 =
"network-status-version 3\n"
"vote-status vote\n"
"consensus-methods 13 14 15 16 17 18 19 20 21\n"
diff --git a/src/tools/include.am b/src/tools/include.am
index 38ed57546f..d0185b5887 100644
--- a/src/tools/include.am
+++ b/src/tools/include.am
@@ -7,23 +7,27 @@ endif
src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c
src_tools_tor_resolve_LDFLAGS =
-src_tools_tor_resolve_LDADD = src/common/libor.a @TOR_LIB_MATH@ @TOR_LIB_WS32@
+src_tools_tor_resolve_LDADD = src/common/libor.a \
+ src/common/libor-ctime.a \
+ @TOR_LIB_MATH@ @TOR_LIB_WS32@
if COVERAGE_ENABLED
src_tools_tor_cov_resolve_SOURCES = src/tools/tor-resolve.c
src_tools_tor_cov_resolve_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_tools_tor_cov_resolve_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_tools_tor_cov_resolve_LDADD = src/common/libor-testing.a \
- @TOR_LIB_MATH@ @TOR_LIB_WS32@
+ src/common/libor-ctime-testing.a \
+ @TOR_LIB_MATH@ @TOR_LIB_WS32@
endif
src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c
src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_tools_tor_gencert_LDADD = src/common/libor.a src/common/libor-crypto.a \
+ src/common/libor-ctime.a \
$(LIBKECCAK_TINY) \
$(LIBDONNA) \
- @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
- @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
if COVERAGE_ENABLED
src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c
@@ -32,18 +36,21 @@ src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \
src/common/libor-crypto-testing.a \
+ src/common/libor-ctime-testing.a \
$(LIBKECCAK_TINY) \
$(LIBDONNA) \
- @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
- @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
endif
src_tools_tor_checkkey_SOURCES = src/tools/tor-checkkey.c
src_tools_tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
-src_tools_tor_checkkey_LDADD = src/common/libor.a src/common/libor-crypto.a \
+src_tools_tor_checkkey_LDADD = src/common/libor.a \
+ src/common/libor-ctime.a \
+ src/common/libor-crypto.a \
$(LIBKECCAK_TINY) \
$(LIBDONNA) \
- @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
- @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
EXTRA_DIST += src/tools/tor-fw-helper/README
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index ed6c0667a1..db308485e6 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -13,19 +13,12 @@
#include <unistd.h>
#endif
-#ifdef __GNUC__
-#define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
-#endif
+#include "compat.h"
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic push
-#endif
/* Some versions of OpenSSL declare X509_STORE_CTX_set_verify_cb twice in
* x509.h and x509_vfy.h. Suppress the GCC warning so we can build with
* -Wredundant-decl. */
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#endif
+DISABLE_GCC_WARNING(redundant-decls)
#include <openssl/evp.h>
#include <openssl/pem.h>
@@ -34,13 +27,7 @@
#include <openssl/obj_mac.h>
#include <openssl/err.h>
-#if __GNUC__ && GCC_VERSION >= 402
-#if GCC_VERSION >= 406
-#pragma GCC diagnostic pop
-#else
-#pragma GCC diagnostic warning "-Wredundant-decls"
-#endif
-#endif
+ENABLE_GCC_WARNING(redundant-decls)
#include <errno.h>
#if 0
@@ -61,21 +48,21 @@
#define DEFAULT_LIFETIME 12
/* These globals are set via command line options. */
-char *identity_key_file = NULL;
-char *signing_key_file = NULL;
-char *certificate_file = NULL;
-int reuse_signing_key = 0;
-int verbose = 0;
-int make_new_id = 0;
-int months_lifetime = DEFAULT_LIFETIME;
-int passphrase_fd = -1;
-char *address = NULL;
-
-char *passphrase = NULL;
-size_t passphrase_len = 0;
-
-EVP_PKEY *identity_key = NULL;
-EVP_PKEY *signing_key = NULL;
+static char *identity_key_file = NULL;
+static char *signing_key_file = NULL;
+static char *certificate_file = NULL;
+static int reuse_signing_key = 0;
+static int verbose = 0;
+static int make_new_id = 0;
+static int months_lifetime = DEFAULT_LIFETIME;
+static int passphrase_fd = -1;
+static char *address = NULL;
+
+static char *passphrase = NULL;
+static size_t passphrase_len = 0;
+
+static EVP_PKEY *identity_key = NULL;
+static EVP_PKEY *signing_key = NULL;
/** Write a usage message for tor-gencert to stderr. */
static void
diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c
index 29f85c4d17..6ac866d3c0 100644
--- a/src/tools/tor-resolve.c
+++ b/src/tools/tor-resolve.c
@@ -80,6 +80,10 @@ build_socks_resolve_request(char **out,
}
ipv6 = reverse && tor_addr_family(&addr) == AF_INET6;
addrlen = reverse ? (ipv6 ? 16 : 4) : 1 + strlen(hostname);
+ if (addrlen > UINT8_MAX) {
+ log_err(LD_GENERAL, "Hostname is too long!");
+ return -1;
+ }
len = 6 + addrlen;
*out = tor_malloc(len);
(*out)[0] = 5; /* SOCKS version 5 */
diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c
index f495743667..24988d510b 100644
--- a/src/trunnel/ed25519_cert.c
+++ b/src/trunnel/ed25519_cert.c
@@ -1,4 +1,4 @@
-/* ed25519_cert.c -- generated by Trunnel v1.4.4.
+/* ed25519_cert.c -- generated by Trunnel v1.4.6.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -157,7 +157,7 @@ ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t
&inp->un_unparsed.n_, inp->un_unparsed.elts_, newlen,
sizeof(inp->un_unparsed.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->un_unparsed.elts_ = newptr;
return 0;
@@ -589,7 +589,7 @@ ed25519_cert_setlen_ext(ed25519_cert_t *inp, size_t newlen)
&inp->ext.n_, inp->ext.elts_, newlen,
sizeof(inp->ext.elts_[0]), (trunnel_free_fn_t) ed25519_cert_extension_free,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->ext.elts_ = newptr;
return 0;
diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h
index 75a82d8aff..28f6feef31 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.4.4.
+/* ed25519_cert.h -- generated by by Trunnel v1.4.6.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c
index 3ef7341ae9..c2717f36bf 100644
--- a/src/trunnel/link_handshake.c
+++ b/src/trunnel/link_handshake.c
@@ -1,4 +1,4 @@
-/* link_handshake.c -- generated by Trunnel v1.4.4.
+/* link_handshake.c -- generated by Trunnel v1.4.6.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -143,7 +143,7 @@ auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen)
&inp->methods.n_, inp->methods.elts_, newlen,
sizeof(inp->methods.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->methods.elts_ = newptr;
return 0;
@@ -452,7 +452,7 @@ certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen)
&inp->body.n_, inp->body.elts_, newlen,
sizeof(inp->body.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->body.elts_ = newptr;
return 0;
@@ -747,7 +747,7 @@ rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_t *inp, size_t newlen)
&inp->sig.n_, inp->sig.elts_, newlen,
sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->sig.elts_ = newptr;
return 0;
@@ -1268,7 +1268,7 @@ auth1_setlen_sig(auth1_t *inp, size_t newlen)
&inp->sig.n_, inp->sig.elts_, newlen,
sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->sig.elts_ = newptr;
return 0;
@@ -1714,7 +1714,7 @@ certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen)
&inp->certs.n_, inp->certs.elts_, newlen,
sizeof(inp->certs.elts_[0]), (trunnel_free_fn_t) certs_cell_cert_free,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->certs.elts_ = newptr;
return 0;
diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h
index 2749ec7dd4..54611b96e8 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.4.4.
+/* link_handshake.h -- generated by by Trunnel v1.4.6.
* 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 9b348a9b30..62662a9369 100644
--- a/src/trunnel/pwbox.c
+++ b/src/trunnel/pwbox.c
@@ -1,4 +1,4 @@
-/* pwbox.c -- generated by Trunnel v1.4.4.
+/* pwbox.c -- generated by Trunnel v1.4.6.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
@@ -151,7 +151,7 @@ pwbox_encoded_setlen_skey_header(pwbox_encoded_t *inp, size_t newlen)
&inp->skey_header.n_, inp->skey_header.elts_, newlen,
sizeof(inp->skey_header.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->skey_header.elts_ = newptr;
return 0;
@@ -226,7 +226,7 @@ pwbox_encoded_setlen_data(pwbox_encoded_t *inp, size_t newlen)
&inp->data.n_, inp->data.elts_, newlen,
sizeof(inp->data.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
- if (newptr == NULL)
+ if (newlen != 0 && newptr == NULL)
goto trunnel_alloc_failed;
inp->data.elts_ = newptr;
return 0;
diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h
index e69e2c1a0e..77a813d123 100644
--- a/src/trunnel/pwbox.h
+++ b/src/trunnel/pwbox.h
@@ -1,4 +1,4 @@
-/* pwbox.h -- generated by by Trunnel v1.4.4.
+/* pwbox.h -- generated by by Trunnel v1.4.6.
* 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 b0292b111a..bfe0262968 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -17,15 +17,6 @@
/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H
-/* Define to 1 if you have the `event_get_method' function. */
-#define HAVE_EVENT_GET_METHOD 1
-
-/* Define to 1 if you have the `event_get_version' function. */
-#define HAVE_EVENT_GET_VERSION 1
-
-/* Define to 1 if you have the `event_set_log_callback' function. */
-#define HAVE_EVENT_SET_LOG_CALLBACK 1
-
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H
@@ -223,13 +214,11 @@
/* Define to 1 iff unaligned int access is allowed */
#define UNALIGNED_INT_ACCESS_OK
-#define HAVE_EVENT_H
-
/* Define to 1 iff we represent negative integers with two's complement */
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.2.8.17-dev"
+#define VERSION "0.2.9.14-dev"
@@ -237,10 +226,7 @@
#define HAVE_STRUCT_IN6_ADDR
#define RSHIFT_DOES_SIGN_EXTEND
#define FLEXIBLE_ARRAY_MEMBER 0
-#define HAVE_EVENT2_EVENT_H
#define SHARE_DATADIR ""
-#define HAVE_EVENT2_DNS_H
-#define HAVE_EVENT_BASE_LOOPEXIT
#define USE_CURVE25519_DONNA
#define ENUM_VALS_ARE_SIGNED 1